All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jadex.platform.service.settings.SettingsService Maven / Gradle / Ivy

package jadex.platform.service.settings;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import jadex.bridge.IInternalAccess;
import jadex.bridge.component.IArgumentsResultsFeature;
import jadex.bridge.component.IExecutionFeature;
import jadex.bridge.service.RequiredServiceInfo;
import jadex.bridge.service.annotation.Service;
import jadex.bridge.service.annotation.ServiceComponent;
import jadex.bridge.service.annotation.ServiceShutdown;
import jadex.bridge.service.annotation.ServiceStart;
import jadex.bridge.service.search.SServiceProvider;
import jadex.bridge.service.types.context.IContextService;
import jadex.bridge.service.types.settings.ISettingsService;
import jadex.commons.IPropertiesProvider;
import jadex.commons.Properties;
import jadex.commons.future.CounterResultListener;
import jadex.commons.future.DelegationResultListener;
import jadex.commons.future.ExceptionDelegationResultListener;
import jadex.commons.future.Future;
import jadex.commons.future.IFuture;
import jadex.commons.future.IResultListener;
import jadex.xml.PropertiesXMLHelper;

/**
 *  Default settings service implementation.
 */
@Service
public class SettingsService implements ISettingsService
{
	// -------- constants --------

	/** The filename extension for settings. */
	public static final String	SETTINGS_EXTENSION	= ".settings.xml";

	//-------- attributes --------
	
	/** The service provider. */
	@ServiceComponent
	protected IInternalAccess	access;
	
	/** The properties filename. */
	protected String filename;
	
	/** The current properties. */
	protected Properties	props;
	
	/** The registered properties provider (id->provider). */
	protected Map	providers;
	
	/** Save settings on exit?. */
	protected boolean	saveonexit;
	
	/** The context service. */
	protected IContextService contextService;
	
	//-------- Service methods --------
	
	/**
	 *  Start the service.
	 *  @return A future that is done when the service has completed starting.  
	 */
	@ServiceStart
	public IFuture	startService()
	{
		this.providers	= new LinkedHashMap();
		Object	soe	= access.getComponentFeature(IArgumentsResultsFeature.class).getArguments().get("saveonexit");
		this.saveonexit	= soe instanceof Boolean && ((Boolean)soe).booleanValue();
		this.filename	= access.getComponentIdentifier().getPlatformPrefix() + SETTINGS_EXTENSION;
		
		final Future	ret	= new Future();
		contextService = SServiceProvider.getLocalService(access, IContextService.class, RequiredServiceInfo.SCOPE_PLATFORM);
		loadProperties().addResultListener(new DelegationResultListener(ret));
		
		return ret;
	}
	
	/**
	 *  Shutdown the service.
	 *  @return A future that is done when the service has completed its shutdown.  
	 */
	@ServiceShutdown
	public IFuture	shutdownService()
	{
		final Future	ret	= new Future();
		if(saveonexit)
		{
			saveProperties(true).addResultListener(new DelegationResultListener(ret));
		}
		else
		{
			ret.setResult(null);
		}
		return ret;
	}
	
	//-------- ISettingsService interface --------
	
	/**
	 *  Register a property provider.
	 *  Settings of registered property providers will be automatically saved
	 *  and restored, when properties are loaded.
	 *  @param id 	A unique id to identify the properties (e.g. component or service name).
	 *  @param provider 	The properties provider.
	 */
	public IFuture	registerPropertiesProvider(String id, IPropertiesProvider provider)
	{
		Future	ret	= new Future();
		if(providers.containsKey(id))
		{
			ret.setException(new IllegalArgumentException("Id already contained: "+id));
		}
		else
		{
//			System.out.println("Added provider: "+id+", "+provider);
			providers.put(id, provider);
			Properties	sub	= props.getSubproperty(id);
			if(sub!=null)
			{
				provider.setProperties(sub).addResultListener(access.getComponentFeature(IExecutionFeature.class).createResultListener(new DelegationResultListener(ret)));
			}
			else
			{
				ret.setResult(null);
			}
		}
		return ret;
	}
	
