Link to home
Start Free TrialLog in
Avatar of john8932
john8932

asked on

Allowing separate classes to interact

The first question is, is this possible i.e. suppose I had two classes - a male class and a female class running at the same time, and i wanted them to interact, like if a command was issued in the male then a variable would change in the female.

The separate programs would update each other while running at the same time.

If this is possible then what would be the easiest way to do it - please tell me know  where to start researching or give any hints.

I am making a program that demonstrates AI.

Thanks anyone
John
Avatar of sciuriware
sciuriware

Are you running 2 programs? Then have them communicate through pipes or sockets.
Are you running 2 threads in a single program? Then have them communicate through static data.

Never say "I have 2 classes ... and I want them to interact" :
classes do nothing in JAVA,
it's the methods that 'do' and the variables and objects that are 'done'.

;JOOP!
Avatar of john8932

ASKER

At the moment a single thread for each program - otherwise too complex and not enough time.

So I need to get the methods in the different classes interacting.

Can you give an example of pipes or sockets please.

Cheers
1) Single thread in a JAVA program is impossible; even a "Hello World" program has already 7 threads. Just for understanding.
2) Methods are always in classes and the interaction is on the data level.
3) The simplest way to create pipes is to have program A start program B:

In A:
...........................
         Process p = Runtime.getRuntime().exec(commandVector, null, dir); // commandVector is a String[] describing B.
         inputStream is = p.getInputStream(); // You can get standard input from B, use a thread!
         outputStream is = p.getOutputStream(); // You can write standard to B's standard input, use a thread!
The effeciency depends on your needs. Highest is unformatted binary data in defined chunks,
that's a matter of smart programming.

;JOOP!
produce in one app a ServerSocket, in the other a normal Socket. So you can make them communicate:

This is a little programm that illustrates how to use the sockets (you have to start it twice!):

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.ServerSocket;
import java.net.Socket;

public class Test {
   public static void main(String[] args) throws Exception {
      Socket socket;
      String output;
      try {
         socket = new Socket("localhost", 1977); // throws Exception below, if no connection is possible
         output = "Test client ";
      } catch(ConnectException ce) {
         // no connection was possible, so make it server
         socket = new ServerSocket(1977).accept();

         output = "Test server ";
      }
      BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      for(int num = 0; num < 16; num++) {
         socket.getOutputStream().write((output + num + "\n").getBytes());
         socket.getOutputStream().flush();
         String outputStr = in.readLine();
         System.out.println(outputStr);
      }
      socket.close();
   }
}
Agreed, but use threads to avoid deadlocks.

;JOOP!
Ok, this time with ALL the comments...

public class Test {
   public static void main(String[] args) throws Exception {
      Socket socket;
      String output;
      try {
         socket = new Socket("localhost", 1977); // throws Exception below, if no connection is possible
         output = "Test client ";
      } catch(ConnectException ce) {
         // no connection was possible, so make app server
         socket = new ServerSocket(1977).accept();
         // be aware that accept is blocking!
         output = "Test server ";
      }
      // for convenience use...
      BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      for(int num = 0; num < 16; num++) {
         socket.getOutputStream().write((output + num + "\n").getBytes());
         // makes sure that we immediately send information
         // this is useful using files, seems not be necessary using sockets
         // socket.getOutputStream().flush();
         
         // this is blocking, too!
         System.out.println(in.readLine());
      }
      socket.close();
   }
}
Ok thanks for the replies.

I think the sockets idea will be the easiest to implement.

I will do it by sending strings:
 -If string equals update male then update male and vice versa, etc etc.

Like so:

public void connect(String update) throws Exception
    {
          Socket socket;
          String output;
          try
          {
                  socket = new Socket("localhost", 1977); // throws Exception below, if no connection is possible
                output = "Test client ";
          }
          catch(ConnectException ce)
          {
               // no connection was possible, so make app server
               socket = new ServerSocket(1977).accept();
               // be aware that accept is blocking!
               output = "Test server ";
              }
              // for convenience use...

              BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            for(int num = 0; num < 2; num++)
            {
                String command = socket.getInputStream();

                  if(command.equals("updateMale"))
                  {
                        malePartnerExercise++;
                        malePartnerFood--;
                        malePartnerBoredom++;
                  }

                  if(update.equals("updateFemale"))
                  {
                          socket.getOutputStream().write(("updateFemale").getBytes());
                  }
            }
              socket.close();
    }

