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

jadex.platform.service.library.LibraryService Maven / Gradle / Ivy

Go to download

The Jadex platform package contains implementations of platform services as well as the platform component itself.

The newest version!
package jadex.platform.service.library;

import jadex.bridge.ComponentIdentifier;
import jadex.bridge.GlobalResourceIdentifier;
import jadex.bridge.IInternalAccess;
import jadex.bridge.IResourceIdentifier;
import jadex.bridge.LocalResourceIdentifier;
import jadex.bridge.ResourceIdentifier;
import jadex.bridge.service.RequiredServiceInfo;
import jadex.bridge.service.annotation.CheckNotNull;
import jadex.bridge.service.annotation.Excluded;
import jadex.bridge.service.annotation.Reference;
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.types.library.IDependencyService;
import jadex.bridge.service.types.library.ILibraryService;
import jadex.bridge.service.types.library.ILibraryServiceListener;
import jadex.bridge.service.types.settings.ISettingsService;
import jadex.commons.IPropertiesProvider;
import jadex.commons.Properties;
import jadex.commons.Property;
import jadex.commons.SUtil;
import jadex.commons.Tuple2;
import jadex.commons.future.CollectionResultListener;
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 java.io.File;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

/**
 *  Library service for loading classpath elements.
 */
@Service(ILibraryService.class)
public class LibraryService	implements ILibraryService, IPropertiesProvider
{
	//-------- constants --------
	
	/** The (standard) Library service name. */
	public static final String LIBRARY_SERVICE = "library_service";
	
	/** The pseudo system classpath rid. */
	public static final IResourceIdentifier SYSTEMCPRID;
	
	static
	{
		IResourceIdentifier res = null;
		try
		{
			res = new ResourceIdentifier(new LocalResourceIdentifier(new ComponentIdentifier("PSEUDO"), new URL("http://SYSTEMCPRID")), null);
		}
		catch(Exception e)
		{
			// should not happen
			e.printStackTrace();
		}
		SYSTEMCPRID = res;
	}
	
	//-------- attributes --------
	
	/** The component. */
	@ServiceComponent
	protected IInternalAccess	component;
	
	/** LibraryService listeners. */
	protected Set listeners;

	/** The init urls. */
	protected Object[] initurls;

	/** The class loader futures for currently loading class loaders. */
	protected Map> clfuts;
	
	/** The map of managed resources 2xrid (local, remote) -> delegate loader). */
	protected Map classloaders;
	
	/** The base classloader. */
	protected ChangeableURLClassLoader baseloader;
	
	/** The delegation root loader. */
	protected DelegationURLClassLoader rootloader;
	protected IResourceIdentifier rootrid;
	// root rid is not set in rootloader as it is not a valid file url

	/** The added links. */
	protected Set> addedlinks;

	/** The remove links. */
	protected Set> removedlinks;
	
	/** The delayed add links (could not directly be added because the parent was not there). */
	protected Set> addtodo;
	
	// cached results
	
	/** The dependencies. */
	protected Tuple2>> rids;
	
	/** The non-managed urls (cached for speed). */
//	protected Set	nonmanaged;
	protected Set	nonmanaged;
	
	//-------- constructors --------
	
	/** 
	 *  Creates a new LibraryService.
	 */ 
	public LibraryService()
	{
		this((ClassLoader)null);
	}
	
	/** 
	 *  Creates a new LibraryService.
	 *  @param urls	Urls may be specified as java.net.URLs, java.io.Files or java.lang.Strings.
	 *  	Strings are interpreted as relative files (relative to current directory),
	 *  	absolute files or URLs (whatever can be found). 
	 */ 
	public LibraryService(Object[] urls)
	{
		this(urls, null);
	}
	
	/** 
	 *  Creates a new LibraryService.
	 *  @param baseloader The base classloader that is parent of all subloaders.
	 */ 
	public LibraryService(ClassLoader baseloader)
	{
		this(null, baseloader, null);
	}
	
	/** 
	 *  Creates a new LibraryService.
	 *  @param urls	Urls may be specified as java.net.URLs, java.io.Files or java.lang.Strings.
	 *  	Strings are interpreted as relative files (relative to current directory),
	 *  	absolute files or URLs (whatever can be found). 
	 */ 
	public LibraryService(Object[] urls, ClassLoader baseloader)
	{
		this(urls, baseloader, null);
	}
	
