diff options
author | Jose <jose@zeroc.com> | 2009-10-02 02:23:52 +0200 |
---|---|---|
committer | Jose <jose@zeroc.com> | 2009-10-02 02:23:52 +0200 |
commit | 1d9f29e281770ecdad4a245271f2b828bd64a32f (patch) | |
tree | ac083f28b06a444e484c24f8fcf1b12a36202c84 /java | |
parent | Updated demo README (diff) | |
download | ice-1d9f29e281770ecdad4a245271f2b828bd64a32f.tar.bz2 ice-1d9f29e281770ecdad4a245271f2b828bd64a32f.tar.xz ice-1d9f29e281770ecdad4a245271f2b828bd64a32f.zip |
3772. Recovering from Glacier2 / Ice router session failure.
Diffstat (limited to 'java')
24 files changed, 2960 insertions, 211 deletions
diff --git a/java/config/common.xml b/java/config/common.xml index 88776ad1cb6..b3f8c1b4434 100644 --- a/java/config/common.xml +++ b/java/config/common.xml @@ -84,7 +84,7 @@ </condition> <condition property="ice.dir" value="${ice.top.dir}/ice"> <and> - <!-- Don't just look for ${ice.top.dir}/../java - we want to make sure we are really + <!-- Don't just look for ${ice.top.dir}/ice/java - we want to make sure we are really in a source distribution. --> <not><isset property="ice.dir"/></not> <available file="${ice.top.dir}/ice/java/src/Ice/Util.java"/> diff --git a/java/demo/Glacier2/build.xml b/java/demo/Glacier2/build.xml index cfb2c825c15..1081e09a996 100644 --- a/java/demo/Glacier2/build.xml +++ b/java/demo/Glacier2/build.xml @@ -13,10 +13,12 @@ <target name="all"> <ant dir="callback"/> + <ant dir="chat"/> </target> <target name="clean"> <ant dir="callback" target="clean"/> + <ant dir="chat" target="clean"/> </target> </project> diff --git a/java/demo/Glacier2/callback/Client.java b/java/demo/Glacier2/callback/Client.java index 399d6eab481..9dcf50085be 100644 --- a/java/demo/Glacier2/callback/Client.java +++ b/java/demo/Glacier2/callback/Client.java @@ -9,57 +9,8 @@ import Demo.*; -public class Client extends Ice.Application +public class Client extends Glacier2.Application { - - static private class SessionRefreshThread extends Thread - { - SessionRefreshThread(Glacier2.RouterPrx router, long timeout) - { - _router = router; - _timeout = timeout; - } - - synchronized public void - run() - { - while(!_terminated) - { - try - { - wait(_timeout); - } - catch(InterruptedException e) - { - } - if(!_terminated) - { - try - { - _router.refreshSession(); - } - catch(Glacier2.SessionNotExistException ex) - { - } - catch(Ice.LocalException ex) - { - } - } - } - } - - synchronized private void - terminate() - { - _terminated = true; - notify(); - } - - final private Glacier2.RouterPrx _router; - final private long _timeout; - private boolean _terminated = false; - } - class ShutdownHook extends Thread { public void @@ -88,118 +39,118 @@ public class Client extends Ice.Application "v: set/reset override context field\n" + "F: set/reset fake category\n" + "s: shutdown server\n" + + "r: restart the session\n" + "x: exit\n" + "?: help\n"); } - public int - run(String[] args) + public void sessionDestroyed() { - if(args.length > 0) - { - System.err.println(appName() + ": too many arguments"); - return 1; - } - - // - // Since this is an interactive demo we want to clear the - // Application installed interrupt callback and install our - // own shutdown hook. - // - setInterruptHook(new ShutdownHook()); - - Ice.RouterPrx defaultRouter = communicator().getDefaultRouter(); - if(defaultRouter == null) - { - System.err.println("no default router set"); - return 1; - } - - Glacier2.RouterPrx router = Glacier2.RouterPrxHelper.checkedCast(defaultRouter); - if(router == null) - { - System.err.println("configured router is not a Glacier2 router"); - return 1; - } + System.out.println("The Glacier2 session has been destroyed."); + } - java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); + public Glacier2.SessionPrx createSession() + { + Glacier2.SessionPrx session; while(true) { + java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); System.out.println("This demo accepts any user-id / password combination."); + String id; + String pw; try { - String id; System.out.print("user id: "); System.out.flush(); id = in.readLine(); - - String pw; + System.out.print("password: "); System.out.flush(); pw = in.readLine(); - - try - { - router.createSession(id, pw); - break; - } - catch(Glacier2.PermissionDeniedException ex) - { - System.out.println("permission denied:\n" + ex.reason); - } - catch(Glacier2.CannotCreateSessionException ex) - { - System.out.println("cannot create session:\n" + ex.reason); - } } catch(java.io.IOException ex) { ex.printStackTrace(); + continue; + } + + try + { + session = router().createSession(id, pw); + break; + } + catch(Glacier2.PermissionDeniedException ex) + { + System.out.println("permission denied:\n" + ex.reason); + } + catch(Glacier2.CannotCreateSessionException ex) + { + System.out.println("cannot create session:\n" + ex.reason); } } + return session; + } - SessionRefreshThread refresh = new SessionRefreshThread(router, router.getSessionTimeout() * 500); - refresh.start(); + public int + runWithSession(String[] args) + throws RestartSessionException + { + if(args.length > 0) + { + System.err.println(appName() + ": too many arguments"); + return 1; + } - String category = router.getCategoryForClient(); - Ice.Identity callbackReceiverIdent = new Ice.Identity(); - callbackReceiverIdent.name = "callbackReceiver"; - callbackReceiverIdent.category = category; - Ice.Identity callbackReceiverFakeIdent = new Ice.Identity(); - callbackReceiverFakeIdent.name = "callbackReceiver"; - callbackReceiverFakeIdent.category = "fake"; + // + // Since this is an interactive demo we want to clear the + // Application installed interrupt callback and install our + // own shutdown hook. + // + setInterruptHook(new ShutdownHook()); - Ice.ObjectPrx base = communicator().propertyToProxy("Callback.Proxy"); - CallbackPrx twoway = CallbackPrxHelper.checkedCast(base); - CallbackPrx oneway = CallbackPrxHelper.uncheckedCast(twoway.ice_oneway()); - CallbackPrx batchOneway = CallbackPrxHelper.uncheckedCast(twoway.ice_batchOneway()); + try + { + Ice.Identity callbackReceiverIdent = createCallbackIdentity("callbackReceiver"); + Ice.Identity callbackReceiverFakeIdent = new Ice.Identity("fake", "callbackReceiver"); - Ice.ObjectAdapter adapter = communicator().createObjectAdapterWithRouter("Callback.Client", defaultRouter); - adapter.add(new CallbackReceiverI(), callbackReceiverIdent); - adapter.add(new CallbackReceiverI(), callbackReceiverFakeIdent); - adapter.activate(); + Ice.ObjectPrx base = communicator().propertyToProxy("Callback.Proxy"); + CallbackPrx twoway = CallbackPrxHelper.checkedCast(base); + CallbackPrx oneway = CallbackPrxHelper.uncheckedCast(twoway.ice_oneway()); + CallbackPrx batchOneway = CallbackPrxHelper.uncheckedCast(twoway.ice_batchOneway()); - CallbackReceiverPrx twowayR = CallbackReceiverPrxHelper.uncheckedCast( - adapter.createProxy(callbackReceiverIdent)); - CallbackReceiverPrx onewayR = CallbackReceiverPrxHelper.uncheckedCast(twowayR.ice_oneway()); + objectAdapter().add(new CallbackReceiverI(), callbackReceiverFakeIdent); - menu(); + CallbackReceiverPrx twowayR = CallbackReceiverPrxHelper.uncheckedCast( + objectAdapter().add(new CallbackReceiverI(), callbackReceiverIdent)); + CallbackReceiverPrx onewayR = CallbackReceiverPrxHelper.uncheckedCast(twowayR.ice_oneway()); - String line = null; - String override = null; - boolean fake = false; - do - { - try + + menu(); + + String line = null; + String override = null; + boolean fake = false; + java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(System.in)); + do { System.out.print("==> "); System.out.flush(); - line = in.readLine(); + try + { + line = in.readLine(); + } + catch(java.io.IOException ex) + { + ex.printStackTrace(); + line = null; + } + if(line == null) { break; } + if(line.equals("t")) { java.util.Map<String, String> context = new java.util.HashMap<String, String>(); @@ -250,7 +201,7 @@ public class Client extends Ice.Application else if(line.equals("F")) { fake = !fake; - + if(fake) { twowayR = CallbackReceiverPrxHelper.uncheckedCast( @@ -265,14 +216,18 @@ public class Client extends Ice.Application onewayR = CallbackReceiverPrxHelper.uncheckedCast( onewayR.ice_identity(callbackReceiverIdent)); } - + System.out.println("callback receiver identity: " + - communicator().identityToString(twowayR.ice_getIdentity())); + communicator().identityToString(twowayR.ice_getIdentity())); } else if(line.equals("s")) { twoway.shutdown(); } + else if(line.equals("r")) + { + restart(); + } else if(line.equals("x")) { // Nothing to do @@ -287,45 +242,12 @@ public class Client extends Ice.Application menu(); } } - catch(java.io.IOException ex) - { - ex.printStackTrace(); - } - catch(Ice.LocalException ex) - { - ex.printStackTrace(); - } - } - while(!line.equals("x")); - - // - // The refresher thread must be terminated before the session - // is destroyed, otherwise it might get - // ObjectNotExistException. - // - refresh.terminate(); - try - { - refresh.join(); - } - catch(InterruptedException e) - { - } - refresh = null; - - try - { - router.destroySession(); + while(!line.equals("x")); } catch(Glacier2.SessionNotExistException ex) { - ex.printStackTrace(); - } - catch(Ice.ConnectionLostException ex) - { - // - // Expected: the router closed the connection. - // + System.err.println(appName() + ": " + ex.toString()); + return 1; } return 0; diff --git a/java/demo/Glacier2/callback/README b/java/demo/Glacier2/callback/README index c6caff987c1..954ca715d52 100644 --- a/java/demo/Glacier2/callback/README +++ b/java/demo/Glacier2/callback/README @@ -16,10 +16,9 @@ In a separate window, start the client: $ java Client -If you plan to run this demo using clients on different hosts than -the router, you must first modify the configuration. You need to -change the Glacier2.Client.Endpoints property in config.glacier2 and -the Ice.Default.Router and Callback.Client.Router properties in -config.client. In all cases you must replace the "-h 127.0.0.1" -parameter with the actual external address of the machine on which -glacier2router is running. +If you plan to run this demo using clients on different hosts than the +router, you must first modify the configuration. You need to change +the Glacier2.Client.Endpoints property in config.glacier2 and the +Ice.Default.Router properties in config.client. In all cases you must +replace the "-h 127.0.0.1" parameter with the actual external address +of the machine on which glacier2router is running. diff --git a/java/demo/Glacier2/callback/config.client b/java/demo/Glacier2/callback/config.client index 174bc7f23e7..bbfb87538b3 100644 --- a/java/demo/Glacier2/callback/config.client +++ b/java/demo/Glacier2/callback/config.client @@ -5,31 +5,12 @@ Ice.Default.Router=DemoGlacier2/router:ssl -p 4064 -h 127.0.0.1 # -# We don't need any endpoints for the client if we use a -# router. Incoming requests are received through connections -# established from the client to the router. -# -Callback.Client.Endpoints= - -# # This must match the value of Callback.Server.Endpoints in # config.server. # Callback.Proxy=callback:tcp -h 127.0.0.1 -p 10000 # -# No active connection management is permitted with Glacier2. -# Connections must remain established. -# -Ice.ACM.Client=0 - -# -# Connection retry is not possible with Glacier2. Connections must -# remain established. -# -Ice.RetryIntervals=-1 - -# # Warn about connection exceptions # #Ice.Warn.Connections=1 diff --git a/java/demo/Glacier2/callback/expect.py b/java/demo/Glacier2/callback/expect.py index 224a9a8e193..adcd6cc7866 100755 --- a/java/demo/Glacier2/callback/expect.py +++ b/java/demo/Glacier2/callback/expect.py @@ -24,8 +24,6 @@ from demoscript.Glacier2 import callback server = Util.spawn('java Server --Ice.PrintAdapterReady') server.expect('.* ready') -sessionserver = Util.spawn('java SessionServer --Ice.PrintAdapterReady') -sessionserver.expect('.* ready') glacier2 = Util.spawn('glacier2router --Ice.Config=config.glacier2 --Ice.PrintAdapterReady --Glacier2.SessionTimeout=5') glacier2.expect('Glacier2.Client ready') @@ -33,4 +31,4 @@ glacier2.expect('Glacier2.Server ready') client = Util.spawn('java Client') -callback.run(client, server, sessionserver, glacier2) +callback.run(client, server, glacier2) diff --git a/java/demo/Glacier2/chat/Chat.ice b/java/demo/Glacier2/chat/Chat.ice new file mode 100644 index 00000000000..d55ec11be98 --- /dev/null +++ b/java/demo/Glacier2/chat/Chat.ice @@ -0,0 +1,31 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef CHAT_ICE +#define CHAT_ICE + +#include <Glacier2/Session.ice> + +module Demo +{ + +interface ChatCallback +{ + void message(string data); +}; + +interface ChatSession extends Glacier2::Session +{ + ["ami"] void setCallback(ChatCallback* callback); + ["ami"] void say(string data); +}; + +}; + +#endif diff --git a/java/demo/Glacier2/chat/Client.java b/java/demo/Glacier2/chat/Client.java new file mode 100644 index 00000000000..f0ab1aa53cf --- /dev/null +++ b/java/demo/Glacier2/chat/Client.java @@ -0,0 +1,544 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.BoxLayout; +import javax.swing.JDialog; +import javax.swing.AbstractAction; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JPopupMenu; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JSplitPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultEditorKit; +import javax.swing.text.Document; +import javax.swing.text.Element; + +import Glacier2.SessionFactoryHelper; +import Glacier2.SessionHelper; +import Glacier2.SessionNotExistException; +import Ice.Current; +import Ice.LocalException; +import Ice.StringSeqHolder; +import Ice.Util; + +// TODO: Simplify the callbacks when http://bugzilla/bugzilla/show_bug.cgi?id=4193 is fixed. + +@SuppressWarnings("serial") +public class Client extends JFrame +{ + public static void + main(final String[] args) + { + SwingUtilities.invokeLater(new Runnable() + { + public void + run() + { + try + { + // + // Create and set up the window. + // + new Client(args); + } + catch(Ice.LocalException e) + { + JOptionPane.showMessageDialog(null, + e.toString(), + "Initialization failed", + JOptionPane.ERROR_MESSAGE); + } + } + }); + } + + + Client(String[] args) + { + // Build the JTextArea that shows the chat conversation. + _output = new JTextArea(""); + _output.setLineWrap(true); + _output.setEditable(false); + + final JPopupMenu textMenu = new JPopupMenu(); + textMenu.add(new DefaultEditorKit.CopyAction()); + textMenu.pack(); + + _output.addMouseListener(new MouseAdapter() + { + public void + mousePressed(MouseEvent e) + { + if(e.isPopupTrigger()) + { + textMenu.show(_output, e.getX(), e.getY()); + } + } + }); + + // Build the JTextArea where the user writes input messages. + _input = new JTextArea(""); + _input.setLineWrap(true); + _input.setEditable(true); + _input.addKeyListener( new KeyListener() + { + public void + keyTyped(KeyEvent e) + { + if(e.getKeyChar() == KeyEvent.VK_ENTER) + { + Document doc = _input.getDocument(); + try + { + String msg = doc.getText(0, doc.getLength()).trim(); + if(msg.length() > 0) + { + _chat.say_async(new Demo.AMI_ChatSession_say() + { + @Override + public void + ice_exception(final LocalException ex) + { + SwingUtilities.invokeLater(new Runnable() { + + public void + run() + { + appendMessage("<system-message> - " + ex); + } + }); + } + + @Override + public void + ice_response() + { + } + }, msg); + } + } + catch(BadLocationException e1) + { + } + + _input.setText(""); + } + } + + public void + keyPressed(KeyEvent e) + { + } + + public void + keyReleased(KeyEvent e) + { + } + }); + + _outputScroll = new JScrollPane(_output); + _outputScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + _outputScroll.setBorder(null); + + _outputScroll.setMinimumSize(new Dimension(100, 100)); + _outputScroll.setPreferredSize(new Dimension(100, 100)); + + JSplitPane verticalSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT); + verticalSplit.setTopComponent(_outputScroll); + + JScrollPane conversationInputScroll = new JScrollPane(_input); + conversationInputScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + conversationInputScroll.setBorder(null); + + conversationInputScroll.setMinimumSize(new Dimension(100, 100)); + conversationInputScroll.setPreferredSize(new Dimension(100, 100)); + verticalSplit.setBottomComponent(conversationInputScroll); + + verticalSplit.setResizeWeight(0.9); + + _output.addComponentListener(new ComponentListener() + { + public void + componentResized(ComponentEvent e) + { + JScrollBar vertivalScrollbar = _outputScroll.getVerticalScrollBar(); + vertivalScrollbar.setValue(vertivalScrollbar.getMaximum()); + } + public void + componentHidden(ComponentEvent e) + { + } + + public void + componentMoved(ComponentEvent e) + { + } + + public void + componentShown(ComponentEvent e) + { + } + }); + + add(verticalSplit, BorderLayout.CENTER); + + JPanel statusPanel = new JPanel(); + JSeparator statusPanelSeparator = new JSeparator(); + _status = new JLabel(); + _status.setText("Disconnected"); + + statusPanel.add(statusPanelSeparator, BorderLayout.NORTH); + statusPanel.add(_status, BorderLayout.SOUTH); + + add(statusPanel, BorderLayout.SOUTH); + + JMenuBar menuBar = new JMenuBar(); + JMenu connectMenu = new JMenu("Connect"); + + _login = new AbstractAction("Login") + { + public void + actionPerformed(ActionEvent e) + { + login(); + } + }; + + _logout = new AbstractAction("Logout") + { + public void + actionPerformed(ActionEvent e) + { + setEnabled(false); + _status.setText("Disconnecting"); + + destroySession(); + _chat = null; + } + }; + _logout.setEnabled(false); + + _exit = new AbstractAction("Exit") + { + public void + actionPerformed(ActionEvent e) + { + exit(); + } + }; + + connectMenu.add(_login); + connectMenu.add(_logout); + if(!System.getProperty("os.name").startsWith("Mac OS")) + { + connectMenu.add(_exit); + } + + menuBar.add(connectMenu); + + setJMenuBar(menuBar); + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + addWindowListener(new WindowAdapter() + { + public void + windowClosing(WindowEvent e) + { + exit(); + } + }); + + pack(); + setSize(640, 480); + locateOnScreen(this); + setVisible(true); + + // Create the labels and text fields. + JLabel hostLabel = new JLabel("Host: ", JLabel.RIGHT); + _hostField = new JTextField("", 12); + _hostField.setText("127.0.0.1"); + JLabel userNameLabel = new JLabel("Username: ", JLabel.RIGHT); + _userNameField = new JTextField("", 12); + _userNameField.setText("test"); + JLabel passwordLabel = new JLabel("Password: ", JLabel.RIGHT); + _passwordField = new JPasswordField("", 12); + _connectionPanel = new JPanel(false); + _connectionPanel.setLayout(new BoxLayout(_connectionPanel, BoxLayout.X_AXIS)); + + JPanel labelPanel = new JPanel(false); + labelPanel.setLayout(new GridLayout(0, 1)); + labelPanel.add(hostLabel); + labelPanel.add(userNameLabel); + labelPanel.add(passwordLabel); + JPanel fieldPanel = new JPanel(false); + fieldPanel.setLayout(new GridLayout(0, 1)); + fieldPanel.add(_hostField); + fieldPanel.add(_userNameField); + fieldPanel.add(_passwordField); + _connectionPanel.add(labelPanel); + _connectionPanel.add(fieldPanel); + + _input.setEnabled(false); + Ice.Properties properties = Ice.Util.createProperties(); + properties.load("config.client"); + StringSeqHolder argHolder = new StringSeqHolder(args); + properties = Util.createProperties(argHolder, properties); + _factory = new SessionFactoryHelper(properties, new SessionFactoryHelper.Callback() + { + // The session helper callbacks are all called from the + // GUI thread. + public void + connected(SessionHelper session) + throws SessionNotExistException + { + // If the session has been reassigned avoid the + // spurious callback. + if(session != _session) + { + return; + } + + // The chat callback servant. We use an anonymous + // inner class since the implementation is very + // simple. + Demo._ChatCallbackDisp servant = new Demo._ChatCallbackDisp() + { + public void + message(final String data, Current current) + { + SwingUtilities.invokeLater(new Runnable() + { + public void + run() + { + appendMessage(data); + } + }); + } + }; + Demo.ChatCallbackPrx callback = Demo.ChatCallbackPrxHelper.uncheckedCast( + _session.addWithUUID(servant)); + + _chat = Demo.ChatSessionPrxHelper.uncheckedCast(_session.session()); + _chat.setCallback_async(new Demo.AMI_ChatSession_setCallback() + { + @Override + public void + ice_exception(LocalException ex) + { + SwingUtilities.invokeLater(new Runnable() + { + + public void + run() + { + destroySession(); + } + }); + } + + @Override + public void + ice_response() + { + SwingUtilities.invokeLater(new Runnable() + { + public void + run() + { + assert _loginDialog != null; + _loginDialog.dispose(); + + _login.setEnabled(false); + _logout.setEnabled(true); + + _input.setEnabled(true); + + _status.setText("Connected with " + _hostField.getText()); + } + }); + } + + }, callback); + } + + public void + disconnected(SessionHelper session) + { + // If the session has been reassigned avoid the + // spurious callback. + if(session != _session) + { + return; + } + + if(_loginDialog != null) + { + _loginDialog.dispose(); + } + + _session = null; + _chat = null; + + _login.setEnabled(true); + _logout.setEnabled(false); + + _input.setEnabled(false); + + _status.setText("Disconnected"); + } + + public void + connectFailed(SessionHelper session, Throwable ex) + { + // If the session has been reassigned avoid the + // spurious callback. + if(session != _session) + { + return; + } + + if(_loginDialog != null) + { + _loginDialog.dispose(); + } + _status.setText(ex.getClass().getName()); + } + + public void + createdCommunicator(SessionHelper session) + { + } + }); + _factory.setRouterIdentity(new Ice.Identity("router", "DemoGlacier2")); + + login(); + } + + protected void + login() + { + String[] options = {"Login", "Cancel" }; + // Show Login Dialog. + int option = JOptionPane.showOptionDialog(this, _connectionPanel, "Login", JOptionPane.OK_CANCEL_OPTION, + JOptionPane.INFORMATION_MESSAGE, null, options, options[0]); + + if(option == 0) + { + _factory.setRouterHost(_hostField.getText()); + // Connect to Glacier2 using SessionFactoryHelper + _session = _factory.connect(_userNameField.getText(), _passwordField.getText()); + String[] cancel = {"Cancel" }; + + // Show Connecting Dialog + JOptionPane pane = new JOptionPane("Please wait while connecting...", JOptionPane.INFORMATION_MESSAGE, + JOptionPane.DEFAULT_OPTION, null, cancel, cancel[0]); + _loginDialog = pane.createDialog(this, "Connecting"); + _loginDialog.setVisible(true); + + // User pressed cancel. + if(pane.getValue() != JOptionPane.UNINITIALIZED_VALUE) + { + // Destroy session + destroySession(); + } + } + } + + private void + destroySession() + { + if(_session != null) + { + _session.destroy(); + _session = null; + } + } + + private void + exit() + { + destroySession(); + dispose(); + Runtime.getRuntime().exit(0); + } + + public void + appendMessage(String message) + { + Document doc = (Document) _output.getDocument(); + Element e = doc.getDefaultRootElement(); + AttributeSet attr = e.getAttributes().copyAttributes(); + try + { + doc.insertString(doc.getLength(), message + "\n", attr); + } + catch(BadLocationException ex) + { + } + _output.setCaretPosition(doc.getLength()); + } + + private static void + locateOnScreen(Component component) + { + Dimension paneSize = component.getSize(); + Dimension screenSize = component.getToolkit().getScreenSize(); + component.setLocation((screenSize.width - paneSize.width) / 2, (screenSize.height - paneSize.height) / 2); + } + + private JLabel _status; + private JTextArea _output; + private JTextArea _input; + private JScrollPane _outputScroll; + + // Login/Logout actions. + private AbstractAction _login; + private AbstractAction _logout; + private AbstractAction _exit; + + // Login dialog + private JDialog _loginDialog; + private JTextField _userNameField; + private JTextField _passwordField; + private JTextField _hostField; + private JPanel _connectionPanel; + + // The session factory and current session. + private SessionFactoryHelper _factory; + private SessionHelper _session; + private Demo.ChatSessionPrx _chat; +} diff --git a/java/demo/Glacier2/chat/README b/java/demo/Glacier2/chat/README new file mode 100644 index 00000000000..1333ed77b7e --- /dev/null +++ b/java/demo/Glacier2/chat/README @@ -0,0 +1 @@ +TODO diff --git a/java/demo/Glacier2/chat/build.xml b/java/demo/Glacier2/chat/build.xml new file mode 100644 index 00000000000..23469f61a21 --- /dev/null +++ b/java/demo/Glacier2/chat/build.xml @@ -0,0 +1,47 @@ +<!-- + ********************************************************************** + + Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. + + This copy of Ice is licensed to you under the terms described in the + ICE_LICENSE file included in this distribution. + + ********************************************************************** +--> + +<project name="demo_Glacier2_chat" default="all" basedir="."> + + <!-- set global properties for this build --> + <property name="top.dir" value="../../.."/> + + <!-- import common definitions --> + <import file="${top.dir}/config/common.xml"/> + + <target name="generate" depends="init"> + <!-- Create the output directory for generated code --> + <mkdir dir="${generated.dir}"/> + <slice2java outputdir="${generated.dir}"> + <includepath> + <pathelement path="${slice.dir}"/> + </includepath> + <fileset dir="." includes="Chat.ice"/> + </slice2java> + </target> + + <target name="compile" depends="generate"> + <mkdir dir="${class.dir}"/> + <javac srcdir=".:${generated.dir}" destdir="${class.dir}" debug="${debug}"> + <exclude name="${generated.dir}/**"/> + <classpath refid="ice.classpath"/> + <compilerarg value="${javac.lint}"/> + </javac> + </target> + + <target name="all" depends="compile"/> + + <target name="clean"> + <delete dir="${generated.dir}"/> + <delete dir="${class.dir}"/> + </target> + +</project> diff --git a/java/demo/Glacier2/chat/config.client b/java/demo/Glacier2/chat/config.client new file mode 100644 index 00000000000..0e7d29d4e67 --- /dev/null +++ b/java/demo/Glacier2/chat/config.client @@ -0,0 +1,39 @@ +# +# Warn about connection exceptions +# +#Ice.Warn.Connections=1 + +# +# Network Tracing +# +# 0 = no network tracing +# 1 = trace connection establishment and closure +# 2 = like 1, but more detailed +# 3 = like 2, but also trace data transfer +# +#Ice.Trace.Network=1 + +# +# Protocol Tracing +# +# 0 = no protocol tracing +# 1 = trace protocol messages +# +#Ice.Trace.Protocol=1 + +# +# Security Tracing +# +# 0 = no security tracing +# 1 = trace messages +# +#IceSSL.Trace.Security=1 + +# +# SSL Configuration +# +Ice.Plugin.IceSSL=IceSSL.PluginFactory +IceSSL.DefaultDir=../../../../certs +IceSSL.Keystore=client.jks +IceSSL.Password=password +IceSSL.Truststore=certs.jks diff --git a/java/demo/Ice/README b/java/demo/Ice/README index 4e430133137..73587d81b47 100644 --- a/java/demo/Ice/README +++ b/java/demo/Ice/README @@ -66,6 +66,11 @@ Demos in this directory: This demo shows how to use sessions to clean up client-specific resources in a server after the client shuts down or crashes. +- swing + + A swing application that shows how to use Asynchronous Method + Invocation (AMI) in a graphical client. + - throughput A simple throughput demo that allows you to send sequences of diff --git a/java/demo/Ice/build.xml b/java/demo/Ice/build.xml index f6313e055bc..2f078a57998 100644 --- a/java/demo/Ice/build.xml +++ b/java/demo/Ice/build.xml @@ -25,6 +25,7 @@ <ant dir="plugin"/> <ant dir="serialize"/> <ant dir="session"/> + <ant dir="swing"/> <ant dir="throughput"/> <ant dir="value"/> </target> @@ -43,6 +44,7 @@ <ant dir="plugin" target="clean"/> <ant dir="serialize" target="clean"/> <ant dir="session" target="clean"/> + <ant dir="swing" target="clean"/> <ant dir="throughput" target="clean"/> <ant dir="value" target="clean"/> </target> diff --git a/java/demo/Ice/swing/.gitignore b/java/demo/Ice/swing/.gitignore new file mode 100644 index 00000000000..e5786e5ddd4 --- /dev/null +++ b/java/demo/Ice/swing/.gitignore @@ -0,0 +1 @@ +Hello.jar diff --git a/java/demo/Ice/swing/Client.java b/java/demo/Ice/swing/Client.java new file mode 100644 index 00000000000..0333548d285 --- /dev/null +++ b/java/demo/Ice/swing/Client.java @@ -0,0 +1,659 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.event.*; + +public class Client extends JFrame +{ + public static void main(final String[] args) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + try + { + // + // Create and set up the window. + // + new Client(args); + } + catch(Ice.LocalException e) + { + JOptionPane.showMessageDialog(null, e.toString(), "Initialization failed", + JOptionPane.ERROR_MESSAGE); + } + } + }); + } + + private void destroyCommunicator() + { + if(_communicator == null) + { + return; + } + + // + // Destroy the Ice communicator. + // + try + { + _communicator.destroy(); + } + catch(Throwable ex) + { + ex.printStackTrace(); + } + finally + { + _communicator = null; + } + } + + Client(String[] args) + { + // + // Initialize an Ice communicator. + // + try + { + Ice.InitializationData initData = new Ice.InitializationData(); + initData.properties = Ice.Util.createProperties(); + initData.properties.load("config.client"); + _communicator = Ice.Util.initialize(args, initData); + } + catch(Throwable ex) + { + handleException(ex); + } + + Container cp = this; + + JLabel l1 = new JLabel("Hostname"); + _hostname = new JTextField(); + JLabel l2 = new JLabel("Mode"); + _mode = new JComboBox(); + JLabel l3 = new JLabel("Timeout"); + _timeoutSlider = new JSlider(0, MAX_TIME); + _timeoutLabel = new JLabel("0.0"); + JLabel l4 = new JLabel("Delay"); + _delaySlider = new JSlider(0, MAX_TIME); + _delayLabel = new JLabel("0.0"); + JPanel buttonPanel = new JPanel(); + _hello = new JButton("Hello World!"); + _shutdown = new JButton("Shutdown"); + _flush = new JButton("Flush"); + _flush.setEnabled(false); + JPanel statusPanel = new JPanel(); + JSeparator statusPanelSeparator = new JSeparator(); + _status = new JLabel(); + _status.setText("Ready"); + + // + // Default to localhost. + // + _hostname.setText("127.0.0.1"); + + final String[] modes = new String[] + { + "Twoway", "Twoway Secure", "Oneway", "Oneway Batch", "Oneway Secure", "Oneway Secure Batch", "Datagram", + "Datagram Batch" + }; + _mode.setModel(new DefaultComboBoxModel(modes)); + + _hello.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + sayHello(); + } + }); + _shutdown.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + shutdown(); + } + }); + _flush.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + flush(); + } + }); + _mode.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + changeDeliveryMode(_mode.getSelectedIndex()); + } + }); + changeDeliveryMode(_mode.getSelectedIndex()); + + _timeoutSlider.addChangeListener(new SliderListener(_timeoutSlider, _timeoutLabel)); + _timeoutSlider.setValue(0); + _delaySlider.addChangeListener(new SliderListener(_delaySlider, _delayLabel)); + _delaySlider.setValue(0); + + GridBagConstraints gridBagConstraints; + + cp.setMaximumSize(null); + cp.setPreferredSize(null); + cp.setLayout(new GridBagLayout()); + + l1.setText("Hostname"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new Insets(5, 5, 5, 5); + cp.add(l1, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new Insets(5, 0, 5, 5); + cp.add(_hostname, gridBagConstraints); + + l2.setText("Mode"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new Insets(0, 5, 5, 0); + cp.add(l2, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new Insets(0, 0, 5, 5); + cp.add(_mode, gridBagConstraints); + + l3.setText("Timeout"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new Insets(0, 5, 5, 0); + cp.add(l3, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = GridBagConstraints.WEST; + cp.add(_timeoutSlider, gridBagConstraints); + + _timeoutLabel.setMinimumSize(new Dimension(20, 17)); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new Insets(0, 5, 5, 5); + cp.add(_timeoutLabel, gridBagConstraints); + + l4.setText("Delay"); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new Insets(0, 5, 5, 0); + cp.add(l4, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 3; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = GridBagConstraints.WEST; + cp.add(_delaySlider, gridBagConstraints); + + _delayLabel.setMinimumSize(new Dimension(20, 17)); + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 3; + gridBagConstraints.anchor = GridBagConstraints.WEST; + gridBagConstraints.insets = new Insets(0, 5, 5, 5); + cp.add(_delayLabel, gridBagConstraints); + + _hello.setText("Hello World!"); + buttonPanel.add(_hello); + + _shutdown.setText("Shutdown"); + buttonPanel.add(_shutdown); + + _flush.setText("Flush"); + buttonPanel.add(_flush); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 4; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.ipady = 5; + cp.add(buttonPanel, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 5; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new Insets(0, 5, 5, 5); + cp.add(statusPanelSeparator, gridBagConstraints); + + gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 6; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new Insets(0, 5, 5, 5); + cp.add(_status, gridBagConstraints); + + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + _shutdownHook = new Thread("Shutdown hook") + { + public void run() + { + destroyCommunicator(); + } + }; + + try + { + Runtime.getRuntime().addShutdownHook(_shutdownHook); + } + catch(IllegalStateException e) + { + // + // Shutdown in progress, ignored + // + } + + addWindowListener(new WindowAdapter() + { + public void windowClosing(WindowEvent e) + { + destroyCommunicator(); + Runtime.getRuntime().removeShutdownHook(_shutdownHook); + dispose(); + Runtime.getRuntime().exit(0); + } + }); + + + setTitle("Ice - Hello World!"); + pack(); + locateOnScreen(this); + setVisible(true); + } + + private enum DeliveryMode + { + TWOWAY, + TWOWAY_SECURE, + ONEWAY, + ONEWAY_BATCH, + ONEWAY_SECURE, + ONEWAY_SECURE_BATCH, + DATAGRAM, + DATAGRAM_BATCH; + + Ice.ObjectPrx apply(Ice.ObjectPrx prx) + { + switch (this) + { + case TWOWAY: + { + prx = prx.ice_twoway(); + break; + } + case TWOWAY_SECURE: + { + prx = prx.ice_twoway().ice_secure(true); + break; + } + case ONEWAY: + { + prx = prx.ice_oneway(); + break; + } + case ONEWAY_BATCH: + { + prx = prx.ice_batchOneway(); + break; + } + case ONEWAY_SECURE: + { + prx = prx.ice_oneway().ice_secure(true); + break; + } + case ONEWAY_SECURE_BATCH: + { + prx = prx.ice_batchOneway().ice_secure(true); + break; + } + case DATAGRAM: + { + prx = prx.ice_datagram(); + break; + } + case DATAGRAM_BATCH: + { + prx = prx.ice_batchDatagram(); + break; + } + } + return prx; + } + + public boolean isBatch() + { + return this == ONEWAY_BATCH || this == DATAGRAM_BATCH || this == ONEWAY_SECURE_BATCH; + } + } + + private Demo.HelloPrx createProxy() + { + String host = _hostname.getText().toString().trim(); + if(host.length() == 0) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _status.setText("No hostname"); + } + }); + return null; + } + + String s = "hello:tcp -h " + host + " -p 10000:ssl -h " + host + " -p 10001:udp -h " + host + " -p 10000"; + Ice.ObjectPrx prx = _communicator.stringToProxy(s); + prx = _deliveryMode.apply(prx); + int timeout = _timeoutSlider.getValue(); + if(timeout != 0) + { + prx = prx.ice_timeout(timeout); + } + return Demo.HelloPrxHelper.uncheckedCast(prx); + } + + class SayHelloI extends Demo.AMI_Hello_sayHello implements Ice.AMISentCallback + { + synchronized public void ice_exception(final Ice.LocalException ex) + { + assert (!_response); + _response = true; + + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + handleException(ex); + } + }); + } + + synchronized public void ice_sent() + { + if(_response) + { + return; + } + + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + if(_deliveryMode == DeliveryMode.TWOWAY || _deliveryMode == DeliveryMode.TWOWAY_SECURE) + { + _status.setText("Waiting for response"); + } + else + { + _status.setText("Ready"); + } + } + }); + } + + synchronized public void ice_response() + { + assert (!_response); + _response = true; + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _status.setText("Ready"); + } + }); + } + + private boolean _response = false; + } + + private void sayHello() + { + Demo.HelloPrx hello = createProxy(); + if(hello == null) + { + return; + } + + int delay = _delaySlider.getValue(); + try + { + if(!_deliveryMode.isBatch()) + { + if(hello.sayHello_async(new SayHelloI(), delay)) + { + if(_deliveryMode == DeliveryMode.TWOWAY || _deliveryMode == DeliveryMode.TWOWAY_SECURE) + { + _status.setText("Waiting for response"); + } + } + else + { + _status.setText("Sending request"); + } + } + else + { + _flush.setEnabled(true); + hello.sayHello(delay); + _status.setText("Queued sayHello request"); + } + } + catch(Ice.LocalException ex) + { + handleException(ex); + } + } + + private void shutdown() + { + Demo.HelloPrx hello = createProxy(); + if(hello == null) + { + return; + } + + try + { + if(!_deliveryMode.isBatch()) + { + hello.shutdown_async(new Demo.AMI_Hello_shutdown() + { + public void ice_exception(final Ice.LocalException ex) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + handleException(ex); + } + }); + } + + public void ice_response() + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _status.setText("Ready"); + } + }); + } + }); + if(_deliveryMode == DeliveryMode.TWOWAY || _deliveryMode == DeliveryMode.TWOWAY_SECURE) + { + _status.setText("Waiting for response"); + } + } + else + { + _flush.setEnabled(true); + hello.shutdown(); + _status.setText("Queued shutdown request"); + } + } + catch(Ice.LocalException ex) + { + handleException(ex); + } + } + + private void flush() + { + new Thread(new Runnable() + { + public void run() + { + try + { + _communicator.flushBatchRequests(); + } + catch(final Ice.LocalException ex) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + handleException(ex); + } + }); + } + } + }).start(); + + _flush.setEnabled(false); + _status.setText("Flushed batch requests"); + } + + private void changeDeliveryMode(long id) + { + switch ((int)id) + { + case 0: + _deliveryMode = DeliveryMode.TWOWAY; + break; + case 1: + _deliveryMode = DeliveryMode.TWOWAY_SECURE; + break; + case 2: + _deliveryMode = DeliveryMode.ONEWAY; + break; + case 3: + _deliveryMode = DeliveryMode.ONEWAY_BATCH; + break; + case 4: + _deliveryMode = DeliveryMode.ONEWAY_SECURE; + break; + case 5: + _deliveryMode = DeliveryMode.ONEWAY_SECURE_BATCH; + break; + case 6: + _deliveryMode = DeliveryMode.DATAGRAM; + break; + case 7: + _deliveryMode = DeliveryMode.DATAGRAM_BATCH; + break; + } + } + + private void handleException(final Throwable ex) + { + // Ignore CommunicatorDestroyedException which could occur on + // shutdown. + if(ex instanceof Ice.CommunicatorDestroyedException) + { + return; + } + ex.printStackTrace(); + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _status.setText(ex.getClass().getName()); + } + }); + } + + private static class SliderListener implements ChangeListener + { + SliderListener(JSlider slider, JLabel label) + { + _slider = slider; + _label = label; + } + + public void stateChanged(ChangeEvent ce) + { + float value = (float)(_slider.getValue() / 1000.0); + _label.setText(String.format("%.1f", value)); + } + + private JSlider _slider; + private JLabel _label; + } + + private static void locateOnScreen(Component component) + { + Dimension paneSize = component.getSize(); + Dimension screenSize = component.getToolkit().getScreenSize(); + component.setLocation((screenSize.width - paneSize.width) / 2, (screenSize.height - paneSize.height) / 2); + } + + private static final int MAX_TIME = 5000; // 5 seconds + + private JTextField _hostname; + private JComboBox _mode; + private JSlider _timeoutSlider; + private JLabel _timeoutLabel; + private JSlider _delaySlider; + private JLabel _delayLabel; + private JButton _hello; + private JButton _shutdown; + private JButton _flush; + private JLabel _status; + + private Ice.Communicator _communicator; + private DeliveryMode _deliveryMode; + private Thread _shutdownHook; +} diff --git a/java/demo/Ice/swing/Hello.ice b/java/demo/Ice/swing/Hello.ice new file mode 100644 index 00000000000..b5c9e4a599e --- /dev/null +++ b/java/demo/Ice/swing/Hello.ice @@ -0,0 +1,24 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#ifndef HELLO_ICE +#define HELLO_ICE + +module Demo +{ + +interface Hello +{ + ["ami"] idempotent void sayHello(int delay); + ["ami"] void shutdown(); +}; + +}; + +#endif diff --git a/java/demo/Ice/swing/README b/java/demo/Ice/swing/README new file mode 100644 index 00000000000..4cd502dda79 --- /dev/null +++ b/java/demo/Ice/swing/README @@ -0,0 +1,12 @@ +This demo illustrates how to write a swing application which invokes +ordinary (twoway) operations, as well as how to make oneway, datagram, +secure, and batched invocations. + +To run the demo, first start the hello server: + +$ cd ../hello +$ java Server + +In a separate window, start the swing client: + +$ java Client diff --git a/java/demo/Ice/swing/build.xml b/java/demo/Ice/swing/build.xml new file mode 100644 index 00000000000..fd6892e81eb --- /dev/null +++ b/java/demo/Ice/swing/build.xml @@ -0,0 +1,44 @@ +<!-- + ********************************************************************** + + Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. + + This copy of Ice is licensed to you under the terms described in the + ICE_LICENSE file included in this distribution. + + ********************************************************************** +--> + +<project name="demo_Ice_swing" default="all" basedir="."> + + <!-- set global properties for this build --> + <property name="top.dir" value="../../.."/> + + <!-- import common definitions --> + <import file="${top.dir}/config/common.xml"/> + + <target name="generate" depends="init"> + <!-- Create the output directory for generated code --> + <mkdir dir="${generated.dir}"/> + <slice2java outputdir="${generated.dir}"> + <fileset dir="." includes="Hello.ice"/> + </slice2java> + </target> + + <target name="compile" depends="generate"> + <mkdir dir="${class.dir}"/> + <javac srcdir=".:${generated.dir}" destdir="${class.dir}" debug="${debug}"> + <exclude name="${generated.dir}/**"/> + <classpath refid="ice.classpath"/> + <compilerarg value="${javac.lint}"/> + </javac> + </target> + + <target name="all" depends="compile"/> + + <target name="clean"> + <delete dir="${generated.dir}"/> + <delete dir="${class.dir}"/> + </target> + +</project> diff --git a/java/demo/Ice/swing/config.client b/java/demo/Ice/swing/config.client new file mode 100644 index 00000000000..45eae32e21f --- /dev/null +++ b/java/demo/Ice/swing/config.client @@ -0,0 +1,26 @@ +# +# Network Tracing +# +# 0 = no network tracing +# 1 = trace connection establishment and closure +# 2 = like 1, but more detailed +# 3 = like 2, but also trace data transfer +# +#Ice.Trace.Network=1 + +# +# Security Tracing +# +# 0 = no security tracing +# 1 = trace messages +# +#IceSSL.Trace.Security=1 + +# +# SSL Configuration +# +Ice.Plugin.IceSSL=IceSSL.PluginFactory +IceSSL.DefaultDir=../../../../certs +IceSSL.Password=password +IceSSL.Keystore=client.jks +IceSSL.Truststore=client.jks diff --git a/java/src/Glacier2/Application.java b/java/src/Glacier2/Application.java new file mode 100644 index 00000000000..fd0812d9958 --- /dev/null +++ b/java/src/Glacier2/Application.java @@ -0,0 +1,552 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package Glacier2; + +/** + * An extension of Ice.Application that makes it easy to write + * Glacier2 applications. + * + * <p> Applications must create a derived class that implements the + * {@link #createSession} and {@link #runWithSession} methods.<p> + * + * The base class invokes {@link #createSession} to create a new + * Glacier2 session and then invokes {@link #runWithSession} in + * which the subclass performs its application logic. The base class + * automatically destroys the session when {@link #runWithSession} + * returns. + * + * If {@link #runWithSession} calls {@link #restart} or raises any of + * the exceptions Ice.ConnectionRefusedException, + * Ice.ConnectionLostException, Ice.UnknownLocalException, + * Ice.RequestFailedException, or Ice.TimeoutException, the base + * class destroys the current session and restarts the application + * with another call to {@link #createSession} followed by + * {@link #runWithSession}. + * + * The application can optionally override the {@link #sessionDestroyed} + * callback method if it needs to take action when connectivity with + * the Glacier2 router is lost. + * + * A program can contain only one instance of this class. + * + * @see Ice.Application + * @see Glacier2.Router + * @see Glacier2.Session + * @see Ice.Communicator + * @see Ice.Logger + * @see #runWithSession + **/ +public abstract class Application extends Ice.Application +{ + /** + * This exception is raised if the session should be restarted. + */ + public class RestartSessionException extends Exception + { + } + + /** + * Initializes an instance that calls {@link Communicator#shutdown} if + * a signal is received. + **/ + public + Application() + { + } + + /** + * Initializes an instance that handles signals according to the signal + * policy. + * + * @param signalPolicy Determines how to respond to signals. + * + * @see SignalPolicy + **/ + public + Application(Ice.SignalPolicy signalPolicy) + { + super(signalPolicy); + } + + + /** + * Called once the communicator has been initialized and the Glacier2 session + * has been established. A derived class must implement <code>runWithSession</code>, + * which is the application's starting method. + * + * @param args The argument vector for the application. <code>Application</code> + * scans the argument vector passed to <code>main</code> for options that are + * specific to the Ice run time and removes them; therefore, the vector passed + * to <code>run</code> is free from Ice-related options and contains only options + * and arguments that are application-specific. + * + * @return The <code>runWithSession</code> method should return zero for successful + * termination, and non-zero otherwise. <code>Application.main</code> returns the + * value returned by <code>runWithSession</code>. + **/ + public abstract int + runWithSession(String[] args) + throws RestartSessionException; + + /** + * Run should not be overridden for Glacier2.Application. Instead + * <code>runWithSession</code> should be used. + */ + final public int + run(String[] args) + { + // This shouldn't be called. + assert false; + return 0; + } + + /** + * Called to restart the application's Glacier2 session. This + * method never returns. + * + * @throws RestartSessionException This exception is always thrown. + **/ + public void + restart() + throws RestartSessionException + { + throw new RestartSessionException(); + } + + /** + * Creates a new Glacier2 session. A call to + * <code>createSession</code> always precedes a call to + * <code>runWithSession</code>. If <code>Ice.LocalException</code> + * is thrown from this method, the application is terminated. + + * @return The Glacier2 session. + **/ + abstract public Glacier2.SessionPrx + createSession(); + + /** + * Called when the base class detects that the session has been destroyed. + * A subclass can override this method to take action after the loss of + * connectivity with the Glacier2 router. + **/ + public void + sessionDestroyed() + { + } + + /** + * Returns the Glacier2 router proxy + * @return The router proxy. + **/ + public static Glacier2.RouterPrx + router() + { + return _router; + } + + /** + * Returns the Glacier2 session proxy + * @return The session proxy. + **/ + public static Glacier2.SessionPrx + session() + { + return _session; + } + + /** + * Returns the category to be used in the identities of all of the client's + * callback objects. Clients must use this category for the router to + * forward callback requests to the intended client. + * @return The category. + * @throws SessionNotExistException No session exists. + **/ + public String + categoryForClient() throws SessionNotExistException + { + if(_router == null) + { + throw new SessionNotExistException(); + } + return _router.getCategoryForClient(); + } + + /** + * Create a new Ice identity for callback objects with the given + * identity name field. + * @return The identity. + * @throws SessionNotExistException No session exists. + **/ + public Ice.Identity + createCallbackIdentity(String name) throws SessionNotExistException + { + return new Ice.Identity(name, categoryForClient()); + } + + /** + * Adds a servant to the callback object adapter's Active Servant Map with a UUID. + * @param servant The servant to add. + * @return The proxy for the servant. + * @throws SessionNotExistException No session exists. + **/ + public Ice.ObjectPrx + addWithUUID(Ice.Object servant) throws SessionNotExistException + { + return objectAdapter().add(servant, createCallbackIdentity(java.util.UUID.randomUUID().toString())); + } + + /** + * Creates an object adapter for callback objects. + * @return The object adapter. + * @throws SessionNotExistException No session exists. + */ + public synchronized Ice.ObjectAdapter + objectAdapter() throws SessionNotExistException + { + if(_adapter == null) + { + if(_router == null) + { + throw new SessionNotExistException(); + } + // TODO: Depending on the resolution of + // http://bugzilla/bugzilla/show_bug.cgi?id=4264 the OA + // name could be an empty string. + String uuid = java.util.UUID.randomUUID().toString(); + _adapter = communicator().createObjectAdapterWithRouter(uuid, _router); + _adapter.activate(); + } + return _adapter; + } + + private class SessionPingThread extends Thread + { + SessionPingThread(Glacier2.RouterPrx router, long period) + { + _router = router; + _period = period; + _done = false; + } + + synchronized public void + run() + { + while(true) + { + _router.refreshSession_async(new Glacier2.AMI_Router_refreshSession() + { + public void + ice_response() + { + } + + public void + ice_exception(Ice.LocalException ex) + { + // Here the session has gone. The thread + // terminates, and we notify the + // application that the session has been + // destroyed. + done(); + sessionDestroyed(); + } + + public void + ice_exception(Ice.UserException ex) + { + // Here the session has gone. The thread + // terminates, and we notify the + // application that the session has been + // destroyed. + done(); + sessionDestroyed(); + } + }); + + if(!_done) + { + try + { + wait(_period); + } + catch(InterruptedException ex) + { + } + } + + if(_done) + { + break; + } + } + } + + public synchronized void + done() + { + if(!_done) + { + _done = true; + notify(); + } + } + + private final Glacier2.RouterPrx _router; + private final long _period; + private boolean _done = false; + } + + protected int + doMain(Ice.StringSeqHolder argHolder, Ice.InitializationData initData) + { + // Set the default properties for all Glacier2 applications. + initData.properties.setProperty("Ice.ACM.Client", "0"); + initData.properties.setProperty("Ice.RetryIntervals", "-1"); + + boolean restart; + Ice.IntHolder ret = new Ice.IntHolder(); + do + { + // A copy of the initialization data and the string seq + // needs to be passed to doMainInternal, as these can be + // changed by the application. + Ice.InitializationData id = (Ice.InitializationData)initData.clone(); + id.properties = id.properties._clone(); + Ice.StringSeqHolder h = new Ice.StringSeqHolder(); + h.value = argHolder.value.clone(); + + restart = doMain(h, id, ret); + } + while(restart); + return ret.value; + } + + private boolean + doMain(Ice.StringSeqHolder argHolder, Ice.InitializationData initData, Ice.IntHolder status) + { + // Reset internal state variables from Ice.Application. The + // remainder are reset at the end of this method. + _callbackInProgress = false; + _destroyed = false; + _interrupted = false; + + boolean restart = false; + status.value = 0; + + SessionPingThread ping = null; + try + { + _communicator = Ice.Util.initialize(argHolder, initData); + + _router = Glacier2.RouterPrxHelper.uncheckedCast(communicator().getDefaultRouter()); + if(_router == null) + { + Ice.Util.getProcessLogger().error("no glacier2 router configured"); + status.value = 1; + } + else + { + // + // The default is to destroy when a signal is received. + // + if(_signalPolicy == Ice.SignalPolicy.HandleSignals) + { + destroyOnInterrupt(); + } + + // If createSession throws, we're done. + try + { + _session = createSession(); + _createdSession = true; + } + catch(Ice.LocalException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + status.value = 1; + } + + if(_createdSession) + { + ping = new SessionPingThread(_router, (_router.getSessionTimeout() * 1000) / 2); + ping.start(); + status.value = runWithSession(argHolder.value); + } + } + } + // We want to restart on those exceptions which indicate a + // break down in communications, but not those exceptions that + // indicate a programming logic error (ie: marshal, protocol + // failure, etc). + catch(RestartSessionException ex) + { + restart = true; + } + catch(Ice.ConnectionRefusedException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.ConnectionLostException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.UnknownLocalException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.RequestFailedException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.TimeoutException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + restart = true; + } + catch(Ice.LocalException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + status.value = 1; + } + catch(java.lang.Exception ex) + { + Ice.Util.getProcessLogger().error("unknown exception" + IceInternal.Ex.toString(ex)); + status.value = 1; + } + catch(java.lang.Error err) + { + // + // We catch Error to avoid hangs in some non-fatal situations + // + Ice.Util.getProcessLogger().error("Java error " + IceInternal.Ex.toString(err)); + status.value = 1; + } + + // This clears any set interrupt. + if(_signalPolicy == Ice.SignalPolicy.HandleSignals) + { + defaultInterrupt(); + } + + synchronized(_mutex) + { + while(_callbackInProgress) + { + try + { + _mutex.wait(); + } + catch(java.lang.InterruptedException ex) + { + } + } + + if(_destroyed) + { + _communicator = null; + } + else + { + _destroyed = true; + // + // And _communicator != null, meaning will be + // destroyed next, _destroyed = true also ensures that + // any remaining callback won't do anything + // + } + } + + if(ping != null) + { + ping.done(); + while(true) + { + try + { + ping.join(); + break; + } + catch(InterruptedException ex) + { + } + } + ping = null; + } + + if(_createdSession && _router != null) + { + try + { + _router.destroySession(); + } + catch(Ice.ConnectionLostException ex) + { + // Expected: the router closed the connection. + } + catch(Glacier2.SessionNotExistException ex) + { + // This can also occur. + } + catch(Throwable ex) + { + // Not expected. + Ice.Util.getProcessLogger().error("unexpected exception when destroying the session:\n" + + IceInternal.Ex.toString(ex)); + } + _router = null; + } + + if(_communicator != null) + { + try + { + _communicator.destroy(); + } + catch(Ice.LocalException ex) + { + Ice.Util.getProcessLogger().error(IceInternal.Ex.toString(ex)); + status.value = 1; + } + catch(java.lang.Exception ex) + { + Ice.Util.getProcessLogger().error("unknown exception" + IceInternal.Ex.toString(ex)); + status.value = 1; + } + _communicator = null; + } + + synchronized(_mutex) + { + if(_appHook != null) + { + _appHook.done(); + } + } + + // Reset internal state. We cannot reset the Application state + // here, since _destroyed must remain true until we re-run + // this method. + _adapter = null; + _router = null; + _session = null; + _createdSession = false; + + return restart; + } + + private static Ice.ObjectAdapter _adapter; + private static Glacier2.RouterPrx _router; + private static Glacier2.SessionPrx _session; + private static boolean _createdSession = false; +} diff --git a/java/src/Glacier2/SessionFactoryHelper.java b/java/src/Glacier2/SessionFactoryHelper.java new file mode 100644 index 00000000000..ac77170b677 --- /dev/null +++ b/java/src/Glacier2/SessionFactoryHelper.java @@ -0,0 +1,355 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package Glacier2; + +/** + * A helper class for using Glacier2 with GUI applications. + * + * Applications should create a session factory for each Glacier2 router to which the application will + * connect. To connect with the Glacier2 router, call {@link SessionFactory#connect}. The callback object is + * notified of the various life cycle events. Once the session is torn down for whatever reason, the application + * can use the session factory to create another connection. + */ +public class SessionFactoryHelper +{ + /** + * A callback class to get notifications of status changes in the Glacier2 session. + * All callbacks on the <code>Callback</code> interface occur in the main swing thread. + */ + public interface Callback + { + /** + * Notifies the application that the communicator was created. + * + * @param session The Glacier2 session. + */ + void + createdCommunicator(SessionHelper session); + + /** + * Notifies the application that the Glacier2 session has been established. + * + * @param session The established session. + */ + + void + connected(SessionHelper session) + throws SessionNotExistException; + + /** + * Notifies the application that the Glacier2 session has been disconnected. + * + * @param session The disconnected session. + */ + void + disconnected(SessionHelper session); + + /** + * Notifies the application that the Glacier2 session establishment failed. + * + * @param session The session reporting the connection + * failure. + * @param ex The exception. + */ + void + connectFailed(SessionHelper session, Throwable ex); + } + + /** + * Creates a SessionFactory object. + * + * @param callback The callback object for notifications. + * @throws {@link Ice.InitializationException} + */ + public + SessionFactoryHelper(Callback callback) throws Ice.InitializationException + { + initialize(callback, new Ice.InitializationData(), Ice.Util.createProperties()); + } + + /** + * Creates a SessionFactory object. + * + * @param initData The initialization data to use when creating the communicator. + * @param callback The callback object for notifications. + * @throws {@link Ice.InitializationException} + */ + public + SessionFactoryHelper(Ice.InitializationData initData, Callback callback) throws Ice.InitializationException + { + initialize(callback, initData, initData.properties); + } + + /** + * Creates a SessionFactory object. + * + * @param properties The properties to use when creating the communicator. + * @param callback The callback object for notifications. + * @throws {@link Ice.InitializationException} + */ + public + SessionFactoryHelper(Ice.Properties properties, Callback callback) throws Ice.InitializationException + { + initialize(callback, new Ice.InitializationData(), properties); + } + + private void + initialize(Callback callback, Ice.InitializationData initData, Ice.Properties properties) + throws Ice.InitializationException + { + if(callback == null) + { + throw new Ice.InitializationException("Attempt to create a SessionFactoryHelper with a null Callback" + + "argument"); + } + + if(initData == null) + { + throw new Ice.InitializationException("Attempt to create a SessionFactoryHelper with a null " + + "InitializationData argument"); + } + + if(properties == null) + { + throw new Ice.InitializationException("Attempt to create a SessionFactoryHelper with a null Properties " + + "argument"); + } + + _callback = callback; + _initData = initData; + _initData.properties = properties; + + // + // Set default properties; + // + _initData.properties.setProperty("Ice.ACM.Client", "0"); + _initData.properties.setProperty("Ice.RetryIntervals", "-1"); + } + + /** + * Set the router object identity. + * + * @return The Glacier2 router's identity. + */ + synchronized public void + setRouterIdentity(Ice.Identity identity) + { + _identity = identity; + } + + /** + * Returns the object identity of the Glacier2 router. + * + * @return The Glacier2 router's identity. + */ + synchronized public Ice.Identity + getRouterIdentity() + { + return _identity; + } + + /** + * Sets the host on which the Glacier2 router runs. + * + * @param hostname The host name (or IP address) of the router host. + */ + synchronized public void + setRouterHost(String hostname) + { + _routerHost = hostname; + } + + /** + * Returns the host on which the Glacier2 router runs. + * + * @return The Glacier2 router host. + */ + synchronized public String + getRouterHost() + { + return _routerHost; + } + + /** + * Sets whether to connect with the Glacier2 router securely. + * + * @param secure If <code>true</code>, the client connects to the router + * via SSL; otherwise, the client connects via TCP. + */ + synchronized public void + setSecure(boolean secure) + { + _secure = secure; + } + + /** + * Returns whether the session factory will establish a secure connection to the Glacier2 router. + * + * @return The secure flag. + */ + synchronized public boolean + getSecure() + { + return _secure; + } + + /** + * Sets the connect and connection timeout for the Glacier2 router. + * + * @param timeoutMillisecs The timeout in milliseconds. A zero + * or negative timeout value indicates that the router proxy has no associated timeout. + */ + synchronized public void + setTimeout(int timeoutMillisecs) + { + _timeout = timeoutMillisecs; + } + + /** + * Returns the connect and connection timeout associated with the Glacier2 router. + * + * @return The timeout. + */ + synchronized public int + getTimeout() + { + return _timeout; + } + + /** + * Sets the Glacier2 router port to connect to. + * + * @param port The port. If 0, then the default port (4063 for TCP or 4064 for SSL) is used. + */ + synchronized public void + setPort(int port) + { + _port = port; + } + + /** + * Returns the Glacier2 router port to connect to. + * + * @return The port. + */ + synchronized public int + getPort() + { + return _port == 0 ? (_secure ? GLACIER2_TCP_PORT : GLACIER2_SSL_PORT) : _port; + } + + /** + * Returns the initialization data used to initialize the communicator. + * + * @return The initialization data. + */ + synchronized public Ice.InitializationData + getInitializationData() + { + return _initData; + } + + /** + * Connects to the Glacier2 router using the associated SSL credentials. + * + * Once the connection is established, {@link Callback#connected} is called on the callback object; + * upon failure, {@link Callback#connectFailed} is called with the exception. + * + * @return The connected session. + */ + synchronized public SessionHelper + connect() + { + SessionHelper session = new SessionHelper(_callback, createInitData()); + session.connect(); + return session; + } + + /** + * Connect the Glacier2 session using user name and password credentials. + * + * Once the connection is established, {@link Callback#connected} is called on the callback object; + * upon failure, {@link Callback#connectFailed) is called with the exception. + * + * @param username The user name. + * @param password The password. + * @return The connected session. + */ + synchronized public SessionHelper + connect(final String username, final String password) + { + SessionHelper session = new SessionHelper(_callback, createInitData()); + session.connect(username, password); + return session; + } + + private Ice.InitializationData + createInitData() + { + // Clone the initialization data and properties. + Ice.InitializationData initData = (Ice.InitializationData)_initData.clone(); + initData.properties = initData.properties._clone(); + + if(initData.properties.getProperty("Ice.Default.Router").length() == 0) + { + StringBuffer sb = new StringBuffer(); + sb.append("\""); + sb.append(Ice.Util.identityToString(_identity)); + sb.append("\""); + sb.append(":"); + + if(_secure) + { + sb.append("ssl -p "); + } + else + { + sb.append("tcp -p "); + } + + if(_port != 0) + { + sb.append(_port); + } + else + { + if(_secure) + { + sb.append(GLACIER2_SSL_PORT); + } + else + { + sb.append(GLACIER2_TCP_PORT); + } + } + + sb.append(" -h "); + sb.append(_routerHost); + if(_timeout > 0) + { + sb.append(" -t "); + sb.append(_timeout); + } + + initData.properties.setProperty("Ice.Default.Router", sb.toString()); + } + return initData; + } + + private Callback _callback; + private String _routerHost = "127.0.0.1"; + private Ice.InitializationData _initData; + private Ice.Identity _identity = new Ice.Identity("router", "Glacier2"); + private boolean _secure = true; + private int _port = 0; + private int _timeout = 10000; + private static final int GLACIER2_SSL_PORT = 4064; + private static final int GLACIER2_TCP_PORT = 4063; +} diff --git a/java/src/Glacier2/SessionHelper.java b/java/src/Glacier2/SessionHelper.java new file mode 100644 index 00000000000..bea4a5dec15 --- /dev/null +++ b/java/src/Glacier2/SessionHelper.java @@ -0,0 +1,496 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2009 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** +package Glacier2; + +import javax.swing.SwingUtilities; + +import Glacier2.SessionFactoryHelper.Callback; + +/** + * A helper class for using Glacier2 with GUI applications. + */ +public class SessionHelper +{ + private class SessionRefreshThread extends Thread + { + SessionRefreshThread(Glacier2.RouterPrx router, long period) + { + _router = router; + _period = period; + _done = false; + } + + synchronized public void + run() + { + while(true) + { + _router.refreshSession_async(new Glacier2.AMI_Router_refreshSession() + { + public void ice_response() + { + } + + public void ice_exception(Ice.LocalException ex) + { + done(); + SessionHelper.this.destroy(); + } + + public void ice_exception(Ice.UserException ex) + { + done(); + SessionHelper.this.destroy(); + } + }); + if(!_done) + { + try + { + wait(_period); + } + catch(InterruptedException ex) + { + } + } + if(_done) + { + break; + } + } + } + + public synchronized void + done() + { + if(!_done) + { + _done = true; + notify(); + } + } + + private final Glacier2.RouterPrx _router; + private final long _period; + private boolean _done = false; + } + + /** + * Creates a Glacier2 session. + * + * @param callback The callback for notifications about session establishment. + * @param initData The {@link Ice.InitializationData} for initializing the communicator. + */ + public SessionHelper(SessionFactoryHelper.Callback callback, Ice.InitializationData initData) + { + _callback = callback; + _initData = initData; + } + + /** + * Destroys the Glacier2 session. + * + * Once the session has been destroyed, {@link Callback.disconnected} is called on + * the associated callback object. + */ + synchronized public void + destroy() + { + if(_destroy) + { + return; + } + _destroy = true; + if(_refreshThread == null) + { + // In this case a connecting session is being + // destroyed. The communicator and session will be + // destroyed when the connection establishment has + // completed. + return; + } + _session = null; + + try + { + Runtime.getRuntime().removeShutdownHook(_shutdownHook); + } + catch(IllegalStateException ex) + { + // Ignore + } + catch(SecurityException ex) + { + // Ignore + } + + // Run the destroyInternal in a thread. This is because it + // destroyInternal makes remote invocations. + new Thread(new Runnable() + { + public void run() + { + destroyInternal(); + } + }).start(); + } + + /** + * Returns the session's communicator object. + * @return The communicator. + */ + synchronized public Ice.Communicator + communicator() + { + return _communicator; + } + + /** + * Returns the category to be used in the identities of all of the client's + * callback objects. Clients must use this category for the router to + * forward callback requests to the intended client. + * @return The category. + * @throws SessionNotExistException No session exists. + **/ + synchronized public String + categoryForClient() + throws SessionNotExistException + { + if(_router == null) + { + throw new SessionNotExistException(); + } + + return _router.getCategoryForClient(); + } + + /** + * Adds a servant to the callback object adapter's Active Servant Map with a UUID. + * @param servant The servant to add. + * @return The proxy for the servant. + * @throws SessionNotExistException No session exists. + **/ + public synchronized Ice.ObjectPrx + addWithUUID(Ice.Object servant) + throws SessionNotExistException + { + if(_router == null) + { + throw new SessionNotExistException(); + } + return internalObjectAdapter().add(servant, new Ice.Identity(java.util.UUID.randomUUID().toString(), + _router.getCategoryForClient())); + } + + /** + * Returns the Glacier2 session proxy. If the session hasn't been established yet, + * or the session has already been destroyed, throws SessionNotExistException. + * @return The session proxy, or throws SessionNotExistException if no session exists. + * @throws SessionNotExistException No session exists. + */ + synchronized public Glacier2.SessionPrx + session() + throws SessionNotExistException + { + if(_session == null) + { + throw new SessionNotExistException(); + } + + return _session; + } + + /** + * Returns true if there is an active session, otherwise returns false. + * @return <code>true</code>if session exists or false if no session exists. + */ + public boolean + isConnected() + { + synchronized(this) + { + return _connected; + } + } + + /** + * Creates an object adapter for callback objects. + * @return The object adapter. + * @throws SessionNotExistException No session exists. + */ + synchronized public Ice.ObjectAdapter + objectAdapter() + throws SessionNotExistException + { + return internalObjectAdapter(); + } + + // Only call this method when the calling thread owns the lock + private Ice.ObjectAdapter + internalObjectAdapter() + throws SessionNotExistException + { + if(_router == null) + { + throw new SessionNotExistException(); + } + if(_adapter == null) + { + // TODO: Depending on the resolution of + // http://bugzilla/bugzilla/show_bug.cgi?id=4264 the OA + // name could be an empty string. + _adapter = _communicator.createObjectAdapterWithRouter(java.util.UUID.randomUUID().toString(), _router); + _adapter.activate(); + } + return _adapter; + } + + private interface ConnectStrategy + { + Glacier2.SessionPrx + connect(Glacier2.RouterPrx router) + throws CannotCreateSessionException, PermissionDeniedException; + } + + /** + * Connects to the Glacier2 router using the associated SSL credentials. + * + * Once the connection is established, {@link Callback#connected} is called on the callback object; + * upon failure, {@link Callback#exception} is called with the exception. + */ + synchronized protected void + connect() + { + connectImpl(new ConnectStrategy() + { + public SessionPrx connect(RouterPrx router) + throws CannotCreateSessionException, PermissionDeniedException + { + return router.createSessionFromSecureConnection(); + } + }); + } + + /** + * Connects a Glacier2 session using user name and password credentials. + * + * Once the connection is established, {@link Callback#connected} is called on the callback object; + * upon failure {@link Callback.exception} is called with the exception. + * + * @param username The user name. + * @param password The password. + */ + synchronized protected void + connect(final String username, final String password) + { + connectImpl(new ConnectStrategy() + { + public SessionPrx connect(RouterPrx router) + throws CannotCreateSessionException, PermissionDeniedException + { + return router.createSession(username, password); + } + }); + } + + synchronized private void + connected(RouterPrx router, SessionPrx session) + { + _router = router; + + if(_destroy) + { + destroyInternal(); + return; + } + + // Assign the session after _destroy is checked. + _session = session; + _connected = true; + + assert _refreshThread == null; + _refreshThread = new SessionRefreshThread(_router, (_router.getSessionTimeout() * 1000)/2); + _refreshThread.start(); + + _shutdownHook = new Thread("Shutdown hook") + { + public void run() + { + SessionHelper.this.destroy(); + } + }; + + try + { + Runtime.getRuntime().addShutdownHook(_shutdownHook); + } + catch(IllegalStateException e) + { + // + // Shutdown in progress, ignored + // + } + catch(SecurityException ex) + { + // Ignore. Unsigned applets cannot registered shutdown + // hooks. + } + + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + try + { + _callback.connected(SessionHelper.this); + } + catch(SessionNotExistException ex) + { + SessionHelper.this.destroy(); + } + } + }); + } + + synchronized private void + destroyInternal() + { + assert _destroy; + + try + { + _router.destroySession(); + } + catch(Ice.ConnectionLostException e) + { + // Expected + } + catch(SessionNotExistException e) + { + // This can also occur. + } + catch(Throwable e) + { + // Not expected. + _communicator.getLogger().warning("SessionHelper: unexpected exception when destroying the session:\n" + e); + } + _router = null; + _connected = false; + + if(_refreshThread != null) + { + _refreshThread.done(); + while(true) + { + try + { + _refreshThread.join(); + break; + } + catch(InterruptedException e) + { + } + } + _refreshThread = null; + } + + try + { + _communicator.destroy(); + } + catch(Throwable ex) + { + } + _communicator = null; + + // Notify the callback that the session is gone. + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _callback.disconnected(SessionHelper.this); + } + }); + } + + private void + connectImpl(final ConnectStrategy factory) + { + assert !_destroy; + + try + { + _communicator = Ice.Util.initialize(_initData); + } + catch(final Ice.LocalException ex) + { + _destroy = true; + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _callback.connectFailed(SessionHelper.this, ex); + } + }); + return; + } + + new Thread(new Runnable() + { + public void run() + { + try + { + SwingUtilities.invokeAndWait(new Runnable() + { + public void run() + { + _callback.createdCommunicator(SessionHelper.this); + } + }); + + Glacier2.RouterPrx routerPrx = Glacier2.RouterPrxHelper.uncheckedCast( + _communicator.getDefaultRouter()); + Glacier2.SessionPrx session = factory.connect(routerPrx); + connected(routerPrx, session); + } + catch (final Exception ex) + { + try + { + _communicator.destroy(); + } + catch(Throwable ex1) + { + } + + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _callback.connectFailed(SessionHelper.this, ex); + } + }); + } + } + }).start(); + } + + private Ice.InitializationData _initData; + private Ice.Communicator _communicator; + private Ice.ObjectAdapter _adapter; + private Glacier2.RouterPrx _router; + private Glacier2.SessionPrx _session; + + private SessionRefreshThread _refreshThread; + private SessionFactoryHelper.Callback _callback; + private boolean _destroy = false; + private boolean _connected = false; + private Thread _shutdownHook; +} diff --git a/java/src/Ice/Application.java b/java/src/Ice/Application.java index 01b42196f8c..6bf0697681c 100644 --- a/java/src/Ice/Application.java +++ b/java/src/Ice/Application.java @@ -177,6 +177,12 @@ public abstract class Application Util.setProcessLogger(new LoggerI(initData.properties.getProperty("Ice.ProgramName"), "")); } + return doMain(argHolder, initData); + } + + protected int + doMain(StringSeqHolder argHolder, Ice.InitializationData initData) + { int status = 0; try @@ -427,7 +433,7 @@ public abstract class Application } /** - * Clears any shutdownn hooks, including any hook established with {@link #destroyOnInterrupt}code> or + * Clears any shutdown hooks, including any hook established with {@link #destroyOnInterrupt}code> or * {@link #shutdownOnInterrupt}. **/ public static void @@ -524,9 +530,10 @@ public abstract class Application } } - static class AppHook extends Thread + // For use by Glacier2.Application + static public class AppHook extends Thread { - void + public void done() { synchronized(_doneMutex) @@ -642,12 +649,12 @@ public abstract class Application private Thread _hook; } - private static String _appName; - private static Communicator _communicator; - private static AppHook _appHook; - private static java.lang.Object _mutex = new java.lang.Object(); - private static boolean _callbackInProgress = false; - private static boolean _destroyed = false; - private static boolean _interrupted = false; - private static SignalPolicy _signalPolicy = SignalPolicy.HandleSignals; + protected static String _appName; + protected static Communicator _communicator; + protected static AppHook _appHook; + protected static java.lang.Object _mutex = new java.lang.Object(); + protected static boolean _callbackInProgress = false; + protected static boolean _destroyed = false; + protected static boolean _interrupted = false; + protected static SignalPolicy _signalPolicy = SignalPolicy.HandleSignals; } diff --git a/java/src/IceInternal/Util.java b/java/src/IceInternal/Util.java index a9eebf126d2..17c53bfa3e6 100644 --- a/java/src/IceInternal/Util.java +++ b/java/src/IceInternal/Util.java @@ -138,14 +138,16 @@ public final class Util loadClass(String className, ClassLoader cl) throws LinkageError { - assert(cl != null); - try - { - return cl.loadClass(className); - } - catch(ClassNotFoundException ex) + if(cl != null) { - // Ignore + try + { + return cl.loadClass(className); + } + catch(ClassNotFoundException ex) + { + // Ignore + } } return null; |