At the moment it does not compile because of this line:

String command = socket.getInputStream();

I need to convert the string from a socketinput- please tell me how I can do this.

Cheers
InputStream command = socket.getInputStream(); // Is not a String.

;JOOP!
replace it by:

String command =  in.readLine();

;JOOP!
Ok I think im getting somewhere now, cheers.

I have not got them mating yet although once I get the connect() method worknig at the right times they will.

Basicly for them to mate the connect method should be running in each program/class.

Any ideas on how to do this?

Maybe when a "mate" command is issued it tells the connect() method in the other program to run therefore a connection is made and they mate i.e. change variables.

Cannot get this going tho.

public void connect(String update) throws Exception
      {
            Socket socket = null;

            try
            {
                System.out.println("Connection made");
                socket = new Socket("localhost", 1977); // throws Exception below, if no connection is possible

                  socket.getOutputStream().write(("connect").getBytes());
            }
            catch(ConnectException ce)
            {
                  System.out.println("No connection made");
                  display(exercise, food, boredom, energy, volatility);
                // no connection was possible, so make app server
                //socket = new ServerSocket(1977).accept();
                // be aware that accept is blocking!
            }
            // for convenience use...

            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

            String command = in.readLine();

            if(command.equals("connect"))
            {
                  if(command.equals("updateFemale")) // incoming
                  {
                        System.out.println("Mating");

                        exercise++;
                        food--;
                        boredom++;

                        display(exercise, food, boredom, energy, volatility);
                  }
            }

            if(update.equals("updateMale"))
            {
                  socket.getOutputStream().write(("updateMale").getBytes());
            }

            socket.close();
      }

Create in each a listener thread that reads all incoming messages and stores them in a Vector (thread safe!).
Your other running code (thread?) can then without any problem:
1) see if there is something in the "inbox",
2) take messages out the box, even not in the order that they came in.

;JOOP!
Ok what would this look like:
      Thread backgroundThread = new Thread(new Runnable() // reads all incoming messages and stores them in a Vector
      {
            public void run()
            {
               while(true)
               {
                     try
                     {
                           BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                           command = in.readLine();
                     }
                     catch(Exception e)
                     {
                     }
               }
            }
      });

and then this to call it:

backgroundThread.start();

Whats a vector by the way?

Cheers
    Thread backgroundThread = new Thread(new Runnable() // reads all incoming messages and stores them in a Vector
should be:
     Thread backgroundThread = new Thread(new Runnable()) // reads all incoming messages and stores them in a Vector

A Vector is a kind of array / list that automatically extends when you add objects (to the end) and shrinks as you
remove objects (from the head). See the JAVADOC for all details.
It is impossible that a thread filling a Vector and another thread removing from a Vector corrupt the Vector.

Now, move the line:
                      BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
to before the while and add:
                      public Vector commands = new Vector();
before the 'run()', then change:
                       command = in.readLine();
into:
                   String line = in.readLine();
                   if(line == null)
                   {
                         break; // out of the while
                   }
                   commands.add(new String(line + '\n'));
consequently:
                 catch(Exception e)
should be:
                 catch(IOException e)
and please don't ignore it, do something.

finally, after the while loop:
            close(i);

now,
            backgroundThread.commands
is a Vector that contains all lines read, unless some already were processed.

This is not exactly the way I did it, but it is a start.


;JOOP!
Okay cool, like this?

Thread backgroundThread = new Thread(new Runnable() // reads all incoming messages and stores them in a Vector
      {
            public Vector commands = new Vector();
            public void run()
            {
               BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

               while(true)
               {
                     try
                     {
                           String line = in.readLine();
                           if(line == null)
                           {
                                 break; // out of the while
                           }
                   commands.add(new String(line + '\n'));
                     }
                     catch(IOException e)
                     {
                     }
               }
               close(i);
            }
      });
Whats that I for please?
Still:
    Thread backgroundThread = new Thread(new Runnable() // reads all incoming messages and stores them in a Vector
should be:
     Thread backgroundThread = new Thread(new Runnable()) // reads all incoming messages and stores them in a Vector
                                                                                        /\
and:
                 catch(IOException e)
                  {                                     // No code?
                  }
correction:
             close(i);
should be of course:
             close(in);


;JOOP!
           catch(IOException e)
                  {
System.out.println("IO error")
                  }
             }
             close(i);
          }
     });
     /\