	/** 
	 *  Creates a new LibraryService.
	 *  @param urls	Urls may be specified as java.net.URLs, java.io.Files or java.lang.Strings.
	 *  	Strings are interpreted as relative files (relative to current directory),
	 *  	absolute files or URLs (whatever can be found). 
	 */ 
	public LibraryService(Object[] urls, ClassLoader baseloader, Map properties)
	{
		this.classloaders = new HashMap();
		this.clfuts = new HashMap>();
		this.listeners	= new LinkedHashSet();
		this.initurls = urls!=null? urls.clone(): null;
		this.baseloader = baseloader!=null? new ChangeableURLClassLoader(null, baseloader)
			: new ChangeableURLClassLoader(null, getClass().getClassLoader());
		this.rootloader = new DelegationURLClassLoader(this.baseloader, null);
		this.addedlinks = new HashSet>();
		this.removedlinks = new HashSet>();
		this.addtodo = new HashSet>();
	}
	
	//-------- methods --------
	
	/**
	 *  Add a new resource identifier.
	 *  @param parid The optional parent rid.
	 *  @param orid The resource identifier.
	 */
	public IFuture addResourceIdentifier(final IResourceIdentifier parid,
		final IResourceIdentifier orid, final boolean workspace)
	{
//		System.out.println("adding: "+orid+" on: "+parid);

		final Future ret = new Future();

		if(parid!=null && !rootloader.getAllResourceIdentifiers().contains(parid))
		{
			ret.setException(new RuntimeException("Parent rid unknown: "+parid));
		}
		else
		{
			getDependencies(orid, workspace).addResultListener(new ExceptionDelegationResultListener
				>>, IResourceIdentifier>(ret)
			{
				public void customResultAvailable(Tuple2>> deps)
				{
					final IResourceIdentifier rid = deps.getFirstEntity();
	//				
					// If could not be resolved to local (or already was local) consider as exception.
					if(rid.getLocalIdentifier()==null)
					{
						ret.setException(new RuntimeException("Global rid could not be resolved to local: "+rid));
					}
					else
					{
						// Check if file exists
						if(checkUrl(rid.getLocalIdentifier().getUrl())==null)
						{
							ret.setException(new RuntimeException("Local rid url invalid: "+rid));
						}
						else
						{
	//						System.out.println("add end "+rid+" on: "+parid);
		
							// Must be added with resolved rid (what about resolving parent)
							addLink(parid, rid);
		
							getClassLoader(rid, deps.getSecondEntity(), parid, workspace).addResultListener(
								new ExceptionDelegationResultListener(ret)
							{
								public void customResultAvailable(final DelegationURLClassLoader chil)
								{
									ret.setResult(rid);
								}
							});
						}
					}
				}
				
				public void exceptionOccurred(Exception exception)
				{
					exception.printStackTrace();
					super.exceptionOccurred(exception);
				}
			});
			
	//		System.out.println("current: "+SUtil.arrayToString(rootloader.getAllResourceIdentifiers()));
		}
		
//		ret.addResultListener(new IResultListener()
//		{
//			public void resultAvailable(IResourceIdentifier result)
//			{
//				System.out.println("good");
//			}
//
//			public void exceptionOccurred(Exception exception)
//			{
//				exception.printStackTrace();
//			}
//		});
		
		return ret;
	}
	
	/**
	 *  Remove a resource identifier.
	 *  @param url The resource identifier.
	 */
	public IFuture removeResourceIdentifier(IResourceIdentifier parid, final IResourceIdentifier rid)
	{
//		System.out.println("remove "+rid);
		final Future ret = new Future();
		
		if(parid!=null && !rootloader.getAllResourceIdentifiers().contains(parid))
		{
			ret.setException(new RuntimeException("Parent rid unknown: "+parid));
		}
		else
		{
			removeLink(parid, rid);
			removeSupport(rid, parid);
			ret.setResult(null);
		}
		
		return ret;
	}
	
	/** 
	 *  Get the root resource identifier.
	 *  @param url The url.
	 *  @return The corresponding resource identifier.
	 */
	public IResourceIdentifier getRootResourceIdentifier()
	{
		return rootrid;
	}
	
	/**
	 *  Get all resource identifiers (does not include urls of parent loader).
	 *  @return The list of resource identifiers.
	 */
	public IFuture> getAllResourceIdentifiers()
	{
		return new Future>(new ArrayList(rootloader.getAllResourceIdentifiers()));
	}
	
	/**
	 *  Get the rids.
	 */
	public IFuture>>> getResourceIdentifiers()
	{
		if(rids==null)
		{
			Map> deps = new HashMap>();
			
			List todo = new ArrayList();
//			todo.add(rootloader.getResourceIdentifier());
			todo.add(null);
			
			while(!todo.isEmpty())
			{
				IResourceIdentifier clrid = todo.remove(0);
				if(SYSTEMCPRID.equals(clrid))
				{
					List mydeps = new ArrayList();
					Set nonmans = getInternalNonManagedURLs();
					for(URI uri: nonmans)
					{
						URL url = SUtil.toURL0(uri);
						if(url!=null)
							mydeps.add(new ResourceIdentifier(new LocalResourceIdentifier(component.getComponentIdentifier().getRoot(), url), null));
					}
					deps.put(clrid, mydeps);
				}
				else
				{
					DelegationURLClassLoader cl = clrid==null? rootloader: classloaders.get(clrid);
					List mydeps = cl.getDelegateResourceIdentifiers();
					deps.put(clrid, mydeps);
					if(clrid==null)
					{
						mydeps.add(SYSTEMCPRID);
					}
					for(IResourceIdentifier rid: mydeps)
					{
						if(!deps.containsKey(rid))
						{
							todo.add(rid);
						}
					}
				}
			}
			
			rids = new Tuple2>>(rootloader.getResourceIdentifier(), deps);
		}
		
		return new Future>>>(rids);
	}
	
