/** * Finds and returns the caches {@link Federation} with the given name. If we don't have it cached * we connect to it and return. We cache these so that we only have to connect to a federation and * find the co-ordinator once. * * @throws Exception If there is a problem connecting to the channel */ private Federation findFederation(String federationName) throws Exception { // NOTE: We maintain a map of all connections we've ever queried for. We have to // join a JGroups channel before we can query anything, so we prefer to only // to this once. When joining a channel there may/may not be a federation // present inside. To find out, we need to source the Oracle... if (federations.containsKey(federationName)) return federations.get(federationName); // we don't know about it Federation federation = new Federation(federationName); federation.connect(); federations.put(federationName, federation); return federation; }
/** * Federation destroying is a bit of a catch-22. We need to connect to the channel that the * federation is operating on in order to determine whether a federation is even active at all * (which it may well not be). So in this case we connect to the channel and then if there is a * federation running in there, we ask that it be destroyed by removing the contained FOM. * * <p>If there are members of the channel who are still currently federates, then this call will * fail and thrown an exception. * * <p>**NOTE** If we ever ask to destroy a federation, we will automatically disconnect from the * channel when done. Fair chance we don't care about it any more once we try a destroy. */ public void destroyFederation(DestroyFederation destroyMessage) throws Exception { // connect to the channel if we are not already Federation federation = findFederation(destroyMessage.getFederationName()); try { federation.sendDestroyFederation(); } finally { // We put this here because the above call may throw an exception if there // are federates still connected (and thus it can't destroy the federation). // That exception would in turn cascade out and prevent us from disconnecting // unless we did something about it! federation.disconnect(); federations.remove(federation); } }
/** * <i>This method is called by the Portico infrastructure during shutdown.</i> * * <p>When the kernel is ready to shutdown, it will call this method, signalling to the connection * that it should disconnect and do any shutdown and cleanup necessary. */ public void disconnect() throws JRTIinternalError { if (this.running == false) { logger.info("jgroups connection is already disconnected"); return; } logger.info("jgroups connection is disconnecting..."); // for each federation we're connected to, disconnect from it for (Federation federation : federations.values()) federation.disconnect(); federations.clear(); logger.info("jgroups connection has disconnected"); }
/** * Resign ourselves from the federation we are currently connected to. This will not disconnect us * from the channel, but will rather just mark us as no longer being a federate (only a channel * member). * * <p>An exception is thrown if any of the checks we run (such as whether we are infact even * connected to a federation at all!) fail. */ public void resignFederation(ResignFederation resignMessage) throws Exception { // make sure we're joined to the federation if (joinedFederation == null || joinedFederation.getManifest().isLocalFederateJoined() == false) { throw new JFederateNotExecutionMember( "Federate [" + resignMessage.getFederateName() + "] not joined to [" + resignMessage.getFederationName() + "]"); } // send out the resign notification joinedFederation.sendResignFederation(resignMessage); // all happy, as we're no longer joined, set out joined channel to null joinedFederation = null; }
/** * Join the federation of the given name. This will find the channel with the same name as the * federation and connect to it. It will then attempt to mark this execution as a federate within * that channel (as opposed to just a regular member). * * <p>== Federate Names == A federation can optionally require that each federate have a unique * name (RID option). If this is not the case, Portico will always ensure that federates do have * unique names by modifying newly joining duplicates. If unique names are enforced and we try to * join with an existing name, an exception is thrown. * * <p>== FOM Modules == If there are additional FOM modules, we attempt a dry-run merge first to * identify any issues. Should that be successful, we join and then apply the changes to our local * copy of the FOM. The Role Call semantics that executes outside of the connection after we join * handles the job of dispersing the new module information to everyone else (who then also apply * them locally). */ public ConnectedRoster joinFederation(JoinFederation joinMessage) throws Exception { // connect to the federation channel if we are not already Federation federation = findFederation(joinMessage.getFederationName()); // validate that our FOM modules can be merged successfully with the existing FOM first logger.debug( "Validate that [" + joinMessage.getJoinModules().size() + "] modules can merge successfully with the existing FOM"); ModelMerger.mergeDryRun(federation.getManifest().getFom(), joinMessage.getJoinModules()); logger.debug("Modules can be merged successfully, continue with join"); // tell the channel that we're joining the federation String joinedName = federation.sendJoinFederation(joinMessage.getFederateName(), this.lrc); // the joined name could be different from what we asked for, so update the request // to make sure it is correct joinMessage.setFederateName(joinedName); // now that we've joined a federation, store it here so we can route messages to it this.joinedFederation = federation; // We have to merge the FOMs together here before we return to the Join handler and // a RoleCall is sent out. We do this because although we receive our own RoleCall // notice (with the additional modules) we won't process it as we can't tell if it's // one we sent out because we joined (and thus need to merge) or because someone else // joined. Additional modules will only be present if it is a new join, so // we could figure it out that way, but that will cause redundant merges for the JVM // binding (as all connections share the same object model reference). To cater to the // specifics of this connection it's better to put the logic in the connection rather than // in the generic-to-all-connections RoleCallHandler. Long way of saying we need to merge // in the additional join modules that were provided here. F*** IT! WE'LL DO IT LIVE! if (joinMessage.getJoinModules().size() > 0) { logger.debug( "Merging " + joinMessage.getJoinModules().size() + " additional FOM modules that we receive with join request"); ObjectModel fom = federation.getManifest().getFom(); fom.unlock(); federation.getManifest().setFom(ModelMerger.merge(fom, joinMessage.getJoinModules())); fom.lock(); } // create and return the roster return new Roster( federation.getManifest().getLocalFederateHandle(), federation.getManifest().getFederateHandles(), federation.getManifest().getFom()); }
/** * Find the channel for the federation we are trying to create (name is used) and then attempt to * install a federation in it. If there is no existing federation in it, one will be created. If * there is an exception will be thrown. */ public void createFederation(CreateFederation createMessage) throws Exception { Federation federation = findFederation(createMessage.getFederationName()); federation.sendCreateFederation(createMessage.getModel()); }
/** * This method should be used when a message has to be sent and then time for responses to be * broadcast back is needed before moving on. In this case, the connection will broadcast the * message and then sleep for an amount of time appropriate based on the underlying comms protocol * in use. For example, the JVM connection won't sleep for long, but a connection sending * information over a network should wait longer. * * @param message The message to broadcast * @throws Exception If there was an error when sending the message */ public void broadcastAndSleep(PorticoMessage message) throws Exception { validateConnected(); joinedFederation.send(message); PorticoConstants.sleep(Configuration.RESPONSE_TIMEOUT); }
/** * Broadcast the given message out to all participants in a federation asynchronously. As soon as * the message has been received for processing or sent, this method is free to return. No * response will be waited for. * * @param message The message to broadcast * @throws Exception If there was an error when sending the message */ public void broadcast(PorticoMessage message) throws JFederateNotExecutionMember, JRTIinternalError { validateConnected(); joinedFederation.send(message); }