This is the ending/closing bracket.

Does not like the close(in); i.e. does not compile
AIPet.java:364: cannot find symbol
symbol: method close(java.io.BufferedReader)
                          close(in);

Please tell me how I need to use the vector and that should be it.

Sorry, sorry it's getting late here.

Your }); is correct.

    close(in);  must be    in.close();

Btw.: prepare that:
                   System.out.println("IO error");         // don't forget the ;
is not seen unless debugged in ECLIPSE,
so make it do something better in the end.

A remark:
I never use this kind of "spontaneous" classes, because it's too difficult
to access their data and methods from another thread.
So, I place the code in a real, separate class and I make the reference 'global'.
All my globals are always blank (package access) statics in a class called <myprogram>Adm
A usefull habit.

;JOOP!
Dont worry about it :)

And I changed it to in.close();

Ive made the variables global i.e. attiributes.

      Thread backgroundThread = new Thread(new Runnable() // reads all incoming messages and stores them in a Vector
      {
            public Vector commands = new Vector();
            public void run()
            {
               BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

               while(true)
               {
                     try
                     {
                           String line = in.readLine();
                           if(line == null)
                           {
                                 break; // out of the while
                           }
                   commands.add(new String(line + '\n'));
                     }
                     catch(IOException ioe)
                     {
                           System.err.println("Caught Exception: " + ioe.getMessage());
                     }
               }
               in.close();      
            }
      });

Still not compiling though.

Saying I need to:

AIPet.java:344: unreported exception java.io.IOException; must be caught or declared to be thrown
                   BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()
                                                                                                     ^
AIPet.java:362: unreported exception java.io.IOException; must be caught or declared to be thrown
                   in.close();
                           ^

Cheers
Infact could these be global, to?

BufferedReader in

and

public Vector commands = new Vector();
Change it to:
// in the data section of your class:

    public static Vector commands = null;

...................

    Thread backgroundThread = new Thread(new Runnable() // reads all incoming messages and stores them in a Vector
     {
          commands = new Vector();
          public void run()
          {
             try
             {
                  BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                  while(true)
                  {
                       String line = in.readLine();
                       if(line == null)
                       {
                            break; // out of the while
                       }
                       commands.add(new String(line + '\n'));
                  }
                  in.close();    
              }
              catch(IOException ioe)
              {
                   System.err.println("Caught Exception: " + ioe.getMessage());
              }
          }
     });

it's better to leave 'in' local.
I would prefer to keep 'commands' inside the thread, but then it should be a separate class
and not, like here, a "last-minute" class.

;JOOP! nearly sleeping.
I made this an attribute:

public static Vector commands = null;

But the compiler is still saying bad things i.e...

AIPet.java:343: <identifier> expected
            commands = new Vector();
                     ^
1 error

That's JAVA 5 stuff, if you are only using String's:

  public static Vector<String> commands = null;

and:

          commands = new Vector<String>();
 
;JOOP!
If you don't know what kind of objects you're going to use in such a case:

  public static Vector<Object> commands = null;

and:

          commands = new Vector<Object>();
 
BUT!!!!!!!!!!!!!!!!!!!!! to what ore you going to cast what you remove from the Vector?
That's a thing to remember for later.

YAWN.
;JOOP!
Nope, still the same error.

Im just wondering when we do get the program compiling, how do we use the commands vector?

Cheers
It compiles with me,

but maybe you didn't add:

import java.util.Vector;

at the top of the page ....


;JOOP!
Nope import java.util.Vector; is there.

This is annoying - One little compile error when we are so almost there.