	/**
	 *  Add a new url.
	 *  @param url The resource identifier.
	 */
	public IFuture addURL(final IResourceIdentifier parid, URL purl)
	{
		final Future ret = new Future();

//		System.out.println("add url: "+url);
		
		URL url = checkUrl(purl);
		if(url==null)
		{
			ret.setException(new RuntimeException("URL not backed by local file: "+purl));
			return ret;
		}
//		System.out.println("add url2: "+url);
		
		// Normalize files to avoid duplicate urls.
		if("file".equals(url.getProtocol()))
		{
			try
			{
				url	= SUtil.getFile(url).getCanonicalFile().toURI().toURL();
			}
			catch(Exception e)
			{
			}
		}
		
		internalGetResourceIdentifier(url).addResultListener(
			new DelegationResultListener(ret)
		{
			public void customResultAvailable(final IResourceIdentifier rid)
			{
				// todo: should be true?
				addResourceIdentifier(parid, rid, true).addResultListener(
					new ExceptionDelegationResultListener(ret)
				{
					public void customResultAvailable(IResourceIdentifier result)
					{
						ret.setResult(rid);
					}
				});
			}
		});
		
		return ret;
	}
	
	/**
	 *  Remove a new url.
	 *  @param url The resource identifier.
	 */
	public IFuture removeURL(final IResourceIdentifier parid, final URL url)
	{
		final Future ret = new Future();
		
		internalGetResourceIdentifier(url).addResultListener(
			new ExceptionDelegationResultListener(ret)
		{
			public void customResultAvailable(IResourceIdentifier result)
			{
				removeResourceIdentifier(parid, result).addResultListener(new DelegationResultListener(ret));
			}
		});
		
		return ret;
	}
	
	/**
	 *  Add a top level url. A top level url will
	 *  be available for all subordinated resources. 
	 *  @param url The url.
	 */
	public IFuture addTopLevelURL(@CheckNotNull URL purl)
	{
		URL url = checkUrl(purl);
		if(url==null)
			return new Future(new RuntimeException("URL not backed by local file: "+purl));
		
		baseloader.addURL(url);
		nonmanaged = null;
		IResourceIdentifier rid = new ResourceIdentifier(new LocalResourceIdentifier(component.getComponentIdentifier().getRoot(), url), null);
		addLink(SYSTEMCPRID, rid);
		notifyAdditionListeners(SYSTEMCPRID, rid);
		return IFuture.DONE;
	}

	/**
	 *  Remove a top level url. A top level url will
	 *  be available for all subordinated resources. 
	 *  @param url The url.
	 *  
	 *  note: top level url removal will only take 
	 *  effect after restart of the platform.
	 */
	public IFuture removeTopLevelURL(@CheckNotNull URL url)
	{
		baseloader.removeURL(url);
		nonmanaged = null;
		IResourceIdentifier rid = new ResourceIdentifier(new LocalResourceIdentifier(component.getComponentIdentifier().getRoot(), url), null);
		removeLink(SYSTEMCPRID, rid);
		notifyRemovalListeners(SYSTEMCPRID, rid);
		return IFuture.DONE;
	}
	
	/**
	 *  Get other contained (but not directly managed) urls from parent classloaders.
	 *  @return The list of urls.
	 */
	public IFuture> getNonManagedURLs()
	{
		Set res = getInternalNonManagedURLs();
		List ret = new ArrayList();
		if(res!=null)
		{
			for(URI uri: res)
			{
				try
				{
					ret.add(uri.toURL());
				}
				catch(Exception e)
				{
					System.out.println("Problem with url: "+uri);
				}
			}
		}
		return new Future>(ret);
	}
	
	/**
	 *  Get other contained (but not directly managed) urls from parent classloaders.
	 *  @return The set of urls.
	 */
	protected Set	getInternalNonManagedURLs()
	{
		if(nonmanaged==null)
		{
			nonmanaged	= new LinkedHashSet();
			collectClasspathURLs(baseloader, nonmanaged, new HashSet());
		}
		return nonmanaged;
	}
	
