/* * File : SingleInstanceJFrame.java * Created : 19-feb-2004 14:01 * By : fbusquets * * JClic - Authoring and playing system for educational activities * * Copyright (C) 2000 - 2005 Francesc Busquets & Departament * d'Educacio de la Generalitat de Catalunya * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details (see the LICENSE file). */ package edu.xtec.jclic; import edu.xtec.util.Check; import edu.xtec.util.Messages; import edu.xtec.util.Options; import edu.xtec.util.ResourceManager; import edu.xtec.util.StrUtils; import java.awt.Dimension; import java.awt.Toolkit; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.InterruptedIOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /** * * @author Francesc Busquets (fbusquets@xtec.cat) * @version 13.09.10 */ public class SingleInstanceJFrame extends javax.swing.JFrame implements Constants{ public static final String OK="OK", CANCEL="CANCEL"; public static final int DEFAULT_TIMEOUT=1000; RunnableComponent rc; javax.swing.JLabel splashLabel; String project; Options options; boolean trace; String playerClass; SocketThread socketThread; boolean initializing; boolean armed; /** Creates a new instance of SingleInstanceJFrame */ public SingleInstanceJFrame(String playerClass, String[] args, String windowTitle, String logoIcon, String frameIcon, int port) { initializing=true; setDefaultCloseOperation(javax.swing.JFrame.DO_NOTHING_ON_CLOSE); this.playerClass=playerClass; options=new Options(this); project=loadArgs(args, options); if(!checkOtherInstance(port)){ build(playerClass, args, windowTitle, frameIcon, logoIcon); armed=true; } } public boolean isArmed(){ return armed; } public static String loadArgs(String[] args, Options options){ String result=null; for(String arg : args){ if(arg!=null && arg.length()>0){ if(arg.startsWith("-")){ String key, value=null; int k=arg.indexOf('='); if(k>0){ key=arg.substring(1, k); value=arg.substring(k+1); } else key=arg.substring(1); options.put(key, value); //System.out.println(key+" is "+value); } else result=arg; } } return result; } protected void build(String playerClass, String[] args, String windowTitle, String frameIcon, String logoIcon) { trace=options.getBoolean(TRACE); if(trace){ for(String arg : args) System.out.println(arg); } initComponents(); setTitle(windowTitle); if(frameIcon!=null) setIconImage(ResourceManager.getImageIcon(frameIcon).getImage()); Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize(); int scrW=(int)screenSize.getWidth(); int scrH=(int)screenSize.getHeight(); splashLabel = new javax.swing.JLabel(" ", ResourceManager.getImageIcon(logoIcon), javax.swing.SwingConstants.CENTER); splashLabel.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); splashLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); splashLabel.setBackground(BG_COLOR); splashLabel.setOpaque(true); //splashLabel.setPreferredSize(new Dimension(scrW-40, scrH-80)); getContentPane().add(splashLabel, java.awt.BorderLayout.CENTER); pack(); setBounds((scrW-getWidth())/2, (scrH-getHeight())/3, scrW-40, scrH-80); setLocation((scrW-getWidth())/2, (scrH-getHeight())/3); if(Check.checkSignature(options, true)) init(); else System.exit(0); } protected void init(){ final javax.swing.RootPaneContainer rpc=this; edu.xtec.util.SwingWorker sw=new edu.xtec.util.SwingWorker(){ final StringBuilder sb = new StringBuilder(); @Override public Object construct(){ try{ // build messages Messages messages=edu.xtec.util.PersistentSettings.getMessages(options, DEFAULT_BUNDLE); messages.addBundle(COMMON_SETTINGS); if(splashLabel!=null) splashLabel.setText(messages.get("LOADING")); Class<?> c=Class.forName(playerClass); java.lang.reflect.Constructor cons=c.getConstructor(new Class[]{edu.xtec.util.Options.class}); rc=(RunnableComponent)cons.newInstance(new Object[]{options}); } catch(Exception ex){ sb.append("ERROR: Unable to start!\n").append(ex); ex.printStackTrace(System.err); } return rc; } @Override public void finished(){ if(getValue()==null){ // no player build! if(splashLabel!=null){ String s=sb.substring(0); splashLabel.setText(s); System.err.println(s); } } else{ // remove label and place player getContentPane().removeAll(); splashLabel=null; //rc.addTo(getContentPane(), java.awt.BorderLayout.CENTER); rc.addTo(rpc, java.awt.BorderLayout.CENTER); getRootPane().revalidate(); // load project javax.swing.SwingUtilities.invokeLater(new Runnable(){ public void run(){ rc.start(project, null); initializing=false; } }); } } }; if(trace) System.out.println(">>> initializing..."); // launch swingWorker sw.start(); } private void initComponents() { addWindowListener(new java.awt.event.WindowAdapter() { @Override public void windowClosing(java.awt.event.WindowEvent evt) { if(rc==null || rc.windowCloseRequested()){ if(socketThread!=null) socketThread.stopSocketThread(); if(rc!=null){ rc.end(); rc=null; } dispose(); } } @Override public void windowActivated(java.awt.event.WindowEvent evt) { if(rc!=null) rc.activate(); } @Override public void windowClosed(java.awt.event.WindowEvent evt) { exitForm(evt); } } ); } /** Exit the Application */ private void exitForm(java.awt.event.WindowEvent evt) { if(socketThread!=null) socketThread.stopSocketThread(); if(rc!=null){ rc.end(); rc=null; } while(socketThread!=null){ Thread.yield(); } System.exit(0); } protected boolean checkOtherInstance(int port){ boolean result=false; try{ socketThread=new SocketThread(port, DEFAULT_TIMEOUT); } catch(Exception ex){ // socket already created! // eat exception } if(socketThread==null){ try{ Socket socket=new Socket(InetAddress.getLocalHost(), port); BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); pw.println(playerClass); pw.println(StrUtils.secureString(project)); pw.println(""); pw.flush(); String response=in.readLine(); result=OK.equals(response); socket.close(); } catch(Exception ex){ System.err.println("Socket error: "+ex); } } else{ result=false; socketThread.start(); } return result; } protected class SocketThread extends Thread{ boolean running; boolean inService; boolean forceSocketClose; ServerSocket ss; int socketTimeOut; SocketThread(int port, int timeOut) throws IOException{ ss=new ServerSocket(port); socketTimeOut=timeOut; running=false; } @Override public void run(){ try{ running=true; ss.setSoTimeout(1000); while(running){ try{ inService=false; Socket socket=ss.accept(); inService=true; BufferedReader in=new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream())); String skPlayerClass=StrUtils.secureString(in.readLine()); String skArg1=StrUtils.nullableString(in.readLine()); String skArg2=StrUtils.nullableString(in.readLine()); boolean result=rc!=null && !initializing && skPlayerClass.equals(playerClass) && skArg1!=null; pw.println(result ? OK : CANCEL); pw.flush(); socket.close(); if(result) rc.newInstanceRequest(skArg1, skArg2); }catch(InterruptedIOException ioex){ // Timeout. start again... }catch(Exception ex){ if(!forceSocketClose) System.err.println("Socket error: "+ex); running=false; } } forceSocketClose=true; ss.close(); } catch (IOException ex){ if(!forceSocketClose) System.err.println("Server socket error: "+ex); } running=false; inService=false; socketThread=null; } public void stopSocketThread(){ if(inService){ running=false; } else { try{ forceSocketClose=true; ss.close(); } catch(IOException ex){ // eat exception } finally{ socketThread=null; running=false; } } } } }