Please can I see what you are compiling so i can compare.
Again, what's exactly the error message. I never compile from the commandline
but by ECLIPSE.

;JOOP!
AIPet.java:344: <identifier> expected
            commands = new Vector<Object>();
                             ^
1 error
where is:
  public static Vector<Object> commands = null;


Can you check this out: this little snippet compiles correctly:

// BEGIN OF SNIPPET
import java.util.Vector;

/**
 * This class serves as a testbank for bug derobation and doesn't rely on own packages.
 */

public class Bugs
{
   public static Vector<String> commands = null;
   /**
    * Program entry point.
    *
    * @param commandLine program command line vector.
    */
   public static void main(String[] commandLine)
   {
      String h = "Hi";
     
      commands = new Vector<String>();
      commands.add(h);
   }
}
// END OF SNIPPET

;JOOP!
Then, suddenly ............ are you using JAVA 5 or JAVA 1.4?
That would turn everything around, then it would be:

// BEGIN OF SNIPPET
import java.util.Vector;

/**
 * This class serves as a testbank for bug derobation and doesn't rely on own packages.
 */

public class Bugs
{
   public static Vector commands = null;
   /**
    * Program entry point.
    *
    * @param commandLine program command line vector.
    */
   public static void main(String[] commandLine)
   {
      String h = "Hi";
     
      commands = new Vector();
      commands.add(h);
   }
}
// END OF SNIPPET

And because of this retrieval would become:

         String results = (String)commands.get(0);

;JOOP!
I do not get an error with the code you sent me.

Maybe I get the error as the:

commands = new Vector<String>();

Is in a thread kind if method - dont know what you call this.

I moved that line to here:

public void run()
          {
                  commands = new Vector<String>();
            
and it compiles fine - is it okay here?

I suppose the last question is: how do I read from a vector i.e. how do I get the connect method carrying out actions according to the vector?

Cheers
I already wrote it above, so here is a more complete version:

String result = "";

     if(!commands.isEmpty())
     {
           result = commands.remove(0);  // Take the 1st object out
     }

Now the code should treat "" as a (temporary) end of input.

Depends on how you want to work. You could also "wait" for data:

     while(commands.isEmpty())      // Endless?
     {
           // Sleep some miliseconds
     }
     result = commands.remove(0);  // Always something


;JOOP!
When a vector is added to, does it work like a stack i.e. is the last object added always index 0. So if I added 1 then object index 0 would be 1, if i then add, then object index 0 would be 2 etc etc.

If it is like that I can use this:

command = commands.get(0);

Ok suppose there is a command issued, like this:

command.equals("updateFemale");

It will work at last.

If in the code the while loop in the thread method has break and it breaks, will that thread be ended?

Sorry about my mistake a second ago with the code.

Please tell me what you think.
>>> When a vector is added to, does it work like a stack i.e. is the last object added always index 0.
You can see it as a kind of array.
When you add 3 objects to an empty Vector, they get indices 0, 1 and 2

>>>If it is like that I can use this: command = commands.get(0);
No in this case you should remove the first object by  result = commands.remove(0);
while new objects are added at the end.
It is also possible to add, modify or remove objects on any other place if you know the index.
Removing the first object renumbers all other indices.

>>>Ok suppose there is a command issued, like this: command.equals("updateFemale"); It will work at last.
I don't understand this.

>>>If in the code the while loop in the thread method has break and it breaks, will that thread be ended?
No, a thread stops only when 'run()' returns and that's your choice.
If the Vector is part of the Thread object, it will 'live' as long as the Thread object.

By the way, it is probably better to make  Thread backgroundThread  static and to make the Vector
an instance local to the Thread object.
Then you can start another Thread without destroying the Vector as you create a new one.
Now the Vector is static and will be deleted with the creation of a new thread.
It's all the choice of the programmer acc.to the situation that must be mastered.

>>>Sorry about my mistake a second ago with the code.

Who doesn't make mistakes? Be glad you're not building airplanes; a little mistake and .............

;JOOP!
ASKER CERTIFIED SOLUTION
Avatar of sciuriware
sciuriware

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Ok sure.
Cheers for the help