/*
 * Copyright (c) OSGi Alliance (2004, 2010). 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.Array;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.equinox.internal.app.AppPersistence;
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: 1.13 $
/** * An OSGi service that represents an installed application and stores * information about it. The application descriptor can be used for instance * creation. * * @version $Revision: 1.13 $ */
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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * The property key for the location of the application. */
public static final String APPLICATION_LOCATION = "application.location"; private final String pid; private final boolean[] locked = {false};
Constructs the ApplicationDescriptor.
Params:
  • 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:
/** * Constructs the {@code ApplicationDescriptor}. * * @param applicationId * The identifier of the application. Its value is also available * as the {@code service.pid} service property of this * {@code ApplicationDescriptor} service. This parameter must not * be {@code null}. * @throws NullPointerException if the specified {@code applicationId} is null. */
protected ApplicationDescriptor(String applicationId) { if (null == applicationId) { throw new NullPointerException("Application ID must not be null!"); } this.pid = applicationId; locked[0] = isPersistentlyLocked(); }
Returns the identifier of the represented application.
Returns:the identifier of the represented application
/** * 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 ApplicationAdminPermission for signer attributes.

This method is used by ApplicationAdminPermission.implies(Permission) method to match target ApplicationDescriptor and filter.

Params:
  • pattern – a pattern for a chain of Distinguished Names. It must not be null.
Throws:
Returns:true if the specified pattern matches at least one of the certificate chains used to authenticate this application
/** * This method verifies whether the specified {@code pattern} * matches the Distinguished Names of any of the certificate chains * used to authenticate this application. * <P> * The {@code pattern} must adhere to the * syntax defined in {@link org.osgi.service.application.ApplicationAdminPermission} * for signer attributes. * <p> * This method is used by {@link ApplicationAdminPermission#implies(java.security.Permission)} method * to match target {@code ApplicationDescriptor} and filter. * * @param pattern a pattern for a chain of Distinguished Names. It must not be null. * @return {@code true} if the specified pattern matches at least * one of the certificate chains used to authenticate this application * @throws NullPointerException if the specified {@code 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 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.

Params:
  • 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.
Throws:
Returns: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.)
/** * 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 {@code Map} will include the service * properties of this {@code ApplicationDescriptor} as well. * <p> * This method will call the {@code getPropertiesSpecific} method * to enable the container implementation to insert application model and/or * container implementation specific properties. * <P> * 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 * ({@code ""})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); Boolean containerLocked = (Boolean) props.remove(APPLICATION_LOCKED); synchronized (locked) { if (containerLocked != null && containerLocked.booleanValue() != locked[0]) { if (locked[0]) lockSpecific(); else unlockSpecific(); } } /* replace the container's lock with the application model's lock, that's the correct */ props.put(APPLICATION_LOCKED, Boolean.valueOf(locked[0])); 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 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.
Params:
  • 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.