	/**
	 *  Deregister a property provider.
	 *  Settings of a deregistered property provider will be saved
	 *  before the property provider is removed.
	 *  @param id 	A unique id to identify the properties (e.g. component or service name).
	 */
	public IFuture	deregisterPropertiesProvider(final String id)
	{
		final Future	ret	= new Future();
		if(!providers.containsKey(id))
		{
			ret.setException(new IllegalArgumentException("Id not contained: "+id));
		}
		else
		{
			IPropertiesProvider	provider	= (IPropertiesProvider)providers.remove(id);
//			System.out.println("Removed provider: "+id+", "+provider);
			if(saveonexit)
			{
//				provider.getProperties().addResultListener(new DelegationResultListener(ret)
				provider.getProperties().addResultListener(access.getComponentFeature(IExecutionFeature.class).createResultListener(new DelegationResultListener(ret)
				{
					public void customResultAvailable(Object result)
					{
						props.removeSubproperties(id);
						props.addSubproperties(id, (Properties)result);
						ret.setResult(null);
					}
				}));
			}
			else
			{
				ret.setResult(null);
			}
		}
		return ret;
	}
	
	/**
	 *  Set the properties for a given id.
	 *  Overwrites existing settings (if any).
	 *  @param id 	A unique id to identify the properties (e.g. component or service name).
	 *  @param properties 	The properties to set.
	 *  @param save 	Save platform properties after setting.
	 *  @return A future indicating when properties have been set.
	 */
	public IFuture	setProperties(String id, Properties props)
	{
//		System.out.println("Set properties: "+id);
		final Future	ret	= new Future();
		this.props.removeSubproperties(id);
		this.props.addSubproperties(id, props);
		
		if(providers.containsKey(id))
		{
			((IPropertiesProvider)providers.get(id)).setProperties(props)
				.addResultListener(access.getComponentFeature(IExecutionFeature.class).createResultListener(new DelegationResultListener(ret)));
		}
		else
		{
			ret.setResult(null);
		}
		
		return ret;
	}
	
	/**
	 *  Get the properties for a given id.
	 *  @param id 	A unique id to identify the properties (e.g. component or service name).
	 *  @return A future containing the properties (if any).
	 */
	public IFuture	getProperties(String id)
	{
		return new Future(props.getSubproperty(id));
	}
	
	
	/**
	 *  Load the default platform properties.
	 *  @return A future indicating when properties have been loaded.
	 */
	public IFuture loadProperties()
	{
		final Future	ret	= new Future();
		
		readOrCreateProperties().addResultListener(new ExceptionDelegationResultListener(ret)
		{
			public void customResultAvailable(Properties mprops)
			{
				props = mprops;
				
				final CounterResultListener	crl	= new CounterResultListener(providers.size(),
					access.getComponentFeature(IExecutionFeature.class).createResultListener(new DelegationResultListener(ret)));
				
				for(Iterator it=providers.keySet().iterator(); it.hasNext(); )
				{
					final String	id	= it.next();
					IPropertiesProvider	provider = providers.get(id);
					
					Properties	sub	= props.getSubproperty(id);
					if(sub!=null)
					{
						provider.setProperties(sub).addResultListener(access.getComponentFeature(IExecutionFeature.class).createResultListener(crl));
					}
					else
					{
						crl.resultAvailable(null);
					}
				}
			}
		});
		
		return ret;
	}
	
	/**
	 * 
	 */
	protected IFuture readOrCreateProperties()
	{
		final Future ret = new Future();
		readPropertiesFromStore().addResultListener(new IResultListener()
		{
			public void resultAvailable(Properties result)
			{
				ret.setResult(result);
			}
			
			public void exceptionOccurred(Exception exception)
			{
				ret.setResult(new Properties());
			}
		});
		return ret;
	}
	
