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

org.osgi.service.application.ApplicationDescriptor Maven / Gradle / Ivy

/*
 * Copyright (c) OSGi Alliance (2004, 2009). All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.osgi.service.application;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.Map;

import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;

/**
 * An OSGi service that represents an installed application and stores
 * information about it. The application descriptor can be used for instance
 * creation.
 * 
 * @version $Revision: 7938 $
 */

public abstract class ApplicationDescriptor {
	/*
	 * NOTE: An implementor may also choose to replace this class in
	 * their distribution with a class that directly interfaces with the
	 * org.osgi.service.application implementation. This replacement class MUST NOT alter the
	 * public/protected signature of this class.
	 */

	/**
	 * The property key for the localized name of the application.
	 */
	public static final String APPLICATION_NAME = "application.name";

	/**
	 * The property key for the localized icon of the application.
	 */
	public static final String APPLICATION_ICON = "application.icon";

	/**
	 * The property key for the unique identifier (PID) of the application.
	 */
	public static final String APPLICATION_PID = Constants.SERVICE_PID;

	/**
	 * The property key for the version of the application.
	 */
	public static final String APPLICATION_VERSION = "application.version";

	/**
	 * The property key for the name of the application vendor.
	 */
	public static final String APPLICATION_VENDOR = Constants.SERVICE_VENDOR;


	/**
	 * The property key for the visibility property of the application.
	 */
	public static final String APPLICATION_VISIBLE = "application.visible";

	/**
	 * The property key for the launchable property of the application.
	 */
	public static final String APPLICATION_LAUNCHABLE = "application.launchable";

	/**
	 * The property key for the locked property of the application.
	 */
	public static final String APPLICATION_LOCKED = "application.locked";

	/**
	 * The property key for the localized description of the application.
	 */
	public static final String APPLICATION_DESCRIPTION = "application.description";

	/**
	 * The property key for the localized documentation of the application.
	 */
	public static final String APPLICATION_DOCUMENTATION = "application.documentation";

	/**
	 * The property key for the localized copyright notice of the application.
	 */
	public static final String APPLICATION_COPYRIGHT = "application.copyright";

	/**
	 * The property key for the localized license of the application.
	 */
	public static final String APPLICATION_LICENSE = "application.license";

	/**
	 * The property key for the application container of the application.
	 */
	public static final String APPLICATION_CONTAINER = "application.container";

	/**
	 * The property key for the location of the application.
	 */
	public static final String APPLICATION_LOCATION = "application.location";

	
	private final String	pid;


	/**
	 * Constructs the ApplicationDescriptor.
	 *
	 * @param applicationId
	 *            The identifier of the application. Its value is also available
	 *            as the service.pid service property of this 
	 *            ApplicationDescriptor service. This parameter must not
	 *            be null.
	 * @throws NullPointerException if the specified applicationId is null.
	 */
	protected  ApplicationDescriptor(String applicationId) {
		if(null == applicationId ) {
			throw new NullPointerException("Application ID must not be null!");
		}
		
		this.pid = applicationId;
		try {
			delegate = new Delegate();
			delegate.setApplicationDescriptor( this, applicationId );
		}
		catch (Exception e) {
			// Too bad ...
			e.printStackTrace();
			System.err
					.println("No implementation available for ApplicationDescriptor, property is: "
							+ Delegate.cName);
		}
	}

	/**
	 * Returns the identifier of the represented application.
	 * 
	 * @return the identifier of the represented application
	 */
	public final String getApplicationId() {
		return pid;
	}

