Access an object from another class - java

I have a few classes and one main method. The program is used to connect to an Access database and retrieve information.
I have a class which deals with the GUI only (to display the results) in one JTextArea box. Another class runs a while loop and pulls in data from the database and assigns it to a String as such:
line = (line+upid+"\t"+StreetNum+"\t"+suburb+"\t"+area+"\t"+price+"\t"+agentID+"\t"+numBeds+"\t"+numBaths+"\t"+spool+"\t"+numGarages+"\t"+date+"\t"+ownerID+"\t"+SaleOrRent+"\n");
Basically my question is how do I access the String line from the GUI Class so that I can use txtArea.setTextto display line bearing in mind the GUI has no Main Method?
EDIT:
To try get around this, I have created a LineObject which takes line in as a parameter. I then call the getLine from the void addComponents but it gives a nullPointerException?
Here is the searchProps class:
import java.awt.Container;
import java.sql.*;
import java.util.*;
import javax.swing.*;
public class searchProps
{
protected String price, area, query, suburb, date, SaleOrRent, strQuery, out, line="";
protected int agentID, upid, StreetNum, numBeds, numBaths, numGarages, ownerID, size;
protected boolean spool;
PropertyObject PropertyArray[] = new PropertyObject[3];
LineObject obj;
JFrame jf;
JTextArea txtArea = new JTextArea();
{
initialFrame();
addComponents();
}
public searchProps(int propID) //search using UPID only
{
try
{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn = DriverManager.getConnection("jdbc:odbc:PropertyOracleDatabase");
Statement s = conn.createStatement();
query = ("SELECT * FROM Properties WHERE UPID = "+propID);
// Fetch table
s.execute(query);
ResultSet rs = s.getResultSet();
while((rs!=null) && (rs.next()))
{
upid=rs.getInt(1);
StreetNum=rs.getInt(2);
suburb=rs.getString(3);
area=rs.getString(4);
price=rs.getString(5);
agentID= rs.getInt(6);
numBeds=rs.getInt(7);
numBaths=rs.getInt(8);
spool=rs.getBoolean(9);
numGarages=rs.getInt(10);
date=rs.getString(11);
ownerID=rs.getInt(12);
SaleOrRent=rs.getString(13);
size++;
line = (line+upid+"\t"+StreetNum+"\t"+suburb+"\t"+area+"\t"+price+"\t"+agentID+"\t"+numBeds+"\t"+numBaths+"\t"+spool+"\t"+numGarages+"\t"+date+"\t"+ownerID+"\t"+SaleOrRent+"\n");
obj= new LineObject(line);
System.out.println(line);
String out = obj.getLine();
System.out.println(out);
}
// close and cleanup
s.close();
conn.close();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
void initialFrame()
{
jf=new JFrame();
jf.setSize (1300,700);
jf.setTitle("Property Oracle | Results Page");
jf.setVisible(true);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
void addComponents()
{
Container con = jf.getContentPane();
con.setLayout(null);
txtArea.setBounds(20,30,1200,600);
con.add(txtArea);
txtArea.setText("UPID\tStreetNum\tSuburb\tArea\tPrice\tAgentID\tBedrooms\tBathrooms\tSwimming Pool\tGarages\tDate\tOwner\tSale/Rent\n");
out = obj.getLine();
System.out.println(out);
}
}
And here is the LineObject class:
public class LineObject
{
protected String line;
public LineObject(String a)
{
line = a;
}
public String getLine()
{
return line;
}
}

I will assume your database access code runs in a separate thread, otherwise typical latency would block the event dispatch thread (EDT). Pass a reference to your JTextArea as a parameter to your database code. Use the reference to update the JTextArea on the EDT:
final String line = …
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ta.append(line);
}
});
A related example is seen here.

Make line a private class field of your class (the one that runs the while loop).
public LoopingClass {
private String line;
public void loopMethod() {
line = //...
}
}
Then create a public getter to this variable.
public LoopingClass {
private String line;
public void loopMethod() {
line = //...
}
public String getLine() {
return line;
}
}
Then from your GUI, call the getter of the object instance.
// somewhere in your GUI class
loopingClassInstance.getLine();

Take a look at the MVC pattern: it's always good practice to decouple the business logic (putting data in a database and building the string "line") from the frontend (GUI).
By the way, since you're building the string by appending more data to it, you should consider using a StringBuilder instead:
StringBuilder lineBuilder = new StringBuilder();
// append data:
lineBuilder.append(someString);
// create a string only when you need it:
String line = lineBuilder.toString();
In this way you are not continuosly creating new strings (which can be expensive in the long run especially if the string keeps growing), but using the buffer provided by the StringBuilder and then creating an actual string only when you need it, e.g., when you need to update your JTextArea.

