/* * JacORB - a free Java ORB * * Copyright (C) 1997-2014 Gerald Brose / The JacORB Team. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.jacorb.imr; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.PrintWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import org.jacorb.config.Configuration; import org.jacorb.config.ConfigurationException; import org.jacorb.imr.AdminPackage.DuplicateServerName; import org.jacorb.imr.AdminPackage.FileOpFailed; import org.jacorb.imr.AdminPackage.IllegalServerName; import org.jacorb.imr.AdminPackage.UnknownHostName; import org.jacorb.imr.RegistrationPackage.DuplicatePOAName; import org.jacorb.imr.RegistrationPackage.IllegalHostName; import org.jacorb.imr.RegistrationPackage.IllegalPOAName; import org.jacorb.imr.RegistrationPackage.InvalidSSDRef; import org.jacorb.orb.LocateReplyReceiver; import org.jacorb.orb.ORB; import org.jacorb.orb.ParsedIOR; import org.jacorb.orb.SystemExceptionHelper; import org.jacorb.orb.giop.ClientConnection; import org.jacorb.orb.giop.ClientConnectionManager; import org.jacorb.orb.giop.GIOPConnection; import org.jacorb.orb.giop.LocateReplyInputStream; import org.jacorb.orb.giop.LocateRequestInputStream; import org.jacorb.orb.giop.LocateRequestOutputStream; import org.jacorb.orb.giop.MessageReceptorPool; import org.jacorb.orb.giop.NoBiDirServerReplyListener; import org.jacorb.orb.giop.ReplyListener; import org.jacorb.orb.giop.ReplyOutputStream; import org.jacorb.orb.giop.RequestInputStream; import org.jacorb.orb.giop.RequestListener; import org.jacorb.orb.giop.ServerGIOPConnection; import org.jacorb.orb.giop.TransportManager; import org.jacorb.orb.iiop.IIOPAddress; import org.jacorb.orb.iiop.IIOPProfile; import org.jacorb.orb.iiop.ServerIIOPConnection; import org.jacorb.orb.listener.NullTCPConnectionListener; import org.jacorb.poa.util.POAUtil; import org.jacorb.util.ObjectUtil; import org.omg.CORBA.SystemException; import org.omg.GIOP.LocateStatusType_1_2; import org.omg.PortableServer.IdAssignmentPolicyValue; import org.omg.PortableServer.LifespanPolicyValue; import org.omg.PortableServer.POA; import org.omg.PortableServer.POAHelper; import org.slf4j.Logger; /** * This is the main class of the JacORB implementation repository. * It keeps track of the registered POAs with lifespan policy * PERSISTENT and provides a way for migrating and restarting * the POAS servers. * * @author Nicolas Noffke * */ public class ImplementationRepositoryImpl extends ImplementationRepositoryPOA { /** * <code>orb</code> is the ORB instance for the IMR. */ private org.omg.CORBA.ORB orb; private org.jacorb.config.Configuration configuration = null; /** the specific logger for this component */ private Logger logger = null; private String iorFile = null; /** * the file containing the serialized server table. Also * used for writing the table to on shutdown. */ private File table_file; private ServerTable server_table; private File table_file_backup; private SocketListener listener; private Thread listenerThread; private int object_activation_retries = 5; private int object_activation_sleep = 50; private boolean allow_auto_register = false; private boolean check_object_liveness = false; private long poaActivationTimeout = 120000; //2 min private WriteThread wt; private boolean updatePending; private ResourceLock server_table_ser_lock; private Shutdown shutdownThread; /** * The constructor. * It builds up the server table and starts up the SocketListener thread. */ public ImplementationRepositoryImpl(org.omg.CORBA.ORB orb) { this.orb = orb; this.server_table_ser_lock = new ResourceLock(); shutdownThread = new Shutdown (); } public void configure(Configuration myConfiguration) throws ConfigurationException { configuration = myConfiguration; wt = new WriteThread (); wt.setName ("IMR Write Thread"); logger = configuration.getLogger("org.jacorb.imr"); String defaultTableFile = "table.dat"; String tableFileStr = configuration.getAttribute("jacorb.imr.table_file", defaultTableFile); //NOTE: deliberate use of ref equivalence check here. I need to find //out if the default case has taken place, in which case, i assume //that the default string ref is just passed through. if (tableFileStr == defaultTableFile) { if (this.logger.isWarnEnabled()) { this.logger.warn("No file for the server table specified! Please configure the property jacorb.imr.table_file!"); this.logger.warn("Will create \"table.dat\" in current directory, if necessary"); } } table_file = new File(tableFileStr); boolean _new_table = false; // try to open table file if (! table_file.exists ()) { _new_table = true; if (this.logger.isInfoEnabled()) { this.logger.info("Table file " + tableFileStr + " does not exist - autocreating it."); } try { table_file.createNewFile (); } catch (IOException ex) { throw new ConfigurationException("Failed to create table file", ex); } } else { if (table_file.isDirectory ()) { throw new ConfigurationException("The table file is a directory! Please check " + table_file.getAbsolutePath()); } if (! table_file.canRead()) { throw new ConfigurationException("The table file is not readable! Please check " + table_file.getAbsolutePath()); } if (! table_file.canWrite()) { throw new ConfigurationException("The table file is not writable! Please check " + table_file.getAbsolutePath()); } } try { if (_new_table) { this.server_table = new ServerTable(); save_server_table(table_file); } else { try { ObjectInputStream _in = new ObjectInputStream(new FileInputStream(table_file)); server_table = (ServerTable)_in.readObject(); _in.close(); } catch (Exception ex) { logger.warn("Failed to read ServerTable -- creating an empty one"); server_table = new ServerTable(); save_server_table(table_file); } } } catch (FileOpFailed ex) { this.logger.error("Failed to read ServerTable", ex); } //should be set. if not, throw this.iorFile = configuration.getAttribute("jacorb.imr.ior_file"); String _backup_file_str = configuration.getAttribute("jacorb.imr.backup_file", ""); //set up server table backup file if (_backup_file_str.length() == 0) { this.logger.warn("No backup file specified!. No backup file will be created"); } if (_backup_file_str.length() > 0) { table_file_backup = new File(_backup_file_str); // try to open backup file if ( ! table_file_backup.exists ()) { _new_table = true; if (this.logger.isInfoEnabled()) { this.logger.info("Backup file " + _backup_file_str + " does not exist - autocreating it."); } try { table_file_backup.createNewFile(); } catch (IOException ex) { throw new ConfigurationException("Failed to create backup file", ex); } } else { if (table_file_backup.isDirectory ()) { throw new ConfigurationException("The backup file is a directory! Please check " + table_file_backup.getAbsolutePath()); } if (! table_file_backup.canRead()) { throw new ConfigurationException("The backup file is not readable! Please check " + table_file_backup.getAbsolutePath()); } if (! table_file_backup.canWrite()) { throw new ConfigurationException("The backup file is not writable! Please check " + table_file_backup.getAbsolutePath()); } } } this.object_activation_retries = configuration.getAttributeAsInteger("jacorb.imr.object_activation_retries", 5); this.object_activation_sleep = configuration.getAttributeAsInteger("jacorb.imr.object_activation_sleep", 50); this.allow_auto_register = configuration.getAttributeAsBoolean("jacorb.imr.allow_auto_register", false); this.check_object_liveness = configuration.getAttributeAsBoolean("jacorb.imr.check_object_liveness", false); this.poaActivationTimeout = configuration.getAttributeAsInteger( "jacorb.imr.timeout", 120000); this.listener = new SocketListener(); this.listener.configure(configuration); this.listenerThread = new Thread(listener); this.listenerThread.setPriority(Thread.MAX_PRIORITY); this.listenerThread.start(); this.wt.setDaemon (true); this.wt.start (); } public String getIORFile() { return this.iorFile; } // implementation of org.jacorb.imr.RegistrationOperations interface /** * This method sets a server down, i.e. not.active. If a request for * that server is encountered, the server is tried to be restarted. * * @param server the servers name. * @exception org.jacorb.imr.UnknownServerName No server with name * <code>server</code> has been registered. */ @Override public void set_server_down(String server) throws UnknownServerName { if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: server " + server + " is going down... "); } ImRServerInfo _server = server_table.getServer(server); _server.setDown(); } /** * This method registers a POA. It has actually two functions: * <ul> * <li> Register a POA that has not yet been registered. It is the added to the * server table. </li> * <li> Reactivating a POA that is not active, but has already an entry * in the server table</li> </ul> * The reason for using only one method for those two tasks is that it is * much more difficult for the ORB, which does the registering, to distinguish * between an newly created POA and a restarted one. * * @param name the POAs name. * @param server the logical server name of the server the running in. * @param host the POAs host. * @param port the POas port. * @exception org.jacorb.imr.RegistrationPackage.IllegalPOAName the POAs name is not valid. * @exception org.jacorb.imr.RegistrationPackage.DuplicatePOAName an active POA with * <code>name</code> is currently registered. * @exception org.jacorb.imr.UnknownServerName The server has not been registered. */ @Override public void register_poa(String name, String server, String host, int port) throws IllegalPOAName, DuplicatePOAName, UnknownServerName { ImRServerInfo _server = null; ImRPOAInfo _poa = null; boolean remap = false; updatePending = true; if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: registering poa " + name + " for server: " + server + " on " + host + ":" + port); } if( allow_auto_register && ! server_table.hasServer( server )) { try { register_server( server, "", "" ); } catch( IllegalServerName isn ) { //ignore } catch( DuplicateServerName dsn ) { //ignore } } _server = server_table.getServer(server); _poa = server_table.getPOA(name); if (_poa == null) { //New POAInfo is to be created _poa = new ImRPOAInfo(name, host, port, _server, poaActivationTimeout); server_table_ser_lock.gainExclusiveLock(); _server.addPOA(_poa); server_table_ser_lock.releaseExclusiveLock(); server_table.putPOA(name, _poa); this.logger.debug("ImR: new poa registered"); } else { // Existing POA is reactivated // Need to check whether the old server is alive. if it is then // throw an exception otherwise we can remap the currently // registered name. if ((_poa.active) || (! server.equals(_poa.server.name))) { byte[] first = _poa.name.getBytes (); byte[] id = new byte [ first.length + 1]; System.arraycopy (first, 0, id, 0, first.length); id[first.length] = org.jacorb.poa.POAConstants.OBJECT_KEY_SEP_BYTE; // If host and port are the same then it must be a replacement as // we could not have got to here if the original was running - we // would have got a socket exception. if (_poa.host.equals (host) && _poa.port == port) { remap = true; } else { // Otherwise try a ping // The ImplRepo may not get chance to update its // internal state due to server non-graceful // shutdown. If the address is actively using by // another server, the new endpoint(host, port) // will replace the old endpoint. boolean enp_reused = server_table.poa_enp_reused (_poa.name, _poa.host, _poa.port); // We can not determine if the address is really // using based on the server table since it may // not be updated. We still need ping the server. remap = ! (checkServerActive (_poa.host, _poa.port, id, enp_reused )); } if (remap == false) { throw new DuplicatePOAName ( "POA " + name + " has already been registered " + "for server " + _poa.server.name ); } this.logger.debug("ImR: Remapping server/port"); } _poa.reactivate(host, port); this.logger.debug("ImR: register_poa, reactivated"); } synchronized (wt) { wt.notify (); } } /** * Register a new host with a server startup daemon. * @param host a HostInfo object containing the hosts name and a reference to its * ServerStartupDaemon object. * * @exception org.jacorb.imr.RegistrationPackage.IllegalHostName <code>name</code> is not valid. * @exception org.jacorb.imr.RegistrationPackage.InvalidSSDRef It was impossible to connect * to the daemon. */ @Override public void register_host(HostInfo host) throws IllegalHostName, InvalidSSDRef { if (host.name == null || host.name.length() == 0) throw new IllegalHostName(host.name); try { host.ssd_ref.get_system_load(); } catch (Exception e) { this.logger.error("Exception while getting system load", e); throw new InvalidSSDRef(); } updatePending = true; server_table.putHost(host.name, new ImRHostInfo(host)); synchronized (wt) { wt.notify (); } } /** * Get host and port (wrapped inside an ImRInfo object) of this repository. * @return the ImRInfo object of this repository. */ @Override public ImRInfo get_imr_info() { return new ImRInfo(listener.getAddress(), listener.getPort()); } // implementation of org.jacorb.imr.AdminOperations interface /** * List all hosts currently registered with this repository. * It is not guaranteed that the references inside the HostInfo * objects are still valid. * * @return an array containing all known hosts. */ @Override public HostInfo[] list_hosts() { return server_table.getHosts(); } /** * List all registered server. The ServerInfo objects contain also * a list of the associated POAs. * * @return an array containing all registered servers. */ @Override public ServerInfo[] list_servers() { ServerInfo [] servers; if (check_object_liveness) { this.logger.debug("ImR: Checking servers"); servers = server_table.getServers(); for (int k=0; k<servers.length; k++) { if (servers[k].active && servers[k].poas.length > 0) { byte[] first = servers[k].poas[0].name.getBytes (); byte[] id = new byte [ first.length + 1]; System.arraycopy (first, 0, id, 0, first.length); id[first.length] = org.jacorb.poa.POAConstants.OBJECT_KEY_SEP_BYTE; if ( ! checkServerActive (servers[k].poas[0].host, servers[k].poas[0].port, id, false)) { try { if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: Setting server " + servers[k].name + " down"); } // Server is not active so set it down server_table.getServer(servers[k].name).setDown(); // Save retrieving the list again. servers[k].active = false; } catch (UnknownServerName e) { if (this.logger.isErrorEnabled()) { this.logger.error("ImR: Internal error - unknown server " + servers[k].name, e); } } } } } } else { servers = server_table.getServers(); } return servers; } /** * Get the ServerInfo object of a specific server. * * @param server the servers name. * @return the ServerInfo object of the server with name <code>server</code> * @exception UnknownServerName the server <code>server</code> has not been registered. */ @Override public ServerInfo get_server_info(String server) throws UnknownServerName { return server_table.getServer(server).toServerInfo(); } /** * Register a logical server. The logical server corresponds to a process * which has a number of POAs. * * @param name the servers name. * @param command the startup command for this server if it should be restarted * on demand. Has to be empty (NOT null) if the server should not be restarted. * @param host the host on which the server should be restarted. Should not * be null, but is ignored if no startup command is specified. * * @exception org.jacorb.imr.AdminPackage.IllegalServerName the servers name is not valid. * @exception org.jacorb.imr.AdminPackage.DuplicateServerName a server with <code>name</code> * has already been registered. */ @Override public void register_server(String name, String command, String host) throws IllegalServerName, DuplicateServerName { updatePending = true; ImRServerInfo _server = new ImRServerInfo(name, host, command); server_table.putServer(name, _server); if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: server " + name + " on " + host + " registered"); } synchronized (wt) { wt.notify (); } } /** * Remove a logical server from the server table. If a server is removed, all of its POAs * are removed as well. * * @param name the servers name. * @exception org.jacorb.imr.UnknownServerName a server with <code>name</code> has not been registered. */ @Override public void unregister_server(String name) throws UnknownServerName { updatePending = true; ImRServerInfo _server = server_table.getServer(name); String[] _poas = _server.getPOANames(); // remove POAs for (int _i = 0; _i < _poas.length; _i++) server_table.removePOA(_poas[_i]); server_table.removeServer(name); if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: server " + name + " unregistered"); } synchronized (wt) { wt.notify (); } } /** * Updates the server with a new command and host. For migrating purposes. * * @param name the servers name. * @param command the new startup command for this server. * @param host the new host. * @exception UnknownServerName a server with <code>name</code> * has not been registered. */ @Override public void edit_server(String name, String command, String host) throws UnknownServerName { updatePending = true; ImRServerInfo _server = server_table.getServer(name); _server.command = command; _server.host = host; if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: server " + name + " edited"); } synchronized (wt) { wt.notify (); } } /** * Hold a server. This causes all requests for this server to be delayed * until it is released. Holding a server is useful for migrating or * maintaining it. There is not timeout set, so requests might be delayed * indefinetly (or, at least, until the communication layer protests;-). * * @param name the servers name. * @exception org.jacorb.imr.UnknownServerName a server with <code>name</code> has not been registered. */ @Override public void hold_server(String name) throws UnknownServerName { ImRServerInfo _server = server_table.getServer(name); _server.holding = true; } /** * Release a server from state "holding". * * @param name the servers name. * @exception org.jacorb.imr.UnknownServerName a server with <code>name</code> has not been registered. */ @Override public void release_server(String name) throws UnknownServerName { ImRServerInfo _server = server_table.getServer(name); _server.release(); } /** * Start a server. * * @param name the servers name. * @exception org.jacorb.imr.UnknownServerName a server with <code>name</code> * has not been registered. */ @Override public void start_server(String name) throws UnknownServerName, ServerStartupFailed { restartServer(server_table.getServer(name)); } /** * Save the server table to a backup file. * @exception org.jacorb.imr.AdminPackage.FileOpFailed something went wrong. */ @Override public void save_server_table() throws FileOpFailed { if (table_file_backup != null) { save_server_table(table_file_backup); } } /** * Shut the repository down orderly, i.e. with saving of the server table. * The actual shutdown is done in the SocketListener thread because, if * done from here, the orb wont shut don correctly because this connection * is still active. (See end of SocketListener.run()) * * @param wait wait_for_completion (from ORB.shutdown()). If false, then the ORB * is forced down, ignoring any open connection. */ @Override public void shutdown(boolean wait) { synchronized (wt) { wt.shutdown (); wt.notify (); } if (listener != null) { listener.stopListening (wait); try { synchronized (listener) { // Wait at most 5 seconds for the listener to shutdown. listenerThread.join(5000); } } catch (InterruptedException e) { this.logger.warn("shutdown wait was interrupted", e); } } try { save_server_table (); } catch (FileOpFailed f) { this.logger.error("ImR: Failed to save backup table.", f); } this.logger.debug("ImR: Finished shutting down"); } /** * Remove a host from the servertable. Hosts are removed * automatically on server startup, if they can't be accessed. * * @param name the hosts name. * @exception UnknownHostName no host with that name known. */ @Override public void unregister_host(String name) throws UnknownHostName{ if (server_table.removeHost(name) == null) throw new UnknownHostName(name); } /** * Convenience method which does the actual serialization. * * @param save_to the file where to write to. */ private void save_server_table(File save_to) throws FileOpFailed { try{ ObjectOutputStream _out = new ObjectOutputStream(new FileOutputStream(save_to)); server_table.table_lock.gainExclusiveLock(); _out.writeObject(server_table); server_table.table_lock.releaseExclusiveLock(); _out.flush(); _out.close(); } catch (Exception e) { this.logger.error("Exception while saving server table", e); throw new FileOpFailed(); } updatePending = false; } /** * Prints the usage screen and exits. */ public static void usage () { System.out.println("Usage: The following properties are useful in conjunction with the \nImplementationRepository:"); System.out.println("\t \"jacorb.imr.endpoint_host\" Address to listen on for requests"); System.out.println("\t \"jacorb.imr.endpoint_port_number\" Port to listen on for requests"); System.out.println("\t \"jacorb.imr.table_file\" The file to store the server table into"); System.out.println("\t \"jacorb.imr.backup_file\" The file to store the server table backup into"); System.out.println("\t \"jacorb.imr.ior_file\" The file to store the ImRs IOR into"); System.out.println("\t \"jacorb.imr.allow_auto_register\" if set to \"on\", servers that don't \n\talready have an entry on their first call to the imr, will get \n\tautomatically registered. Otherwise, an UnknownServer exception \n\tis thrown."); System.exit(0); } /** * The main method. "Parses" the arguments and sets the corresponding * attributes up, creates a new ImplementationRepositoryImpl instance and * runs the ORB. */ public static void main(String[] args) throws Exception { // translate any properties set on the commandline but after the // class name to a properties java.util.Properties argProps = ObjectUtil.argsToProps( args ); argProps.setProperty("jacorb.implname", "the_ImR"); argProps.setProperty("jacorb.use_imr", "off"); argProps.setProperty("jacorb.use_tao_imr", "off"); boolean printIOR = false; for (int i = 0; i < args.length; i++) { if ("-printIOR".equals(args[i])) { printIOR = true; } } //Write IOR to file org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init( args, argProps ); ImplementationRepositoryImpl _imr = new ImplementationRepositoryImpl(orb); _imr.configure(((org.jacorb.orb.ORB) orb).getConfiguration()); POA root_poa = POAHelper.narrow(orb.resolve_initial_references("RootPOA")); root_poa.the_POAManager().activate(); org.omg.CORBA.Policy[] policies = new org.omg.CORBA.Policy[2]; policies[0] = root_poa.create_lifespan_policy(LifespanPolicyValue.PERSISTENT); policies[1] = root_poa.create_id_assignment_policy(IdAssignmentPolicyValue.USER_ID); POA imr_poa = root_poa.create_POA( "ImRPOA", root_poa.the_POAManager(), policies ); for (int i=0; i<policies.length; i++) { policies[i].destroy(); } byte[] id = "ImR".getBytes(); imr_poa.activate_object_with_id( id, _imr ); PrintWriter _out = new PrintWriter (new FileOutputStream(new File(_imr.getIORFile()))); final org.omg.CORBA.Object imrReference = imr_poa.servant_to_reference(_imr); _out.println(orb.object_to_string(imrReference)); _out.flush(); _out.close(); if (printIOR) { System.out.println ("SERVER IOR: " + orb.object_to_string(imrReference)); System.out.flush(); } _imr.shutdownThread.setDaemon (true); _imr.shutdownThread.setName ("Shutdown Thread"); Runtime.getRuntime().addShutdownHook(_imr.shutdownThread); orb.run(); } private void restartServer(ImRServerInfo server) throws ServerStartupFailed { // server might be holding server.awaitRelease(); if(! server.active ) { if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: server " + server.name + " is down"); } if (server.command.length() == 0) { //server can't be restarted, send exception throw new ServerStartupFailed("Server " + server.name + " can't be restarted because" + " of missing startup command"); } // we have to synchronize here to avoid a server to be // restarted multiple times by requests that are // received in the gap between the first try to // restart and the reactivation of the POAs. // restarting is set back to false when the first POA // is reactivated and the server goes back to active // (see ImRPOAInfo.reactivate()). if (server.shouldBeRestarted()) { try { // If there is no SSD for the host, we get an // NullPointerException. In a further // version, we might choose another random // SSD. ImRHostInfo _host = server_table.getHost(server.host); if( _host == null ) { throw new ServerStartupFailed( "Unknown host: >>" + server.host + "<<" ); } if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: will restart " + server.name); } _host.startServer(server.command, orb); } catch (ServerStartupFailed ssf) { server.setNotRestarting(); throw ssf; } catch (Exception e) { server.setNotRestarting(); this.logger.error("Exception while restarting server", e); // sth wrong with daemon, remove from table server_table.removeHost(server.host); throw new ServerStartupFailed("Failed to connect to host!"); } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: somebody else is restarting " + server.name); } } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("ImR: server " + server.name + " is active"); } } } /** * Inner class SocketListener, responsible for accepting * connection requests. *Very* close to inner class Listener in * orb/BasicAdapter.java. * <br> When a connection is accepted a * new RequestReceptor thread is started. */ private class SocketListener implements Runnable { private ServerSocket server_socket; private int port = 0; private String address; private int timeout = 0; private boolean run = true; private boolean wait = false; private MessageReceptorPool receptor_pool = null; private RequestListener request_listener = null; private ReplyListener reply_listener = null; private TransportManager transport_manager = null; /** * The constructor. It sets up the ServerSocket and starts the thread. */ public SocketListener() { request_listener = new ImRRequestListener(); reply_listener = new NoBiDirServerReplyListener(); } public void configure(Configuration myConfiguration) throws ConfigurationException { // Moved from the constructor to facilitate logging. receptor_pool = new MessageReceptorPool("server", "ImplementationRepository", myConfiguration); try { int endpoint_port = configuration.getAttributeAsInteger( "jacorb.imr.endpoint_port_number",0); String endpoint_host = configuration.getAttribute("jacorb.imr.endpoint_host", ""); if (endpoint_host.length() > 0) { server_socket = new ServerSocket( endpoint_port, 50, //default backlog, see jdk doc InetAddress.getByName(endpoint_host)); } else { //no explicit address given, listen anywhere server_socket = new ServerSocket(endpoint_port); } if( endpoint_host.length() > 0 ) { address = endpoint_host; } else { address = IIOPAddress.getLocalHostAddress(logger); } if( address.indexOf("/") >= 0 ) { address = address.substring(address.indexOf("/") + 1); } port = server_socket.getLocalPort(); if (logger.isDebugEnabled()) { logger.debug("ImR Listener at " + port + ", " + address); } } catch (Exception e) { throw new ConfigurationException("Listener: Couldn't init", e); } this.transport_manager = new TransportManager(); this.transport_manager.configure(configuration); } /** * Get the port this SocketListener is listening on. * * @return the port */ public int getPort() { return port; } /** * The internet address of the Socket this thread is listening on. * * @return the address of the socket. */ public String getAddress() { return address; } /** * Set the connection timeout. * * @param timeout the timeout. */ public void setTimeout( int timeout ) { this.timeout = timeout; } /** * The threads main event loop. Listenes on the socket * and starts new RequestReceptor threads on accepting. * <br> On termination does the actual shutdown of the * repository. */ @Override public void run() { while( run ) { try { Socket socket = server_socket.accept(); socket.setSoTimeout( timeout ); org.jacorb.orb.iiop.ServerIIOPConnection transport = new ServerIIOPConnection (socket, false, new NullTCPConnectionListener() ); // TODO // no SSL transport.configure(configuration); GIOPConnection connection = new ServerGIOPConnection( transport.get_server_profile(), transport, request_listener, reply_listener, null, null); connection.configure(configuration); receptor_pool.connectionCreated( connection ); } catch (Exception _e) { // when finishing, we do a close() on // server_socket from "outside" and that causes an // exception here. But since we wanted it this // way, we don't display the Exception to avoid // confusing users. if (run) { logger.debug("Internal Exception, can be ignored", _e); } } } // doing the actual shutdown of the implementation // repository here orb.shutdown(wait); } /** * Causes the event loop to terminate by closing the ServerSocket. * * @param wait for ORB.shutdown(). */ public void stopListening(boolean wait) { run = false; this.wait = wait; try { server_socket.close(); } catch (Exception _e) { logger.error("Exception while closing server socket", _e); } } } private boolean checkServerActive(String host, int port, byte []object_key, boolean enp_reused) { ClientConnectionManager cm = null; IIOPAddress address = null; ClientConnection connection = null; LocateRequestOutputStream lros = null; LocateReplyReceiver receiver = null; LocateReplyInputStream lris = null; boolean result = false; cm = ((org.jacorb.orb.ORB)orb).getClientConnectionManager (); try { address = new IIOPAddress (host, port); address.configure(configuration); IIOPProfile iiopProfile = new IIOPProfile(address, object_key, ((org.jacorb.orb.ORB)orb).getGIOPMinorVersion()); iiopProfile.configure(configuration); connection = cm.getConnection(iiopProfile); } catch(ConfigurationException e) { logger.error("Failed to configure", e); } if (this.logger.isDebugEnabled()) { this.logger.debug("Pinging " + host + " / " + port); } try { lros = new LocateRequestOutputStream ( orb, object_key, connection.getId(), 2); receiver = new LocateReplyReceiver(); receiver.configure (configuration); connection.sendRequest( lros, receiver, lros.getRequestId (), true ); // response expected lris = receiver.getReply(); switch (lris.rep_hdr.locate_status.value ()) { case LocateStatusType_1_2._UNKNOWN_OBJECT: case LocateStatusType_1_2._LOC_SYSTEM_EXCEPTION: { SystemException se = SystemExceptionHelper.read( lris ); if (enp_reused && (se instanceof org.omg.CORBA.OBJECT_NOT_EXIST)) { // Endpoint is reused by another server and the new server // returned OBJECT_NOT_EXIST, so give up using this endpoint. // The return value can be interpreted as not available endpoint. result = false; } else { result = true; } break; } case LocateStatusType_1_2._OBJECT_HERE: case LocateStatusType_1_2._OBJECT_FORWARD: case LocateStatusType_1_2._OBJECT_FORWARD_PERM: case LocateStatusType_1_2._LOC_NEEDS_ADDRESSING_MODE: default: { result = true; break; } } } catch (Throwable ex) { this.logger.debug("Exception while checking server active", ex); result = false; } finally { cm.releaseConnection (connection); } return result; } /** * Inner class ImRRequestListener. Receives messages. */ private class ImRRequestListener implements RequestListener { public ImRRequestListener() { } /** * receive and dispatch requests * * @param request a <code>byte[]</code> value * @param connection a <code>GIOPConnection</code> value */ @Override public void requestReceived( byte[] request, GIOPConnection connection ) { logger.debug("requestReceived"); connection.incPendingMessages(); final RequestInputStream in = new RequestInputStream( orb, connection, request ); try { replyNewLocation( ((org.jacorb.orb.ORB)orb).mapObjectKey( ParsedIOR.extractObjectKey(in.req_hdr.target, (org.jacorb.orb.ORB)orb)), in.req_hdr.request_id, in.getGIOPMinor(), connection, false); } finally { in.close(); } } @Override public void locateRequestReceived( byte[] request, GIOPConnection connection ) { connection.incPendingMessages(); final LocateRequestInputStream in = new LocateRequestInputStream( orb, request ); try { replyNewLocation(ParsedIOR.extractObjectKey(in.req_hdr.target, (org.jacorb.orb.ORB) orb), in.req_hdr.request_id, in.getGIOPMinor(), connection, true); } finally { in.close(); } } @Override public void cancelRequestReceived( byte[] request, GIOPConnection connection ) { //ignore } public void fragmentReceived( byte[] fragment, GIOPConnection connection ) { //ignore } public void connectionClosed() { } /** * The actual core method of the implementation repository. * Causes servers to start, looks up new POA locations in * the server table. */ private void replyNewLocation( byte[] object_key, int request_id, int giop_minor, GIOPConnection connection, boolean isLocateRequest) { String _poa_name = POAUtil.extractImplName( object_key ) + '/' + POAUtil.extractPOAName( object_key ); // look up POA in table ImRPOAInfo _poa = server_table.getPOA( _poa_name ); if (_poa == null) { sendSysException( new org.omg.CORBA.TRANSIENT( "POA " + _poa_name + " unknown" ), connection, request_id, giop_minor ); return; } // get server of POA ImRServerInfo _server = _poa.server; if (logger.isDebugEnabled()) { logger.debug("ImR: Looking up: " + _server.name); } // There is only point pinging the remote object if server // is active and either the QoS to ping returned objects // is true or the ServerStartUpDaemon is active and there // is a command to run - if not, even if the server isn't // actually active, we can't restart it so just allow this // to fall through and throw the TRANSIENT below. boolean ssd_valid = ( (_server.command.length() != 0) && (server_table.getHost(_server.host) != null) ); if (_server.active && (check_object_liveness || ssd_valid)) { // At this point the server *might* be running - we // just want to verify it. if (! checkServerActive (_poa.host, _poa.port, object_key, false)) { // Server is not active so set it down _server.setDown (); } } try { restartServer( _server ); } catch( ServerStartupFailed ssf ) { if (logger.isInfoEnabled()) { logger.info("Object (" + _server.name + ") on " + _poa.host + '/' + _poa.port + " not reachable"); } sendSysException( new org.omg.CORBA.TRANSIENT(ssf.reason), connection, request_id, giop_minor ); return; } // POA might not be active boolean _old_poa_state = _poa.active; // wait for POA to be reregistered. if( ! _poa.awaitActivation() ) { // timeout reached sendSysException( new org.omg.CORBA.TRANSIENT("Timeout exceeded"), connection, request_id, giop_minor ); return; } ReplyOutputStream out = new ReplyOutputStream( orb, request_id, org.omg.GIOP.ReplyStatusType_1_2.LOCATION_FORWARD, giop_minor, isLocateRequest, logger); // The typecode is for org.omg.CORBA.Object, but avoiding // creation of new ObjectHolder Instance. IIOPAddress addr = new IIOPAddress (_poa.host,(short)_poa.port); org.omg.IOP.IOR _ior = null; try { addr.configure(configuration); IIOPProfile p = new IIOPProfile (addr,object_key,giop_minor); p.configure(configuration); _ior = ParsedIOR.createObjectIOR ((ORB)orb, p); } catch(ConfigurationException e) { logger.error("Error while configuring address/profile", e); } if( !_old_poa_state ) { // if POA has been reactivated, we have to wait for // the requested object to become ready again. This is // for avoiding clients to get confused by // OBJECT_NOT_EXIST exceptions which they might get // when trying to contact the server too early. org.omg.CORBA.Object _object = orb.string_to_object( (new ParsedIOR( (org.jacorb.orb.ORB) orb, _ior)).getIORString()); // Sort of busy waiting here, no other way possible for( int _i = 0; _i < object_activation_retries; _i++ ) { try { Thread.sleep( object_activation_sleep ); // This will usually throw an OBJECT_NOT_EXIST if( ! _object._non_existent() ) // "CORBA ping" { break; } } catch(Exception _e) { logger.info("Exception while waiting for object", _e); } } } try { // write new location to stream out.write_IOR(_ior); if (logger.isDebugEnabled()) { logger.debug("ImR: Sending location forward for " + _server.name); } connection.sendReply( out ); } catch( IOException _e ) { logger.error("Exception while writing new location", _e); sendSysException( new org.omg.CORBA.UNKNOWN(_e.toString()), connection, request_id, giop_minor ); } } /** * Convenience method for sending a CORBA System Exception back to * the client. * * @param sys_ex the exception to send back. */ private void sendSysException( org.omg.CORBA.SystemException sys_ex, GIOPConnection connection, int request_id, int giop_minor ) { ReplyOutputStream out = new ReplyOutputStream( orb, request_id, org.omg.GIOP.ReplyStatusType_1_2.SYSTEM_EXCEPTION, giop_minor, false, logger); SystemExceptionHelper.write( out, sys_ex ); try { connection.sendReply( out ); } catch( IOException _e ) { logger.error("Exception while sending SystemException to client", _e); } } } /** * <code>WriteThread</code> runs as a background thread which will write the * server table out whenever any modifications are made. */ private class WriteThread extends Thread { boolean done; public WriteThread () { } /** * <code>run</code> continiously loops until the shutdown is called. */ @Override public void run () { while (true) { try { server_table_ser_lock.gainExclusiveLock(); save_server_table (table_file); } catch (FileOpFailed ex) { logger.error("Exception while saving server table", ex); } finally { server_table_ser_lock.releaseExclusiveLock(); } if (done) { break; } // If by the time we have written the server table another request has arrived // which requires an update don't bother entering the wait state. if ( ! updatePending) { try { synchronized (this) { this.wait (); } } catch (InterruptedException ex) {} logger.debug("ImR: IMR write thread waking up to save server table... "); } } } /** * <code>shutdown</code> toggles the thread to shut itself down. */ public void shutdown () { done = true; } } /** * <code>Shutdown</code> is a thread that is run the Java 1.3 (and greater) * virtual machine upon receiving a Ctrl-C or kill -INT. */ private class Shutdown extends Thread { @Override public synchronized void run () { logger.debug("ImR: Shutting down"); shutdown(true); } } } // ImplementationRepositoryImpl