	/**
	 * This method verifies whether the specified pattern
	 * matches the Distinguished Names of any of the certificate chains
	 * used to authenticate this application.
	 * 

* The pattern must adhere to the * syntax defined in {@link org.osgi.service.application.ApplicationAdminPermission} * for signer attributes. *

* This method is used by {@link ApplicationAdminPermission#implies(java.security.Permission)} method * to match target ApplicationDescriptor and filter. * * @param pattern a pattern for a chain of Distinguished Names. It must not be null. * @return true if the specified pattern matches at least * one of the certificate chains used to authenticate this application * @throws NullPointerException if the specified pattern is null. * @throws IllegalStateException if the application descriptor was * unregistered */ public abstract boolean matchDNChain( String pattern ); /** * Returns the properties of the application descriptor as key-value pairs. * The return value contains the locale aware and unaware properties as * well. The returned Map will include the service * properties of this ApplicationDescriptor as well. *

* This method will call the getPropertiesSpecific method * to enable the container implementation to insert application model and/or * container implementation specific properties. *

* The returned {@link java.util.Map} will contain the standard OSGi service * properties as well * (e.g. service.id, service.vendor etc.) and specialized application * descriptors may offer further service properties. The returned Map contains * a snapshot of the properties. It will not reflect further changes in the * property values nor will the update of the Map change the corresponding * service property. * * @param locale * the locale string, it may be null, the value null means the * default locale. If the provided locale is the empty String * ("")then raw (non-localized) values are returned. * * @return copy of the service properties of this application descriptor service, * according to the specified locale. If locale is null then the * default locale's properties will be returned. (Since service * properties are always exist it cannot return null.) * * @throws IllegalStateException * if the application descriptor is unregistered */ public final Map getProperties(String locale) { Map props = getPropertiesSpecific( locale ); /* currently the ApplicationDescriptor manages the load/save of locking */ boolean isLocked = delegate.isLocked(); // the real locking state Boolean containerLocked = (Boolean)props.remove( APPLICATION_LOCKED ); if( containerLocked != null && containerLocked.booleanValue() != isLocked ) { try { if( isLocked ) /* if the container's information is not correct */ lockSpecific(); /* about the locking state (after loading the lock states) */ else unlockSpecific(); }catch( Exception e ) {} } /* replace the container's lock with the application model's lock, that's the correct */ props.put( APPLICATION_LOCKED, new Boolean( isLocked ) ); return props; } /** * Container implementations can provide application model specific * and/or container implementation specific properties via this * method. * * Localizable properties must be returned localized if the provided * locale argument is not the empty String. The value * null indicates to use the default locale, for other * values the specified locale should be used. * * The returned {@link java.util.Map} must contain the standard OSGi service * properties as well * (e.g. service.id, service.vendor etc.) and specialized application * descriptors may offer further service properties. * The returned Map * contains a snapshot of the properties. It will not reflect further changes in the * property values nor will the update of the Map change the corresponding * service property. * @param locale the locale to be used for localizing the properties. * If null the default locale should be used. If it is * the empty String ("") then raw (non-localized) values * should be returned. * * @return the application model specific and/or container implementation * specific properties of this application descriptor. * * @throws IllegalStateException * if the application descriptor is unregistered */ protected abstract Map getPropertiesSpecific(String locale); /** * Launches a new instance of an application. The args parameter specifies * the startup parameters for the instance to be launched, it may be null. *

* The following steps are made: *

    *
  • Check for the appropriate permission. *
  • Check the locking state of the application. If locked then throw * an {@link ApplicationException} with the reason code * {@link ApplicationException#APPLICATION_LOCKED}. *
  • Calls the launchSpecific() method to create and start an application * instance. *
  • Returns the ApplicationHandle returned by the * launchSpecific() *
* The caller has to have ApplicationAdminPermission(applicationPID, * "launch") in order to be able to perform this operation. *

* The Map argument of the launch method contains startup * arguments for the * application. The keys used in the Map must be non-null, non-empty String * objects. They can be standard or application * specific. OSGi defines the org.osgi.triggeringevent * key to be used to * pass the triggering event to a scheduled application, however * in the future it is possible that other well-known keys will be defined. * To avoid unwanted clashes of keys, the following rules should be applied: *

    *
  • The keys starting with the dash (-) character are application * specific, no well-known meaning should be associated with them.
  • *
  • Well-known keys should follow the reverse domain name based naming. * In particular, the keys standardized in OSGi should start with * org.osgi..
  • *
*

* The method is synchronous, it return only when the application instance was * successfully started or the attempt to start it failed. *

* This method never returns null. If launching an application fails, * the appropriate exception is thrown. * * @param arguments * Arguments for the newly launched application, may be null * * @return the registered ApplicationHandle, which represents the newly * launched application instance. Never returns null. * * @throws SecurityException * if the caller doesn't have "lifecycle" * ApplicationAdminPermission for the application. * @throws ApplicationException * if starting the application failed * @throws IllegalStateException * if the application descriptor is unregistered * @throws IllegalArgumentException * if the specified Map contains invalid keys * (null objects, empty String or a key that is not * String) */ public final ApplicationHandle launch(Map arguments) throws ApplicationException { try { delegate.launch(arguments); }catch( SecurityException se ) { isLaunchableSpecific(); /* check whether the bundle was uninstalled */ /* if yes, throws IllegalStateException */ throw se; /* otherwise throw the catched SecurityException */ } if( !isLaunchableSpecific() ) throw new ApplicationException(ApplicationException.APPLICATION_NOT_LAUNCHABLE, "Cannot launch the application!"); try { return launchSpecific(arguments); } catch(IllegalStateException ise) { throw ise; } catch(SecurityException se) { throw se; } catch( ApplicationException ae) { throw ae; } catch(Exception t) { throw new ApplicationException(ApplicationException.APPLICATION_INTERNAL_ERROR, t); } } /** * Called by launch() to create and start a new instance in an application * model specific way. It also creates and registeres the application handle * to represent the newly created and started instance and registeres it. * The method is synchronous, it return only when the application instance was * successfully started or the attempt to start it failed. *

* This method must not return null. If launching the application * failed, and exception must be thrown. * * @param arguments * the startup parameters of the new application instance, may be * null * * @return the registered application model * specific application handle for the newly created and started * instance. * * @throws IllegalStateException * if the application descriptor is unregistered * @throws Exception * if any problem occurs. */ protected abstract ApplicationHandle launchSpecific(Map arguments) throws Exception; /** * This method is called by launch() to verify that according to the * container, the application is launchable. * * @return true, if the application is launchable according to the * container, false otherwise. * * @throws IllegalStateException * if the application descriptor is unregistered */ protected abstract boolean isLaunchableSpecific(); /** * Schedules the application at a specified event. Schedule information * should not get lost even if the framework or the device restarts so it * should be stored in a persistent storage. The method registers a * {@link ScheduledApplication} service in Service Registry, representing * the created schedule. *

* The Map argument of the method contains startup * arguments for the application. The keys used in the Map must be non-null, * non-empty String objects. The argument values must be * of primitive types, wrapper classes of primitive types, String * or arrays or collections of these. *

* The created schedules have a unique identifier within the scope of this * ApplicationDescriptor. This identifier can be specified * in the scheduleId argument. If this argument is null, * the identifier is automatically generated. * * @param scheduleId * the identifier of the created schedule. It can be null, * in this case the identifier is automatically generated. * @param arguments * the startup arguments for the scheduled application, may be * null * @param topic * specifies the topic of the triggering event, it may contain a * trailing asterisk as wildcard, the empty string is treated as * "*", must not be null * @param eventFilter * specifies and LDAP filter to filter on the properties of the * triggering event, may be null * @param recurring * if the recurring parameter is false then the application will * be launched only once, when the event firstly occurs. If the * parameter is true then scheduling will take place for every * event occurrence; i.e. it is a recurring schedule * * @return the registered scheduled application service * * @throws NullPointerException * if the topic is null * @throws InvalidSyntaxException * if the specified eventFilter is not syntactically correct * @throws ApplicationException * if the schedule couldn't be created. The possible error * codes are *

    *
  • {@link ApplicationException#APPLICATION_DUPLICATE_SCHEDULE_ID} * if the specified scheduleId is already used * for this ApplicationDescriptor *
  • {@link ApplicationException#APPLICATION_SCHEDULING_FAILED} * if the scheduling failed due to some internal reason * (e.g. persistent storage error). *
  • {@link ApplicationException#APPLICATION_INVALID_STARTUP_ARGUMENT} * if the specified startup argument doesn't satisfy the * type or value constraints of startup arguments. *
* @throws SecurityException * if the caller doesn't have "schedule" * ApplicationAdminPermission for the application. * @throws IllegalStateException * if the application descriptor is unregistered * @throws IllegalArgumentException * if the specified Map contains invalid keys * (null objects, empty String or a key that is not * String) */ public final ScheduledApplication schedule(String scheduleId, Map arguments, String topic, String eventFilter, boolean recurring) throws InvalidSyntaxException, ApplicationException { isLaunchableSpecific(); // checks if the ApplicationDescriptor was already unregistered try { return delegate.schedule(scheduleId, arguments, topic, eventFilter, recurring); }catch( SecurityException se ) { isLaunchableSpecific(); /* check whether the bundle was uninstalled */ /* if yes, throws IllegalStateException */ throw se; /* otherwise throw the catched SecurityException */ } } /** * Sets the lock state of the application. If an application is locked then * launching a new instance is not possible. It does not affect the already * launched instances. * * @throws SecurityException * if the caller doesn't have "lock" ApplicationAdminPermission * for the application. * @throws IllegalStateException * if the application descriptor is unregistered */ public final void lock() { try { delegate.lock(); }catch( SecurityException se ) { isLaunchableSpecific(); /* check whether the bundle was uninstalled */ /* if yes, throws IllegalStateException */ throw se; /* otherwise throw the catched SecurityException */ } lockSpecific(); } /** * This method is used to notify the container implementation that the * corresponding application has been locked and it should update the * application.locked service property accordingly. * @throws IllegalStateException * if the application descriptor is unregistered */ protected abstract void lockSpecific(); /** * Unsets the lock state of the application. * * @throws SecurityException * if the caller doesn't have "lock" ApplicationAdminPermission * for the application. * @throws IllegalStateException * if the application descriptor is unregistered */ public final void unlock() { try { delegate.unlock(); }catch( SecurityException se ) { isLaunchableSpecific(); /* check whether the bundle was uninstalled */ /* if yes, throws IllegalStateException */ throw se; /* otherwise throw the catched SecurityException */ } unlockSpecific(); } /** * This method is used to notify the container implementation that the * corresponding application has been unlocked and it should update the * application.locked service property accordingly. * @throws IllegalStateException * if the application descriptor is unregistered */ protected abstract void unlockSpecific(); Delegate delegate; /** * This class will load the class named * by the org.osgi.vendor.application.ApplicationDescriptor and delegate * method calls to an instance of the class. */ static class Delegate { static String cName; static Class implementation; static Method setApplicationDescriptor; static Method isLocked; static Method lock; static Method unlock; static Method schedule; static Method launch; static { AccessController.doPrivileged(new PrivilegedAction() { public Object run() { cName = System.getProperty("org.osgi.vendor.application.ApplicationDescriptor"); if (cName == null) { throw new NoClassDefFoundError("org.osgi.vendor.application.ApplicationDescriptor property must be set"); } try { implementation = Class.forName(cName); } catch (ClassNotFoundException e) { throw new NoClassDefFoundError(e.toString()); } try { setApplicationDescriptor = implementation.getMethod("setApplicationDescriptor", new Class[] {ApplicationDescriptor.class, String.class}); isLocked = implementation.getMethod("isLocked", new Class[] {}); lock = implementation.getMethod("lock", new Class[] {}); unlock = implementation.getMethod("unlock", new Class[] {}); schedule = implementation.getMethod("schedule", new Class[] {String.class, Map.class, String.class, String.class, boolean.class}); launch = implementation.getMethod("launch", new Class[] {Map.class}); } catch (NoSuchMethodException e) { throw new NoSuchMethodError(e.toString()); } return null; } }); } Object target; Delegate() throws Exception { target = AccessController.doPrivileged(new PrivilegedExceptionAction() { public Object run() throws Exception { return implementation.newInstance(); } }); } void setApplicationDescriptor(ApplicationDescriptor d, String pid ) { try { try { setApplicationDescriptor.invoke(target, new Object[] {d, pid}); } catch (InvocationTargetException e) { throw e.getTargetException(); } } catch (Error e) { throw e; } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException(e); } } boolean isLocked() { try { try { return ((Boolean)isLocked.invoke(target, new Object[] {})).booleanValue(); } catch (InvocationTargetException e) { throw e.getTargetException(); } } catch (Error e) { throw e; } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException(e); } } void lock() { try { try { lock.invoke(target, new Object[] {}); } catch (InvocationTargetException e) { throw e.getTargetException(); } } catch (Error e) { throw e; } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException(e); } } void unlock() { try { try { unlock.invoke(target, new Object[] {}); } catch (InvocationTargetException e) { throw e.getTargetException(); } } catch (Error e) { throw e; } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException(e); } } ScheduledApplication schedule(String scheduleId, Map args, String topic, String filter, boolean recurs) throws InvalidSyntaxException, ApplicationException { try { try { return (ScheduledApplication)schedule.invoke(target, new Object[] {scheduleId, args, topic, filter, new Boolean(recurs)}); } catch (InvocationTargetException e) { throw e.getTargetException(); } } catch (InvalidSyntaxException e) { throw e; } catch (ApplicationException e) { throw e; } catch (Error e) { throw e; } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException(e); } } void launch(Map arguments) throws ApplicationException { try { try { launch.invoke(target, new Object[] {arguments}); } catch (InvocationTargetException e) { throw e.getTargetException(); } } catch (ApplicationException e) { throw e; } catch (Error e) { throw e; } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException(e); } } } }