I run this official Java example in Eclipse, with jeromq-0.3.2.jar library, it doesn't work if I "run" it, it only works if I set some break point and "debug" it.
It seems the message is lost. My own application using route-req pattern has this problem too.
This is their official example, if this doesn't work, what can? can someone try it and figure out why?
http://zguide.zeromq.org/java:rtreq
or codes are here:
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;
import java.util.Random;
/**
* ROUTER-TO-REQ example
*/
public class rtreq
{
private static Random rand = new Random();
private static final int NBR_WORKERS = 10;
private static class Worker extends Thread {
#Override
public void run() {
Context context = ZMQ.context(1);
Socket worker = context.socket(ZMQ.REQ);
ZHelper.setId (worker); // Set a printable identity
worker.connect("tcp://localhost:5671");
int total = 0;
while (true) {
// Tell the broker we're ready for work
worker.send ("Hi Boss");
// Get workload from broker, until finished
String workload = worker.recvStr ();
boolean finished = workload.equals ("Fired!");
if (finished) {
System.out.printf ("Completed: %d tasks\n", total);
break;
}
total++;
// Do some random work
try {
Thread.sleep (rand.nextInt (500) + 1);
} catch (InterruptedException e) {
}
}
worker.close();
context.term();
}
}
/**
* While this example runs in a single process, that is just to make
* it easier to start and stop the example. Each thread has its own
* context and conceptually acts as a separate process.
*/
public static void main (String[] args) throws Exception {
Context context = ZMQ.context(1);
Socket broker = context.socket(ZMQ.ROUTER);
broker.bind("tcp://*:5671");
for (int workerNbr = 0; workerNbr < NBR_WORKERS; workerNbr++)
{
Thread worker = new Worker ();
worker.start ();
}
// Run for five seconds and then tell workers to end
long endTime = System.currentTimeMillis () + 5000;
int workersFired = 0;
while (true) {
// Next message gives us least recently used worker
String identity = broker.recvStr ();
broker.sendMore (identity);
broker.recvStr (); // Envelope delimiter
broker.recvStr (); // Response from worker
broker.sendMore ("");
// Encourage workers until it's time to fire them
if (System.currentTimeMillis () < endTime)
broker.send ("Work harder");
else {
broker.send ("Fired!");
if (++workersFired == NBR_WORKERS)
break;
}
}
broker.close();
context.term();
}
}
To make this example works, you need to be sure that each worker has unique id.
As an example, I removed
ZHelper.setId (worker); // Set a printable identity
and add a constructor to Worker class with workerId string.
Result:
import org.zeromq.ZMQ;
import java.util.Random;
/**
* ROUTER-TO-REQ example
*/
public class rtreq {
private static Random rand = new Random();
private static final int NBR_WORKERS = 10;
private static class Worker extends Thread {
private String workerId;
Worker(String workerId) {
this.workerId = workerId;
}
#Override
public void run() {
ZMQ.Context context = ZMQ.context(1);
ZMQ.Socket worker = context.socket(ZMQ.REQ);
worker.setIdentity(workerId.getBytes());
worker.connect("tcp://localhost:5671");
int total = 0;
while (true) {
// Tell the broker we're ready for work
worker.send("Hi Boss");
// Get workload from broker, until finished
String workload = worker.recvStr();
boolean finished = workload.equals("Fired!");
if (finished) {
System.out.printf(workerId + " completed: %d tasks\n", total);
break;
}
total++;
// Do some random work
try {
Thread.sleep(rand.nextInt(500) + 1);
} catch (InterruptedException e) {
}
}
worker.close();
context.term();
}
}
/**
* While this example runs in a single process, that is just to make
* it easier to start and stop the example. Each thread has its own
* context and conceptually acts as a separate process.
*/
public static void main(String[] args) throws Exception {
ZMQ.Context context = ZMQ.context(1);
ZMQ.Socket broker = context.socket(ZMQ.ROUTER);
broker.bind("tcp://*:5671");
// starting all workers
for (int workerNbr = 0; workerNbr < NBR_WORKERS; workerNbr++) {
Thread worker = new Worker("worker-" + workerNbr);
worker.start();
}
// Run for five seconds and then tell workers to end
long endTime = System.currentTimeMillis() + 5000;
int workersFired = 0;
while (true) {
// Next message gives us least recently used worker
String identity = broker.recvStr();
broker.sendMore(identity);
broker.recvStr(); // Envelope delimiter
broker.recvStr(); // Response from worker
broker.sendMore("");
// Encourage workers until it's time to fire them
if (System.currentTimeMillis() < endTime)
broker.send("Work harder");
else {
broker.send("Fired!");
if (++workersFired == NBR_WORKERS)
break;
}
}
broker.close();
context.term();
}
}
System out:
worker-2 completed: 27 tasks
worker-3 completed: 20 tasks
worker-8 completed: 24 tasks
worker-6 completed: 23 tasks
worker-0 completed: 20 tasks
worker-9 completed: 21 tasks
worker-7 completed: 20 tasks
worker-5 completed: 21 tasks
worker-4 completed: 19 tasks
worker-1 completed: 25 tasks
Related
I Programmed an Discord Bot to make an Time Role (a Role that Changes its name every second to: "{UserName} --D --H --min --sec).
It worked Fine, but after an hour it does not worked anymore, but I did not change anything in the Code.
I have an Command ("{prefix} shutdown"), it basicly do ShardManager.shutdown(); and than I get an error.
Code for the Command:
package Discord.Commands.TimeRank;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
import Discord.Bot;
import Discord.Commands.Command;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.exceptions.RateLimitedException;
public class startTimeRole extends Command {
public static Server server;
public Timer timer;
public static boolean isRunning = false;
#Override
public void set(String prefix, String help) {
prefix = "timeRole";
help = Bot.prefix + " timeRole <STATE> -> STATE = start, pause";
super.set(prefix, help);
}
#Override
public void Execute(MessageReceivedEvent event, String[] args) {
super.Execute(event, args);
server = server.get(event.getGuild());
if (args.length <= 1) {
event.getTextChannel().sendMessage("Please give at least one **Argument**!").queue();
help(event);
return;
} else {
if (args[1].equals("start")) {
if (!isRunning) {
timer = new Timer();
event.getTextChannel().sendMessage("Activated timeRole!").queue();
isRunning = true;
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
for (Client client : server.onlineClients) {
Role timeRole = null;
for (Role role : client.member.getRoles()) {
if (timeRole != null) {
break;
}
if (role.getName().startsWith(client.member.getUser().getName())) {
client.onlineTime.setTime(role.getName().substring(client.member.getUser().getName().length() + 1));
client.onlineTime.plus(10);
timeRole = role;
}
}
if (timeRole != null) {
timeRole.getManager()
.setName(client.member.getUser().getName() + " " + client.onlineTime.get())
.queue();
} else {
try {
server.guild
.addRoleToMember(client.member,
server.guild.createRole()
.setName(client.member.getUser().getName() + " "
+ client.onlineTime.get())
.complete(true))
.queue();
} catch (RateLimitedException e) {
e.printStackTrace();
}
}
}
}
}, 0, 10000);
return;
} else {
event.getTextChannel().sendMessage("timeRole is Active!").queue();
help(event);
return;
}
} else if (args[1].equals("pause")) {
if (isRunning) {
timer.cancel();
event.getTextChannel().sendMessage("Paused timeRole").queue();
;
isRunning = false;
} else {
event.getTextChannel().sendMessage("timeRole is Paused!").queue();
help(event);
return;
}
return;
} else {
event.getTextChannel().sendMessage("Please give one **Argument**").queue();
help(event);
return;
}
}
}
}
Code for the "Server" Class:
package Discord.Commands.TimeRank;
import java.util.ArrayList;
import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
public class Server {
public ArrayList<Client> clients = new ArrayList<Client>();
public ArrayList<Client> onlineClients = new ArrayList<Client>();
public Guild guild;
private Server(Guild guild) {
this.guild = guild;
}
public static Server get(Guild guild) {
Server server = new Server(guild);
server.clients = server.getMembersAsClients();
server.onlineClients = server.getOnlineMembersAsClients();
return server;
}
public ArrayList<Client> getMembersAsClients() {
ArrayList<Client> clients = new ArrayList<Client>();
System.out.println(guild.getMembers().size());
for (Member member : guild.getMembers()) {
if (!member.getUser().isBot()) {
clients.add(new Client(member));
}
}
return clients;
}
public ArrayList<Client> getOnlineMembersAsClients() {
ArrayList<Client> onlineClients = new ArrayList<Client>();
for (Client client : clients) {
if (!client.member.getUser().isBot()) {
if (client.member.getOnlineStatus() == OnlineStatus.ONLINE) {
onlineClients.add(client);
}
}
}
return onlineClients;
}
}
and Time Class:
package Discord.Commands.TimeRank;
public class Time {
public int Days, Hours, Minutes, Seconds;
public Time() {
}
public String get() {
return Days + "D " + Hours + "H " + Minutes + "min " + Seconds + "sec ";
}
public void plus(int plus) {
Seconds += plus;
if (Seconds >= 60) {
Seconds = 0;
Minutes++;
if (Minutes >= 60) {
Minutes = 0;
Hours++;
if (Hours >= 24) {
Hours = 0;
Days++;
return;
}
}
}
}
public void setTime(String Time) {
String[] Time_ = Time.split(" ");
int Days = Integer.parseInt(Time_[0].split("D")[0]);
int Hours = Integer.parseInt(Time_[1].split("H")[0]);
int Minutes = Integer.parseInt(Time_[2].split("min")[0]);
int Seconds = Integer.parseInt(Time_[3].split("sec")[0]);
this.Days = Days;
this.Hours = Hours;
this.Minutes = Minutes;
this.Seconds = Seconds;
}
}
And the Error:
[DefaultShardManager] INFO RateLimiter - Waiting for 1 bucket(s) to finish. Average queue size of 2 requests
Exception in thread "Timer-0" java.util.concurrent.RejectedExecutionException: The Requester has been stopped! No new requests can be requested!
at net.dv8tion.jda.internal.requests.Requester.request(Requester.java:107)
at net.dv8tion.jda.internal.requests.RestActionImpl.queue(RestActionImpl.java:189)
at net.dv8tion.jda.internal.managers.ManagerBase.queue(ManagerBase.java:121)
at net.dv8tion.jda.api.requests.RestAction.queue(RestAction.java:411)
at net.dv8tion.jda.api.requests.RestAction.queue(RestAction.java:377)
at Discord.Commands.TimeRank.startTimeRole$1.run(startTimeRole.java:67)
at java.util.TimerThread.mainLoop(Unknown Source)
at java.util.TimerThread.run(Unknown Source)
Sorry for that much Code but I just thoght that you could need it to know what, what is.
If you have some questions Just ask them. :D
You are being rate limited. Different requests to the api have different limits on how often you are allowed to perform them. When spamming a request you can easily hit such a limit.
Switching the bot works because every new bot starts at zero until you hit the limit again.
My advice is to decrease the number of requests. Trying to circumvent the limit it therefore abusing the api is against TOS.
I have a gameObject with an ID, the gameObjects are spawned by giving initial ID: 1 , then any after spawned will be +1 so next is ID: 2.
I have two buttons that check current gameObjects ID#, BackOneButton (-1) and PlusOneButton (+1).
Currently it works but only if the array of gameObjects have IDs in order like for example [gameObject-ID:1], [gameObject-ID:2], [gameObject-ID:3]
But since you can self destruct a certain gameObject, here is where the error is --->
Now the array is not in order for example [gameObject-ID:1], [gameObject-ID:3], [gameObject-ID:4]. So if I'm currently in [gameObject-ID:3] and I use the BackOneButton and looks for ID: 2 it won't find it BUT there is ID:1. That's my error, I can't seem to figure out how to handle this.
Basically, How do I handle missing increments and skip over the missing?
Left Button (MinusOneButton)
void ButtonAction_LeftMinusOne()
{
// Get list of all gameObjects and -1 current to switch
string objName = manager.currentObjectTransform.name;
string[] splitArray = objName.Split('_');
string idObjNumber = splitArray[1];
switch (idObjNumber)
{
case "0":
// not supposed to be ID: 0
break;
case "1":
// nothing to go back to, this is ID: 1
break;
default:
// currently in (ID: 2 & Up) second object
int currentID = int.Parse(idObjNumber);
string idBackOne = (currentID - 1).ToString();
GameObject[] allObjInFull = GameObject.FindGameObjectsWithTag("Object");
if (allObjInFull.Length >= 2)
{
for (int i = 0; i < allObjInFull.Length; i++)
{
if (allObjInFull[i].transform.name.Contains(idBackOne))
{
// Set Camera
camera.transform.parent = allObjInFull[i].transform.GetChild(0).GetChild(1);
camera.transform.position = allObjInFull[i].transform.GetChild(0).GetChild(1).position;
camera.transform.rotation = allObjInFull[i].transform.GetChild(0).GetChild(1).rotation;
}
}
}
break;
}
}
Right Button (PlusOneButton)
void ButtonAction_RightPlusOne()
{
// Get list of all objects and +1 current to switch
string objName = manager.currentObjectTransform.name;
string[] splitArray = objName.Split('_');
string idObjNumber = splitArray[1];
switch (idObjNumber)
{
case "0":
// not supposed to be ID: 0
break;
default:
// currently in (ID: 1 & Up) object
int currentID = int.Parse(idObjNumber);
string idPlusOne = (currentID + 1).ToString();
GameObject[] allObjInFull = GameObject.FindGameObjectsWithTag("Object");
if (allObjInFull.Length >= 2)
{
for (int i = 0; i < allObjInFull.Length; i++)
{
if (allObjInFull[i].transform.name.Contains(idPlusOne))
{
// Set Camera
camera.transform.parent = allObjInFull[i].transform.GetChild(0).GetChild(1);
camera.transform.position = allObjInFull[i].transform.GetChild(0).GetChild(1).position;
camera.transform.rotation = allObjInFull[i].transform.GetChild(0).GetChild(1).rotation;
}
}
}
break;
}
}
It would be way better (especially regarding maintenance) and more efficient to have a central manager class with a List<GameObject> where you simply Add and Remove items dynamically. (Since you already seem to have one in manager I would rather extend that one)
public static class ObjectsManager
{
// If you are not concerned about
// capsulation you could ofcourse make this public as well
// but I thought this is cleaner
private static List<GameObject> objects;
// Read-only property
public static int Count
{
get
{
Initialize();
return objects.Count;
}
}
// initialize the list once
// I first had this in e.g. Awake
// but now you can easily use this in multiple scenes
public static void Initialize(bool force reinitialize = false)
{
if(objects != null && ! reinitialize) return;
objects = FindObjectsWithTag("Object").ToList();
}
public static void Add(GameObject newObject)
{
Initialize();
if(objects.Contains(newObject) return;
objects.Add(newObject);
}
public static void Destroy(GameObject toDestroy)
{
Initialize();
if(objects.Contains(toDestroy)
{
objects.Remove(toDestroy);
}
Object.Destroy(toDestroy);
}
public static int IndexOf(GameObject obj)
{
Initialize();
return objects.IndexOf(obj);
}
public static GameObject GetByIndex(int index)
{
Initialize();
// Use modulo to wrap around the index in case
// +1 or -1 exceeds the list ends
// in your case you might not need it
// but I decided to add it to be more flexible
var nextIndex = (index + 1) % objects.Count;
return objects[index];
}
}
Everytime you Instantiate a new object make sure to also call
ObjectsManager.Add(newObject);
and everytime where you destroy an object rather use
ObjectsManager.Destroy(objectToDestroy);
so it is also removed from the list first.
Then you can easily use
var currentIndex = ObjectsManager.IndexOf(certainObject);
to get the current index of an object and simply move through the index (+1, -1)
var nextObject = ObjectsManager.GetByIndex(currentIndex + 1);
var lastObject = Objects manager.GetByIndex(currentIndex - 1);
In case you switch the scene you have reinitialize the list once in order to get rid of null references
ObjectsManager.Initialize(true);
In your example code you would e.g. use something like
void ButtonAction_LeftMinusOne()
{
GameObject currentObject = manager.currentObjectTransform.gameObject;
int currentIndex = ObjectsManager.IndexOf(currentObject);
if(currentIndex < 0)
{
Debug.LogErrorFormat(this, "Object {0} is not in list!", currentObject.name);
return;
}
if(currentIndex == 0)
{
// nothing to do go back to
// Except you want wrap around then simply remove this check
Debug.Log("Already is first object in list", this);
return;
}
GameObject newObject = ObjectsManager.GetByIndex(currentIndex - 1);
Transform childOfNewObject = newObject.GetChild(0).GetChild(1);
// Set Camera
// Using simply SetParent with parameter worldPositionStays=false
// reduces it to one single call
camera.transform.SetParent( childOfNewObject, false);
}
And accordingly
void ButtonAction_RightPlusOne()
{
GameObject currentObject = manager.currentObjectTransform.gameObject;
int currentIndex = ObjectsManager.IndexOf(currentObject);
if(currentIndex < 0)
{
Debug.LogErrorFormat(this, "Object {0} is not in list!", currentObject.name);
return;
}
if(currentIndex == ObjectsManager.Count - 1)
{
// nothing to do go forward to
// Except you want wrap around then simply remove this check
Debug.Log("Already is last object in list", this);
return;
}
GameObject newObject = ObjectsManager.GetByIndex(currentIndex + 1);
Transform childOfNewObject = newObject.GetChild(0).GetChild(1);
// Set Camera
// Using simply SetParent with parameter worldPositionStays=false
// reduces it to one single call
camera.transform.SetParent( childOfNewObject, false);
}
Ok, so I'm having problems playing my wav sounds in the most efficient way as possible. IF I get rid of the 'clip.setFramePosition' in my Audio class, then the sound loops (obviously because the clip keeps reversing back to frame 0). IF I take away the 'clip.setFramePosition' method, the sound plays ONCE, and once only (probably because it needs to be set BACK to it's original frame position).
Two classes are involved:
/**
* Initalise sound clips
*/
public static Clip reverb = loadClip("/SFX_Intro/reverb.wav");
public static Clip glitch = loadClip("/SFX_Intro/glitch.wav");
public static Clip menu = loadClip("/SFX_Ambience/menu.wav");
/*
* audio input to enable sound
*/
private static AudioInputStream ais;
/**
* load sound clip
*/
private static Clip loadClip(String resourceName) {
try {
ais = AudioSystem.getAudioInputStream(Audio.class.getResource(resourceName));
DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat());
Clip clip = (Clip) AudioSystem.getLine(info);
clip.open(ais);
return clip;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* play sound clip
*/
public static void play(Clip clip) {
if (clip != null) {
try {
clip.rewind();
clip.setFramePosition(0);
clip.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* loop sound
*/
public static void loop(Clip clip, int amount) {
if (clip != null) {
try {
clip.loop(amount);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Stop sound
*/
public static void stop(Clip clip) {
clip.stop();
}
and...
/**
* alpha vars
*/
private int alpha = 220;
private final int MIN_ALPHA = 0;
private Color c;
private Rectangle r;
/**
* class importations
*/
private Counter[] delay;
/**
* Constructor
*/
public Intro(GameStateManager gsm) {
super(gsm);
delay = new Counter[3];
delay[0] = new Counter();
delay[1] = new Counter();
delay[2] = new Counter();
}
/**
* Update
*/
public void update() {
Cursor.setCursor(Cursor.INVISIBLE);
}
/**
* Draw
*/
public void draw(Graphics2D g) {
delay[2].count(2);
if (delay[2].bFinished) {
r = new Rectangle(0, 0, GamePanel.WIDTH, GamePanel.HEIGHT);
c = new Color(0, 0, 0, alpha);
g.drawImage(Texture.plogo_01, GamePanel.WIDTH / 2
- Texture.plogo_01.getWidth() / 2, GamePanel.HEIGHT / 2
- Texture.plogo_01.getHeight() / 2, null);
if (alpha != MIN_ALPHA) {
alpha -= 1;
}
else if (alpha == MIN_ALPHA) { // Once faded in...
g.drawImage(Texture.plogo_02, GamePanel.WIDTH / 2
- Texture.plogo_02.getWidth() / 2, GamePanel.HEIGHT / 2
- Texture.plogo_02.getHeight() / 2, null);
delay[0].count(0.005); // set delay time
Audio.play(Audio.glitch); // Play glitch sound
}
if (delay[0].bFinished) { // Once delay time has finished...
g.setColor(Color.BLACK);
g.fill(r);
delay[1].count(3); // set another delay time
if (delay[1].bFinished) {
gsm.setState(GameStateManager.MENU); // change to menu state
return;
}
}
g.setColor(c);
g.fill(r);
}
}
/**
* Key input
*/
public void keyPressed(int key) {
}
public void keyReleased(int key) {
}
I hope someone can help solve this problem. Thank you in advance.
Solved. I added an 'if' statement inside the 'Play' method:
public static void play(Clip clip) {
try {
clip.start();
int MAX_FRAMES = clip.getFrameLength();
if (clip.getFramePosition() == MAX_FRAMES) {
clip.setFramePosition(0);
stop(clip);
}
} catch (Exception e) {
e.printStackTrace();
}
return;
}
I have nokia xpressmusic 5130 c-2 working on symbian (j2me) the volume buttons has been broken, so i decided to make a j2me program to control the volume (increase,decrease)
I have found many codes through the internet but often not work or have many errors because not complied with the program flow diagram and screen
regards
If the device have support to the JSR 256: Mobile Sensor API, then you can use your API:
http://jcp.org/en/jsr/detail?id=256
My class that uses the JSR 256:
/*
* REVISION HISTORY:
*
* Date Author(s)
* CR Headline
* =============================================================================
* 22/Oct/2009 Douglas Daniel Del Frari
* <CR51674> Initial Version
* =============================================================================
* 25/Feb/2010 Douglas Daniel Del Frari
* <CR52577> Added more one sensor (charge state) to detection of charger state.
* =============================================================================
*/
package j2me.mobilesensor;
import java.io.IOException;
import javax.microedition.io.Connector;
import javax.microedition.sensor.Data;
import javax.microedition.sensor.DataListener;
import javax.microedition.sensor.SensorConnection;
import javax.microedition.sensor.SensorInfo;
import javax.microedition.sensor.SensorManager;
/**
* This class uses the resource of the mobile sensor (JSR-256) to capture the
* phone battery level, and the volume level of the phone
*
* #author Douglas D. Del Frari (douglas.frari#gmail.com)
*/
public class MobileSensorManager implements DataListener, Runnable{
/** ID for the alert dialog */
public static final int ID_DIALOG_BATTERY_CHARGE = 90;
/**
* String used to get the battery charge level.
*/
private static final String BATTERY = "battery_charge";
/**
* String used to get the charger state (plugged on or off).
*/
private static final String BATTERY_CHARGER_STATE = "charger_state";
/**
* String used to get the sound level
*/
private static final String SOUND_LEVEL = "sound_level_setting";
// sensors
private static SensorConnection batterySensor = null;
private SensorConnection soundSensor = null;
// SensorInfo objects containing info about
private SensorInfo infos[];
// Is sensor thread running?
private static boolean isStopped = false;
// Buffer for the sensor data
private static final int BUFFER_SIZE = 1;
/**
* Indicate the minimal value of battery level of the game
*/
public static final int BATTERY_LIFE_LIMIT = 25;
// Thread for initializing and listening
private Thread thread = null;
/*
* Sensor quantity string received from the dataReceived() method
*/
private String sensorString = "";
// Sensor value (battery_charge)
private String batteryString = "";
private String volumeString = "";
private boolean isActiveBatterySensor;
private boolean isLowBatteryCharge;
private int batteryChargeValue;
private int volumeValue;
private SensorConnection batteryChargerState;
private boolean chargeState;
// instance this class
private static MobileSensorManager instance;
/**
* default constructor
*/
private MobileSensorManager() {
}
/**
* Get the MobileSensorManager instance
*
* #return instance this
*/
public static MobileSensorManager getInstance() {
if (instance == null) {
instance = new MobileSensorManager();
}
return instance;
}
/**
* #param stopped
*/
private synchronized void setStopped(boolean stopped) {
isStopped = stopped;
notify();
if (thread != null)
thread = null;
}
/**
* start the mobile sensors
*/
public synchronized void start() {
setStopped(false);
if (thread == null)
thread = new Thread(this);
thread.start();
}
/**
* stop the mobile sensors
*/
public synchronized void stop() {
setStopped(true);
thread = null;
}
/* (non-Javadoc)
* #see java.lang.Runnable#run()
*/
public void run() {
initSensors();
}
/**
* Initializes (opens) the sensors and sets the DataListener. Takes also
* care of removing the DataListeners and closing the connections
*/
private synchronized void initSensors() {
batterySensor = openSensor(BATTERY);
if (batterySensor == null) {
isActiveBatterySensor = false;
return;
} else {
isActiveBatterySensor = true;
}
batteryChargerState = openSensor(BATTERY_CHARGER_STATE);
soundSensor = openSensor(SOUND_LEVEL);
try {
batterySensor.setDataListener(this, BUFFER_SIZE);
if (soundSensor !=null) {
soundSensor.setDataListener(this, BUFFER_SIZE);
}
if (batteryChargerState != null) {
batteryChargerState.setDataListener(this, BUFFER_SIZE);
}
while (!isStopped) {
try {
wait();
} catch (InterruptedException ie) {
}
}
batterySensor.removeDataListener();
if (soundSensor !=null) {
soundSensor.removeDataListener();
}
if (batteryChargerState != null) {
batteryChargerState.removeDataListener();
}
} catch (IllegalMonitorStateException imse) {
imse.printStackTrace();
} catch (IllegalArgumentException iae) {
iae.printStackTrace();
}
try {
if (batterySensor!=null) {
batterySensor.close();
}
if (soundSensor !=null) {
soundSensor.close();
}
if (batteryChargerState != null) {
batteryChargerState.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
if (isStopped) {
batterySensor = null;
soundSensor = null;
batteryChargerState = null;
}
}
/**
* Searches sensors of desired quantity and if found returns a
* SensorConnection opened to it.
*
* #return SensorConnection, which has been opened to a sensor matching the
* criteria
*/
private SensorConnection openSensor(String quantity) {
infos = SensorManager.findSensors(quantity, null);
if (infos.length == 0)
return null;
String sensor_url = infos[0].getUrl();
try {
return (SensorConnection) Connector.open(sensor_url);
} catch (IOException ioe) {
ioe.printStackTrace();
return null;
}
}
/* (non-Javadoc)
* #see javax.microedition.sensor.DataListener#dataReceived(javax.microedition.sensor.SensorConnection, javax.microedition.sensor.Data[], boolean)
*/
public void dataReceived(SensorConnection sensor, Data[] data, boolean isDataLost) {
sensorString = sensor.getSensorInfo().getQuantity();
int values[] = data[0].getIntValues();
if (sensorString.equals(BATTERY)) {
setBatteryString("" + values[0] + "%");
batteryChargeValue = values[0];
}
if (sensorString.equals(SOUND_LEVEL)) {
setVolumeString("" + values[0] + " sound level");
volumeValue = values[0];
}
if (sensorString.equals(BATTERY_CHARGER_STATE)) {
int value = values[0];
if (value == 0)
chargeState = false;
else if (value == 1)
chargeState = true;
}
if (values[0] <= BATTERY_LIFE_LIMIT) {
isLowBatteryCharge = true;
} else {
isLowBatteryCharge = false;
}
}
/**
* #return the batteryString
*/
public String getBatteryString() {
return batteryString;
}
/**
* #param batteryString the batteryString to set
*/
public void setBatteryString(String batteryString) {
this.batteryString = batteryString;
}
/**
* #return the isLowBatteryCharge
*/
public boolean isLowBatteryCharge() {
return isLowBatteryCharge;
}
/**
* #return the isActiveBatterySensor
*/
public boolean isActiveBatterySensor() {
return isActiveBatterySensor;
}
/**
* #return the batteryChargeValue
*/
public int getBatteryChargeValue() {
return batteryChargeValue;
}
/**
* #param volumeString the volumeString to set
*/
public void setVolumeString(String volumeString) {
this.volumeString = volumeString;
}
/**
* #return the volumeString
*/
public String getVolumeString() {
return volumeString;
}
/**
* #param volumeValue the volumeValue to set
*/
public void setVolumeValue(int volumeValue) {
this.volumeValue = volumeValue;
}
/**
* #return the volumeValue
*/
public int getVolumeValue() {
return volumeValue;
}
/**
* Get state of battery charge
*
* #return True if the charge is plugged in, otherwise not plugged in
*/
public boolean isChargedState() {
return chargeState;
}
}
In GAE, I've got a table full of "one offs" -- things like "last-used sequence number" and the like that don't really fall into other tables. It's a simple String-key with String-value pair.
I've got some code to grab a named integer and increment it, like so:
#PersistenceCapable(detachable="true")
public class OneOff
{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String dataKey;
#Persistent
private String value;
public OneOff(String kk, String vv)
{
this.dataKey = kk;
this.value = vv;
}
public static OneOff persistOneOff(String kk, String vv)
{
OneOff oneoff= new OneOff(kk, vv);
PersistenceManager pm = PMF.get().getPersistenceManager();
try
{
pm.makePersistent(oneoff);
}
finally
{
pm.close();
}
return oneoff;
}
// snip...
#SuppressWarnings("unchecked")
synchronized
public static int getIntValueForKeyAndIncrement(String kk, int deFltValue)
{
int result = 0;
OneOff oneOff = null;
PersistenceManager pm = PMF.get().getPersistenceManager();
Query query = pm.newQuery(OneOff.class);
query.setFilter("dataKey == kkParam");
query.declareParameters("String kkParam");
List<OneOff> oneOffs = (List<OneOff>) query.execute(kk);
int count = oneOffs.size();
if (count == 1)
{
oneOff = oneOffs.get(0);
result = Integer.parseInt(oneOff.value);
}
else if (count == 0)
{
oneOff = new OneOff(kk, "default");
result = deFltValue;
}
else
{
// Log WTF error.
}
// update object in DB.
oneOff.value = "" + (result+1);
try
{
pm.makePersistent(oneOff);
}
finally
{
pm.close();
}
return result;
}
// etc...
However, when I make these calls:
int val1 = OneOff.getIntValueForKeyAndIncrement("someKey", 100);
int val2 = OneOff.getIntValueForKeyAndIncrement("someKey", 100);
int val3 = OneOff.getIntValueForKeyAndIncrement("someKey", 100);
Sometimes I get the desired increment and sometimes I get the same value. It appears that my DB access is running asynchronously, when I'd like to lock the DB for this particular transaction.
I thought that
synchronized
public static
was supposed to do that for me, but apparently not (probably due to multiple instances running!)
At any rate -- how do I do the thing that I want? (I want to lock my DB while I get & update this value, to make the whole thing concurrency-safe.)
Thanks!
== EDIT ==
I have accepted Robert's as the correct answer, since transactions were, indeed, what I wanted. However, for completeness, I have added my updated code below. I think it's correct, although I'm not sure about the if(oneOff==null) clause (the try-catch bit.)
public static int getIntValueForKeyAndIncrement(String kk, int defltValue)
{
int result = 0;
Entity oneOff = null;
int retries = 3;
// Using Datastore Transactions
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
while (true)
{
com.google.appengine.api.datastore.Transaction txn = datastore.beginTransaction();
try
{
Key oneOffKey = KeyFactory.createKey("OneOff", kk);
oneOff = datastore.get (oneOffKey);
result = Integer.parseInt((String) oneOff.getProperty("value"));
oneOff.setProperty("value", "" + (result+1));
datastore.put(oneOff);
txn.commit();
break;
}
catch (EntityNotFoundException ex)
{
result = defltValue;
}
catch (ConcurrentModificationException ex)
{
if (--retries < 0)
{
throw ex;
}
}
if (oneOff == null)
{
try
{
Key oneOffKey = KeyFactory.createKey("OneOff", kk);
oneOff = new Entity(oneOffKey);
oneOff.setProperty("value", "" + (defltValue+1));
datastore.put(txn, oneOff);
datastore.put(oneOff);
txn.commit();
break;
}
finally
{
if (txn.isActive())
{
txn.rollback();
}
}
}
else
{
if (txn.isActive())
{
txn.rollback();
}
}
}
return result;
}
You should be updating your values inside a transaction. App Engine's transactions will prevent two updates from overwriting each other as long as your read and write are within a single transaction. Be sure to pay attention to the discussion about entity groups.