blob: 9d5ad175e0ad346d1b02078a4015d6894a306bb5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/* TODO: - Complete deployment of items that are in the deployment
directory.
*/
package org.apache.river.container.deployer;
import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.security.CodeSource;
import java.security.Permission;
import java.security.Principal;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.jini.security.policy.DynamicPolicyProvider;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileType;
import org.apache.river.container.ConfigurationException;
import org.apache.river.container.Context;
import org.apache.river.container.FileUtility;
import org.apache.river.container.Init;
import org.apache.river.container.Injected;
import org.apache.river.container.InjectionStyle;
import org.apache.river.container.LocalizedRuntimeException;
import org.apache.river.container.MessageNames;
import org.apache.river.container.Name;
import org.apache.river.container.PropertiesFileReader;
import org.apache.river.container.Strings;
import org.apache.river.container.Utils;
import org.apache.river.container.classloading.ClasspathFilter;
import org.apache.river.container.classloading.VirtualFileSystemClassLoader;
import org.apache.river.container.codebase.CodebaseContext;
import org.apache.river.container.codebase.CodebaseHandler;
import org.apache.river.container.el.ArgsParser;
import org.apache.river.container.liaison.VirtualFileSystemConfiguration;
import org.apache.river.container.work.ContextualWorkManager;
import org.apache.river.container.work.WorkManager;
/**
* Deployer that instantiates applications or services based on the
* com.sun.jini.starter API
*/
public class StarterServiceDeployer implements StarterServiceDeployerMXBean {
private static final Logger log =
Logger.getLogger(StarterServiceDeployer.class.getName(), MessageNames.BUNDLE_NAME);
@Injected(style = InjectionStyle.BY_TYPE)
private FileUtility fileUtility = null;
@Injected(style = InjectionStyle.BY_TYPE)
private Context context;
@Name
private String myName = null;
@Injected(style = InjectionStyle.BY_TYPE)
private CodebaseHandler codebaseHandler = null;
private String config = Strings.STARTER_SERVICE_DEPLOYER_CONFIG;
private ASTconfig configNode = null;
public String getConfig() {
return config;
}
public void setConfig(String config) {
this.config = config;
}
@Injected(style = InjectionStyle.BY_TYPE)
private PropertiesFileReader propertiesFileReader = null;
@Injected(style = InjectionStyle.BY_TYPE)
private ArgsParser argsParser = null;
@Injected(style = InjectionStyle.BY_TYPE)
WorkManager workManager = null;
@Injected(style=InjectionStyle.BY_TYPE)
ContextualWorkManager contextualWorkManager=null;
@Injected(style = InjectionStyle.BY_TYPE)
private DynamicPolicyProvider securityPolicy = null;
public void addPlatformCodebaseJars(CodebaseContext codebaseContext) throws IOException {
ASTcodebase codebaseNode = (ASTcodebase) configNode.search(new Class[]{
ASTconfig.class, ASTclassloader.class, ASTcodebase.class
}).get(0);
/*
Register the platform codebase jars with the codebase service.
*/
for (int i = 0; i < codebaseNode.jjtGetNumChildren(); i++) {
String jarFile = codebaseNode.jjtGetChild(i).toString();
FileObject fo = fileUtility.getLibDirectory().resolveFile(jarFile);
codebaseContext.addFile(fo);
log.log(Level.FINE, MessageNames.ADDED_PLATFORM_CODEBASE_JAR,
jarFile);
}
}
public String[] constructArgs(String argLine) {
String[] args = null;
if (argLine == null) {
args = new String[0];
} else {
args = argsParser.toArgs(argLine);
}
return args;
}
public VirtualFileSystemClassLoader createServiceClassloader(FileObject serviceRoot, CodeSource codeSource) throws IOException, FileSystemException {
String parentLoaderName = configNode.search(
new Class[]{ASTconfig.class, ASTclassloader.class, ASTparent.class}).get(0).jjtGetChild(0).toString();
log.log(Level.FINE, MessageNames.SERVICE_PARENT_CLASSLOADER_IS, parentLoaderName);
ClassLoader parentLoader = (ClassLoader) context.get(parentLoaderName);
VirtualFileSystemClassLoader cl =
createChildOfGivenClassloader(parentLoader, codeSource);
/*
Include platform jars from the container's lib directory.
*/
ASTclasspath platformJarSpec = (ASTclasspath) configNode.search(new Class[]{ASTconfig.class,
ASTclassloader.class, ASTjars.class, ASTclasspath.class}).get(0);
addPlatformJarsToClassloader(platformJarSpec, cl);
addLibDirectoryJarsToClasspath(serviceRoot, cl);
return cl;
}
protected void addLibDirectoryJarsToClasspath(FileObject serviceRoot, VirtualFileSystemClassLoader cl) throws FileSystemException {
/*
Add the jar files from the service's 'lib' directory.
*/
FileObject libDir = serviceRoot.resolveFile(Strings.LIB);
List<FileObject> jarFiles = Utils.findChildrenWithSuffix(libDir,
Strings.DOT_JAR);
for (FileObject jarFile : jarFiles) {
cl.addClassPathEntry(libDir, jarFile.getName().getBaseName());
}
}
protected void addPlatformJarsToClassloader(ASTclasspath platformJarSpec, VirtualFileSystemClassLoader cl) throws IOException, LocalizedRuntimeException {
log.log(Level.FINE, MessageNames.ADDING_CLASSPATH_ENTRY, new Object[]{platformJarSpec.toString()});
List<ClasspathFilter> filters = ClasspathFilterBuilder.filtersFromClasspathExpression(platformJarSpec);
cl.addClasspathFilters(filters, fileUtility.getLibDirectory());
}
protected VirtualFileSystemClassLoader createChildOfGivenClassloader(ClassLoader parent, CodeSource codeSource) {
/*
Create the service classloader.
*/
VirtualFileSystemClassLoader cl =
new VirtualFileSystemClassLoader(null, parent, codeSource);
return cl;
}
public void exportServiceCodebaseJars(FileObject serviceRoot, CodebaseContext codebaseContext) throws FileSystemException {
/*
Register the service's codebase jars with the codebase service.
*/
FileObject libDlDir = serviceRoot.resolveFile(Strings.LIB_DL);
List<FileObject> dljarFiles = Utils.findChildrenWithSuffix(libDlDir,
Strings.DOT_JAR);
for (FileObject jarFile : dljarFiles) {
codebaseContext.addFile(jarFile);
}
}
@Init
public void init() {
try {
tryInitialize();
} catch (Throwable ex) {
log.log(Level.SEVERE, MessageNames.STARTER_SERVICE_DEPLOYER_FAILED_INIT,
ex);
throw new ConfigurationException(ex,
MessageNames.STARTER_SERVICE_DEPLOYER_FAILED_INIT);
}
}
public void launchService(final ApplicationEnvironment env, Properties startProps, final String[] args) {
final String startClassName = startProps.getProperty(Strings.START_CLASS);
/*
Launch the service.
*/
log.log(Level.FINE, MessageNames.CALLING_MAIN, new Object[]{
startClassName, Utils.format(args)
});
Runnable task = new Runnable() {
@Override
public void run() {
try {
env.setServiceInstance(instantiateService(env.getClassLoader(), startClassName, args));
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
};
env.getWorkingContext().getWorkManager().queueTask(env.getClassLoader(), task);
}
public Properties readStartProperties(FileObject serviceRoot) throws FileSystemException, LocalizedRuntimeException, IOException {
/*
Read the start.properties file.
*/
FileObject startProperties = serviceRoot.resolveFile(Strings.START_PROPERTIES);
if (startProperties == null || !startProperties.getType().equals(FileType.FILE)
|| !startProperties.isReadable()) {
throw new LocalizedRuntimeException(MessageNames.BUNDLE_NAME,
MessageNames.CANT_READ_START_PROPERTIES,
new Object[]{Strings.START_PROPERTIES,
serviceRoot.getName().getBaseName()});
}
Properties startProps = propertiesFileReader.getProperties(startProperties);
return startProps;
}
public void setupLiaisonConfiguration(FileObject serviceArchive, FileObject serviceRoot, VirtualFileSystemClassLoader cl) throws ConfigurationException {
/*
Setup the liaison configuration.
*/
try {
File workingDir = null;
if (serviceArchive != null) {
workingDir = new File(serviceArchive.getURL().toURI());
} else {
workingDir = new File(serviceRoot.getURL().toURI());
}
grantPermissions(cl,
new Permission[]{new FilePermission(workingDir.getAbsolutePath(), Strings.READ)});
Utils.logClassLoaderHierarchy(log, Level.FINE, this.getClass());
String configName = VirtualFileSystemConfiguration.class.getName();
invokeStatic(cl, configName,
Strings.SET_WORKING_DIRECTORY,
workingDir);
/*
Setup the "special" variables in the configuration.
*/
ASTconfiguration configurationNode = (ASTconfiguration) configNode.search(new Class[]{ASTconfig.class, ASTconfiguration.class}).get(0);
for (int i = 0; i < configurationNode.jjtGetNumChildren(); i++) {
ASTconfigEntry cfgEntryNode = (ASTconfigEntry) configurationNode.jjtGetChild(i);
String varName = cfgEntryNode.jjtGetChild(0).toString();
String contextVarName = cfgEntryNode.jjtGetChild(1).toString();
Object contextValue = context.get(contextVarName);
if (contextValue != null) {
invokeStatic(cl, configName,
Strings.PUT_SPECIAL_ENTRY,
new Class[] {String.class, Object.class},
Strings.DOLLAR + varName, contextValue);
} else {
log.log(Level.WARNING, MessageNames.MISSING_SPECIAL_VALUE,
new Object[] {getConfig(), varName, contextVarName});
}
}
} catch (Exception ex) {
log.log(Level.WARNING, MessageNames.EXCEPTION_THROWN, Utils.stackTrace(ex));
throw new ConfigurationException(ex,
MessageNames.STARTER_SERVICE_DEPLOYER_FAILED_INIT);
}
}
private void tryInitialize() throws IOException, ParseException {
log.log(Level.FINE, MessageNames.STARTER_SERVICE_DEPLOYER_STARTING, myName);
/*
Read and parse the configuration file.
*/
FileObject configFile = fileUtility.getProfileDirectory().resolveFile(config);
InputStream in = configFile.getContent().getInputStream();
configNode = DeployerConfigParser.parseConfig(in);
log.log(Level.FINE,MessageNames.STARTER_SERVICE_DEPLOYER_INITIALIZED,
new Object[] {myName} );
}
public ServiceLifeCycle deployServiceArchive(FileObject serviceArchive) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
// Create an application environment
ApplicationEnvironment env = new ApplicationEnvironment();
env.setServiceArchive(serviceArchive);
env.setServiceRoot(
serviceArchive.getFileSystem().getFileSystemManager().createFileSystem(Strings.JAR, serviceArchive));
String serviceName = findServiceName(env.getServiceArchive(), env.getServiceRoot());
env.setServiceName(serviceName);
ServiceLifeCycle slc=StarterServiceLifeCycleSM.newStarterServiceLifeCycle(env, this);
return slc;
}
private String findServiceName(FileObject serviceArchive, FileObject serviceRoot) {
if (serviceArchive != null) {
return serviceArchive.getName().getBaseName();
}
return serviceRoot.getName().getBaseName();
}
private URL findServiceURL(FileObject serviceArchive, FileObject serviceRoot) throws FileSystemException {
if (serviceArchive != null) {
return serviceArchive.getURL();
}
return serviceRoot.getURL();
}
void prepareService(ApplicationEnvironment env) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
CodeSource serviceCodeSource =
new CodeSource(findServiceURL(env.getServiceArchive(), env.getServiceRoot()),
new Certificate[0]);
log.log(Level.INFO, MessageNames.CODESOURCE_IS,
new Object[]{env.getServiceName(), serviceCodeSource});
VirtualFileSystemClassLoader cl = createServiceClassloader(env.getServiceRoot(), serviceCodeSource);
env.setClassLoader(cl);
/*
Create a codebase context.
*/
CodebaseContext codebaseContext =
codebaseHandler.createContext(env.getServiceName());
env.setCodebaseContext(codebaseContext);
addPlatformCodebaseJars(codebaseContext);
exportServiceCodebaseJars(env.getServiceRoot(), codebaseContext);
/*
Setup the classloader's codebase annotation.
*/
cl.setCodebase(codebaseContext.getCodebaseAnnotation());
/*
Grant the appropriate permissions to the service's classloader and
protection domain.
*/
Permission[] perms = createPermissionsInClassloader(cl);
grantPermissions(cl, perms);
setupLiaisonConfiguration(env.getServiceArchive(), env.getServiceRoot(), cl);
/*
* Create a working context (work manager).
*/
env.setWorkingContext(contextualWorkManager.createContext(env.getServiceName()));
}
void launchService(ApplicationEnvironment env) throws FileSystemException, IOException {
Properties startProps = readStartProperties(env.getServiceRoot());
String argLine = startProps.getProperty(Strings.START_PARAMETERS);
final String[] args = constructArgs(argLine);
launchService(env, startProps, args);
log.log(Level.INFO, MessageNames.COMPLETED_SERVICE_DEPLOYMENT, env.getServiceName());
}
Permission[] createPermissionsInClassloader(ClassLoader cl) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
List<Permission> perms = new ArrayList<Permission>();
// Get all the permission nodes from the config.
Class[] path = new Class[]{ASTconfig.class, ASTgrant.class, ASTpermission.class};
List<ASTNode> permNodes = configNode.search(path);
// Create a permission for each
for (ASTNode node : permNodes) {
String className = (String) ((ASTsymbol) node.jjtGetChild(0)).getValue();
Object permissionConstructorArgs[] = new String[node.jjtGetNumChildren() - 1];
for (int i = 0; i < permissionConstructorArgs.length; i++) {
permissionConstructorArgs[i] = (String) ((ASTliteral) node.jjtGetChild(i + 1)).getValue();
}
Permission perm = (Permission) invokeConstructor(cl, className, permissionConstructorArgs);
perms.add(perm);
}
return perms.toArray(new Permission[0]);
}
private Object invokeStatic(ClassLoader cl, String className, String methodName,
Object... parms) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class clazz = Class.forName(className, true, cl);
Class[] parameterTypes = new Class[parms.length];
for (int i = 0; i < parms.length; i++) {
parameterTypes[i] = parms[i].getClass();
}
Method method = clazz.getMethod(methodName, parameterTypes);
return method.invoke(null, parms);
}
private Object invokeStatic(ClassLoader cl, String className, String methodName, Class[] argTypes,
Object... parms) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Class clazz = Class.forName(className, true, cl);
Method method = clazz.getMethod(methodName, argTypes);
return method.invoke(null, parms);
}
private Object invokeConstructor(ClassLoader cl, String className,
Object... parms) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName(className, true, cl);
Class[] parameterTypes = new Class[parms.length];
for (int i = 0; i < parms.length; i++) {
parameterTypes[i] = parms[i].getClass();
}
Constructor method = clazz.getConstructor(parameterTypes);
return method.newInstance(parms);
}
private void grantPermissions(ClassLoader cl, Permission[] perms) {
try {
Class clazz = Class.forName(VirtualFileSystemConfiguration.class.getName(), true, cl);
securityPolicy.grant(clazz, new Principal[0], perms);
} catch (Throwable t) {
throw new ConfigurationException(MessageNames.FAILED_DEPLOY_SERVICE, t);
}
}
private Object instantiateService(ClassLoader cl, String className, String[] parms)
throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, InstantiationException {
Class clazz = Class.forName(className, true, cl);
log.log(Level.FINE, MessageNames.CLASSLOADER_IS,
new Object[]{clazz.getName(), clazz.getClassLoader().toString()});
// Get this through dynamic lookup becuase it won't be in the parent
// classloader!
Class lifeCycleClass = Class.forName(Strings.LIFECYCLE_CLASS, true, cl);
Constructor[] constructors = clazz.getDeclaredConstructors();
System.out.println("Class is " + clazz);
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i];
System.out.println("Found constructor " + constructor + " on " + className);
}
Constructor constructor = clazz.getDeclaredConstructor(new Class[]{String[].class, lifeCycleClass});
constructor.setAccessible(true);
return constructor.newInstance(parms, null);
}
/**
* Attempt to stop the service in an orderly fashion.
* Go to the service, see if it implements Administrable, then get the
* admin proxy and see if it implements DestroyAdmin. If so, call it.
* @param env
*/
public void stopService(ApplicationEnvironment env) {
/* Option 1 - Service has a getAdmin() method - it probably implements
* Administrable.
*/
Object serviceInstance=env.getServiceInstance();
Method getAdmin=null;
try {
getAdmin=serviceInstance.getClass().getMethod(Strings.GET_ADMIN, new Class[0]);
} catch (Exception ex) {
// Silent catch - leave it null;
}
if (getAdmin != null) {
}
}
}