blob: a0bdc8957fc7fa374e47245c024d3f224f5cbe27 [file] [log] [blame]
/*
* $Header: /cvshome/build/org.osgi.service.application/src/org/osgi/service/application/ApplicationHandle.java,v 1.41 2006/07/10 12:02:31 hargrave Exp $
*
* Copyright (c) OSGi Alliance (2004, 2006). 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.*;
import org.osgi.framework.Constants;
/**
* ApplicationHandle is an OSGi service interface which represents an instance
* of an application. It provides the functionality to query and manipulate the
* lifecycle state of the represented application instance. It defines constants
* for the lifecycle states.
*/
public abstract class ApplicationHandle {
/*
* 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 unique identifier (PID) of the application
* instance.
*/
public static final String APPLICATION_PID = Constants.SERVICE_PID;
/**
* The property key for the pid of the corresponding application descriptor.
*/
public final static String APPLICATION_DESCRIPTOR = "application.descriptor";
/**
* The property key for the state of this appliction instance.
*/
public final static String APPLICATION_STATE = "application.state";
/**
* The application instance is running. This is the initial state of a newly
* created application instance.
*/
public final static String RUNNING = "RUNNING";
/**
* The application instance is being stopped. This is the state of the
* application instance during the execution of the <code>destroy()</code>
* method.
*/
public final static String STOPPING = "STOPPING";
private final String instanceId;
private final ApplicationDescriptor descriptor;
/**
* Application instance identifier is specified by the container when the
* instance is created. The instance identifier must remain static for the
* lifetime of the instance, it must remain the same even across framework
* restarts for the same application instance. This value must be the same
* as the <code>service.pid</code> service property of this application
* handle.
* <p>
* The instance identifier should follow the following scheme:
* &lt;<i>application descriptor PID</i>&gt;.&lt;<i>index</i>&gt;
* where &lt;<i>application descriptor PID</i>&gt; is the PID of the
* corresponding <code>ApplicationDescriptor</code> and &lt;<i>index</i>&gt;
* is a unique integer index assigned by the application container.
* Even after destroying the application index the same index value should not
* be reused in a reasonably long timeframe.
*
* @param instanceId the instance identifier of the represented application
* instance. It must not be null.
*
* @param descriptor the <code>ApplicationDescriptor</code> of the represented
* application instance. It must not be null.
*
* @throws NullPointerException if any of the arguments is null.
*/
protected ApplicationHandle(String instanceId, ApplicationDescriptor descriptor ) {
if( (null == instanceId) || (null == descriptor) ) {
throw new NullPointerException("Parameters must not be null!");
}
this.instanceId = instanceId;
this.descriptor = descriptor;
try {
delegate = new Delegate();
delegate.setApplicationHandle( this, descriptor.delegate );
}
catch (Exception e) {
// Too bad ...
e.printStackTrace();
System.err
.println("No implementation available for ApplicationDescriptor, property is: "
+ Delegate.cName);
}
}
/**
* Retrieves the <code>ApplicationDescriptor</code> to which this
* <code>ApplicationHandle</code> belongs.
*
* @return The corresponding <code>ApplicationDescriptor</code>
*/
public final ApplicationDescriptor getApplicationDescriptor() {
return descriptor;
}
/**
* Get the state of the application instance.
*
* @return the state of the application.
*
* @throws IllegalStateException
* if the application handle is unregistered
*/
public abstract String getState();
/**
* Returns the unique identifier of this instance. This value is also
* available as a service property of this application handle's service.pid.
*
* @return the unique identifier of the instance
*/
public final String getInstanceId() {
return instanceId;
}
/**
* The application instance's lifecycle state can be influenced by this
* method. It lets the application instance perform operations to stop
* the application safely, e.g. saving its state to a permanent storage.
* <p>
* The method must check if the lifecycle transition is valid; a STOPPING
* application cannot be stopped. If it is invalid then the method must
* exit. Otherwise the lifecycle state of the application instance must be
* set to STOPPING. Then the destroySpecific() method must be called to
* perform any application model specific steps for safe stopping of the
* represented application instance.
* <p>
* At the end the <code>ApplicationHandle</code> must be unregistered.
* This method should free all the resources related to this
* <code>ApplicationHandle</code>.
* <p>
* When this method is completed the application instance has already made
* its operations for safe stopping, the ApplicationHandle has been
* unregistered and its related resources has been freed. Further calls on
* this application should not be made because they may have unexpected
* results.
*
* @throws SecurityException
* if the caller doesn't have "lifecycle"
* <code>ApplicationAdminPermission</code> for the corresponding application.
*
* @throws IllegalStateException
* if the application handle is unregistered
*/
public final void destroy() {
try {
delegate.destroy();
}catch( SecurityException se ) {
descriptor.isLaunchableSpecific(); /* check whether the bundle was uninstalled */
/* if yes, throws IllegalStateException */
throw se; /* otherwise throw the catched SecurityException */
}
destroySpecific();
}
/**
* Called by the destroy() method to perform application model specific
* steps to stop and destroy an application instance safely.
*
* @throws IllegalStateException
* if the application handle is unregistered
*/
protected abstract void destroySpecific();
Delegate delegate;
/**
* This class will load the class named
* by the org.osgi.vendor.application.ApplicationHandle and delegate
* method calls to an instance of the class.
*/
static class Delegate {
static String cName;
static Class implementation;
static Method setApplicationHandle;
static Method destroy;
static {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run(){
cName = System.getProperty("org.osgi.vendor.application.ApplicationHandle");
if (cName == null) {
throw new NoClassDefFoundError("org.osgi.vendor.application.ApplicationHandle property must be set");
}
try {
implementation = Class.forName(cName);
}
catch (ClassNotFoundException e) {
throw new NoClassDefFoundError(e.toString());
}
try {
setApplicationHandle = implementation.getMethod("setApplicationHandle",
new Class[] {ApplicationHandle.class, Object.class});
destroy = implementation.getMethod("destroy",
new 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 setApplicationHandle(ApplicationHandle d, ApplicationDescriptor.Delegate descriptor ) {
try {
try {
setApplicationHandle.invoke(target, new Object[] {d, descriptor.target});
}
catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
catch (Error e) {
throw e;
}
catch (RuntimeException e) {
throw e;
}
catch (Throwable e) {
throw new RuntimeException(e.toString());
}
}
void destroy() {
try {
try {
destroy.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.toString());
}
}
}
}