Throws:
Returns:the application model specific and/or container implementation specific properties of this application descriptor.
/** * 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 * {@code locale} argument is not the empty String. The value * {@code 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 {@code 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 {@code null} the default locale should be used. If it is * the empty String ({@code ""}) 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 ApplicationException with the reason code 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.

Params:
  • arguments – Arguments for the newly launched application, may be null
Throws:
Returns:the registered ApplicationHandle, which represents the newly launched application instance. Never returns null.
/** * Launches a new instance of an application. The {@code args} parameter * specifies the startup parameters for the instance to be launched, it may * be null. * <p> * The following steps are made: * <UL> * <LI>Check for the appropriate permission. * <LI>Check the locking state of the application. If locked then throw an * {@link ApplicationException} with the reason code * {@link ApplicationException#APPLICATION_LOCKED}. * <LI>Calls the {@code launchSpecific()} method to create and start an * application instance. * <LI>Returns the {@code ApplicationHandle} returned by the * launchSpecific() * </UL> * The caller has to have ApplicationAdminPermission(applicationPID, * "launch") in order to be able to perform this operation. * <P> * The {@code Map} argument of the launch method contains startup arguments * for the application. The keys used in the Map must be non-null, non-empty * {@code String} objects. They can be standard or application specific. * OSGi defines the {@code 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: * <ul> * <li>The keys starting with the dash (-) character are application * specific, no well-known meaning should be associated with them.</li> * <li>Well-known keys should follow the reverse domain name based naming. * In particular, the keys standardized in OSGi should start with * {@code org.osgi.}.</li> * </ul> * <P> * The method is synchronous, it return only when the application instance * was successfully started or the attempt to start it failed. * <P> * This method never returns {@code 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 {@code 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 {@code Map} contains * invalid keys (null objects, empty {@code String} or a key that is * not {@code String}) */
public final ApplicationHandle launch(Map arguments) throws ApplicationException { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(new ApplicationAdminPermission(this, ApplicationAdminPermission.LIFECYCLE_ACTION)); synchronized (locked) { if (locked[0]) throw new ApplicationException(ApplicationException.APPLICATION_LOCKED, "Application is locked, can't launch!"); } if (!isLaunchableSpecific()) throw new ApplicationException(ApplicationException.APPLICATION_NOT_LAUNCHABLE, "Cannot launch the application!"); checkArgs(arguments, false); try { return launchSpecific(arguments); } catch (IllegalStateException | SecurityException | ApplicationException ise) { throw ise; } 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.

Params:
  • arguments – the startup parameters of the new application instance, may be null
Throws:
Returns:the registered application model specific application handle for the newly created and started instance.
/** * 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. * <P> * This method must not return {@code 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.
Throws:
Returns:true, if the application is launchable according to the container, false otherwise.
/** * 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 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.

Params:
  • scheduleId – the identifier of the created schedule. It can be null, in this case the identifier is automatically generated.
  • arguments – the startup arguments for the scheduled application, may be null
  • 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
  • eventFilter – specifies and LDAP filter to filter on the properties of the triggering event, may be null
  • 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
Throws:
Returns:the registered scheduled application service
/** * 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. * <p> * The {@code Map} argument of the method contains startup arguments for the * application. The keys used in the Map must be non-null, non-empty * {@code String} objects. The argument values must be of primitive types, * wrapper classes of primitive types, {@code String} or arrays or * collections of these. * <p> * The created schedules have a unique identifier within the scope of this * {@code ApplicationDescriptor}. This identifier can be specified in the * {@code scheduleId} argument. If this argument is {@code null}, the * identifier is automatically generated. * * @param scheduleId the identifier of the created schedule. It can be * {@code 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 {@code null} * @throws InvalidSyntaxException if the specified {@code eventFilter} is * not syntactically correct * @throws ApplicationException if the schedule couldn't be created. The * possible error codes are * <ul> * <li> * {@link ApplicationException#APPLICATION_DUPLICATE_SCHEDULE_ID} if * the specified {@code scheduleId} is already used for this * {@code ApplicationDescriptor} <li> * {@link ApplicationException#APPLICATION_SCHEDULING_FAILED} if the * scheduling failed due to some internal reason (e.g. persistent * storage error). <li> * {@link ApplicationException#APPLICATION_INVALID_STARTUP_ARGUMENT} * if the specified startup argument doesn't satisfy the type or * value constraints of startup arguments. * </ul> * @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 {@code Map} contains * invalid keys (null objects, empty {@code String} or a key that is * not {@code String}) */
public final ScheduledApplication schedule(String scheduleId, Map arguments, String topic, String eventFilter, boolean recurring) throws InvalidSyntaxException, ApplicationException { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(new ApplicationAdminPermission(this, ApplicationAdminPermission.SCHEDULE_ACTION)); arguments = checkArgs(arguments, true); isLaunchableSpecific(); // checks if the ApplicationDescriptor was already unregistered return AppPersistence.addScheduledApp(this, scheduleId, arguments, topic, eventFilter, recurring); }
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.
  • IllegalStateException – if the application descriptor is unregistered
/** * 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() { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(new ApplicationAdminPermission(this, ApplicationAdminPermission.LOCK_ACTION)); synchronized (locked) { if (locked[0]) return; locked[0] = true; lockSpecific(); saveLock(true); } }
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:
/** * This method is used to notify the container implementation that the * corresponding application has been locked and it should update the * {@code 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.
  • IllegalStateException – if the application descriptor is unregistered
/** * 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() { SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(new ApplicationAdminPermission(this, ApplicationAdminPermission.LOCK_ACTION)); synchronized (locked) { if (!locked[0]) return; locked[0] = false; unlockSpecific(); saveLock(false); } }
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:
/** * This method is used to notify the container implementation that the * corresponding application has been unlocked and it should update the * {@code application.locked} service property accordingly. * @throws IllegalStateException * if the application descriptor is unregistered */
protected abstract void unlockSpecific(); private void saveLock(boolean locked) { AppPersistence.saveLock(this, locked); } private boolean isPersistentlyLocked() { return AppPersistence.isLocked(this); } private static final Collection scalars = Arrays.asList(new Class[] {String.class, Integer.class, Long.class, Float.class, Double.class, Byte.class, Short.class, Character.class, Boolean.class}); private static final Collection scalarsArrays = Arrays.asList(new Class[] {String[].class, Integer[].class, Long[].class, Float[].class, Double[].class, Byte[].class, Short[].class, Character[].class, Boolean[].class}); private static final Collection primitiveArrays = Arrays.asList(new Class[] {long[].class, int[].class, short[].class, char[].class, byte[].class, double[].class, float[].class, boolean[].class}); private static Map checkArgs(Map arguments, boolean validateValues) throws ApplicationException { if (arguments == null) return arguments; Map copy = validateValues ? new HashMap() : null; for (Iterator entries = arguments.entrySet().iterator(); entries.hasNext();) { Map.Entry entry = (Entry) entries.next(); if (!(entry.getKey() instanceof String)) throw new IllegalArgumentException("Invalid key type: " + entry.getKey() == null ? "<null>" : entry.getKey().getClass().getName()); if ("".equals(entry.getKey())) //$NON-NLS-1$ throw new IllegalArgumentException("Empty string is an invalid key"); if (validateValues) validateValue(entry, copy); } return validateValues ? copy : arguments; } private static void validateValue(Map.Entry entry, Map copy) throws ApplicationException { Class clazz = entry.getValue().getClass(); // Is it in the set of scalar types if (scalars.contains(clazz)) { copy.put(entry.getKey(), entry.getValue()); return; } // Is it an array of primitives or scalars if (scalarsArrays.contains(clazz) || primitiveArrays.contains(clazz)) { int arrayLength = Array.getLength(entry.getValue()); Object copyOfArray = Array.newInstance(entry.getValue().getClass().getComponentType(), arrayLength); System.arraycopy(entry.getValue(), 0, copyOfArray, 0, arrayLength); copy.put(entry.getKey(), copyOfArray); return; } // Is it a Collection of scalars if (entry.getValue() instanceof Collection) { Collection valueCollection = (Collection) entry.getValue(); for (Iterator it = valueCollection.iterator(); it.hasNext();) { Class containedClazz = it.next().getClass(); if (!scalars.contains(containedClazz)) { throw new ApplicationException(ApplicationException.APPLICATION_INVALID_STARTUP_ARGUMENT, "The value for key \"" + entry.getKey() + "\" is a collection that contains an invalid value of type \"" + containedClazz.getName() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } copy.put(entry.getKey(), new ArrayList((Collection) entry.getValue())); return; } throw new ApplicationException(ApplicationException.APPLICATION_INVALID_STARTUP_ARGUMENT, "The value for key \"" + entry.getKey() + "\" is an invalid type \"" + clazz.getName() + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } }