| /* |
| * 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) { |
| |
| } |
| |
| } |
| } |