	/**
	 * Reads and returns the stored Properties, usually from a file.
	 * @return {@link Properties}
	 * @throws FileNotFoundException
	 * @throws Exception
	 * @throws IOException
	 */
	protected IFuture readPropertiesFromStore() //throws FileNotFoundException, IOException 
	{
		final Future ret = new Future();
		
		// Todo: Which class loader to use? library service unavailable, because it depends on settings service?
		getFile(filename).addResultListener(new ExceptionDelegationResultListener(ret)
		{
			public void customResultAvailable(File file)
			{
				if(!file.exists())
				{
					getFile("default"+SETTINGS_EXTENSION).addResultListener(new ExceptionDelegationResultListener(ret)
					{
						public void customResultAvailable(File file)
						{
							proceed(file);
						}
					});
				}
				else
				{
					proceed(file);
				}
			}
			
			protected void proceed(File file)
			{
				FileInputStream fis = null;
				try
				{
					fis = new FileInputStream(file);
					Properties props = (Properties)PropertiesXMLHelper.read(fis, getClass().getClassLoader());
					ret.setResult(props);
				}
				catch(Exception e)
				{
					ret.setException(e);
				}
				finally
				{
					if(fis!=null)
					{
						try
						{
							fis.close();
						}
						catch(Exception e)
						{
						}
					}
				}
			}
		});
		
		return ret;
	}

	/**
	 *  Save the platform properties to the default location.
	 *  @return A future indicating when properties have been saved.
	 */
	public IFuture	saveProperties()
	{
		return saveProperties(false);
	}
	
	/**
	 *  Save the platform properties to the default location.
	 *  @param shutdown	Flag indicating if called during shutdown.
	 *  @return A future indicating when properties have been saved.
	 */
	public IFuture	saveProperties(boolean shutdown)
	{
//		System.out.println("Save properties"+(shutdown?" (shutdown)":""));
		final Future	ret	= new Future();
		
		IResultListener	rl	= new DelegationResultListener(ret)
		{
			public void customResultAvailable(Object result)
			{
				writePropertiesToStore(props);
				ret.setResult(null);
			}

			
		};
		rl	= shutdown ? rl : access.getComponentFeature(IExecutionFeature.class).createResultListener(rl); 
		final CounterResultListener	crl	= new CounterResultListener(providers.size(), rl);
		
		for(Iterator it=providers.keySet().iterator(); it.hasNext(); )
		{
			final String	id	= (String)it.next();
			IPropertiesProvider	provider	= (IPropertiesProvider)providers.get(id);
			rl	= new DelegationResultListener(ret)
			{
				public void customResultAvailable(Object result)
				{
					props.removeSubproperties(id);
					props.addSubproperties(id, (Properties)result);
					crl.resultAvailable(null);
				}
			};
			rl	= shutdown ? rl : access.getComponentFeature(IExecutionFeature.class).createResultListener(rl); 
			provider.getProperties().addResultListener(rl);
		}
		
		return ret;
	}
	
	/**
	 *  Set the save on exit policy.
	 *  @param saveonexit The saveonexit flag.
	 */
	public IFuture setSaveOnExit(boolean saveonexit)
	{
		this.saveonexit = saveonexit;
		return IFuture.DONE;
	}
	
	//-------- Helper methods --------

	/**
	 * Writes the given properties into a Store, usually a file.
	 * @throws FileNotFoundException
	 * @throws Exception
	 * @throws IOException
	 */
	protected void writePropertiesToStore(final Properties props) //throws FileNotFoundException, Exception, IOException 
	{
		// Todo: Which class loader to use? library service unavailable, because
		// it depends on settings service?
		getFile(filename).addResultListener(new IResultListener()
		{
			public void resultAvailable(File file)
			{
				FileOutputStream os = null;
				try
				{
					os = new FileOutputStream(file);
					PropertiesXMLHelper.write(props, os, getClass().getClassLoader());
				}
				catch(Exception e)
				{
					System.out.println("Warning: Could not save settings: "+e);
				}
				finally
				{
					if(os!=null)
					{
						try
						{
							os.close();
						}
						catch(Exception e)
						{
						}
					}
				}
			}
			
			public void exceptionOccurred(Exception exception)
			{
				
			}
		});
	
	}
	
	/**
	 * Returns the File object for a path to a file.
	 * @param path Path to the file
	 * @return The File Object for the given path.
	 */
	protected IFuture getFile(String path) 
	{
		return contextService.getFile(path);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy