I am currently developing a MOBA like game with libGDX, but I am struggling with the Server. Currently I have got a thread for every connection and a critical region wich stores the data. My problem is, that there are Threads, which are writing into the critical region and reading from it simultaneously. Can somebody help me out?
Here are my classes:
Clientthread:
package at.mobaserver;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import at.motm.model.GameObject;
import at.motm.model.Projectile;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Rectangle;
public class ClientConnection implements Runnable
{
private ObjectInputStream ois;
private ObjectOutputStream oos;
private Socket socket;
private GameServer server;
private GameObject receivedObject;
private Rectangle r;
private Projectile projectile;
private boolean found;
public ClientConnection(Socket socket, GameServer server)
{
try
{
this.socket = socket;
this.oos = new ObjectOutputStream(socket.getOutputStream());
this.ois = new ObjectInputStream(socket.getInputStream());
this.server = server;
this.receivedObject = null;
this.projectile = null;
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
#Override
public void run()
{
try
{
this.oos.writeLong(Thread.currentThread().getId());
this.oos.flush();
}
catch (IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
while (true)
{
try
{
// Add GameObjects to list
// this.server.getMutex().lock();
this.oos.reset();
// Hitdetection
if (this.projectile != null)
{
projectileHitDetection();
}
// Write Objects to client
this.oos.writeObject(this.server.getSendingList());
this.oos.flush();
// Add Objects to Severobject Arraylist
// this.server.getMutex().lock();
addObjectsToList();
// Add Projectiles to Serverobject Arraylist
readProjectiles();
// this.server.getMutex().unlock();
// this.server.getMutex().unlock();
}
catch (ClassNotFoundException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
break;
}
}
}
private void readProjectiles() throws ClassNotFoundException, IOException
{
synchronized (this.server.getProjectiles())
{
this.projectile = (Projectile) this.ois.readObject();
if (this.projectile != null)
{
this.found = false;
for (int i = 0; i < this.server.getProjectiles().size(); i++)
{
if (this.server.getProjectiles().get(i).getThreadID() == Thread
.currentThread().getId())
{
this.server.getProjectiles().set(i, this.projectile);
this.found = true;
break;
}
}
if (!this.found)
{
this.server.getProjectiles().add(this.projectile);
}
}
}
}
private void addObjectsToList() throws ClassNotFoundException, IOException
{
synchronized (this.server.getObjects())
{
this.receivedObject = (GameObject) this.ois.readObject();
this.found = false;
for (int i = 0; i < this.server.getObjects().size(); i++)
{
if (this.server.getObjects().get(i).getThreadID() == Thread
.currentThread().getId())
{
this.server.getObjects().set(i, this.receivedObject);
this.found = true;
break;
}
}
if (!this.found)
{
this.server.addObject(this.receivedObject);
}
}
}
private void projectileHitDetection()
{
synchronized (this.server.getObjects())
{
this.r = new Rectangle(this.projectile.getPosition().x,
this.projectile.getPosition().y,
this.projectile.getWidth(), this.projectile.getHeight());
for (int i = 0; i < this.server.getObjects().size(); i++)
{
if (this.server.getObjects().get(0).getTeam() != this.projectile
.getTeam())
{
Rectangle r1 = new Rectangle(this.server.getObjects()
.get(0).getPosition().x, this.server.getObjects()
.get(0).getPosition().y, this.server.getObjects()
.get(0).getWidth(), this.server.getObjects().get(0)
.getHeight());
if (Intersector.overlaps(this.r, r1))
{
System.out.println("test");
this.server.getProjectiles().remove(this.projectile);
break;
}
}
}
}
}
}
Critical region:
package at.mobaserver;
import java.util.ArrayList;
import at.motm.model.GameObject;
import at.motm.model.Projectile;
public class GameServer
{
private ArrayList<GameObject> objects;
private ArrayList<Projectile> projectiles;
private ArrayList<GameObject> sendList;
public GameServer()
{
this.objects = new ArrayList<GameObject>();
this.projectiles = new ArrayList<Projectile>();
this.sendList = new ArrayList<GameObject>();
}
public ArrayList<GameObject> getSendingList()
{
synchronized (this.sendList)
{
this.sendList.clear();
for (GameObject o : this.objects)
{
if (o.isAlive())
{
this.sendList.add(o);
}
}
this.sendList.addAll(this.projectiles);
return this.sendList;
}
}
public ArrayList<GameObject> getObjects()
{
synchronized (this.objects)
{
return this.objects;
}
}
public void setObjects(ArrayList<GameObject> objects)
{
synchronized (objects)
{
this.objects = objects;
}
}
public void addObject(GameObject o)
{
synchronized (this.sendList)
{
this.objects.add(o);
}
}
public ArrayList<Projectile> getProjectiles()
{
synchronized (this.projectiles)
{
return this.projectiles;
}
}
public void setProjectiles(ArrayList<Projectile> projectiles)
{
synchronized (this.projectiles)
{
this.projectiles = projectiles;
}
}
public void addProjectile(Projectile o)
{
synchronized (this.sendList)
{
this.projectiles.add(o);
}
}
}
I have solved my problem. I used CopyAndWriteArrayList instead of normal ArrayLists.
Related
So, I have a university project in java in which I'm making a bomberman game that should be run on a server. I'm having problems because of some timers that are creating new threads that are changing the object Map while I'm trying to send it.
I tried to use the synchronize(Map) on the code blocks of the timers and the outputstream but didn't work
I also have a problem where I want to keep my class Image_Library as a transient because it doesn't need to be sent over the stream, but when I mark the ArrayLists inside as such they're just considered null instead.
Here's some code:
public class GameHandler extends Thread{
private ArrayList<Client> Players = new ArrayList<Client>();
private ArrayList<Bomber> Characters = new ArrayList<Bomber>();
public GameLogic L;
transient private Image_Library lib;
private Map m;
private ArrayList<Integer> Actions = new ArrayList<Integer>();
GameHandler(Client x1, Client x2) {
x1.AddToGame(this);
x2.AddToGame(this);
Players.add(x1);
Players.add(x2);
}
#Override
public void run() {
boolean init=true;
for(int i=0;i<Players.size();i++) {
Create_New_Socket(Players.get(i));
}
Timer tt = new Timer();
tt.schedule(new Player_Info_Query(),0,200);
while(true) {
if(!init){
}
else {
int i;
for(i=0;i<Players.size();i++)
if(Players.get(i).GetBomber()==null)
break;
if(i==Players.size()) {
init=false;
tt.cancel();
try {
init_game();
for(i=0;i<Players.size();i++) {
Players.get(i).dos.writeUTF("game_start");
}
Timer tt2 = new Timer();
tt2.schedule(new Update_Task(),100);
} catch (SlickException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
private class Player_Info_Query extends TimerTask{
#Override
public void run() {
for(int i=0;i<Players.size();i++) {
if(Players.get(i).GetBomber()!=null)
continue;
try {
Players.get(i).Request_Client_Player();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private class Update_Task extends TimerTask{
#Override
public void run() {
// TODO Auto-generated method stub
try {
while(Actions.size()!=0) {
L.Action(Actions.get(0));
Actions.remove(0);
}
lib.Run_Changes();
} catch (SlickException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
for(int i=0;i<Players.size();i++)
try {
Players.get(i).Send_Map(m);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Timer tt2 = new Timer();
tt2.schedule(new Update_Task(),100);
}
}
private void Create_New_Socket(Client x){
try {
x.objectsocket = new Socket(x.datasocket.getInetAddress(),x.outputsocket);
System.out.println("SOCKET CREATED " + x.objectsocket);
x.oos = new ObjectOutputStream(x.objectsocket.getOutputStream());
x.ois = new ObjectInputStream(x.objectsocket.getInputStream());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// obtaining input and out streams
}
private void init_game() throws SlickException {
lib = new Image_Library();
Layout_Logic map_gen = new Layout_Logic(lib);
m = map_gen.Generate_Standard_Map();
L=new GameLogic(lib,m);
Characters = new ArrayList<Bomber>();
for(int i=0;i<Players.size();i++){
Characters.add(Players.get(i).GetBomber());
}
L.Place_Characters(Characters);
}
public void Buffer_Input(int key){
Actions.add(key);
}
}
package GameLogic;
import java.io.Serializable;
import java.util.ArrayList;
import org.newdawn.slick.SlickException;
public class Image_Library implements Serializable{
/**
*
*/
private static final long serialVersionUID = -8459759623730666317L;
private ArrayList<Element> Flagged = new ArrayList<Element>();
private ArrayList<String> Flagged_images = new ArrayList<String>();
public Map m;
public void Flag_For_Change(Element x,String img){
Flagged.add(x);
Flagged_images.add(img);
}
public void Run_Changes() throws SlickException {
while(Flagged.size()!=0 && Flagged_images.size()!=0) {
Flagged.get(0).Set_Image(Flagged_images.get(0));
Flagged.remove(0);
Flagged_images.remove(0);
}
}
}
package GameLogic;
import org.newdawn.slick.SlickException;
import java.util.Timer;
import java.util.TimerTask;
public class Bomber extends Element{
/**
*
*/
private static final long serialVersionUID = -9085303141098935687L;
transient protected boolean bomb_cd;
transient protected boolean Walking_cd;
static private int cooldown = 500; //milliseconds
static private int bomb_cooldown=1000;
static protected int move_res = 5;
protected int progression_count=move_res;
protected int direction; //0-down 1-left 2-up 3-right
static protected String StopDown="StopDown";
static protected String Down1="Down1";
static protected String Down2="Down2";
static protected String StopUp="StopUp";
static protected String Up1="Up1";
static protected String Up2="Up2";
static protected String StopLeft="StopLeft";
static protected String Left1="Left1";
static protected String Left2="Left2";
static protected String StopRight="StopRight";
static protected String Right1="Right1";
static protected String Right2="Right2";
protected int upkey,downkey,rightkey,leftkey,actionkey;
private int player;
public Bomber(int x,int y,Image_Library lib,Map m,int c1,int c2,int k1,int k2, int k3, int k4,int k5,int p) throws SlickException{
Coordinate tmp = new Coordinate(x,y);
Coord=tmp;
Solid=true;
this.lib=lib;
this.m=m;
img=StopDown;
upkey=k1;
downkey=k2;
leftkey=k3;
rightkey=k4;
actionkey=k5;
player=p;
img = StopDown;
bomb_cd=false;
Walking_cd=false;
//direction=0;
GUI_Scale=64;
}
public void MoveUp() {
Coord.MoveUp();
direction=2;
Start_Walking();
}
public void MoveRight() {
Coord.MoveRight();
direction=3;
Start_Walking();
}
public void MoveLeft() {
Coord.MoveLeft();
direction=1;
Start_Walking();
}
public void MoveDown() {
Coord.MoveDown();
direction=0;
Start_Walking();
}
private void Start_Walking(){
Walking_cd=true;
Timer tt = new Timer();
tt.schedule(new Walking(this),0,cooldown/move_res);
progression_count=0;
}
public void Used_Bomb(){
bomb_cd=true;
Timer tt = new Timer();
tt.schedule(new Bomb_Cd(), bomb_cooldown);
}
public boolean Can_Use_Bomb(){
return !bomb_cd;
}
public boolean Can_Walk() {
return !Walking_cd;
}
public boolean Death_Check(){
return m.Has_Explosion(Coord.getX(),Coord.getY());
}
private class Bomb_Cd extends TimerTask{
public void run(){
synchronized(m) {
bomb_cd=false;
}
}
}
private class Walking extends TimerTask{
transient private boolean feet=false;
private Bomber person;
Walking (Bomber person){
this.person=person;
bomb_cd=true;
}
public void run(){
progression_count++;
if(feet) {
if(direction==0)
lib.Flag_For_Change(person,Down1);
if(direction==1)
lib.Flag_For_Change(person,Left1);
if(direction==2)
lib.Flag_For_Change(person,Up1);
if(direction==3)
lib.Flag_For_Change(person,Right1);
}
else {
if(direction==0)
lib.Flag_For_Change(person,Down2);
if(direction==1)
lib.Flag_For_Change(person,Left2);
if(direction==2)
lib.Flag_For_Change(person,Up2);
if(direction==3)
lib.Flag_For_Change(person,Right2);
}
feet=!feet;
if(progression_count==move_res){
Walking_cd=false;
if(direction==0)
lib.Flag_For_Change(person,StopDown);
if(direction==1)
lib.Flag_For_Change(person,StopLeft);
if(direction==2)
lib.Flag_For_Change(person,StopUp);
if(direction==3)
lib.Flag_For_Change(person,StopRight);
bomb_cd=false;
this.cancel();
}
}
}
}
client connection works (I use telnet), but nothing happens when I write a message with any client - even the condition with empty char (for disconnection).
I don't understand why. I get capacity and port with args[], and I start the server.
I already tested a simpler version with just a server which can handle one by one client, and it works.
public class EchoClient extends Thread {
EchoServerForPool serv;
BufferedReader inchan;
DataOutputStream outchan;
Socket socket;
int port;
public EchoClient(EchoServerForPool serv) {
// TODO Auto-generated constructor stub
this.serv = serv;
}
#Override
public void run() {
// TODO Auto-generated method stub
Socket s;
while (true) {
synchronized (this.serv) {
if (this.serv.stillWaiting() == 0) {
try {
this.serv.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
s = this.serv.removeFirstSocket();
serv.newConnect();
}
try {
inchan = new BufferedReader(new InputStreamReader(s.getInputStream()));
outchan = new DataOutputStream(s.getOutputStream());
String message = inchan.readLine();
if (message.equals("")) {
System.out.println("fin de connection");
break;
}
outchan.writeChars(message + "\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
s.close();
synchronized (serv) {
serv.clientLeft();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class EchoServerForPool extends Thread {
ArrayList<EchoClient> clients;
ArrayList<Socket> sockets;
Socket client;
int nbLocalhost = 0;
int capacity, port, nbConnectedClient, nbWaitingSocket;
public EchoServerForPool(int capacity, int port) {
// TODO Auto-generated constructor stub
this.capacity = capacity;
this.port = port;
clients = new ArrayList<EchoClient>(capacity);
sockets = new ArrayList<Socket>();
for (int i = 0; i < clients.size(); i++) {
EchoClient ec_i = new EchoClient(this);
clients.add(ec_i);
ec_i.start();
}
}
public Socket removeFirstSocket() {
Socket res = sockets.get(0);
sockets.remove(0);
return res;
}
public void newConnect() {
nbConnectedClient++;
nbWaitingSocket--;
}
public int stillWaiting() {
return nbWaitingSocket;
}
public void clientLeft() {
nbConnectedClient--;
}
#Override
public void run() {
// TODO Auto-generated method stub
try {
ServerSocket serv = new ServerSocket(this.port);
while (true) {
this.client = serv.accept();
synchronized (this) {
nbLocalhost++;
System.out.println(client.getInetAddress().getHostName() + "-" + nbLocalhost + " connected");
sockets.add(client);
nbWaitingSocket++;
notify();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class EchoPoolThread {
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
int capacity = Integer.parseInt(args[1]);
EchoClient.EchoServerForPool serveur = new EchoClient.EchoServerForPool(capacity, port);
System.out.println("start server");
serveur.start();
}
}
EDIT : the problem was I iterate with clients.size() which is 0 instead of capacity, for fill my clients list....
I've refactored your code a little bit to have a working solution. You can work from there to create a pool, right now the number of clients isn't limited.
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* #author Gianlu
*/
public class Main {
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
int capacity = Integer.parseInt(args[1]);
EchoServerForPool serveur = new EchoServerForPool(capacity, port);
System.out.println("start server");
serveur.start();
}
public static class EchoServerForPool extends Thread {
private final int port;
private final ExecutorService executor;
private final ArrayList<EchoClient> clients;
private int nbLocalhost = 0;
public EchoServerForPool(int capacity, int port) {
this.port = port;
clients = new ArrayList<>();
executor = Executors.newFixedThreadPool(capacity);
}
#Override
public void run() {
try {
ServerSocket serv = new ServerSocket(this.port);
while (true) {
Socket socket = serv.accept();
EchoClient client = new EchoClient(this, socket);
executor.submit(client);
clients.add(client);
nbLocalhost++;
System.out.println(socket.getInetAddress().getHostName() + "-" + nbLocalhost + " connected");
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void terminated(EchoClient client) {
clients.remove(client);
}
}
public static class EchoClient implements Runnable {
private final EchoServerForPool pool;
private final Socket socket;
private final BufferedReader inchan;
private final DataOutputStream outchan;
public EchoClient(EchoServerForPool pool, Socket socket) throws IOException {
this.pool = pool;
this.socket = socket;
this.inchan = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.outchan = new DataOutputStream(socket.getOutputStream());
}
#Override
public void run() {
try {
while (true) {
String message = inchan.readLine();
if (message.equals("")) {
System.out.println("fin de connection");
break;
}
outchan.writeChars(message + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
pool.terminated(this);
}
}
}
As with basically every exchanger task, I have a producer filling up an empty buffer2, a consumer clearing a full buffer1 and when each thread is done, they should exchange their respective buffers.
I am really unsure about where and how to apply the exchange. I defined readyconsumer and readyproducer as booleans, so that a third thread can check whether it's time to exchange the buffers once both are true. This should solve the problem I had doing it with two threads, where the program was stuck with both threads at wait() (which it unfortunately still is).
This is what the code looks like at the moment. Can anyone help me in which class I have to exchange and at what point in the code? Thank you very much in advance!
class Buffer {
static boolean readyconsumer, readyproducer = false;
volatile int count; // number of put actions
static int max = 10;
Buffer() {
count = 0;
}
public synchronized void put() {
if (count == max) {
readyproducer = true;
System.out.println(" wait ");
try {
wait();
} catch (InterruptedException e) {
}
}
count++;
System.out.println("put " + count);
notifyAll();
}
public synchronized void get() {
if (count == 0) {
readyconsumer = true;
System.out.println(" wait");
try {
wait();
} catch (InterruptedException e) {
}
}
count--;
System.out.println("get " + count);
notifyAll();
}
}
class CheckandSwitch extends ProdCon {
public void run() {
while (true) {
if (Buffer.readyconsumer && Buffer.readyproducer) {
try {
ProdCon.buffer2 = exchanger.exchange(ProdCon.buffer1);
ProdCon.buffer1 = exchanger.exchange(ProdCon.buffer2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Buffer.readyconsumer = false;
Buffer.readyproducer = false;
buffer1.count = 0;
buffer2.count = 10;
notifyAll();
}
}
}
}
class Consumer extends ProdCon {
static Buffer buffer;
Consumer(Buffer b) {
super();
buffer = b;
b.count = 10;
}
public void run() {
while (true) {
consume();
buffer.get();
}
}
private void consume() {
System.out.println("consume");
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
}
class Producer extends ProdCon {
static Buffer buffer;
Producer(Buffer b) {
super();
buffer = b;
b.count = 0;
}
public void run() {
while (true) {
produce();
buffer.put();
}
}
private void produce() {
System.out.println("produce ");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
}
import java.util.concurrent.*;
public class ProdCon extends Thread {
static Exchanger<Buffer> exchanger = new Exchanger<Buffer>();
static Buffer buffer1, buffer2 = null;
public static void main(String[] args) {
buffer1 = new Buffer();
buffer2 = new Buffer();
new Consumer(buffer1).start();
new Producer(buffer2).start();
new CheckandSwitch().start();
}
}
You could use an Exchanger.
Here's the code from the javadoc tweaked into a working example.
class DataBuffer<T> {
T data = null;
public boolean isFull() {
return data != null;
}
public boolean isEmpty() {
return data == null;
}
public T get() {
T d = data;
data = null;
return d;
}
public void put(T data) {
this.data = data;
}
}
class FillAndEmpty {
Exchanger<DataBuffer<Integer>> exchanger = new Exchanger<>();
DataBuffer<Integer> initialEmptyBuffer = new DataBuffer<>();
DataBuffer<Integer> initialFullBuffer = new DataBuffer<>();
int countDown = 10;
class FillingLoop implements Runnable {
#Override
public void run() {
DataBuffer currentBuffer = initialEmptyBuffer;
try {
while (currentBuffer != null && countDown > 0) {
addToBuffer(currentBuffer);
if (currentBuffer.isFull()) {
currentBuffer = exchanger.exchange(currentBuffer);
}
}
} catch (InterruptedException ex) {
}
}
private void addToBuffer(DataBuffer<Integer> currentBuffer) {
currentBuffer.put(countDown--);
}
}
class EmptyingLoop implements Runnable {
#Override
public void run() {
DataBuffer<Integer> currentBuffer = initialFullBuffer;
try {
while (currentBuffer != null) {
takeFromBuffer(currentBuffer);
if (currentBuffer.isEmpty()) {
currentBuffer = exchanger.exchange(currentBuffer);
}
}
} catch (InterruptedException ex) {
}
}
private void takeFromBuffer(DataBuffer<Integer> currentBuffer) {
System.out.println(currentBuffer.get());
}
}
void start() {
new Thread(new FillingLoop()).start();
new Thread(new EmptyingLoop()).start();
}
}
public void test() {
System.out.println("Hello");
new FillAndEmpty().start();
}
I want to prevent multiple instances of application being launched in java. I know 2 methods for this:
locking file
locking socket
But which is one is more efficient and good to use? Which one should I use?
Any other solution to do the same are also welcome.
There is a library called jUnique which does that and will save you the bother of implementing it yourself.
If you deploy with Java WebStart the SingleInstanceService does this.
See http://download.oracle.com/javase/6/docs/technotes/guides/javaws/developersguide/faq.html#218
EDIT: I tried that with Win200864b(version isn't important) and alive JFrame and move toFront() or Iconified in SystemTray with JFrame.DO_NOTHING_ON_CLOSE
public interface ApplicationStartedListener {
void applicationStarted();
void foreignApplicationStarted(String name);
void messageArrived(Object obj);
}
//
import java.io.Serializable;
public class ClassCheck implements Serializable {
private static final long serialVersionUID = 1L;
private String className = null;
public ClassCheck() {
}
public ClassCheck(String className) {
setClassName(className);
}
#Override
public String toString() {
return this.className;
}
public String getClassName() {
return this.className;
}
public void setClassName(String className) {
this.className = className;
}
}
//
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.InputEvent;
import java.io.File;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class RunOnceFromFile {
private SingleInstanceController sic = null;
private JFrame frame;
private Robot r;
private JTextField tf;
public RunOnceFromFile() {
try {
r = new Robot();
} catch (AWTException ex) {
ex.printStackTrace();
}
sic = new SingleInstanceController(new File(System.getProperty("java.io.tmpdir") + "Example.file"), "sic_example_application");
if (sic.isOtherInstanceRunning()) {
sic.sendMessageToRunningApplication("toFront");
System.exit(0);
} else {
frame = new JFrame("TEST");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 300);
tf = new JTextField("JTextFiled");
frame.add(tf, BorderLayout.NORTH);
frame.setExtendedState(Frame.ICONIFIED);
frame.setExtendedState(Frame.NORMAL);
frame.setExtendedState(frame.getExtendedState() | JFrame.ICONIFIED);
frame.setExtendedState(frame.getExtendedState() & (~JFrame.ICONIFIED));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
sic.registerApplication();
sic.addApplicationStartedListener(new ApplicationStartedListener() {
public void applicationStarted() {
Runnable doRun = new Runnable() {
public void run() {
frame.toFront();
}
};
SwingUtilities.invokeLater(doRun);
}
public void foreignApplicationStarted(final String name) {
Runnable doRun = new Runnable() {
public void run() {
frame.toFront();
}
};
SwingUtilities.invokeLater(doRun);
}
public void messageArrived(final Object obj) {
Runnable doRun = new Runnable() {//activateWindow(frame);
public void run() {
frame.toFront();
}
};
SwingUtilities.invokeLater(doRun);
}
private void activateWindow(JFrame frame) {
frame.setExtendedState(Frame.ICONIFIED);
frame.setExtendedState(Frame.NORMAL);
frame.setAlwaysOnTop(true);
frame.setAlwaysOnTop(false);
Point location = MouseInfo.getPointerInfo().getLocation();
Point locationOnScreen = frame.getLocationOnScreen();
r.mouseMove(locationOnScreen.x + 100, locationOnScreen.y + 10);
r.mousePress(InputEvent.BUTTON1_MASK);
r.mouseRelease(InputEvent.BUTTON1_MASK);
r.mouseMove(location.x, location.y);
}
});
}
}
public static void main(String[] args) {
RunOnceFromFile roff = new RunOnceFromFile();
}
}
//
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
public class SingleInstanceController {
private String appname = null;
private Socket client = null;
private File file = null;
private ArrayList<ApplicationStartedListener> listener = null;
private ObjectInputStream ois = null;
private ObjectOutputStream oos = null;
private boolean result = false;
private ServerSocket server = null;
public SingleInstanceController(String appname) {
this(new File(System.getProperty("java.io.tmpdir") + "/923jhakE53Kk9235b43.6m7"), appname);
}
public SingleInstanceController(File file, String appname) {
this.file = file;
this.appname = appname;
this.listener = new ArrayList<ApplicationStartedListener>();
}
public void addApplicationStartedListener(ApplicationStartedListener asl) {
this.listener.add(asl);
}
public void removeApplicationStartedListener(ApplicationStartedListener asl) {
this.listener.remove(asl);
}
public boolean isOtherInstanceRunning() {
if (!this.file.exists()) {
return false;
}
return sendMessageToRunningApplication(new ClassCheck(this.appname));
}
public boolean sendMessageToRunningApplication(final Object obj) {
this.result = false;
try {
this.client = new Socket("localhost", getPortNumber());
new Thread(new Runnable() {
public void run() {
try {
SingleInstanceController.this.oos = new ObjectOutputStream(SingleInstanceController.this.client.getOutputStream());
SingleInstanceController.this.ois = new ObjectInputStream(SingleInstanceController.this.client.getInputStream());
SingleInstanceController.this.oos.writeObject(obj);
SingleInstanceController.this.oos.flush();
SingleInstanceController.this.result = SingleInstanceController.this.ois.readBoolean();
} catch (IOException e) {
SingleInstanceController.this.result = false;
}
}
}).start();
for (int i = 0; i < 10; i++) {
if (this.result == true) {
break;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.client.close();
return this.result;
} catch (IOException e) {
return false;
}
}
public boolean registerApplication() {
try {
if (!this.file.exists()) {
if (!this.file.getParentFile().mkdirs() && !this.file.getParentFile().exists()) {
return false;
}
if (!this.file.createNewFile()) {
return false;
}
}
BufferedWriter wuffy = new BufferedWriter(new FileWriter(this.file));
int port = getFreeServerSocket();
if (port != -1) {
startServer();
}
wuffy.write(String.valueOf(port));
wuffy.close();
return true;
} catch (IOException e) {
return false;
}
}
protected void messageArrived(Object obj) {
for (ApplicationStartedListener asl : this.listener) {
asl.messageArrived(obj);
}
}
protected void applicationStartet() {
for (ApplicationStartedListener asl : this.listener) {
asl.applicationStarted();
}
}
protected void foreignApplicationStarted(String name) {
for (ApplicationStartedListener asl : this.listener) {
asl.foreignApplicationStarted(name);
}
}
private int getPortNumber() {
try {
BufferedReader buffy = new BufferedReader(new FileReader(this.file));
int port = Integer.parseInt(buffy.readLine().trim());
buffy.close();
return port;
} catch (Exception e) {
return -1;
}
}
private void startServer() {
new Thread(new Runnable() {
public void run() {
while (true) {
try {
SingleInstanceController.this.client = SingleInstanceController.this.server.accept();
if (SingleInstanceController.this.client.getInetAddress().isLoopbackAddress()) {
new Thread(new Runnable() {
public void run() {
try {
SingleInstanceController.this.oos = new ObjectOutputStream(SingleInstanceController.this.client.getOutputStream());
SingleInstanceController.this.ois = new ObjectInputStream(SingleInstanceController.this.client.getInputStream());
Object obj = SingleInstanceController.this.ois.readObject();
if (obj instanceof ClassCheck) {
if (obj.toString().equals(SingleInstanceController.this.appname)) {
SingleInstanceController.this.oos.writeBoolean(true);
applicationStartet();
} else {
SingleInstanceController.this.oos.writeBoolean(false);
foreignApplicationStarted(obj.toString());
}
} else {
messageArrived(obj);
SingleInstanceController.this.oos.writeBoolean(true);
}
SingleInstanceController.this.oos.flush();
SingleInstanceController.this.client.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
private int getFreeServerSocket() {
for (int i = 2000; i < 10000; i++) {
try {
this.server = new ServerSocket(i);
return i;
} catch (IOException ignore) {
}
}
return -1;
}
}
My vote goes to locking on a port (i think this is what you mean by socket). I don't know the exact reason for this. But in fact i come across only this as a solution in most practical projects. Though i will be happy to hear the alternative ways.
In response to your question, the port solution will keep more resources from the machine:
- You will keep a port locked: ports are limited and you may find problems with firewalls or other programs listening on the same port.
- You'll need an active thread.
The file solution will use less resources from the machine, to avoid locking the file forever you need to add a thread, to delete the file, in the addShutdownHook method from Runtime.
the serversocket solution is cross-platform . And will not be vulnerable to the program crashing and not resetting the lock.
File lock is better way to do- imo. When you create the file in User's Home directory, this will still work in a multi-user environment.
I came across - JUnique - haven't had a chance to use it
http://www.sauronsoftware.it/projects/junique/manual.php
I know that this question is pretty old, but I have to solve the same problem at the moment. I prefer the socket solution because I have never thought that such kind of task should have anything to do with the file system. It is better to solve the problem in memory and not in the file system I think.
I have coded a simple application in Java that downloads particular images from a list of html links provided. Everything was working fine until I added the feature of having to download from a list of html links rather than just one. I had to implement the wait() and notify() methods which forced me to change the approach a little. Now, the downloads work fine, but the GUI does not update while the download is in progress.
I make the 1st thread wait from HTML.java and notify it at the end of DownloadImages.java. For this I had to invoke buttonPressed class as an object rather than a thread, which is why I think my GUI won't update.
Is there a way to simplify or make thread-usage more efficient in my code? Thanks in advance.
Here is skeleton of my code:
/*Test.java*/
package my;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Test extends javax.swing.JFrame {
public static buttonPressed bp;
public static boolean alldone;
/** Creates new form Test */
public Test() {
initComponents();
}
public static class buttonPressed implements Runnable {
Thread t1, t2;
buttonPressed() {
t1 = new Thread(this, "downloadAction");
t1.start();
}
public void suspendThread() {
System.out.println("suspended");
alldone = false;
}
public synchronized void resumeThread() {
System.out.println("resumed");
alldone = true;
notify();
}
public void run() {
String[] len = new String[]{/*list of urls*/};
for (int i = 0; i < len.length; i++) {
System.out.println("going times: " + i);
t2 = new Thread(new HTML(), "HTMLthread");
t2.start();
synchronized (this) {
while (!alldone) {
try {
wait();
} catch (InterruptedException ex) {
Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
}
}
private void downloadActionPerformed(java.awt.event.ActionEvent evt) {
bp = new buttonPressed();
try {
bp.t1.join();
} catch (InterruptedException e) {
System.out.println("Main Thread: interrupted");
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Test().setVisible(true);
}
});
}
private javax.swing.JButton download;
public static javax.swing.JProgressBar progress;
}
/*HTML.java*/
package my;
import java.util.ArrayList;
class HTML implements Runnable {
private Thread t3;
public HTML() {
Test.bp.suspendThread();
}
public void run() {
downloadHTML();
ArrayList xyz = parseHTML();
t3 = new Thread(new DownloadImages(xyz), "DownDecrypt");
t3.start();
}
private void downloadHTML() {
// Downloads the HTML file
}
private ArrayList parseHTML() {
// Parses the HTML file and gets links to images
return new ArrayList();
}
}
/*DownloadImages.java*/
package my;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
class DownloadImages implements Runnable {
static int current = 0, previous = 0;
static boolean speedFlag;
ArrayList<String> links = new ArrayList<String>();
private Thread t4;
public DownloadImages(ArrayList param1) {
this.links = param1;
speedFlag = true;
}
public void run() {
t4 = new Thread(new getSpeed(), "getSpeed");
t4.start();
download(links);
}
private void download(ArrayList<String> param1) {
String[] imgurl = new String[param1.size()];
URLConnection conn = null;
InputStream is = null;
ByteArrayOutputStream bais = null;
int prog;
for (int i = 0; i < param1.size(); i++) {
current = 0;
imgurl[i] = param1.get(i);
try {
conn = new URL(imgurl[i]).openConnection();
int fsize = conn.getContentLength();
is = new BufferedInputStream(conn.getInputStream());
bais = new ByteArrayOutputStream();
byte[] byteChunk = new byte[1024];
int n;
while ((n = is.read(byteChunk)) > 0) {
bais.write(byteChunk, 0, n);
current = current + 1024;
prog = (int) (current * 100.0 / fsize);
Test.progress.setValue(prog);
}
} catch (MalformedURLException ex) {
Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException ex) {
Logger.getLogger(DownloadImages.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
byte[] imgBytes = bais.toByteArray();
try {
FileOutputStream fos = new FileOutputStream(i + ".jpg");
fos.write(imgBytes);
fos.flush();
fos.close();
} catch (FileNotFoundException ex) {
System.out.println("FileNotFoundException : " + ex);
} catch (IOException e) {
e.printStackTrace();
}
}
speedFlag = false;
// Resume the thread to start downloading the next link
Test.bp.resumeThread();
}
private static class getSpeed implements Runnable {
int kbytesPerSecond;
private final int fireTime;
public getSpeed() {
fireTime = 1000;
}
public void run() {
while (speedFlag) {
try {
Thread.sleep(fireTime);
} catch (InterruptedException ex) {
Logger.getLogger(getSpeed.class.getName()).log(Level.SEVERE, null, ex);
}
kbytesPerSecond = (((current - previous) / 1024) / (fireTime / 1000));
System.out.println(kbytesPerSecond);
previous = current;
}
}
}
}
As far as the GUI is concerned you need to read about Swing concurrency. In short, use SwingWorker.
Mind that you use old AWT stuff (java.awt.EventQueue).
I suggest you have an ExecutorService like Executors.newCachedThreadPool and submit() the tasks to it. Collect the Future objects so you know when they are done. This will be more efficient and manageable than creating Threads all over the place.
You can have just one pool like
static final ExecutorService POOL = Executors.newCachedThreadPool();
to submit a task
POOL.submit(new Callable<Void>() {
public Void call() throws InterruptedException {
while (speedFlag) {
Thread.sleep(1000);
kbytesPerSecond = (current - previous) / 1024;
System.out.println(kbytesPerSecond);
previous = current;
}
}
});
Even better for repeating tasks is to use a scheduled executor service.
static final ScheduledExecutorService POOL = Executors.newScheduledThreadPool(4);
Future task = POOL.scheduleAtFixedRate(new Runnable() {
public void run() {
kbytesPerSecond = (current - previous) / 1024;
System.out.println(kbytesPerSecond);
previous = current;
}
}, 1, 1, TimeUnit.SECONDS);
// to end the task
task.cancel(false);