Related

How to randomly communicate with a process without freezing the GUI in Java Swing?

I'm building a chess GUI application whose job is to display the board and the pieces and prevent illegal moves from being entered.
It should also have features that involve communication with a chess engine (e.g. stockfish). This is what I'm struggling with right now. The chess engine is an exe file that is accessed using ProcessBuilder:
Process chessEngineProcess = new ProcessBuilder(chessEngineUrl).start();
InputStream processInputStream = chessEngineProcess.getInputStream();
OutputStream processOutputStream = chessEngineProcess.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(processOutputStream));
BufferedReader reader = new BufferedReader(new InputStreamReader(processInputStream));
I want to send strings (commands in the UCI protocol) to the engine to which it responds by continuously outputting text for a few seconds or longer. This hangs up the GUI. I need to update a textArea (in real-time) in the GUI based on the engine's output. This wouldn't be a one-off type of operation. I would like to randomly do this (send a command and update GUI in real time) whenever certain GUI events happen (e.g. user makes a move).
I know that I need to do the stream reading in another thread and I know about SwingWorker but I simply can't get it to work properly.
What I tried:
Since the stream reading is a blocking operation (we keep waiting for output from the engine) the stream reading thread never terminates.
With that in mind, I tried creating a class that extends SwingWorker<Void, String> and sets up and contains the chessEngineProcess (as well as its stream reader and writer) as a private member variable. I implemented the doInBackground and process methods. I also had a public method in this class for sending a command to the engine.
public void sendCommandToEngine(String command) {
try {
writer.write(command + '\n');
writer.flush();
} catch (IOException e) {
JOptionPane.showMessageDialog(null, e.getMessage());
}
}
I do the stream reading in the doInBackground and then publish the output and update the GUI in the process method.
This results in very strange behavior when I send commands to the engine from my GUI classes (e.g. from event listeners). The displayed output is (sometimes partly and sometimes entirely?) wrong and often I get exceptions thrown.
I am at a loss and very desperate so please help! This is a very important project. Feel free to suggest any solution that you think would work!
EDIT:
I get a null pointer exception with the following stack trace:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
at Moves.Move.isMovePossible(Move.java:84)
at Moves.Move.executeMove(Move.java:68)
at gui.ChessBoard.performEngineMove(ChessBoard.java:328)
at gui.MainFrame.receiveEnginesBestMove(MainFrame.java:180)
at gui.EngineWorker.process(EngineWorker.java:91)
at javax.swing.SwingWorker$3.run(SwingWorker.java:414)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.run(SwingWorker.java:832)
at sun.swing.AccumulativeRunnable.run(AccumulativeRunnable.java:112)
at javax.swing.SwingWorker$DoSubmitAccumulativeRunnable.actionPerformed(SwingWorker.java:842)
at javax.swing.Timer.fireActionPerformed(Timer.java:313)
at javax.swing.Timer$DoPostEvent.run(Timer.java:245)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)
Some details:
Basically I have a "MainFrame" class that is a JFrame that contains all my GUI elements. This is where I add event listeners to my components. In certain event listeners, I call sendCommandToEngine. This will start the blocked doInBackground as the engine starts sending responses.
The process method can then call performEnginesMove on the chessBoard (which is a MainFrame component displaying the chess board) if it detects that a "best move" was output by the engine.
The performEnginesMove function checks if the move is valid (possible) and then makes the move on the board (with the help of the Move class).
For some reason, this doesn't work right.
I built a delegate for the Process and ProcessBuilder classes to show how the rest of the code should be used. I call those classes GameEngineProcess and GameEngineProcessBuilder respectively.
GameEngineProcess is creating the responses, which are simple Strings to be appended directly into the JTextArea of the player's GUI. It actually extends Thread to let it run asynchronously. So the implementation of this specific class is not what you are asking for, but it is used to simulate the Process class. I added some delay in the responses of this class to simulate the time needed by the engine to generate them.
Then there is the custom class OnUserActionWorker which extends SwingWorker and does asynchronously what you are asking for: it receives the responses from the engine process and forwards them to the GUI which updates its JTextArea. This class is used once per engine request, ie we create and execute a new instance of this class for every request the user creates while interacting with the GUI. Note that this does not mean the engine is closed and reopened for each request. The GameEngineProcess is started once and then remains running for the whole game uptime.
I am assuming you have a means of telling whether a single engine request has all its responses completed. For the sake of simplicity in this code I wrote, there exists a message (of type String) which is written each time at the process stream to indicate the end of the responses per request. This is the END_OF_MESSAGES constant. So this lets the OnUserActionWorker know when to terminate receiving responses, so the next instance of it will be later created for each new request.
And finally there is the GUI, which is a JFrame consisting of a JTextArea and a grid of buttons the player can interact with and send a request-command to the engine depending on the button pressed. Again I am using Strings as the commands but I'm guessing this is probably what you will need too in this case.
Follows the code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.List;
import java.util.Objects;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
public class Main {
//Just a simple 'flag' to indicate end of responses per engine request:
private static final String END_OF_MESSAGES = "\u0000\u0000\u0000\u0000";
//A class simulating the 'ProcessBuilder' class:
private static class GameEngineProcessBuilder {
private String executionCommand;
public GameEngineProcessBuilder(final String executionCommand) {
this.executionCommand = executionCommand;
}
public GameEngineProcessBuilder command(final String executionCommand) {
this.executionCommand = executionCommand;
return this;
}
public GameEngineProcess start() throws IOException {
final GameEngineProcess gep = new GameEngineProcess(executionCommand);
gep.setDaemon(true);
gep.start();
return gep;
}
}
//A class simulating the 'Process' class:
private static class GameEngineProcess extends Thread {
private final String executionCommand; //Actually not used.
private final PipedInputStream stdin, clientStdin;
private final PipedOutputStream stdout, clientStdout;
public GameEngineProcess(final String executionCommand) throws IOException {
this.executionCommand = Objects.toString(executionCommand); //Assuming nulls allowed.
//Client side streams:
clientStdout = new PipedOutputStream();
clientStdin = new PipedInputStream();
//Remote streams (of the engine):
stdin = new PipedInputStream(clientStdout);
stdout = new PipedOutputStream(clientStdin);
}
public OutputStream getOutputStream() {
return clientStdout;
}
public InputStream getInputStream() {
return clientStdin;
}
#Override
public void run() {
try {
final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(stdout));
final BufferedReader br = new BufferedReader(new InputStreamReader(stdin));
String line = br.readLine();
while (line != null) {
for (int i = 0; i < 10; ++i) { //Simulate many responses per request.
Thread.sleep(333); //Simulate a delay in the responses.
bw.write(line + " (" + i + ')'); //Echo the line with the index.
bw.newLine();
bw.flush();
}
bw.write(END_OF_MESSAGES); //Indicate termination of this particular request.
bw.newLine();
bw.flush();
line = br.readLine();
}
System.out.println("Process gracefull shutdown.");
}
catch (final InterruptedException | IOException x) {
System.err.println("Process termination with error: " + x);
}
}
}
//This is the SwingWorker that handles the responses from the engine and updates the GUI.
private static class OnUserActionWorker extends SwingWorker<Void, String> {
private final GameFrame gui;
private final String commandToEngine;
private OnUserActionWorker(final GameFrame gui,
final String commandToEngine) {
this.gui = Objects.requireNonNull(gui);
this.commandToEngine = Objects.toString(commandToEngine); //Assuming nulls allowed.
}
//Not on the EDT...
#Override
protected Void doInBackground() throws Exception {
final BufferedWriter bw = gui.getEngineProcessWriter();
final BufferedReader br = gui.getEngineProcessReader();
//Send request:
bw.write(commandToEngine);
bw.newLine();
bw.flush();
//Receive responses:
String line = br.readLine();
while (line != null && !line.equals(END_OF_MESSAGES)) {
publish(line); //Use 'publish' to forward the text to the 'process' method.
line = br.readLine();
}
return null;
}
//On the EDT...
#Override
protected void done() {
gui.responseDone(); //Indicate end of responses at the GUI level.
}
//On the EDT...
#Override
protected void process(final List<String> chunks) {
chunks.forEach(chunk -> gui.responsePart(chunk)); //Sets the text of the the text area of the GUI.
}
}
//The main frame of the GUI of the user/player:
private static class GameFrame extends JFrame implements Runnable {
private final JButton[][] grid;
private final JTextArea output;
private BufferedReader procReader;
private BufferedWriter procWriter;
public GameFrame(final int rows,
final int cols) {
super("Chess with remote engine");
output = new JTextArea(rows, cols);
output.setEditable(false);
output.setFont(new Font(Font.MONOSPACED, Font.ITALIC, output.getFont().getSize()));
final JPanel gridPanel = new JPanel(new GridLayout(0, cols));
grid = new JButton[rows][cols];
for (int row = 0; row < rows; ++row)
for (int col = 0; col < cols; ++col) {
final JButton b = new JButton(String.format("Chessman %02d,%02d", row, col));
b.setPreferredSize(new Dimension(b.getPreferredSize().width, 50));
b.addActionListener(e -> sendCommandToEngine("Click \"" + b.getText() + "\"!"));
gridPanel.add(b);
grid[row][col] = b;
}
final JScrollPane outputScroll = new JScrollPane(output);
outputScroll.setPreferredSize(gridPanel.getPreferredSize());
final JPanel contents = new JPanel(new BorderLayout());
contents.add(gridPanel, BorderLayout.LINE_START);
contents.add(outputScroll, BorderLayout.CENTER);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
super.getContentPane().add(contents);
super.pack();
}
//Utility method to enable/disable all the buttons of the grid at once:
private void gridSetEnabled(final boolean enabled) {
for (final JButton[] row: grid)
for (final JButton b: row)
b.setEnabled(enabled);
}
//This is the method that sends the next request to the engine:
private void sendCommandToEngine(final String commandToEngine) {
gridSetEnabled(false);
output.setText("> Command accepted.");
new OnUserActionWorker(this, commandToEngine).execute();
}
public BufferedReader getEngineProcessReader() {
return procReader;
}
public BufferedWriter getEngineProcessWriter() {
return procWriter;
}
//Called by 'SwingWorker.process':
public void responsePart(final String msg) {
output.append("\n" + msg);
}
//Called by 'SwingWorker.done':
public void responseDone() {
output.append("\n> Response finished.");
gridSetEnabled(true);
}
#Override
public void run() {
try {
//Here you build and start the process:
final GameEngineProcess proc = new GameEngineProcessBuilder("stockfish").start();
//Here you obtain the I/O streams:
procWriter = new BufferedWriter(new OutputStreamWriter(proc.getOutputStream()));
procReader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
//Finally show the GUI:
setLocationRelativeTo(null);
setVisible(true);
}
catch (final IOException iox) {
JOptionPane.showMessageDialog(null, iox.toString());
}
}
}
public static void main(final String[] args) {
new GameFrame(3, 3).run(); //The main thread starts the game, which shows the GUI...
}
}
And finally, another important assumption I made is that when the user interacts with the GUI, the GUI blocks input (but keeps on responding to other events). This prevents the user to have more than one active requests to the engine simultaneously. By blocking input I simply mean that when you click on a button, first all buttons are disabled and then the command is sent to the engine. The buttons are all then reenabled when all the responses for the latest made request finish.
If you need to have more than one requests possible at the same time to a single engine then you will probably need to synchronize the access of some of the GUI's methods and also make sure that each OnUserActionWorker can distinguish its responses from the others. So that would be a different story, but let me know if this is what you want instead.
To test the responsiveness of the EDT while the responses are being received you can for example simply resize the window with the mouse while the (ten) responses are still being received, or just notice that the responses are printed into the JTextArea at real time.
Hope it helps.