	/**
	 *  Get all urls (managed, indirect and non-managed from parent loader).
	 *  @return The list of urls.
	 */
	public IFuture> getAllURLs()
	{
//		final long	start	= System.currentTimeMillis();
		
		final Future> ret = new Future>();
		getAllResourceIdentifiers().addResultListener(new ExceptionDelegationResultListener, List>(ret)
		{
			public void customResultAvailable(List result)
			{
				final List res = new ArrayList();
				for(int i=0; i re = getInternalNonManagedURLs();
				for(URI uri: re)
				{
					URL url = SUtil.toURL0(uri);
					if(url!=null)
						res.add(url);
				}
//				res.addAll();
				
//				System.out.println("getAllUrls: "+(System.currentTimeMillis()-start));
				ret.setResult(res);
			}
		});
		return ret;
	}
		
	/** 
	 *  Returns the current ClassLoader.
	 *  @return the current ClassLoader
	 */
	@Excluded
	public @Reference IFuture getClassLoader(IResourceIdentifier rid)
	{
		return getClassLoader(rid, true);
	}
	
	/** 
	 *  Returns the current ClassLoader.
	 *  @param rid The resource identifier (null for current global loader).
	 *  @return the current ClassLoader
	 */
	@Excluded()
	public IFuture getClassLoader(final IResourceIdentifier rid, boolean workspace)
	{
		final Future ret = new Future();
		
		if(rid==null || rid.equals(rootloader.getResourceIdentifier()))
		{
			ret.setResult(rootloader);
//			System.out.println("root classloader: "+rid);
		}
		else if(isLocal(rid) && getInternalNonManagedURLs().contains(SUtil.toURI0(rid.getLocalIdentifier().getUrl())))
		{
			ret.setResult(baseloader);
//			System.out.println("base classloader: "+rid);
		}	
		else
		{
			// Resolve global rid or local rid from same platform.
			if(rid.getGlobalIdentifier()!=null || isLocal(rid))
			{
				getClassLoader(rid, null, rootloader.getResourceIdentifier(), workspace).addResultListener(new ExceptionDelegationResultListener(ret)
				{
					public void customResultAvailable(DelegationURLClassLoader result)
					{
						ret.setResult(result);
//						System.out.println("custom classloader: "+result.hashCode()+" "+rid);
					}
				});
			}
			else
			{
				ret.setResult(null);
			}
		}
		
		return ret;
	}
	
	/** 
	 *  Get the resource identifier for an url.
	 *  @return The resource identifier.
	 */
	public IFuture getResourceIdentifier(final URL url)
	{
		final Future ret = new Future();
		
		component.getServiceContainer().searchService(IDependencyService.class, RequiredServiceInfo.SCOPE_PLATFORM)
			.addResultListener(new ExceptionDelegationResultListener(ret)
		{
			public void customResultAvailable(IDependencyService drs)
			{
				drs.getResourceIdentifier(url).addResultListener(new DelegationResultListener(ret));
			}
		});
		
		return ret;
	}

	//-------- listener methods --------
	
    /**
	 *  Add an Library Service listener.
	 *  The listener is registered for changes in the loaded library states.
	 *  @param listener The listener to be added.
	 */
	public IFuture addLibraryServiceListener(ILibraryServiceListener listener)
	{
		if(listener==null)
			throw new IllegalArgumentException();
			
		listeners.add(listener);
		return IFuture.DONE;
	}

	/**
	 *  Remove an Library Service listener.
	 *  @param listener  The listener to be removed.
	 */
	public IFuture removeLibraryServiceListener(ILibraryServiceListener listener)
	{
		listeners.remove(listener);
		return IFuture.DONE;
	}
	
	//-------- internal methods --------
	
	/**
	 *  Get or create a classloader for a rid.
	 */
	protected IFuture getClassLoader(final IResourceIdentifier rid, 
		Map> alldeps, final IResourceIdentifier support, final boolean workspace)
	{
		final Future ret;
		
		// full=(global, local) calls followed by any other call are ok, because global and local can be cached
		// pure global call followed by pure local call -> would mean rids have not been resolved
		// pure local call followed by pure global call -> would mean rids have not been resolved
		final IResourceIdentifier lrid = ResourceIdentifier.getLocalResourceIdentifier(rid);
		if(isLocal(rid) && getInternalNonManagedURLs().contains(SUtil.toURI0(rid.getLocalIdentifier().getUrl())))
		{
			ret	= new Future((DelegationURLClassLoader)null);
		}
		else if(clfuts.containsKey(rid))
		{
			ret	= clfuts.get(rid);
		}
		else if(lrid!=null && clfuts.containsKey(lrid))
		{
			ret	= clfuts.get(lrid);
		}
		else
		{
	//		final URL url = rid.getLocalIdentifier().getSecondEntity();
			
			ret = new Future();
			DelegationURLClassLoader cl = (DelegationURLClassLoader)classloaders.get(rid);
			
			if(cl!=null)
			{
				addSupport(rid, support);
				ret.setResult(cl);
			}
			else
			{
				clfuts.put(rid, ret);
				if(lrid!=null)
					clfuts.put(lrid, ret);
//				if(rid.getGlobalIdentifier()==null)
//					System.out.println("getClassLoader(): "+rid);
				
				if(alldeps==null)
				{
//					System.out.println("getdeps in getcl: "+rid);
					getDependencies(rid, workspace).addResultListener(
						new ExceptionDelegationResultListener>>, DelegationURLClassLoader>(ret)
					{
						public void customResultAvailable(Tuple2>> deps)
						{
							createClassLoader(deps.getFirstEntity(), deps.getSecondEntity(), support, workspace).addResultListener(new DelegationResultListener(ret)
							{
								public void customResultAvailable(DelegationURLClassLoader result)
								{
									clfuts.remove(rid);
									clfuts.remove(lrid);
									super.customResultAvailable(result);
								}
								
								public void exceptionOccurred(Exception exception)
								{
									clfuts.remove(rid);
									clfuts.remove(lrid);
									super.exceptionOccurred(exception);
								}
							});
						}
						
						public void exceptionOccurred(Exception exception)
						{
							clfuts.remove(rid);
							clfuts.remove(lrid);
							super.exceptionOccurred(exception);
						}
					});
				}
				else
				{
					createClassLoader(rid, alldeps, support, workspace).addResultListener(new DelegationResultListener(ret)
					{
						public void customResultAvailable(DelegationURLClassLoader result)
						{
							clfuts.remove(rid);
							clfuts.remove(lrid);
							super.customResultAvailable(result);
						}
						
						public void exceptionOccurred(Exception exception)
						{
							clfuts.remove(rid);
							clfuts.remove(lrid);
							super.exceptionOccurred(exception);
						}
					});
				}
			}
		}
		
		return ret;
	}
	
	/**
	 *  Create a new classloader.
	 */
	protected IFuture createClassLoader(final IResourceIdentifier rid, 
		Map> alldeps, final IResourceIdentifier support, final boolean workspace)
	{
		// Class loaders shouldn't be created for local URLs, which are already available in base class loader.
		assert rid.getLocalIdentifier()==null || !isLocal(rid) || !getInternalNonManagedURLs().contains(SUtil.toURI0(rid.getLocalIdentifier().getUrl()));
		
		final Future ret = new Future();
//		final URL url = rid.getLocalIdentifier().getSecondEntity();
		final List deps = alldeps.get(rid);

		final DelegationURLClassLoader cl = createNewDelegationClassLoader(rid, baseloader, null);
		classloaders.put(rid, cl);
		// Add also local rid to ensure that classloader will be found for these searches as well
		if(rid.getGlobalIdentifier()!=null && rid.getLocalIdentifier()!=null)
		{
			IResourceIdentifier localrid = ResourceIdentifier.getLocalResourceIdentifier(rid);
			if(localrid!=null)
			{
				classloaders.put(localrid, cl);
			}
		}
		
//		System.out.println("createClassLoader() put: "+rid);
		
		CollectionResultListener lis = new CollectionResultListener
			(deps.size(), true, new ExceptionDelegationResultListener, DelegationURLClassLoader>(ret)
		{
			public void customResultAvailable(Collection result)
			{
				// Strip null values of provided dependencies from results.
				for(Iterator it=result.iterator(); it.hasNext(); )
				{
					if(it.next()==null)
						it.remove();
				}
				
				for(DelegationURLClassLoader dcl: result)
				{
					cl.addDelegateClassLoader(dcl);
				}
				addSupport(rid, support);
				ret.setResult(cl);
			}
		});
		
		for(int i=0; i>>> 
		getDependencies(final IResourceIdentifier rid, final boolean workspace)
	{
		final Future>>> ret = new Future>>>();
		
		component.getServiceContainer().searchService(IDependencyService.class, RequiredServiceInfo.SCOPE_PLATFORM)
			.addResultListener(new ExceptionDelegationResultListener>>>(ret)
		{
			public void customResultAvailable(IDependencyService drs)
			{
				drs.loadDependencies(rid, workspace).addResultListener(new DelegationResultListener>>>(ret));
			}
		});
		
		return ret;
	}
	
	/**
	 *  Add support for a rid.
	 */
	protected void addSupport(IResourceIdentifier rid, IResourceIdentifier parid)
	{
		if(rid==null)
			throw new IllegalArgumentException("Rid must not null.");
				
		DelegationURLClassLoader pacl = parid==null || rootrid.equals(parid)? rootloader: (DelegationURLClassLoader)classloaders.get(parid);
		DelegationURLClassLoader cl = (DelegationURLClassLoader)classloaders.get(rid);
		pacl.addDelegateClassLoader(cl);
		if(cl.addParentClassLoader(pacl))
		{
			rids = null;
			notifyAdditionListeners(parid, rid);
		}
		
		// Check if pending user entries can be restored
		for(Iterator> it=addtodo.iterator(); it.hasNext(); )
		{
			Tuple2 link = it.next();
			if(rid.equals(link.getFirstEntity()))
			{
				addResourceIdentifier(link.getFirstEntity(), link.getSecondEntity(), true);
				it.remove();
			}
		}
	}
	
	/**
	 *  Remove support for a rid.
	 */
	protected void removeSupport(IResourceIdentifier rid, IResourceIdentifier parid)
	{
		if(rid==null)
			throw new IllegalArgumentException("Rid must not null.");

		DelegationURLClassLoader pacl = parid==null || rootrid.equals(parid)? rootloader: (DelegationURLClassLoader)classloaders.get(parid);
		DelegationURLClassLoader cl = (DelegationURLClassLoader)classloaders.get(rid);
		pacl.removeDelegateClassLoader(cl);
		if(cl.removeParentClassLoader(pacl))
		{
			rids = null;
			notifyRemovalListeners(parid, rid);
		}
		
		// If last support, delete removeSupport to children.
		if(!cl.hasParentClassLoader())
		{
			DelegationURLClassLoader[] dels = cl.getDelegateClassLoaders();
			for(DelegationURLClassLoader del: dels)
			{
				removeSupport(del.getResourceIdentifier(), rid);
			}
			classloaders.remove(rid);
			classloaders.remove(ResourceIdentifier.getLocalResourceIdentifier(rid));
		}
	}
	
	/**
	 *  Notify listeners about addition.
	 */
	protected void notifyAdditionListeners(final IResourceIdentifier parid, final IResourceIdentifier rid)
	{
		boolean rem = addedlinks.contains(new Tuple2(parid, rid));
		// Do not notify listeners with lock held!
		ILibraryServiceListener[] lis = (ILibraryServiceListener[])listeners.toArray(new ILibraryServiceListener[listeners.size()]);
		for(int i=0; i()
			{
				public void resultAvailable(Void result)
				{
				}
				public void exceptionOccurred(Exception exception) 
				{
					// todo: how to handle timeouts?! allow manual retry?
	//				exception.printStackTrace();
					removeLibraryServiceListener(liscopy);
				};
			});
		}
	}
	
	/**
	 *  Notify listeners about removal.
	 */
	protected void notifyRemovalListeners(final IResourceIdentifier parid, final IResourceIdentifier rid)
	{
		// Do not notify listeners with lock held!
		ILibraryServiceListener[] lis = (ILibraryServiceListener[])listeners.toArray(new ILibraryServiceListener[listeners.size()]);
		for(int i=0; i()
			{
				public void resultAvailable(Void result)
				{
				}
				public void exceptionOccurred(Exception exception) 
				{
					// todo: how to handle timeouts?! allow manual retry?
	//				exception.printStackTrace();
					removeLibraryServiceListener(liscopy);
				};
			});
		}
	}
	
	/**
	 *  Get the resource identifier for an url.
	 */
	protected IFuture internalGetResourceIdentifier(final URL url)
	{
		final Future ret = new Future();
		
		component.getServiceContainer().searchService(IDependencyService.class, RequiredServiceInfo.SCOPE_PLATFORM)
			.addResultListener(component.createResultListener(new ExceptionDelegationResultListener(ret)
		{
			public void customResultAvailable(IDependencyService drs)
			{
				drs.getResourceIdentifier(url).addResultListener(new DelegationResultListener(ret));
			}
		}));
		
		return ret;
	}
	
	//-------- methods --------
	
	/**
	 *  Start the service.
	 */
	@ServiceStart
	public IFuture	startService()
	{
		try
		{
			this.rootrid = new ResourceIdentifier(new LocalResourceIdentifier(component.getComponentIdentifier(), new URL("http://ROOTRID")), null);
			this.rootloader.setResourceIdentifier(rootrid);
//			this.classloaders.put(rootrid, rootloader);
		}
		catch(Exception e)
		{
			// should not happen
			e.printStackTrace();
		}
		
		final Future	urlsdone	= new Future();
		if(initurls!=null)
		{
			CounterResultListener lis = new CounterResultListener(
				initurls.length, new DelegationResultListener(urlsdone));
			for(int i=0; i	ret	= new Future();
		urlsdone.addResultListener(new DelegationResultListener(ret)
		{
			public void customResultAvailable(Void result) 
			{
				component.getServiceContainer().searchService(ISettingsService.class, RequiredServiceInfo.SCOPE_PLATFORM)
					.addResultListener(new ExceptionDelegationResultListener(ret)
				{
					public void customResultAvailable(ISettingsService settings)
					{
						settings.registerPropertiesProvider(LIBRARY_SERVICE, LibraryService.this)
							.addResultListener(new DelegationResultListener(ret));
					}
					public void exceptionOccurred(Exception exception)
					{
						// No settings service: ignore
						ret.setResult(null);
					}
				});
			}
		});
		return ret;
	}

	/** 
	 *  Shutdown the service.
	 *  Releases all cached resources and shuts down the library service.
	 *  @param listener The listener.
	 */
	@ServiceShutdown
	public IFuture	shutdownService()
	{
//		System.out.println("shut");
		final Future	saved	= new Future();
		component.getServiceContainer().searchService(ISettingsService.class, RequiredServiceInfo.SCOPE_PLATFORM)
			.addResultListener(new ExceptionDelegationResultListener(saved)
		{
			public void customResultAvailable(ISettingsService settings)
			{
				settings.deregisterPropertiesProvider(LIBRARY_SERVICE)
					.addResultListener(new DelegationResultListener(saved));
			}
			public void exceptionOccurred(Exception exception)
			{
				// No settings service: ignore
				saved.setResult(null);
			}
		});
		
		final Future	ret	= new Future();
		saved.addResultListener(new DelegationResultListener(ret)
		{
			public void customResultAvailable(Void result)
			{
				synchronized(this)
				{
//					libcl = null;
					listeners.clear();
					ret.setResult(null);
				}
			}
		});
			
		return ret;
	}

	//-------- IPropertiesProvider interface --------
		
	/**
	 *  Collect all URLs belonging to a class loader.
	 */
	protected void	collectClasspathURLs(ClassLoader classloader, Set set, Set jarnames)
	{
		assert classloader!=null;
		
		if(classloader.getParent()!=null)
		{
			collectClasspathURLs(classloader.getParent(), set, jarnames);
		}
		
		if(classloader instanceof URLClassLoader)
		{
			URL[] urls = ((URLClassLoader)classloader).getURLs();
			for(int i=0; i set, Set jarnames)
	{
		File	file	= SUtil.urlToFile(url.toString());
		if(file!=null && file.exists() && !file.isDirectory())	// Todo: load manifest also from directories!?
		{
			JarFile jarfile = null;
	        try 
	        {
	            jarfile	= new JarFile(file);
	            Manifest manifest = jarfile.getManifest();
	            if(manifest!=null)
	            {
	                String	classpath	= manifest.getMainAttributes().getValue(new Attributes.Name("Class-Path"));
	                if(classpath!=null)
	                {
	                	StringTokenizer	tok	= new StringTokenizer(classpath, " ");
	            		while(tok.hasMoreElements())
	            		{
	            			String path = tok.nextToken();
	            			File	urlfile;
	            			
	            			// Search in directory of original jar (todo: also search in local dir!?)
	            			urlfile = new File(file.getParentFile(), path);
	            			
	            			// Try as absolute path
	            			if(!urlfile.exists())
	            			{
	            				urlfile	= new File(path);
	            			}
	            			
	            			// Try as url
	            			if(!urlfile.exists())
	            			{
	            				urlfile	= SUtil.urlToFile(path);
	            			}
	
	            			if(urlfile!=null && urlfile.exists())
	            			{
		            			try
			                	{
		            				if(urlfile.getName().endsWith(".jar"))
		            				{
		            					String jarname	= getJarName(urlfile.getName());
		            					jarnames.add(jarname);
		            				}
		            				URL depurl = urlfile.toURI().toURL();
		            				set.add(depurl.toURI());
		            				collectManifestURLs(depurl.toURI(), set, jarnames);
		            			}
		                    	catch (Exception e)
		                    	{
		                    		component.getLogger().warning("Error collecting manifest URLs for "+urlfile+": "+e);
		                    	}
	                    	}
	            			else if(!path.endsWith(".jar") || !jarnames.contains(getJarName(path)))
	            			{
	            				component.getLogger().warning("Jar not found: "+file+", "+path);
	            			}
	               		}
	                }
	            }
		    }
		    catch(Exception e)
		    {
				component.getLogger().warning("Error collecting manifest URLs for "+url+": "+e);
		    }
	        finally
	        {
	        	try
	        	{
	        		if(jarfile!=null)
	        			jarfile.close();
	        	}
	        	catch(Exception e)
	        	{
	        	}
	        }
		}
	}
	
	/**
	 *  Test if a rid is local to this platform.
	 */
	protected boolean isLocal(IResourceIdentifier rid)
	{
//		return rid.getLocalIdentifier()!=null && rid.getLocalIdentifier().getComponentIdentifier().equals(component.getComponentIdentifier().getRoot());		
		return rid.getLocalIdentifier()!=null && rid.getLocalIdentifier().getHostIdentifier().equals(
			SUtil.getMacAddress()!=null ? SUtil.getMacAddress() : rootrid.getLocalIdentifier().getComponentIdentifier().getName());
	}
	
	/**
	 *  Add a link.
	 */
	protected void addLink(IResourceIdentifier parid, IResourceIdentifier rid)
	{
		Tuple2 link = new Tuple2(parid, rid);
		if(!removedlinks.remove(link))
		{
			addedlinks.add(link);
		}
	}
	
	/**
	 *  Remove a link.
	 */
	protected void removeLink(IResourceIdentifier parid, IResourceIdentifier rid)
	{
		Tuple2 link = new Tuple2(parid, rid);
		if(!addedlinks.remove(link))
		{
			removedlinks.add(link);
		}
	}
	
	/**
	 *  Get the removable links.
	 */
	public IFuture>> getRemovableLinks()
	{
		Set> ret = (Set>)((HashSet)addedlinks).clone();
		return new Future>>(ret);
	}

	/**
	 *  Update from given properties.
	 */
	public IFuture setProperties(Properties props)
	{
		Properties[] links = props.getSubproperties("link");
		
		Set> todo = new HashSet>();
		
		addtodo.clear();
		for(int i=0; i(a, b));
				}
				else
				{
					addtodo.add(new Tuple2(a, b));
				}
			}
		}
		
		for(Tuple2 link: todo)
		{
			addResourceIdentifier(link.getFirstEntity(), link.getSecondEntity(), true); // workspace?
		}
		
//		System.out.println("todo: "+todo);
//		System.out.println("addtodo: "+addtodo);
		
		return IFuture.DONE;
	}
	
	/**
	 *  Write current state into properties.
	 */
	public IFuture getProperties()
	{
		Properties props	= new Properties();
		
		for(Tuple2 link: addedlinks)
		{
			Properties plink = new Properties();
			Properties a = ridToProperties(link.getFirstEntity());
			Properties b = ridToProperties(link.getSecondEntity());
			plink.addSubproperties("a", a);
			plink.addSubproperties("b", b);
			props.addSubproperties("link", plink);
		}
		
		return new Future(props);		
	}
	
	/**
	 *  Create properties from rid.
	 *  @param The resource identifier.
	 *  @return rid The resource identifier properties.
	 */
	public Properties ridToProperties(IResourceIdentifier rid)
	{
		Properties ret = new Properties();
		
		if(rid!=null && rid.getGlobalIdentifier()!=null)
		{
			ret.addProperty(new Property("gid_ri", rid.getGlobalIdentifier().getResourceId()));
			ret.addProperty(new Property("gid_vi", rid.getGlobalIdentifier().getVersionInfo()));
//			ret.addProperty(new Property("url", rid.getGlobalIdentifier().getRepositoryInfo()));
		}
		if(rid!=null && rid.getLocalIdentifier()!=null)
		{
			ret.addProperty(new Property("lid_url", SUtil.convertPathToRelative(rid.getLocalIdentifier().getUrl().toString())));
			// todo: check if own platform cid?
//			ret.addProperty(new Property("lid_cid", rid.getLocalIdentifier().getComponentIdentifier()));
		}
		
		return ret;
	}
	
	/**
	 *  Create a rid from properties.
	 *  @param rid The resource identifier properties.
	 *  @return The resource identifier.
	 */
	public IResourceIdentifier ridFromProperties(Properties rid)
	{
		String gid_ri = rid.getStringProperty("gid_ri");
		String gid_vi = rid.getStringProperty("gid_vi");
		
		String lid_url = rid.getStringProperty("lid_url");
		
		GlobalResourceIdentifier gid = null;
		if(gid_vi!=null)
		{
			gid = new GlobalResourceIdentifier(gid_ri, null, gid_vi);
		}
		
		LocalResourceIdentifier lid = null;
		if(lid_url!=null)
		{
			try
			{
				URL	url	= SUtil.getFile(new URL(lid_url)).getCanonicalFile().toURI().toURL();
//				System.out.println("url: "+url);
				lid = new LocalResourceIdentifier(component.getComponentIdentifier().getRoot(), url);
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}
		
		return gid!=null || lid!=null? new ResourceIdentifier(lid, gid): null;
	}
	
	/**
	 *  Check if a local url is backed by a file.
	 */
	protected URL checkUrl(URL url)
	{
		URL ret = null;
		
		if("file".equals(url.getProtocol()))
		{
			File f = SUtil.getFile(url);
			if(f.exists())
			{
				try
				{
					ret = f.getCanonicalFile().toURI().toURL();
				}
				catch(Exception e)
				{
					throw new RuntimeException(e);
				}
			}
		}
		
		return ret;
	}
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy