gt: 			node = nodeSerializer.deserialize(nn);
context: /*****************************************************************
   JADE - Java Agent DEvelopment Framework is a framework to develop
   multi-agent systems in compliance with the FIPA specifications.
   Copyright (C) 2000 CSELT S.p.A.
   
   GNU Lesser General Public License
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation,
   version 2.1 of the License.
   
   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
   Lesser General Public License for more details.
   
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the
   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA  02111-1307, USA.
   *****************************************************************/
  
  package jade.core.faultRecovery;
  
  //#J2ME_EXCLUDE_FILE
  //#APIDOC_EXCLUDE_FILE
  
  import jade.core.GenericCommand;
  import jade.core.HorizontalCommand;
  import jade.core.ServiceHelper;
  import jade.core.VerticalCommand;
  import jade.core.Service;
  import jade.core.BaseService;
  import jade.core.ServiceException;
  import jade.core.Filter;
  import jade.core.Agent;
  import jade.core.AID;
  import jade.core.Node;
  import jade.core.NodeDescriptor;
  import jade.core.AgentContainer;
  import jade.core.MainContainer;
  import jade.core.Profile;
  import jade.core.ProfileException;
  import jade.core.IMTPException;
  import jade.core.nodeMonitoring.NodeMonitoringService;
  import jade.core.nodeMonitoring.UDPNodeMonitoringService;
  import jade.core.replication.MainReplicationService;
  import jade.core.replication.MainReplicationSlice;
  
  import jade.util.Logger;
  
  import java.io.*;
  import java.util.Map;
  import java.util.Iterator;
  
  
  /**
   The FaultRecovery service allows recovering a platform after a fault
   and a successive restart of the main container.
   
   On a Main container this service keeps track of platform nodes in a 
   persistent storage. When the platform shuts down the persistent storage
   is cleared. At bootstrap time the service gets all nodes from
   the persistent storage (there are nodes only if the main container 
   is restarting after a crash) and notifies them (if still alive)
   about the recovery.
   
   On peripheral containers, the node, when notified about a main recovery,
   re-adds itself to the recovered main (actually this is done by the
   ServiceManager) and issues a Service.REATTACHED incoming V-Command.
   The FaultRecovery service filter intercepts this command and re-adds
   all agents living in the container.
   
   @author Giovanni Caire - TILAB
   */
  public class FaultRecoveryService extends BaseService {
  	public static final String NAME = FaultRecoveryHelper.SERVICE_NAME;
  	
  	public static final String CLEAN_STORAGE = "jade_core_faultRecovery_FaultRecoveryService_cleanstorage";
  	public static final String PERSISTENT_STORAGE_CLASS = "jade_core_faultRecovery_FaultRecoveryService_persistentstorage";
  	public static final String ORPHAN_NODE_POLICY = "jade_core_faultRecovery_FaultRecoveryService_orphannodepolicy";
  	
  	public static final String PERSISTENT_STORAGE_CLASS_DEFAULT = "jade.core.faultRecovery.FSPersistentStorage";
  	
  	public static final String ORPHAN_NODE_POLICY_RECOVER = "RECOVER";
  	public static final String ORPHAN_NODE_POLICY_KILL = "KILL";
  	public static final String ORPHAN_NODE_POLICY_IGNORE = "IGNORE";
  	
  	// Horizontal command to force the termination of an orphan node when the KILL Orphan Node Policy is set
  	public static final String H_KILLNODE = "1";
  	
  	private AgentContainer myContainer;
  	private MainContainer myMain;
  	
  	private Filter inpFilter;
  	private Filter outFilter;
  	
  	private PersistentStorage myPS;
  	private NodeSerializer nodeSerializer;
  	
  	private boolean bootComplete = false;
  	private String orphanNodePolicy;
  	
  	public void init(AgentContainer ac, Profile p) throws ProfileException {
  		super.init(ac, p);
  		myContainer = ac;
  		myMain = myContainer.getMain();
  		if (myMain != null) {
  			// Create the command filters
  			inpFilter = new MainCommandIncomingFilter();
  			outFilter = new MainCommandOutgoingFilter();
  			
  			// Initialize the serializers
  			nodeSerializer = new NodeSerializer();
  			
  			// Read the policy to handle orphan nodes
  			orphanNodePolicy = p.getParameter(ORPHAN_NODE_POLICY, ORPHAN_NODE_POLICY_RECOVER);
  			
  			// Initialize the PersistentStorage
  			String psClass = p.getParameter(PERSISTENT_STORAGE_CLASS, PERSISTENT_STORAGE_CLASS_DEFAULT);
  			try {
  				myLogger.log(Logger.CONFIG, "Loading PersistentStorage of class "+psClass);
  				myPS = (PersistentStorage) Class.forName(psClass).newInstance();
  				myPS.init(p);
  				boolean cleanStorage = p.getBooleanProperty(CLEAN_STORAGE, false);
  				if (cleanStorage) {
  					myLogger.log(Logger.CONFIG, "Clearing PersistentStorage ...");
  					myPS.clear(true);
  				}
  			}
  			catch (Exception e) {
  				String msg = "Error initializing PersistentStorage. ";
  				myLogger.log(Logger.SEVERE, msg, e);
  				if (myPS != null) {
  					myPS.close();
  				}
  				throw new ProfileException(msg, e);
  			}
  		}
  		else {
  			// Create the command incoming filter
  			inpFilter = new ContainerCommandIncomingFilter();
  		}
  	}
  	
  	public void boot(Profile p) throws ServiceException {
  		if (myMain != null) {
  			try {
  				String[] platformInfo = myPS.getPlatformInfo();
  				String oldPlatformName = (platformInfo != null ? platformInfo[0] : null);
  				String oldAddress = (platformInfo != null ? platformInfo[1] : null);
  				String currentPlatformName = myContainer.getPlatformID();
  				String currentAddress = myContainer.getServiceManager().getLocalAddress();
  				myPS.storePlatformInfo(currentPlatformName, currentAddress);
  				if (currentPlatformName.equals(oldPlatformName)) {
  					MainReplicationService replService = (MainReplicationService) myContainer.getServiceFinder().findService(MainReplicationService.NAME);
  					// Do not activate the fault recovery procedure if the Main Container is replicated: in that case all containers were already adopted
  					// by existing Main Container replicas. 
  					if (replService == null || replService.getAllSlices().length <= 1) {
  						// FAULT RECOVERY PROCEDURE
  						myLogger.log(Logger.INFO, "Initiating fault recovery procedure...");
  						// Recover all non-child nodes first
  						Map allNodes = myPS.getAllNodes(false);
  						Iterator it = allNodes.keySet().iterator();
  						while (it.hasNext()) {
  							String name = (String) it.next();
  							checkNode(name, (byte[]) allNodes.get(name), oldAddress, currentAddress);
  						}
  						// Then recover all child nodes
  						allNodes = myPS.getAllNodes(true);
  						it = allNodes.keySet().iterator();
  						while (it.hasNext()) {
  							String name = (String) it.next();
  							checkNode(name, (byte[]) allNodes.get(name), oldAddress, currentAddress);
  						}
  						
  						myLogger.log(Logger.INFO, "Fault recovery procedure completed.");
  					}
  				}
  				else {
  					// This is a different platform and we don't want to reattach agents with a different platform name.
  					// Also clear old stuff in the persistent storage
  					myPS.clear(false);
  				}
  			}
  			catch (Exception e) {
  				String msg = "Error recovering from previous fault. ";
  				myLogger.log(Logger.SEVERE, msg, e);
  				throw new ServiceException(msg, e);
  			}
  		}
  		bootComplete = true;
  	}
  	
  	public void shutdown() {
  		if (myPS != null) {
  			try {
  				myPS.clear(true);
  				myPS.close();
  			}
  			catch (Exception e) {
  				myLogger.log(Logger.WARNING, "Unexpected error clearing PersistentStorage. ", e);
  			}
  		}
  	}
  	
  	public String getName() {
  		return NAME;
  	}
  	
  	public Filter getCommandFilter(boolean direction) {
  		if(direction == Filter.INCOMING) {
  			return inpFilter;
  		}
  		else {
  			return outFilter;
  		}
  	}
  	
  	public Service.Slice getLocalSlice() {
  		return new Service.Slice() {
  			public Service getService() {
  				return FaultRecoveryService.this;
  			}
  			
  			public Node getNode() throws ServiceException {
  				try {
  					return FaultRecoveryService.this.getLocalNode();
  				} catch (IMTPException imtpe) {
  					throw new ServiceException("Problem in contacting the IMTP Manager", imtpe);
  				}
  			}
  			
  			public VerticalCommand serve(HorizontalCommand cmd) {
  				try {
  					String cmdName = cmd.getName();
  					if (cmdName.equals(H_KILLNODE)) {
  						suicide();
  					}
  				} catch (Throwable t) {
  					cmd.setReturnValue(t);
  				}
  				return null;
  			}
  		};
  	}
  	
  	public ServiceHelper getHelper(Agent a) throws ServiceException {
  		// The agent is passed to the helper in the init() method
  		return new FaultRecoveryHelper() {
  			public void init(Agent a) {
  				// Just do nothing
  			}
  			
  			public void reattach() throws ServiceException {
  				try {
  					Node n = myContainer.getNodeDescriptor().getNode();
  					String pmAddress = myContainer.getServiceManager().getLocalAddress();
  					n.platformManagerDead(pmAddress, pmAddress);
  				}
  				catch (IMTPException imtpe) {
  					throw new ServiceException("Communication error: "+imtpe.getMessage(), imtpe);
  				}
  			}
  		};
  	}
  	
  	private void checkNode(String name, byte[] nn, String oldAddress, String currentAddress) {
  		if (myLogger.isLoggable(Logger.FINE)) {
  			myLogger.log(Logger.FINE, "Recovering node "+name+" ...");
  		}
  		Node node = null;
  		try {
  