Having trouble with text file input in Java

My professor really threw us into this project with a blindfold on. We didn't go into depth on using and inserting files into Java. I'm getting a ton of errors, which are most likely due to my incorrect insertion of the file. I saved the text file in the same place the class file is saved on my computer, assuming that would be necessary. I've moved it around multiple places on my computer trying to get it to work. Here is the main program. I'm sorry if it's completely incorrect.
To explain what we're supposed to be doing further, here is the link to the prompt with the pseudocode. I haven't attempted to do all the actions listed because I haven't gotten the file to insert correctly yet.
http://jcsites.juniata.edu/faculty/rhodes/cs1/projects/program9Gen.html
Edit: This is the whole program in its glory. The class was created in a separate project as our introduction to Java classes. We were just told to use it again and insert the main program at the bottom just for ease of grading.
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class GenSeq
{
private String seq;
private String species;
private String startCodon;
private String stopCodon;
private String shape;
private String chromosomeLocation;
public GenSeq (String seq, String species, String startCodon, String stopCodon,
String shape, String chromosomeLocation){
this.seq = seq;
this.species = species;
this.startCodon = startCodon;
this.stopCodon = stopCodon;
this.shape = shape;
this.chromosomeLocation = chromosomeLocation;
}
//Allowing the program to later set constructors
//Creating all the appropriate getters and setters for the instance variables
public void setSpecies(String newSpecies){
species = newSpecies;
}
public String getSpecies(){
return species;
}
public void setStartCodon(String newStartCodon){
startCodon = newStartCodon;
}
public String getStartCodon(){
return startCodon;
}
public void setStopCodon(String newStopCodon){
stopCodon = newStopCodon;
}
public String getStopCodon(){
return stopCodon;
}
public void setShape(String newShape){
shape = newShape;
}
public String getShape(){
return shape;
}
public void setChromosomeLocation(String newChromosomeLocation){
chromosomeLocation = newChromosomeLocation;
}
public String getChromosomeLocation(){
return chromosomeLocation;
}
public String toString(){
return "Sequence length: " + seq.length() +
"\nSpecies: "+ species +
"\nStart Codon: "+ startCodon +
"\nStart Codon: "+ stopCodon+
"\nShape: "+ shape +
"\nChromosomal Location: " + chromosomeLocation;
//Creating a toString method to hold all the class data
}
}
public static void main (String args[ ])
{
GenSeq seqA = null;
//Setting constructor to default if not set
//Opening the file
Scanner inputStream = null;
String seq;
try
{
inputStream = new Scanner (new File ("W:\jcsites.junata.edu\students\morrian14\seq.txt"));
}
catch (FileNotFoundException e)
{
System.out.println ("Error opening the file ");
System.exit (0);
}
do{
inputStream = inputStream.trim();
if ('>' == inputStream.charAt(0)){
seq = inputStream.nextLine();
}
}
while (inputStream.hasNextLine());
inputStream.close();
}
The error is this same one repeated continuously
File: C:\LEXIPC\Users\Alexis\GenSeq.java [line: 83]
Error: class, interface, or enum expected
One obvious issue, the last line is clearly meant to have been written as inputStream.close(); and not input.Stream.close(); you will probably need a try .. catch ... around closing the stream too
What exactly is your question? A few notes though...
Get rid of the do{} while() and just do something like this:
while(inputStream.hasNextLine(){
if('>' == inputStream.charAt(0))
seq = inputStream.nextLine();
}
inputStream.close();
I am a bit confused as to why you are recycling seq to read from the file, as that is what you are using as your file's name. A better way to do this would be to use a File class for your file names. Consider: File seq = new File(.../filename.txt).
Also, if you find that you are using too many try/catch blocks, consider using an exception handling class to clean up your code.

How can i decouple my networkmanager using events?

i'm writing a program that connects with various TCP network devices. The GUI is made using JavaFX. The whole connection part is in its own package "Network". Roughly described, it looks like this: (I don't know much about UML, no blaming plaese :/ - i just needed a way to quickly describe how my program structure looks). http://i.stack.imgur.com/PSdsH.jpg
okay thats how it is:
The TCP classes are stored in a synchronized List in "NetworkManager". These classes hold information about the connection (how much data received yet, ip, mac etc.). The Rcv-Thread constantly tries to receive data.
well, this is what i want:
As soon as the Rcv-Thread receives a specific message, the controller should be invoked to do something (GUI refresh or whatever). Also the controller should stay decoupled from the "Network" module-> it is reused in another project. I want to achieve this behaviour through an custom event. In short: TCP-Rcv-Thread needs to be able to give information to the Controller. But i dont really know how to get it all to work. Lets see where i am:
I have an event class in the "Network" module.
import java.util.EventObject;
public class XEvent extends EventObject{
String message;
public XEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
I have a listener class in the "Network" module.
import java.util.EventListener;
public interface XListener extends EventListener{
void handlerMethod1(XEvent event);
void handlerMethod2(XEvent event);
}
I tried to prepare my Rcv-Thread for firing the event:
import javax.swing.event.EventListenerList;
import java.io.IOException;
public class ReceiveDataThread implements Runnable {
protected EventListenerList listenerList = new EventListenerList();
}
protected void addXListener(XListener xListener) {
listenerList.add(XListener.class, xListener);
}
protected void removeListener(XListener xListener) {
listenerList.remove(XListener.class, xListener);
}
protected void fireHandlerMethod1(String message) {
XEvent event = null;
Object[] list = listenerList.getListenerList();
for (int i = 0; i < list.length; i += 2) {
if (list[i] == XListener.class) {
if (event == null) event = new XEvent(this, message);
XListener l = (XListener) list[i + 1];
l.handlerMethod1(event);
}
}
}
protected void fireHandlerMethod2(String message) {
XEvent event = null;
Object[] list = listenerList.getListenerList();
for (int i = 0; i < list.length; i += 2) {
if (list[i] == XListener.class) {
if (event == null) event = new XEvent(this, message);
XListener l = (XListener) list[i + 1];
l.handlerMethod2(event);
}
}
}
#Override
public void run() {
String s;
while (!stopThread) {
s = receiveData();
System.out.println("test");
fireHandlerMethod1(s);
}
}
The Controller (this class should react on the custom events) implements the Listener:
public class Controller implements Initializable, XListener {
#Override
public void handlerMethod1(XEvent event) {
System.out.println("Event1: " + event.getMessage());
}
#Override
public void handlerMethod2(XEvent event) {
}
}
And from there on i'm not really shure how to get it work that my events (fired from my Rcv-Thread) are noticed by my controller class. I think i have to add a listener to every Rcv-Thread object via the controller class (just like when i use a ButtonListener, ...) . The problem is: from my TCP Class i can't access the Rcv-Thread-object's addXListener method - even when set to public (but i can access the Rcv-Thread-Classes from the list). I tried to read as much as i can about the problem but cant figure out how to get this to work. What am i missing?
edit1: TCP class:
public class TCPClass{
private Thread receiveDataThread;
private String MAC;
private InetAddress IP;
private Socket socket = new Socket();
private int tcpSendPort;
private int timeOut = 10;
private ObjectOutputStream objectOutputStream;
private BufferedReader bufferedReader;
private String connectionStatus = "offline";
public TCPClass(DatagramPacket datagramPacket) {
IP = datagramPacket.getAddress();
setConnectionStatusOnline();
tcpSendPort = 50000 + NetworkManager.getNumberOfConnections();
MAC = extractMac(datagramPacket);
}
public void connect(int tcpPort) {
try {
socket = new Socket(IP, tcpPort, null, tcpSendPort);
bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
receiveDataThread = new Thread(new ReceiveDataThread(this));
receiveDataThread.start();
InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
System.out.println("on MAC: " + getMAC() + "\non Device:" + toString());
}
if (socket.isConnected()) {
setConnectionStatusConnected();
}
}
}
The NetworkManager creates an object of TCPClass and calls the connect() method.
Ok so after days i figured it out myself.
The main problem was that i was not able to call the addXListener() method of Rcv-Thread from the Controller. I took the Custom Event stuff out of the Rcv-Thread and moved it to the TCP-Class. Now i'm able to add the Listener to these classes. If i want to fire an event from the Rcv-Thread i simply call fireHandlerMethod() from its superclass (TCP-Class) - and everything works as expected.

Read an AMF object with flex socket

I'm currently trying to communicate between java and flex by using sockets and AMF serialized objects.
On the java side I use Amf3Input and Amf3Output from BlazeDS (flex-messaging-common.jar and flex-messaging-core.jar).
The connection is correctly established, and if i try to send object from flex to java, i can easily read objects :
FLEX side :
protected function button2_clickHandler(event:MouseEvent):void
{
var tmp:FlexAck = new FlexAck;
tmp.id="123456789123456789123456789";
tmp.name="A";
tmp.source="Aaaaaa";
tmp.ackGroup=false;
s.writeObject(tmp);
s.flush();
}
JAVA side :
ServerSocket servSoc = new ServerSocket(8888);
Socket s = servSoc.accept();
Amf3Output amf3Output = new Amf3Output(SerializationContext.getSerializationContext());
amf3Output.setOutputStream(s.getOutputStream());
Amf3Input amf3Input = new Amf3Input(SerializationContext.getSerializationContext());
amf3Input.setInputStream(s.getInputStream());
while(true)
{
try
{
Object obj = amf3Input.readObject();
if(obj!=null){
if (obj instanceof AckOrder){
System.out.println(((AckOrder)obj).getId());
}
}
}
catch (Exception e)
{
e.printStackTrace();
break;
}
}
amf3Output.close();
amf3Input.close();
servSoc.close();
In this way it works perfectly, but the problem is to read objects sent from the java side.
The code I use in java is :
for(int i=0;i<10;i++){
ack = new AckOrder(i,"A","B", true);
amf3Output.writeObject(ack);
amf3Output.writeObjectEnd();
amf3Output.flush();
}
I have an handler on ProgressEvent.SOCKET_DATA :
trace((s.readObject() as FlexAck).id);
But I have errors such as :
Error #2030: End of File detected
Error #2006: Index Out of bound
If i add manipulations on ByteArrays, i manage to read the first object, but not the following.
s.readBytes(tmp,tmp.length);
content = clone(tmp);
(content.readObject());
trace("########################## OK OBJECT RECEIVED");
var ack:FlexAck = (tmp.readObject() as FlexAck);
trace("**********************> id = "+ack.id);
I've spent many our trying to find something in several forums etc, but nothing helped.
So if someone could help me it would be great.
Thanks
Sylvain
EDIT :
Here is an example that I thought should work, but doesn't I hope that it's better illustrate what I aim to do (permanent connection with socket and an exchange of messages).
Java class :
import java.net.ServerSocket;
import java.net.Socket;
import awl.oscare.protocol.AckOrder;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf3Input;
import flex.messaging.io.amf.Amf3Output;
public class Main {
public static void main(String[] args) {
while(true)
{
try {
ServerSocket servSoc = new ServerSocket(8888);
Socket s = servSoc.accept();
System.out.println("connection accepted");
Amf3Output amf3Output = new Amf3Output(SerializationContext.getSerializationContext());
amf3Output.setOutputStream(s.getOutputStream());
Amf3Input amf3Input = new Amf3Input(SerializationContext.getSerializationContext());
amf3Input.setInputStream(s.getInputStream());
while(true)
{
try
{
System.out.println("Reading object");
Object obj = amf3Input.readObject();
if(obj!=null)
{
System.out.println(obj.getClass());
if (obj instanceof AckOrder)
{
AckOrder order = new AckOrder();
order.setId(((AckOrder)obj).getId());
order.setName(((AckOrder)obj).getName());
order.setSource(((AckOrder)obj).getSource());
order.setAckGroup(((AckOrder)obj).isAckGroup());
System.out.println(((AckOrder)obj).getId());
amf3Output.writeObject(order);
amf3Output.writeObjectEnd();
amf3Output.flush();
}
}
}
catch (Exception e)
{
e.printStackTrace();
break;
}
}
amf3Output.close();
amf3Input.close();
servSoc.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Java Serializable object :
package protocol;
import java.io.Serializable;
public class AckOrder implements Serializable {
private static final long serialVersionUID = 5106528318894546695L;
private String id;
private String name;
private String source;
private boolean ackGroup = false;
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public void setSource(String source) {
this.source = source;
}
public String getSource() {
return this.source;
}
public void setAckGroup(boolean ackGroup) {
this.ackGroup = ackGroup;
}
public boolean isAckGroup() {
return this.ackGroup;
}
public AckOrder()
{
super();
}
}
Flex Side :
Main flex code :
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
import mx.utils.object_proxy;
private var _socket:Socket = new Socket();;
private function onCreationComplete():void
{
this._socket.connect("localhost",8888);
this._socket.addEventListener(ProgressEvent.SOCKET_DATA, onData);
}
private function onData(e:ProgressEvent):void
{
if(this._socket.bytesAvailable)
{
this._socket.endian = Endian.LITTLE_ENDIAN;
var objects:Array = [];
try{
while(this._socket.bytesAvailable > 0)
{
objects.push(this._socket.readObject());
}
}catch(e:Error){trace(e.message);}
trace("|"+(objects)+"|");
}
}
protected function sendButton_clickHandler(event:MouseEvent):void
{
var tmp:FlexAck = new FlexAck;
tmp.id="1";
tmp.name="A";
tmp.source="B";
tmp.ackGroup=false;
this._socket.writeObject(tmp);
this._socket.flush();
}
]]>
</fx:Script>
<s:Button x="0" y="0" name="send" label="Send" click="sendButton_clickHandler(event)"/>
Flex serializable object :
package
{
[Bindable]
[RemoteClass(alias="protocol.AckOrder")]
public class FlexAck
{
public function FlexAck()
{
}
public var id:String;
public var name:String;
public var source:String;
public var ackGroup:Boolean;
}
}
Edit 25/05/2011 :
I've added those listeners in my flex code :
this._socket.addEventListener(Event.ACTIVATE,onActivate);
this._socket.addEventListener(Event.CLOSE,onClose);
this._socket.addEventListener(Event.CONNECT,onConnect);
this._socket.addEventListener(Event.DEACTIVATE,onDeactivate);
this._socket.addEventListener(IOErrorEvent.IO_ERROR,onIOerror);
this._socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onSecurityError);
But There's no errors and I still don't manage to receive objects correctly.
You have to send the AMF data as ByteArray on the server:
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
amf3Output.setOutputStream(baos);
amf3Output.writeObject(order);
amf3Output.flush();
amf3Output.close();
s.getOutputStream().write(baos.toByteArray());
Then
this._socket.readObject()
works as expected !
Hi the problem is caused by the following:
An AMF stream is stateful. When it serializes objects, it compresses them relative to objects that it have already been written.
Compression is achieved by referencing previously sent class descriptions, string values and objects using indexes (so for example, if the first string you sent was "heloWorld", when you later send that string, the AMF stream will sent string index 0).
Unfortunately, ByteArray and Socket do not maintain reference tables between readObject calls. Thus, even if you keep appending your newly read objects to the end of the same ByteArray object, each call to readObject instantiates new reference tables, discarding previously created ones (this means it should work for repeated references to the same string within an object tree)
In your example, you are always writing the same string values to properties. Thus when you send the second object, its string properties are not serialized as strings, but as references to the strings in the previously written object.
The solution, is to create a new AMF stream for each object you send.
This is complete rubbish of course(!) It means we can't really utilize the compression in custom protocols. It would be much better if our protocols could decide when to reset the these reference tables, perhaps when they got too big.
For example, if you have an RPC protocol, it would be nice to have an AMF stream pass the remote method names as references rather than strings for speed...
I haven't checked but I think this sort of thing is done by RTMP. The reason it probably wouldn't have been made available in developer objects like ByteArray and Socket (sigh, I hope this isn't true) is because Adobe wants to push us towards LCDS...
Addendum/edit: just found this, which provides a solution http://code.google.com/p/cvlib/
After looking at the code, I think what you want to do on the Java end is this:
for(int i=0;i<10;i++){
ack = new AckOrder(i,"A","B", true);
amf3Output.writeObject(ack);
}
amf3Output.flush();
When you do 'flush', you're sending information over the socket so you only had one object being sent at a time. On the Flex end, you should always try to see what's the length of the object and make sure you're not going over it which would cause this error.
EDIT:
private var _socket:Socket = new Socket();
private function onCreationComplete():void
{
// Add connection socket info here
this._socket.addEventListener(ProgressEvent.SOCKET_DATA, onData);
}
// This gets called every time we get new info, as in after the server flushes
private function onData(e:ProgressEvent):void
{
if(this._socket.bytesAvailable)
{
this._socket.endian = Endian.LITTLE_ENDIAN; // Might not be needed, but often is
// Try to get objects
var objects:Array = [];
try{
while(this._socket.bytesAvailable > 0)
{
objects.push(this._socket.readObject());
}
}catch(e:Error){}
// Do something with objects array
}
}
The onData function is called continually (every time the server sends info) since everything is asynchronous.

How can I pass a variable to an inner thread class?

I want to load an image at periodic intervals to an imageitem. My outer class is generating the URL and I need to pass it to the innerclass. How do I achieve this?
public class MapTimer extends TimerTask{
public void run() {
System.out.println("Map starting...");
String URL=null,serverquery=null;
try {
sendMessage(this.message);
item.setLabel(item.getLabel()+"start");
serverquery=receiveMessage();
item.setLabel(item.getLabel()+"stop");
URL = getURL(serverquery); // my url to be passed to innerclass
System.out.println("URl is "+serverquery);
item.setLabel(URL+item.getLabel());
Thread t = new Thread() {
public void run() {
item.setLabel(item.getLabel()+"6");
try {
Image image = loadImage(URL); // using url
System.out.println("GEtting image....");
item = new ImageItem(null, image, 0, null);
form.append(item);
display.setCurrent(form);
} catch (IOException ioe) {
item.setLabel("Error1");
}
catch (Exception ioe) {
item.setLabel("Error1");
}
}
};
t.start(); // write post-action user code here
}
catch(Exception e){
System.out.println("Error3"+e);
}
}
}
How do I pass the URL to my innerthread class?
You have to declare the variable final or don't use a variable but a field in the class.
public class YourClass {
private String url;
public void yourMethod {
url = getURL(serverquery);
System.out.println("URl is "+serverquery);
item.setLabel(URL+item.getLabel());
Thread t = new Thread() {
public void run() {
item.setLabel(item.getLabel()+"6");
try {
Image image = loadImage(url); // using url
System.out.println("GEtting image....");
item = new ImageItem(null, image, 0, null);
form.append(item);
display.setCurrent(form);
} catch (IOException ioe) {
item.setLabel("Error1");
}
catch (Exception ioe) {
item.setLabel("Error1");
}
}
};
t.start(); // write post-action user code here
}
}
It should work if your URL is contained in a final reference. This means that the reference won't move. Consequently, you can't initialize it to null and then call getURL, you need to declare it at this point:
final String URL = getURL(serverquery);
I found that there are (at least) 2 major ways of doing this on whever your thread gets the information or recieves the information (in your case URL)
1) gets the informations:
what you want to do is keep a reference of the object containing the data you need (like URL) in your Thread, and when your ready the Thread gets the next item/URL to load.
public class MapTimer extends TimerTask{
public void run() {
...
}
private URLGenerator urlGenerator = null;
public MapTimer(URLGenerator urlGen){
...
}
This will give you the option to get the next URL when your thread is free (with a if(urlGen != null) of course)
2) Receives Information
Works better for cases where the thread is called only when the rest of the program wants to (user inputing data etc)
public class MapTimer extends TimerTask{
public void run() {
...
}
private URL urlToLoad = null;
public void setURL(URL urlToLoad){
...
//store and wake up thread
}
this way your thread recieve with a setter the data it needs to run, processes it then waits for the next data to be send to it (of course you need to be careful of thread issues like multiple calls etc.)
Hope this helps you
Jason
Just make the field a static variable belonging to the class and not any object instance of the class, like this:
public YourClass {
private static String url;
//...
}
Please note however that the static variable will be shared among all object instances of the class.

Categories

Resources