i am programming a mobile application in j2me in which i need to send an image from one mobile to another via sms.
the problem is being encountered at the receiving end.the image is not being decoded properly.it is throwing ioexception....i m posting the code here..plz help me.
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import javax.wireless.messaging.*;
import java.io.IOException;
import javax.microedition.lcdui.game.*;
import java.lang.*;
import java.io.*;
public class receive_mms extends MIDlet implements CommandListener
{
Display disp;
//TextBox txtbox;
MessageConnection msgConn;
Message msg;
Form frm=null;
byte[] msgrev;
byte[] data;
//String msgrev;
Image im=null;
Image im1=null;
ImageItem img=null;
int i,j;
ByteArrayInputStream bais = null;
Command cmd_exit;
public receive_mms(){
disp=Display.getDisplay(this);
frm=new Form("photo dikho");
i=frm.getWidth();
j=frm.getHeight();
cmd_exit=new Command("exit",Command.EXIT,1);
frm.addCommand(cmd_exit);
frm.setCommandListener(this);
disp.setCurrent(frm);
Thread t1 = new Thread()
{
public void run()
{recieve();}
};
t1.start();
//txtbox=new TextBox("Recieve Text","",100,TextField.ANY);
}
public void commandAction(Command c,Displayable d)
{
if(c==cmd_exit)
{
notifyDestroyed();
}
}
public void startApp(){/*
disp.setCurrent(frm);
Thread t1 = new Thread()
{
public void run()
{recieve();}
};
t1.start();
*/
}
public void pauseApp(){}
public void destroyApp(boolean unconditional){}
public void recieve(){
//while(true)
//{
String mSenderAddress="";
try{
msgConn = (MessageConnection) Connector.open("sms://:1234");
System.out.println("11");
msg = msgConn.receive();// start listening and stuck here until a msg is received
System.out.println("12");
mSenderAddress = msg.getAddress();// Get info from message, from where da msg is beign sent
System.out.println("3");
System.out.println("add"+ mSenderAddress);
System.out.println("msg aya:" + msg);
msgConn.close();
}catch(Exception e){System.out.println(e);}
if (msg instanceof BinaryMessage) {
//try{
msgrev = ((BinaryMessage)msg).getPayloadData();
data=msgrev.toByteArray();
String val= new String(data);
System.out.println("yahoo");
System.out.println("yahoo1");
System.out.println(val);
create(data);
}
}
public void create(byte[] bs)
{
try
{
String str=bs.toString();
/*
StringBuffer d=new StringBuffer();
bais=new ByteArrayInputStream(bs);
DataInputStream ds=new DataInputStream(bais);
int len=bs.length;
System.out.println("len="+len);
if(len!=0)
{
int ch=0;
while((ch=ds.read())!=-1)
{
d.append((char)ch);
}
}
System.out.println(d);
str=d.toString();
*/
//str=bs.toString();
InputStream is= this.getClass().getResourceAsStream(str);
System.out.println("string is"+str);
im = (Image)Image.createImage(is);
System.out.println("line");
im1 = (Image)Image.createImage(im, 0, 0, i, j, Sprite.TRANS_NONE);
img = new ImageItem("yeh photo snd hui", im1, Item.LAYOUT_CENTER, "kyu nhi dikh rhi", Item.BUTTON);
frm.append(img);
}
catch (Exception e)
{
System.out.println(e);
}
}
}
You're doing a few very odd things:
converting the byte array to a String, particularly using byte[].toString()
attempting to get an InputStream by calling Class.getResourceAsStream() with a String that has been created from the byte array.
using SMS to send an Image
Class.getResourceAsStream() is intended to take a String identifying a resource file within the MIDlet's jar file.
The correct way to do this is to get the byte[] from the BinaryMessage and use this to create an Image using Image.createImage(bytes, 0, bytes.length);
Although, as you're sending it using SMS, I'd hope it was a very small image indeed or anybody using this app will incur high costs from splitting a large image over several SMSs. Beware also that some networks limit the number of parts that an SMS can be split into.
You would be much better off researching the MMS sending functionality provided by JSR 205.
You are getting wrong the data stream, here is how you must do it:
public void create(byte[] bs)
{
try
{
im = (Image)Image.createImage(bs, 0, bs.length);
im1 = (Image)Image.createImage(im, 0, 0, i, j, Sprite.TRANS_NONE);
img = new ImageItem("yeh photo snd hui", im1, Item.LAYOUT_CENTER, "kyu nhi dikh rhi", Item.BUTTON);
frm.append(img);
}
catch (Exception e)
{
System.out.println(e);
}
}
This should work.
Related
I am attempting to close a stream coming from an http request using Retrofit and rxjava, either because it timedOut, or because I need to change details that went into the request. Both appear to work perfectly, as when I cancel subscription I get the doOnCancel debug message and when doOnNext is completed I get the doOnTerminate message. I also do not receive inputLines from multiple threads. However, my thread count rises every single time either of the above actions happen. It appears that responsebody.close is not releasing their resources and therefore the thread is not dying (I also have gotten error messages along the lines of "OKHTTP leaked. did you close youre responseBody?")
Does anyone have any suggestions?
public boolean closeSubscription() {
flowableAlive = false;
subscription.cancel();
return true;
}
public void subscribeToFlowable() {
streamFlowable.observeOn(Schedulers.newThread()).subscribeOn(Schedulers.newThread())
.doOnTerminate(() -> log.debug("TERMINATED")).doOnCancel(() -> log.debug("FLOWABLE CANCELED"))
.subscribe(new Subscriber<ResponseBody>() {
#Override
public void onSubscribe(Subscription s) {
subscription = s;
subscription.request(Long.MAX_VALUE);
}
#Override
public void onNext(ResponseBody responseBody) {
log.debug("onNext called");
String inputLine;
try (InputStream inputStream = responseBody.byteStream()) {
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
while (flowableAlive && ((inputLine = br.readLine()) != null)) {
log.debug("stream receive input line for thread " + name);
log.debug(inputLine);
}
} catch (IOException e) {
log.debug("error occurred");
log.debug(e.getMessage());
}
}
#Override
public void onError(Throwable t) {
log.debug("error");
flowableAlive = false;
}
#Override
public void onComplete() {
log.debug("completed");
closeSubscription();
flowableAlive = false;
}
});
}
The result of subscribe() is Disposable object. You should store it as a filed and call Disposable.dispose() on it later as shown here:
https://proandroiddev.com/disposing-on-android-the-right-way-97bd55cbf970
Tour OkHttp call will be interrupted properly because dispose() interrupts thread on which the call runs and OkHttp checks regularly if Thread was interrupted to stop transfer when that happened - it's called cooperative cancelling/interruption.
I have written a custom rest Service on an Xpage, which is tied to a bean. The Xpage is:
<xe:restService
id="restServiceCustom"
pathInfo="custom"
ignoreRequestParams="false"
state="false"
preventDojoStore="true">
<xe:this.service>
<xe:customRestService
contentType="application/json"
serviceBean="XXXX.PCServiceBean">
</xe:customRestService>
</xe:this.service>
</xe:restService>
I cobbled together my java agent from some excellent posts around the net. I have just started on the GET. My code runs but I it seems pretty slow (on my dev server). I want to make it as fast as possible. I am using a ViewEntryCollection and I am "flushing" at each record which I assume is streaming.
I am putting my own "[" in the code, so I assume that I am not doing something right, as I never saw any examples of anyone else doing this.
Any suggestions would be greatly appreciated.
package com.XXXXX.bean;
import java.io.IOException;
import java.io.Writer;
import java.util.Vector;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.openntf.domino.Database;
import org.openntf.domino.Session;
import org.openntf.domino.View;
import org.openntf.domino.ViewEntry;
import org.openntf.domino.ViewEntryCollection;
import org.openntf.domino.utils.Factory;
import com.ibm.commons.util.io.json.JsonException;
import com.ibm.commons.util.io.json.util.JsonWriter;
import com.ibm.domino.services.ServiceException;
import com.ibm.domino.services.rest.RestServiceEngine;
import com.ibm.xsp.extlib.component.rest.CustomService;
import com.ibm.xsp.extlib.component.rest.CustomServiceBean;
public class PCServiceBean extends CustomServiceBean {
#Override
public void renderService(CustomService service, RestServiceEngine engine) throws ServiceException {
try {
HttpServletRequest request = engine.getHttpRequest();
HttpServletResponse response = engine.getHttpResponse();
response.setHeader("Content-Type", "application/json; charset=UTF-8");
String method = request.getMethod();
if (method.equals("GET")) {
this.doGet(request, response);
} else if (method.equals("POST")) {
this.doPost(request, response);
} else if (method.equals("PUT")) {
this.doPut(request, response);
} else if (method.equals("DELETE")) {
this.doDelete(request, response);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void doDelete(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
}
private void doPut(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
}
private void doPost(HttpServletRequest request, HttpServletResponse response) {
// TODO Auto-generated method stub
}
private void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, JsonException {
Session session = Factory.getSession();
Database DB = session.getDatabase(session.getCurrentDatabase().getServer(), "scoApps\\PC\\PCData.nsf");
View pcView = DB.getView("viewAllByStatus");
int i = 1;
Writer out = response.getWriter();
JsonWriter writer = new JsonWriter(out, false);
writer.out("[");
ViewEntryCollection vec = pcView.getAllEntries();
int count = vec.getCount();
for (ViewEntry entry : vec) {
Vector<?> columnValues = entry.getColumnValues();
writer.startObject();
writer.startProperty("unid");
writer.outStringLiteral(String.valueOf(columnValues.get(1)));
writer.endProperty();
writer.startProperty("status");
writer.outStringLiteral(String.valueOf(columnValues.get(0)));
writer.endProperty();
writer.startProperty("assetTag");
writer.outStringLiteral(String.valueOf(columnValues.get(2)));
writer.endProperty();
writer.startProperty("serialNumber");
writer.outStringLiteral(String.valueOf(columnValues.get(3)));
writer.endProperty();
writer.startProperty("model");
writer.outStringLiteral(String.valueOf(columnValues.get(4)));
writer.endProperty();
writer.startProperty("currentLocation");
writer.outStringLiteral(String.valueOf(columnValues.get(5)));
writer.endProperty();
writer.endObject();
if (i != count) {
i = i + 1;
writer.out(",");
writer.flush();
}
}
writer.out("]");
writer.flush();
}
}
Change your code to
JsonWriter writer = new JsonWriter(out, false);
writer.startArray();
ViewEntryCollection vec = pcView.getAllEntries();
int count = vec.getCount();
for (ViewEntry entry : vec) {
Vector<?> columnValues = entry.getColumnValues();
writer.startArrayItem();
writer.startObject();
writer.startProperty("unid");
writer.outStringLiteral(String.valueOf(columnValues.get(1)));
writer.endProperty();
...
writer.endObject();
writer.endArrayItem();
}
writer.endArray();
writer.flush();
It uses JsonWriter's
startArray() and endArray() instead of out("[") and out("]")
startArrayItem() and endArrayItem() instead of out(",") and flush()
The JSON response string gets shorter if you set JsonWriter's compact option to true:
JsonWriter writer = new JsonWriter(out, true);
I see two problems.
First - use ViewNavigator. Here's good explanation of its performance gain.
https://www.mindoo.com/web/blog.nsf/dx/17.01.2013085308KLEB9S.htm
Second - prepare your JSON in advance. This is very good technique to avoid unnecessary code (and time to process it) to get JSON data from Domino documents.
https://quintessens.wordpress.com/2015/09/05/working-with-json-in-your-xpages-application/
I thought this would be a simple question but I am having trouble finding an answer. I have a single ImageView object associated with a JavaFX Scene object and I want to load large images in from disk and display them in sequence one after another using the ImageView. I have been trying to find a good way to repeatedly check the Image object and when it is done loading in the background set it to the ImageView and then start loading a new Image object. The code I have come up with (below) works sometimes and sometimes it doesn't. I am pretty sure I am running into issues with JavaFX and threads. It loads the first image sometimes and stops. The variable "processing" is a boolean instance variable in the class.
What is the proper way to load an image in JavaFX in the background and set it to the ImageView after it is done loading?
public void start(Stage primaryStage) {
...
ImageView view = new ImageView();
((Group)scene.getRoot()).getChildren().add(view);
...
Thread th = new Thread(new Thread() {
public void run() {
while(true) {
if (!processing) {
processing = true;
String filename = files[count].toURI().toString();
Image image = new Image(filename,true);
image.progressProperty().addListener(new ChangeListener<Number>() {
#Override public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number progress) {
if ((Double) progress == 1.0) {
if (! image.isError()) {
view.setImage(image);
}
count++;
if (count == files.length) {
count = 0;
}
processing = false;
}
}
});
}
}
}
});
}
I actually think there's probably a better general approach to satisfying whatever your application's requirements are than the approach you are trying to use, but here is my best answer at implementing the approach you describe.
Create a bounded BlockingQueue to hold the images as you load them. The size of the queue may need some tuning: too small and you won't have any "buffer" (so you won't be able to take advantage of any that are faster to load than the average), too large and you might consume too much memory. The BlockingQueue allows you to access it safely from multiple threads.
Create a thread that simply loops and loads each image synchronously, i.e. that thread blocks while each image loads, and deposits them in the BlockingQueue.
Since you want to try to display images up to once per FX frame (i.e. 60fps), use an AnimationTimer. This has a handle method that is invoked on each frame render, on the FX Application Thread, so you can implement it just to poll() the BlockingQueue, and if an image was available, set it in the ImageView.
Here's an SSCCE. I also indicated how to do this where you display each image for a fixed amount of time, as I think that's a more common use case and might help others looking for similar functionality.
import java.io.File;
import java.net.MalformedURLException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javafx.animation.AnimationTimer;
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
public class ScreenSaver extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
Button startButton = new Button("Choose image directory...");
startButton.setOnAction(e -> {
DirectoryChooser chooser= new DirectoryChooser();
File dir = chooser.showDialog(primaryStage);
if (dir != null) {
File[] files = Stream.of(dir.listFiles()).filter(file -> {
String fName = file.getAbsolutePath().toLowerCase();
return fName.endsWith(".jpeg") | fName.endsWith(".jpg") | fName.endsWith(".png");
}).collect(Collectors.toList()).toArray(new File[0]);
root.setCenter(createScreenSaver(files));
}
});
root.setCenter(new StackPane(startButton));
primaryStage.setScene(new Scene(root, 800, 800));
primaryStage.show();
}
private Parent createScreenSaver(File[] files) {
ImageView imageView = new ImageView();
Pane pane = new Pane(imageView);
imageView.fitWidthProperty().bind(pane.widthProperty());
imageView.fitHeightProperty().bind(pane.heightProperty());
imageView.setPreserveRatio(true);
Executor exec = Executors.newCachedThreadPool(runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
final int imageBufferSize = 5 ;
BlockingQueue<Image> imageQueue = new ArrayBlockingQueue<Image>(imageBufferSize);
exec.execute(() -> {
int index = 0 ;
try {
while (true) {
Image image = new Image(files[index].toURI().toURL().toExternalForm(), false);
imageQueue.put(image);
index = (index + 1) % files.length ;
}
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// This will show a new image every single rendering frame, if one is available:
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
Image image = imageQueue.poll();
if (image != null) {
imageView.setImage(image);
}
}
};
timer.start();
// This wait for an image to become available, then show it for a fixed amount of time,
// before attempting to load the next one:
// Duration displayTime = Duration.seconds(1);
// PauseTransition pause = new PauseTransition(displayTime);
// pause.setOnFinished(e -> exec.execute(createImageDisplayTask(pause, imageQueue, imageView)));
// exec.execute(createImageDisplayTask(pause, imageQueue, imageView));
return pane ;
}
private Task<Image> createImageDisplayTask(PauseTransition pause, BlockingQueue<Image> imageQueue, ImageView imageView) {
Task<Image> imageDisplayTask = new Task<Image>() {
#Override
public Image call() throws InterruptedException {
return imageQueue.take();
}
};
imageDisplayTask.setOnSucceeded(e -> {
imageView.setImage(imageDisplayTask.getValue());
pause.playFromStart();
});
return imageDisplayTask ;
}
public static void main(String[] args) {
launch(args);
}
}
I am trying to connect my Desktop to the PHILIPS Hue light server using java.
When the code runs, it will flow into the Controller.java. When that happens, the FindBridges method in Controller.java runs. This is where the error occurs. In debugging, it displays a NullPointerException in thread "AWT-Event-Queue-0".
I presume that the server/lightbulb cannot be found at all, even though it is turned on and my android application can connect to it.
The error is stated below:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at com.philips.lighting.gui.DesktopView$1.actionPerformed(DesktopView.java:72)
Controller.java
package com.philips.lighting;
import java.util.List;
import java.util.Random;
import javax.swing.JDialog;
import com.philips.lighting.hue.sdk.upnp.*;
import com.philips.lighting.data.HueProperties;
import com.philips.lighting.gui.AccessPointList;
import com.philips.lighting.gui.DesktopView;
import com.philips.lighting.gui.LightColoursFrame;
import com.philips.lighting.gui.PushLinkFrame;
import com.philips.lighting.hue.sdk.PHAccessPoint;
import com.philips.lighting.hue.sdk.PHBridgeSearchManager;
import com.philips.lighting.hue.sdk.PHHueSDK;
import com.philips.lighting.hue.sdk.PHMessageType;
import com.philips.lighting.hue.sdk.PHSDKListener;
import com.philips.lighting.model.PHBridge;
import com.philips.lighting.model.PHBridgeResourcesCache;
import com.philips.lighting.model.PHHueError;
import com.philips.lighting.model.PHHueParsingError;
import com.philips.lighting.model.PHLight;
import com.philips.lighting.model.PHLightState;
public class Controller {
private PHHueSDK phHueSDK;
private DesktopView desktopView;
private PushLinkFrame pushLinkDialog;
private LightColoursFrame lightColoursFrame;
private static final int MAX_HUE=65535;
private Controller instance;
public Controller(DesktopView view) {
this.desktopView = view;
this.phHueSDK = PHHueSDK.getInstance(); // or phHueSDK = PHHueSDK.getInstance();
this.instance = this;
}
public void findBridges() {
//To uniquely identify your app in the bridge whitelist we recommend you set your app name, and the device
phHueSDK.setAppName("SmartShowroomApp"); // e.g. phHueSDK.setAppName("QuickStartApp");
phHueSDK.setDeviceName("SmartDevice"); // e.g. If you are programming for Android: phHueSDK.setDeviceName(android.os.Build.MODEL);
phHueSDK = PHHueSDK.getInstance();
PHBridgeSearchManager sm = (PHBridgeSearchManager) phHueSDK.getSDKService(PHHueSDK.SEARCH_BRIDGE);
sm.search(true, true);
//This starts a UPNP/Portal Search and takes around 10 seconds.
//The PHSDKListener (onAccessPointsFound) will be notified with the bridges found.
}
private PHSDKListener listener = new PHSDKListener() {
#Override
public void onAccessPointsFound(List<PHAccessPoint> accessPointsList) {
// Handle your bridge search results here.
//Typically if multiple results are returned you will want to display them in a list
// and let the user select their bridge.
//If one is found you may opt to connect automatically to that bridge.
phHueSDK = PHHueSDK.getInstance();
desktopView.getFindingBridgeProgressBar().setVisible(false);
if (accessPointsList != null && accessPointsList.size() > 0)
{
AccessPointList accessPointList = new AccessPointList(accessPointsList, instance);
accessPointList.setVisible(true);
accessPointList.setLocationRelativeTo(null); // Centre the AccessPointList Frame
phHueSDK.getAccessPointsFound().clear(); // Clear all connected access points
phHueSDK.getAccessPointsFound().addAll(accessPointsList); // Adds multiple results to the list
}
else
{
PHBridgeSearchManager sm = (PHBridgeSearchManager) phHueSDK.getSDKService(PHHueSDK.SEARCH_BRIDGE);
sm.search(false, false, true);
}
}
#Override
public void onAuthenticationRequired(PHAccessPoint accessPoint) {
// Start the Pushlink Authentication.
phHueSDK = PHHueSDK.getInstance();
desktopView.getFindingBridgeProgressBar().setVisible(false);
phHueSDK.startPushlinkAuthentication(accessPoint);
// Arriving here indicates that Pushlinking is required (to prove the User has physical access to the bridge).
//Typically here you will display a pushlink image (with a timer) indicating to to the user they need to push the button on their bridge within 30 seconds.
pushLinkDialog = new PushLinkFrame(instance);
pushLinkDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
pushLinkDialog.setModal(true);
pushLinkDialog.setLocationRelativeTo(null); // Center the dialog.
pushLinkDialog.setVisible(true);
}
#Override
public void onBridgeConnected(PHBridge bridge) {
phHueSDK = PHHueSDK.getInstance();
phHueSDK.setSelectedBridge(bridge);
phHueSDK.enableHeartbeat(bridge, PHHueSDK.HB_INTERVAL);
// Here it is recommended to set your connected bridge in your sdk object (as above) and start the heartbeat.
// At this point you are connected to a bridge so you should pass control to your main program/activity.
// Also it is recommended you store the connected IP Address/ Username in your app here.
//This will allow easy automatic connection on subsequent use.
// Remember to disable the heartbeat when exiting your app
//phHueSDK.disableAllHeartbeat();
//If you are only interested in a particular resource (e.g. Lights), you can enable the multi resource heartbeat as follows:
//PHHeartbeatManager heartbeatManager = PHHeartbeatManager.getInstance();
//heartbeatManager.enableLightsHeartbeat(bridge, PHHueSDK.HB_INTERVAL);
// To stop the heartbeat you can use either of the below
//heartbeatManager.disableLightsHeartbeat(bridge);
//heartbeatManager.disableAllHeartbeats(bridge);
desktopView.getFindingBridgeProgressBar().setVisible(false);
String username = HueProperties.getUsername();
String lastIpAddress = bridge.getResourceCache().getBridgeConfiguration().getIpAddress();
System.out.println("On connected: IP " + lastIpAddress);
HueProperties.storeUsername(username);
HueProperties.storeLastIPAddress(lastIpAddress);
HueProperties.saveProperties();
// Update the GUI.
desktopView.getLastConnectedIP().setText(lastIpAddress);
desktopView.getLastUserName().setText(username);
// Close the PushLink dialog (if it is showing).
if (pushLinkDialog!=null && pushLinkDialog.isShowing()) {
pushLinkDialog.setVisible(false);
}
// Enable the Buttons/Controls to change the hue bulbs.s
desktopView.getRandomLightsButton().setEnabled(true);
desktopView.getSetLightsButton().setEnabled(true);
}
#Override
public void onCacheUpdated(List cacheNotificationsList, PHBridge bridge) {
// Here you receive notifications that the BridgeResource Cache was updated. Use the PHMessageType to
// check which cache was updated, e.g.
if (cacheNotificationsList.contains(PHMessageType.LIGHTS_CACHE_UPDATED)) {
System.out.println("Lights Cache Updated ");
}
}
#Override
public void onConnectionLost(PHAccessPoint accessPoint) {
// Here you would handle the loss of connection to your bridge.
phHueSDK = PHHueSDK.getInstance();
if (accessPoint == null)
{
System.out.println("Please reconnect to your bridge.");
}
}
#Override
public void onConnectionResumed(PHBridge bridge) {
PHHueSDK phHueSDK = PHHueSDK.getInstance();
for (int i = 0; i < phHueSDK.getDisconnectedAccessPoint().size(); i++)
{
if (phHueSDK.getDisconnectedAccessPoint().get(i).getIpAddress()
.equals(bridge.getResourceCache().getBridgeConfiguration().getIpAddress())) {
phHueSDK.getDisconnectedAccessPoint().remove(i);
}
}
}
#Override
public void onError(int code, final String message) {
// Here you can handle events such as Bridge Not Responding, Authentication Failed and Bridge Not Found.
if (code == PHHueError.BRIDGE_NOT_RESPONDING) {
desktopView.getFindingBridgeProgressBar().setVisible(false);
desktopView.getFindBridgesButton().setEnabled(true);
desktopView.getConnectToLastBridgeButton().setEnabled(true);
desktopView.showDialog(message);
}
else if (code == PHMessageType.PUSHLINK_BUTTON_NOT_PRESSED) {
pushLinkDialog.incrementProgress();
}
else if (code == PHMessageType.PUSHLINK_AUTHENTICATION_FAILED) {
if (pushLinkDialog.isShowing()) {
pushLinkDialog.setVisible(false);
desktopView.showDialog(message);
}
else {
desktopView.showDialog(message);
}
desktopView.getFindBridgesButton().setEnabled(true);
}
else if (code == PHMessageType.BRIDGE_NOT_FOUND) {
desktopView.getFindingBridgeProgressBar().setVisible(false);
desktopView.getFindBridgesButton().setEnabled(true);
desktopView.showDialog(message);
}
}
#Override
public void onParsingErrors(List<PHHueParsingError> parsingErrorsList) {
// Any JSON parsing errors are returned here.
//Typically your program should never return these.
for (PHHueParsingError parsingError: parsingErrorsList) {
System.out.println("ParsingError : " + parsingError.getMessage());
}
}
};
public PHSDKListener getListener() {
return listener;
}
public void setListener(PHSDKListener listener) {
this.listener = listener;
}
public void randomLights() {
PHBridge bridge = phHueSDK.getSelectedBridge();
PHBridgeResourcesCache cache = bridge.getResourceCache();
// And now you can get any resource you want, for example:
List<PHLight> allLights = cache.getAllLights();
Random rand = new Random();
for (PHLight light : allLights) {
PHLightState lightState = new PHLightState();
lightState.setHue(rand.nextInt(MAX_HUE));
bridge.updateLightState(light, lightState); // If no bridge response is required then use this simpler form.
}
}
public void showControlLightsWindow() {
if (lightColoursFrame == null) {
lightColoursFrame = new LightColoursFrame();
}
lightColoursFrame.setLocationRelativeTo(null); // Centre window
lightColoursFrame.setVisible(true);
}
/**
* Connect to the last known access point.
* This method is triggered by the Connect to Bridge button but it can equally be used to automatically connect to a bridge.
*
*/
public boolean connectToLastKnownAccessPoint() {
String username = HueProperties.getUsername();
String lastIpAddress = HueProperties.getLastConnectedIP();
if (username==null || lastIpAddress == null) {
desktopView.showDialog("Missing Last Username or Last IP. Last known connection not found.");
return false;
}
//Obviously, every time a user opens up their Android hue app or application you don't want them to have to select their bridge, authenticate pushlink everytime.
//The recommended way to overcome this issue is to store the connected IP Address/Username (using your preferred method storage) and if set try to connect automatically.
PHAccessPoint accessPoint = new PHAccessPoint();
accessPoint.setIpAddress(lastIpAddress);
accessPoint.setUsername(username);
phHueSDK.connect(accessPoint);
return true;
//Note that the .connect method returns control to your PHSDKListener, so when connected the onBridgeConnected will be called again, and if your users Bridge IP has changed for example, onError will be called and can be handled programatically.
}
public void enableFindBridgesButton() {
desktopView.getFindBridgesButton().setEnabled(true);
}
public void showProgressBar() {
desktopView.getFindingBridgeProgressBar().setVisible(true);
}
}
DesktopView.java
package com.philips.lighting.gui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import layout.TableLayout;
import com.philips.lighting.Controller;
import com.philips.lighting.data.HueProperties;
/**
* DesktopView.java
*
* The main GUI showing last connected IP/Username and buttons for Finding Bridges and Changing the Hue Lights, once connected to a bridge.
*
*/
public class DesktopView extends JFrame {
private static final long serialVersionUID = -7469471678945429320L;
private Controller controller;
private JButton setLightsButton;
private JButton randomLightsButton;
private JButton findBridgesButton;
private JButton connectToLastBridgeButton;
private JProgressBar findingBridgeProgressBar;
private JTextField lastConnectedIP;
private JTextField lastUserName;
public DesktopView(){
setTitle("Hue Desktop");
JPanel mainPanel = new JPanel();
// TODO - Move to another class
JPanel controls = new JPanel();
controls.setLayout(new GridLayout(2,3));
findingBridgeProgressBar = new JProgressBar();
findingBridgeProgressBar.setBorderPainted(false);
findingBridgeProgressBar.setIndeterminate(true);
findingBridgeProgressBar.setVisible(false);
//Set up components preferred size
String lastUsername = HueProperties.getUsername();
String lastConnectedIPStr = HueProperties.getLastConnectedIP();
JLabel labelLastConIP = new JLabel("Last Connected IP:");
lastConnectedIP = new JTextField(lastConnectedIPStr);
lastConnectedIP.setEditable(false);
JLabel labelLastUsername = new JLabel("Last UserName:");
lastUserName = new JTextField(lastUsername);
lastUserName.setEditable(false);
findBridgesButton = new JButton("Find New Bridges");
findBridgesButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
findBridgesButton.setEnabled(false);
connectToLastBridgeButton.setEnabled(false);
controller.findBridges();
findingBridgeProgressBar.setBorderPainted(true);
findingBridgeProgressBar.setVisible(true);
}
});
connectToLastBridgeButton = new JButton("Auto Connect");
connectToLastBridgeButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
if (controller.connectToLastKnownAccessPoint()) {
connectToLastBridgeButton.setEnabled(false);
findBridgesButton.setEnabled(false);
findingBridgeProgressBar.setBorderPainted(true);
findingBridgeProgressBar.setVisible(true);
}
}
});
setLightsButton = new JButton("Change Light Colours");
setLightsButton.setEnabled(false);
setLightsButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
controller.showControlLightsWindow();
}
});
randomLightsButton = new JButton("Randomize Lights");
randomLightsButton.setEnabled(false);
randomLightsButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
controller.randomLights();
}
});
double border = 10;
double size[][] =
{{border, 160, 20, 300, 20, 160}, // Columns
{border, 26, 10, 26, 26, 26,6,26}}; // Rows
mainPanel.setLayout (new TableLayout(size));
mainPanel.add(labelLastConIP, " 1, 1");
mainPanel.add(lastConnectedIP, " 3, 1");
mainPanel.add(labelLastUsername, " 1, 3");
mainPanel.add(lastUserName, " 3, 3");
mainPanel.add(findingBridgeProgressBar, " 3, 5");
mainPanel.add(connectToLastBridgeButton, " 5, 1");
mainPanel.add(findBridgesButton, " 5, 3");
mainPanel.add(randomLightsButton, " 5, 5");
mainPanel.add(setLightsButton, " 5, 7");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setPreferredSize(new Dimension(700,270));
getContentPane().add(new JLabel(" An example Java/Swing Desktop Application to control your Hue Lights."), BorderLayout.NORTH);
getContentPane().add(mainPanel, BorderLayout.CENTER);
//4. Size the frame.
pack();
setLocationRelativeTo(null); // Centre the window.
setVisible(true);
}
public void setController(Controller controller) {
this.controller = controller;
}
public JButton getSetLightsButton() {
return setLightsButton;
}
public JButton getRandomLightsButton() {
return randomLightsButton;
}
public JButton getFindBridgesButton() {
return findBridgesButton;
}
public JButton getConnectToLastBridgeButton() {
return connectToLastBridgeButton;
}
public void showDialog(String message) {
JOptionPane.showMessageDialog(this, message);
}
public JProgressBar getFindingBridgeProgressBar() {
return findingBridgeProgressBar;
}
public JTextField getLastConnectedIP() {
return lastConnectedIP;
}
public JTextField getLastUserName() {
return lastUserName;
}
}
HueDesktop.java
package com.philips.lighting;
import com.philips.lighting.data.HueProperties;
import com.philips.lighting.gui.DesktopView;
import com.philips.lighting.hue.sdk.PHHueSDK;
/**
* HueDesktop.java
* An example Java/Swing Desktop application illustrating how to connect to a bridge and change your Hue lights
* using a Java Desktop Application.
*
* For more information on programming for Hue see:
* http://developers.meethue.com
*
*/
class HueDesktop {
public static void main(String args[]) {
new HueDesktop();
}
public HueDesktop() {
PHHueSDK phHueSDK = PHHueSDK.create();
// Load in HueProperties, if first time use a properties file is created.
HueProperties.loadProperties();
// Set Up the View (A JFrame, MenuBar and Console).
DesktopView desktopView = new DesktopView();
// Bind the Model and View
Controller controller = new Controller(desktopView);
desktopView.setController(controller);
// Register the PHSDKListener to receive callbacks from the bridge.
phHueSDK.getNotificationManager().registerSDKListener(controller.getListener());
}
}
Did you ever solve this? In future it is probably best to post hue Java SDK issues on the GitHub site. https://github.com/PhilipsHue/PhilipsHueSDK-Java-MultiPlatform-Android/issues
I would have seen this sooner on here (I wrote this code btw so am possibly the culprit).
I do remember seeing a similar issue before, am pretty sure it was related to Macs and the JDK Compiler level used (I possibly used an incompatible Swing component on Mac and JDK 1.6). Can you let me know your OS and JDK Compiler level and I will check this further?
I got some questions regarding the use of threads, specially when you have to wait for a thread to be finished so you can perform other operations.
In my app, I use threads for operations such as http connections or when I read from or write to a RecordStore.
For example in the following class that I use to initialize my thread, I retrieve some customers from a webservice using the method called HttpQueryCustomers.
public class thrLoadCustomers implements Runnable {
private RMSCustomer mRMSCustomer;
private String mUrl;
public thrLoadCustomers(RMSCustomer rmsCust, String url) {
mRMSCustomer = rmsCust;
mUrl = url;
}
public void run() {
String jsonResultados = "";
try {
jsonResultados = HttpQueryCustomers();
} catch (IOException ex) {
//How to show a message from here??
} catch (SecurityException se) {
//How to show a message here??
} catch (NullPointerException npe) {
//How to show a message from here??
}
if (!jsonResultados.equals("")) {
try {
mRMSCustomer.save(jsonResultados);
} catch (RecordStoreException ex) {
//How to show a message from here???
}
}
}
public String HttpQueryCustomers() throws IOException,SecurityException,NullPointerException {
StringBuffer stringBuffer = new StringBuffer();
HttpConnection hc = null;
InputStream is = null;
System.out.println(mUrl);
try {
hc = (HttpConnection) Connector.open(mUrl);
if (hc.getResponseCode() == HttpConnection.HTTP_OK) {
is = hc.openInputStream();
int ch;
while ((ch = is.read()) != -1) {
stringBuffer.append((char) ch);
}
}
} finally {
is.close();
hc.close();
}
String jsonData = stringBuffer.toString();
return jsonData.toString();
}
}
Notice in the above class that I pass a parameter called rmsCust of the type RMSCustomer
RMSCustomer is a class that I use to handle all the operations related to RMS:
public class RMSCustomer {
private String mRecordStoreName;
private Customer[] mCustomerList;
public RMSCustomer(String recordStoreName) {
mRecordStoreName = recordStoreName;
}
public Customer[] getCustomers() {
return mCustomerList;
}
public Customer get(int index) {
return mCustomerList[index];
}
public void save(String data) throws RecordStoreException,JSONException,NullPointerException {
RecordStore rs = null;
int idNuevoRegistro;
String stringJSON;
try {
rs = RecordStore.openRecordStore(mRecordStoreName, true);
JSONArray js = new JSONArray(data);
//Set the size of the array
mCustomerList = new Customer[js.length()];
for (int i = 0; i < js.length(); i++) {
JSONObject jsObj = js.getJSONObject(i);
stringJSON = jsObj.toString();
idNuevoRegistro = addRecord(stringJSON, rs);
//Add a new Customer to the array
mCustomerList[i] = initializeCustomer(stringJSON, idNuevoRegistro);
}
} finally {
if (rs != null) {
rs.closeRecordStore();
}
}
}
public int addRecord(String stringJSON, RecordStore rs) throws JSONException,RecordStoreException {
byte[] raw = stringJSON.getBytes();
int idNuevoRegistro = rs.addRecord(raw, 0, raw.length);
return idNuevoRegistro;
}
public Customer initializeCustomer(String stringJSON, int idRecord) throws JSONException {
Customer c = new Customer();
JSONObject jsonObj = new JSONObject(stringJSON);
// Set Customer properties
//...
return c;
}
}
This class is used to show a list of customer and ,as you can see, it extends the List class and receives an array of Customers as a parameter.
public class ListCustomers extends List {
private final Customer[] mData;
public static ListCustomers create(Customer[] data) {
int i = 0;
for (; i < data.length; i++) {
if (data[i] == null) {
break;
}
}
String[] names = new String[i];
for (int j = 0; j < i; j++) {
names[j] = data[j].name;
}
return new ListCustomers(names, data);
}
protected ListCustomers(String names[], Customer[] data) {
super("List of Customer", IMPLICIT, names, null);
mData = data;
}
public Customer getSelectedObject() {
return mData[this.getSelectedIndex()];
}
}
Finally this is how I call the thread from the MIDlet (using all the 3 previous classes) when I want to show a List of Customers:
private void showCustomerList(String url) {
showWaitForm();
if (scrCustomerList == null) {
rmsCustomers = new RMSCustomer("rmsCustomers");
thrLoadCustomers load = new thrLoadCustomers(rmsCustomers, url);
Thread t = new Thread(load);
t.start();
try {
t.join();
} catch (InterruptedException ex) {
}
scrCustomerList = ListCustomers.create(rmsCustomers.getCustomers());
scrCustomerList.addCommand(cmdSelect);
scrCustomerList.addCommand(cmdBack);
scrCustomerList.setCommandListener(this);
}
mDisplay.setCurrent(scrCustomerList);
}
Now here's the problems I have :
The showWaitForm() doesn't work (it sets a form with a Gauge as the
Current form)
I don't know how to show all the exceptions that might be thrown from
within the thrLoadCustomers class.
I don't know whether using t.join() is the best choice
The last question is about something the book I'm reading says :
Threads, in particular, can be a scarce commodity. The MSA
specification requires that an application must be allowed to create
ten threads. Just because you can doesn’t mean you should. In general,
try to use the fewest resources possible so that your application will
run as smoothly as possible
This is the first time a use threads, and in my app I might have up to 10 threads (classes). However, I will only execute once thread at the time, will I be going against what the previous quotation says??
I hope I'm not asking too many questions. Thank you very much for your help.
P.D Much of the code I posted here wouldn't have been possible with the help of Gregor Ophey
Question #1 is about a different problem not related to threading, and for which very little code is shown. I'd suggest you to post a new dedicated question with proper explanation of the issue.
Questions #2 and #3: You could define a wrapper class like this:
public class WSResult {
private boolean success; //true if the WS call went ok, false otherwise
private String errorMessage; //Error message to display if the WS call failed.
private Object result; //Result, only if the WS call succeeded.
private boolean completed = false;
//TODO getter and setters methods here
}
In your screen, you can create an instance of result and wait for it:
WSResult result = new WSResult();
//Start thread here
new Thread(new LoadCustomersTask(result)).start();
//This is old school thread sync.
synchronized(result){
while(!result.isCompleted()){
result.wait();
}
}
//Here the thread has returned, and we can diaplay the error message if any
if(result.isSuccess()){
} else {
//Display result.getErrorMessage()
}
Then your runnable would be like this:
class LoadCustomersTask implements Runnable {
private final WSResult result;
public LoadCustomersTask(WSResult res){
result = res;
}
public void run(){
//Do the WS call
//If it went well
result.setSuccess(true);
result.setResult(jsonResultados);
//Else
result.setSuccess(false);
result.setErrorMessage("Your error message");
//In any case, mark as completed
result.setcompleted(true);
//And notify awaiting threads
synchronized(result){
result.notifyAll();
}
}
}
You can also do it with thread.join, but wait/notify is better because you not making the screen depend on the particular thread where the runnable runs. You can wait/notify on the result instance, as shown, or on the runnable if it is intended for a single use.
Question #4: Yes threads must not be abused, specially in JavaME where programs usually run in single core CPUs with a frecuency in the order of MHz. Try not to have more than 1-3 threads running at the same time. If you really need to, consider using a single thread for running all background tasks (a blocking queue).