Added property setting to components called out in config.xml Completed monitoring of deployment directory - services now auto-deploy while container is live.
Project: http://git-wip-us.apache.org/repos/asf/river-container/repo Commit: http://git-wip-us.apache.org/repos/asf/river-container/commit/60c5c7fe Tree: http://git-wip-us.apache.org/repos/asf/river-container/tree/60c5c7fe Diff: http://git-wip-us.apache.org/repos/asf/river-container/diff/60c5c7fe Branch: refs/heads/master Commit: 60c5c7fe71d7a61b7f4d97688914cb708bef24f5 Parents: 42f2ef2 Author: Greg Trasuk <tras...@trasuk.com> Authored: Thu Nov 28 02:37:51 2013 -0500 Committer: Greg Trasuk <tras...@trasuk.com> Committed: Thu Nov 28 02:37:51 2013 -0500 ---------------------------------------------------------------------- product/src/main/root/bin/logging.properties | 3 +- .../src/main/root/profile/client/client-app.cfg | 95 +++++++++ product/src/main/root/profile/client/config.xml | 4 +- .../root/profile/client/service-starter.cfg | 95 --------- .../src/main/root/profile/default/config.xml | 3 +- .../root/profile/default/service-starter.cfg | 2 +- .../org/apache/river/container/Bootstrap.java | 103 ++++++--- .../apache/river/container/MessageNames.java | 11 +- .../deployer/StarterServiceDeployer.java | 51 +++-- .../deployer/StarterServiceLifeCycleSM.java | 1 + .../container/deployer/StartupDeployer.java | 211 +++++++++++++++---- .../apache/river/container/Messages.properties | 9 + 12 files changed, 401 insertions(+), 187 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/product/src/main/root/bin/logging.properties ---------------------------------------------------------------------- diff --git a/product/src/main/root/bin/logging.properties b/product/src/main/root/bin/logging.properties index 62fab38..aebd1a5 100644 --- a/product/src/main/root/bin/logging.properties +++ b/product/src/main/root/bin/logging.properties @@ -50,7 +50,7 @@ java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter # Provides extra control for each logger. ############################################################ -org.apache.river.container.level = FINER +org.apache.river.container.level = FINE org.apache.river.container.ShowContextToConsole.level=INFO @@ -58,4 +58,5 @@ net.jini.config.level=INFO org.apache.river.container.security.ContainerCodePolicy.level=INFO org.apache.river.container.deployer.ClasspathFilterBuilder.level=INFO org.apache.river.container.deployer.DeployerConfigParser=INFO +org.apache.river.container.hsm.level=SEVERE net.jini.config.level=INFO http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/product/src/main/root/profile/client/client-app.cfg ---------------------------------------------------------------------- diff --git a/product/src/main/root/profile/client/client-app.cfg b/product/src/main/root/profile/client/client-app.cfg new file mode 100644 index 0000000..d62e2cc --- /dev/null +++ b/product/src/main/root/profile/client/client-app.cfg @@ -0,0 +1,95 @@ +/* + * 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. + */ + +// Sample file that our policy file parser should be able to process. + +// Grants given to all applications. +grant { + //java.security.AllPermission; + //java.io.FilePermission "${serviceArchive}" "read"; + java.io.FilePermission "-" "read"; + java.net.SocketPermission "*" "connect,listen,accept,resolve"; + + // Required for VFSFileManager + java.util.PropertyPermission "java.io.tmpdir" "read"; + java.util.PropertyPermission "os.*" "read"; + java.util.PropertyPermission "path.*" "read"; + java.lang.RuntimePermission "getClassLoader"; + java.lang.RuntimePermission "setContextClassLoader"; + /* net.jini.security.Security requires createSecurityManager, but we + don't grant 'setSecurityManager'. */ + java.lang.RuntimePermission "createSecurityManager"; + java.lang.RuntimePermission "getProtectionDomain"; + java.lang.RuntimePermission "setFactory"; + java.lang.RuntimePermission "modifyThread"; + java.lang.RuntimePermission "modifyThreadGroup"; + java.security.SecurityPermission "getDomainCombiner"; + java.security.SecurityPermission "createAccessControlContext"; + java.security.SecurityPermission "getPolicy"; + + // BasicProxyPreparer requirements: + javax.security.auth.AuthPermission "getSubject"; + // Should not be required once UmbrellaGrantPolicy is implemented. + // TODO: Config file parser doesn't recognize \" + net.jini.security.GrantPermission "delim=| java.lang.RuntimePermission |accessClassInPackage.com.sun.proxy|"; + + net.jini.security.policy.UmbrellaGrantPermission; + com.sun.jini.thread.ThreadPoolPermission "getSystemThreadPool"; + com.sun.jini.thread.ThreadPoolPermission "getUserThreadPool"; + com.sun.jini.discovery.internal.EndpointInternalsPermission "set"; + com.sun.jini.discovery.internal.EndpointInternalsPermission "get"; + java.lang.reflect.ReflectPermission "suppressAccessChecks"; + net.jini.export.ExportPermission "exportRemoteInterface.*"; + net.jini.discovery.DiscoveryPermission "*"; + java.lang.RuntimePermission "shutdownHooks"; + java.util.PropertyPermission "*" "read"; + + java.lang.RuntimePermission "accessClassInPackage.com.sun.proxy"; + + // Only in client configuration - apps can call System.exit() + java.lang.RuntimePermission "exitVM.*"; +} + +classloader { + // Variables required to set up the application classloader. + //For a privileged application deployer, parent=containerClassLoader; + parent systemClassLoader; + + jars { + commons-vfs2-2.0.jar, + commons-logging-1.1.1.jar, + jsk-platform-${jsk-version}.jar, + jsk-lib-${jsk-version}.jar, + jsk-resources-${jsk-version}.jar, + river-container-core-1.0-SNAPSHOT.jar(org.apache.river.container.liaison.Strings, + org.apache.river.container.liaison.VirtualFileSystemConfiguration, + org.apache.river.container.liaison.VirtualFileSystemConfiguration$MyConfigurationFile, + "META-INF/services/*") + } + + codebase {jsk-dl-${jsk-version}.jar} +} + +configuration { + // Anything on the left-hand side of '=' is set into the application config + // as a "special variable, accessible through '$name'. + discoveryGroup=defaultDiscoveryGroup; + + // For privileged deployer, include + // context=context; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/product/src/main/root/profile/client/config.xml ---------------------------------------------------------------------- diff --git a/product/src/main/root/profile/client/config.xml b/product/src/main/root/profile/client/config.xml index 1c108d1..9b01480 100644 --- a/product/src/main/root/profile/client/config.xml +++ b/product/src/main/root/profile/client/config.xml @@ -39,13 +39,11 @@ <!-- Deployer for 'service-starter'-style applications. --> <cfg:component class="org.apache.river.container.deployer.StarterServiceDeployer"> - <cfg:property name="config" value="service-starter.cfg"/> - <cfg:property name="deployDirectory" value="deploy"/> + <cfg:property name="config" value="client-app.cfg"/> </cfg:component> <!-- Deployer for 'service-starter'-style applications. --> <cfg:component class="org.apache.river.container.deployer.ClientAppDeployer"> - <cfg:property name="config" value="client-app.cfg"/> <cfg:property name="deployDirectory" value="deploy"/> </cfg:component> http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/product/src/main/root/profile/client/service-starter.cfg ---------------------------------------------------------------------- diff --git a/product/src/main/root/profile/client/service-starter.cfg b/product/src/main/root/profile/client/service-starter.cfg deleted file mode 100644 index d62e2cc..0000000 --- a/product/src/main/root/profile/client/service-starter.cfg +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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. - */ - -// Sample file that our policy file parser should be able to process. - -// Grants given to all applications. -grant { - //java.security.AllPermission; - //java.io.FilePermission "${serviceArchive}" "read"; - java.io.FilePermission "-" "read"; - java.net.SocketPermission "*" "connect,listen,accept,resolve"; - - // Required for VFSFileManager - java.util.PropertyPermission "java.io.tmpdir" "read"; - java.util.PropertyPermission "os.*" "read"; - java.util.PropertyPermission "path.*" "read"; - java.lang.RuntimePermission "getClassLoader"; - java.lang.RuntimePermission "setContextClassLoader"; - /* net.jini.security.Security requires createSecurityManager, but we - don't grant 'setSecurityManager'. */ - java.lang.RuntimePermission "createSecurityManager"; - java.lang.RuntimePermission "getProtectionDomain"; - java.lang.RuntimePermission "setFactory"; - java.lang.RuntimePermission "modifyThread"; - java.lang.RuntimePermission "modifyThreadGroup"; - java.security.SecurityPermission "getDomainCombiner"; - java.security.SecurityPermission "createAccessControlContext"; - java.security.SecurityPermission "getPolicy"; - - // BasicProxyPreparer requirements: - javax.security.auth.AuthPermission "getSubject"; - // Should not be required once UmbrellaGrantPolicy is implemented. - // TODO: Config file parser doesn't recognize \" - net.jini.security.GrantPermission "delim=| java.lang.RuntimePermission |accessClassInPackage.com.sun.proxy|"; - - net.jini.security.policy.UmbrellaGrantPermission; - com.sun.jini.thread.ThreadPoolPermission "getSystemThreadPool"; - com.sun.jini.thread.ThreadPoolPermission "getUserThreadPool"; - com.sun.jini.discovery.internal.EndpointInternalsPermission "set"; - com.sun.jini.discovery.internal.EndpointInternalsPermission "get"; - java.lang.reflect.ReflectPermission "suppressAccessChecks"; - net.jini.export.ExportPermission "exportRemoteInterface.*"; - net.jini.discovery.DiscoveryPermission "*"; - java.lang.RuntimePermission "shutdownHooks"; - java.util.PropertyPermission "*" "read"; - - java.lang.RuntimePermission "accessClassInPackage.com.sun.proxy"; - - // Only in client configuration - apps can call System.exit() - java.lang.RuntimePermission "exitVM.*"; -} - -classloader { - // Variables required to set up the application classloader. - //For a privileged application deployer, parent=containerClassLoader; - parent systemClassLoader; - - jars { - commons-vfs2-2.0.jar, - commons-logging-1.1.1.jar, - jsk-platform-${jsk-version}.jar, - jsk-lib-${jsk-version}.jar, - jsk-resources-${jsk-version}.jar, - river-container-core-1.0-SNAPSHOT.jar(org.apache.river.container.liaison.Strings, - org.apache.river.container.liaison.VirtualFileSystemConfiguration, - org.apache.river.container.liaison.VirtualFileSystemConfiguration$MyConfigurationFile, - "META-INF/services/*") - } - - codebase {jsk-dl-${jsk-version}.jar} -} - -configuration { - // Anything on the left-hand side of '=' is set into the application config - // as a "special variable, accessible through '$name'. - discoveryGroup=defaultDiscoveryGroup; - - // For privileged deployer, include - // context=context; -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/product/src/main/root/profile/default/config.xml ---------------------------------------------------------------------- diff --git a/product/src/main/root/profile/default/config.xml b/product/src/main/root/profile/default/config.xml index 06108ed..7d81f52 100644 --- a/product/src/main/root/profile/default/config.xml +++ b/product/src/main/root/profile/default/config.xml @@ -40,14 +40,13 @@ <!-- Deployer for 'service-starter'-style applications. --> <cfg:component class="org.apache.river.container.deployer.StarterServiceDeployer"> <cfg:property name="config" value="service-starter.cfg"/> - <cfg:property name="deployDirectory" value="deploy"/> </cfg:component> <!-- Deployer for applications that are in the 'deploy' directory at startup. --> <cfg:component class="org.apache.river.container.deployer.StartupDeployer"> - <cfg:property name="config" value="service-starter.cfg"/> <cfg:property name="deployDirectory" value="deploy"/> + <cfg:property name="autoDeploy" value="true"/> </cfg:component> <!-- Deployer for 'system apps' like the remote deployment service http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/product/src/main/root/profile/default/service-starter.cfg ---------------------------------------------------------------------- diff --git a/product/src/main/root/profile/default/service-starter.cfg b/product/src/main/root/profile/default/service-starter.cfg index d62e2cc..4b2a5a0 100644 --- a/product/src/main/root/profile/default/service-starter.cfg +++ b/product/src/main/root/profile/default/service-starter.cfg @@ -46,7 +46,7 @@ grant { javax.security.auth.AuthPermission "getSubject"; // Should not be required once UmbrellaGrantPolicy is implemented. // TODO: Config file parser doesn't recognize \" - net.jini.security.GrantPermission "delim=| java.lang.RuntimePermission |accessClassInPackage.com.sun.proxy|"; + // net.jini.security.GrantPermission "delim=| java.lang.RuntimePermission |accessClassInPackage.com.sun.proxy|"; net.jini.security.policy.UmbrellaGrantPermission; com.sun.jini.thread.ThreadPoolPermission "getSystemThreadPool"; http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/river-container-core/src/main/java/org/apache/river/container/Bootstrap.java ---------------------------------------------------------------------- diff --git a/river-container-core/src/main/java/org/apache/river/container/Bootstrap.java b/river-container-core/src/main/java/org/apache/river/container/Bootstrap.java index d5a2fed..0943d4c 100644 --- a/river-container-core/src/main/java/org/apache/river/container/Bootstrap.java +++ b/river-container-core/src/main/java/org/apache/river/container/Bootstrap.java @@ -17,6 +17,10 @@ */ package org.apache.river.container; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; import java.io.File; import java.io.FileNotFoundException; import java.io.InputStream; @@ -39,22 +43,23 @@ import org.apache.river.container.config.*; import org.xml.sax.SAXException; /** - Bootstrap loader for the container. Performs roughly the following: <ul> - <li>Based on the configuration parameter fed in at the command line, determine - the configuration directory and the config file.</li> <li>Read the - configuration file</li> <li>Based on the classpath declared in the config file, - create the container's classloader.</li> <li>Using that classloader, create the - context.</li> <li>Load any command-line parameters into the context</li> - <li>Create all the elements (beans, discovery sets, etc) that are called out in - the config file and put them into the context. This will cause those beans to - setup and initialize themselves.</li> </li> - - @author trasukg + * Bootstrap loader for the container. Performs roughly the following: <ul> + * <li>Based on the configuration parameter fed in at the command line, + * determine the configuration directory and the config file.</li> <li>Read the + * configuration file</li> <li>Based on the classpath declared in the config + * file, create the container's classloader.</li> <li>Using that classloader, + * create the context.</li> <li>Load any command-line parameters into the + * context</li> + * <li>Create all the elements (beans, discovery sets, etc) that are called out + * in the config file and put them into the context. This will cause those beans + * to setup and initialize themselves.</li> </li> + * + * @author trasukg */ public class Bootstrap { - private static final Logger log = - Logger.getLogger(Bootstrap.class.getName(), MessageNames.BUNDLE_NAME); + private static final Logger log + = Logger.getLogger(Bootstrap.class.getName(), MessageNames.BUNDLE_NAME); public static void main(String args[]) { try { @@ -117,37 +122,37 @@ public class Bootstrap { List<String> seen, Map<String, Classpath> classpaths, String id) throws MalformedURLException { - if (classLoaders.containsKey(id ) ){ + if (classLoaders.containsKey(id)) { return classLoaders.get(id); } if (seen.contains(id)) { - throw new ConfigurationException(MessageNames.CIRCULAR_CLASSPATH, id); + throw new ConfigurationException(MessageNames.CIRCULAR_CLASSPATH, id); } // Add the id to the list of classloaders we have attempted to build. seen.add(id); - Classpath classpath=classpaths.get(id); - if (classpath==null) { + Classpath classpath = classpaths.get(id); + if (classpath == null) { throw new ConfigurationException(MessageNames.CLASSPATH_UNDEFINED, id); } - String parentClasspathId=classpath.getParent(); - ClassLoader parentClassLoader=null; - if (parentClasspathId!=null && !Strings.EMPTY.equals(parentClasspathId) ){ - parentClassLoader=resolveClassLoader(classLoaders, seen, classpaths, parentClasspathId); + String parentClasspathId = classpath.getParent(); + ClassLoader parentClassLoader = null; + if (parentClasspathId != null && !Strings.EMPTY.equals(parentClasspathId)) { + parentClassLoader = resolveClassLoader(classLoaders, seen, classpaths, parentClasspathId); } else { /* Should be the 'extension' classloader. */ - parentClassLoader=Bootstrap.class.getClassLoader().getParent(); + parentClassLoader = Bootstrap.class.getClassLoader().getParent(); } URL[] classpathUrls; classpathUrls = findClasspathURLS(classpath.getValue()); - + SettableCodebaseClassLoader classLoader = new SettableCodebaseClassLoader(classpathUrls, parentClassLoader); classLoaders.put(id, classLoader); log.log(Level.FINE, MessageNames.CONFIGURED_CLASSPATH, new Object[]{ - id, - Utils.format(classpathUrls)}); + id, + Utils.format(classpathUrls)}); seen.remove(id); - return classLoader; + return classLoader; } static void initializeContainer(String args[]) throws SAXException, JAXBException, FileNotFoundException, MalformedURLException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, ConfigurationException, Exception { @@ -228,6 +233,16 @@ public class Bootstrap { Class compClass = Class.forName(c.getClazz(), true, classLoader); String name = c.getName(); Object instance = compClass.newInstance(); + for (Property p : c.getProperty()) { + setPropertyOnComponent(instance, p.getName(), p.getValue()); + log.log(Level.FINER, MessageNames.SET_PROPERTY_ON_COMPONENT, + new Object[] { + p.getName(), + c.getClazz(), + c.getName(), + p.getValue() + }); + } if (name == null || name.trim().length() == 0) { putMethod.invoke(context, instance); } else { @@ -250,6 +265,42 @@ public class Bootstrap { throw new ConfigurationException(MessageNames.UNSUPPORTED_ELEMENT, element.getClass().getName()); } } + + private static void setPropertyOnComponent(Object instance, String propertyName, String propertyValue) { + try { + BeanInfo info = Introspector.getBeanInfo(instance.getClass()); + PropertyDescriptor pd=findPropertyDescriptor(info, propertyName); + Object convertedValue=convert(propertyValue, pd.getPropertyType()); + pd.getWriteMethod().invoke(instance, convertedValue); + } catch (Throwable t) { + throw new ConfigurationException(t, MessageNames.FAILED_TO_SET_PROPERTY, propertyName, instance.getClass(), propertyValue); + } + } + + private static PropertyDescriptor findPropertyDescriptor(BeanInfo info, String propertyName) throws IntrospectionException { + for (PropertyDescriptor possible: info.getPropertyDescriptors()) { + if (propertyName.equals(possible.getName())) { + return possible; + } + } + throw new IntrospectionException(propertyName); + } + + private static Object convert(String value, Class targetType) { + if (targetType.equals(Boolean.class) || targetType.equals(boolean.class)) { + return Boolean.parseBoolean(value); + } else if (targetType.equals(String.class)) { + return value; + } else if (targetType.equals(Integer.class) || targetType.equals(int.class)) { + return Integer.parseInt(value); + } else if (targetType.equals(Double.class) || targetType.equals(double.class)) { + return Double.parseDouble(value); + } else if (targetType.equals(Float.class) || targetType.equals(float.class)) { + return Float.parseFloat(value); + } + throw new UnsupportedOperationException(); + } + /* static URL[] findClasspathURLS(ContainerConfig containerConfig) throws MalformedURLException { String classpathStr = http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/river-container-core/src/main/java/org/apache/river/container/MessageNames.java ---------------------------------------------------------------------- diff --git a/river-container-core/src/main/java/org/apache/river/container/MessageNames.java b/river-container-core/src/main/java/org/apache/river/container/MessageNames.java index 2ffa0d5..a566d03 100644 --- a/river-container-core/src/main/java/org/apache/river/container/MessageNames.java +++ b/river-container-core/src/main/java/org/apache/river/container/MessageNames.java @@ -44,6 +44,8 @@ public class MessageNames { ADDED_PLATFORM_CODEBASE_JAR="addedPlatformCodebaseJar", ADDING_CLASSPATH_ENTRY="addingClasspathEntry", ANNOTATED_OBJECT_DEPLOYER_HAS_UNRESOLVED_DEPENDENCIES="annotatedObjectDeployerHasUnresolvedDependencies", + AUTO_DEPLOYER_FAILED_INIT="autoDeployerFailedInit", + AUTO_DEPLOYER_STARTING="autoDeployerStarting", BAD_CLASSPATH_EXPR="badClasspathExpression", BAD_MEMBER_FOR_INJECTED_ANNOTATION="badMemberForInjectedAnnotation", BAD_MEMBER_FOR_NAME_ANNOTATION="badMemberForNameAnnotation", @@ -79,6 +81,8 @@ public class MessageNames { FAILED_CLEAN_SHUTDOWN="failedCleanShutdown", FAILED_DEPLOY_SERVICE="failedDeployService", FAILED_READ_PROPERTIES="failedReadProperties", + FAILED_TO_REMOVE_MBEAN="failedToRemoveMBean", + FAILED_TO_SET_PROPERTY="failedToSetProperty", FOUND_NO_SERVICE_ARCHIVES="foundNoServiceArchives", FOUND_SERVICE_ARCHIVES="foundServiceArchives", ILLEGAL_ARGUMENT_EXCEPTION="illegalArgumentException", @@ -108,10 +112,12 @@ public class MessageNames { READING_OBJECT_NON_ANNOTATED_MEMBER_FOUND="readingObject.nonAnnotatedMemberFound", RECEIVED_START="receivedStart", RECEIVED_START_WITH_ARGS="receivedStartWithArgs", + SCANNING_DEPLOYMENT_DIRECTORY="scanningDeploymentDirectory", SECURITY_INIT_FAILED="securityInitializationFailed", SECURITY_INIT_SUCCEEDED="securityInitializationSucceeded", SECURITY_INIT_WRONG_POLICY="securityInitializationWrongPolicy", SERVICE_PARENT_CLASSLOADER_IS="serviceParentClassloaderIs", + SET_PROPERTY_ON_COMPONENT="setPropertyOnComponent", SHOW_COMMAND_LINE_ARGUMENTS="showCommandLineArguments", SHUTDOWN_FAILED="shutdownFailed", SHUTDOWN_METHOD_HAS_PARAMETERS="shutdownMethodHasParameters", @@ -119,10 +125,13 @@ public class MessageNames { STARTER_SERVICE_DEPLOYER_FAILED_INIT="starterServiceDeployerFailedInit", STARTER_SERVICE_DEPLOYER_INITIALIZED="starterServiceDeployerInitialized", STARTER_SERVICE_DEPLOYER_STARTING="starterServiceDeployerStarting", + STARTING_SERVICE="startingService", STARTUP_DEPLOYER_FAILED_INIT="startupDeployerFailedInit", STARTUP_DEPLOYER_INITIALIZED="startupDeployerInitialized", STARTUP_DEPLOYER_STARTING="startupDeployerStarting", + STOPPING_SERVICE="stoppingService", SYSTEM_CLASSLOADER_IS="systemClassLoaderIs", UNRESOLVED_DEPENDENCY="unresolvedDependency", - UNSUPPORTED_ELEMENT="unsupportedElement"; + UNSUPPORTED_ELEMENT="unsupportedElement", + UPDATING_SERVICE="updatingService"; } http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceDeployer.java ---------------------------------------------------------------------- diff --git a/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceDeployer.java b/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceDeployer.java index d98ddd1..61766e1 100644 --- a/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceDeployer.java +++ b/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceDeployer.java @@ -31,14 +31,20 @@ import java.lang.reflect.Method; import java.net.URL; import java.security.CodeSource; import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; import java.security.Principal; import java.security.cert.Certificate; import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; import java.util.List; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; +import net.jini.security.GrantPermission; import net.jini.security.policy.DynamicPolicyProvider; +import net.jini.security.policy.UmbrellaGrantPermission; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileType; @@ -69,8 +75,8 @@ import org.apache.river.container.work.WorkManager; */ public class StarterServiceDeployer implements StarterServiceDeployerMXBean { - private static final Logger log = - Logger.getLogger(StarterServiceDeployer.class.getName(), MessageNames.BUNDLE_NAME); + 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) @@ -132,8 +138,8 @@ public class StarterServiceDeployer implements StarterServiceDeployerMXBean { 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); + VirtualFileSystemClassLoader cl + = createChildOfGivenClassloader(parentLoader, codeSource); /* Include platform jars from the container's lib directory. */ @@ -169,8 +175,8 @@ public class StarterServiceDeployer implements StarterServiceDeployerMXBean { /* Create the service classloader. */ - VirtualFileSystemClassLoader cl = - new VirtualFileSystemClassLoader(null, parent, codeSource); + VirtualFileSystemClassLoader cl + = new VirtualFileSystemClassLoader(null, parent, codeSource); return cl; } @@ -232,7 +238,7 @@ public class StarterServiceDeployer implements StarterServiceDeployerMXBean { throw new LocalizedRuntimeException(MessageNames.BUNDLE_NAME, MessageNames.CANT_READ_START_PROPERTIES, new Object[]{Strings.START_PROPERTIES, - serviceRoot.getName().getBaseName()}); + serviceRoot.getName().getBaseName()}); } Properties startProps = propertiesFileReader.getProperties(startProperties); return startProps; @@ -252,6 +258,7 @@ public class StarterServiceDeployer implements StarterServiceDeployerMXBean { workingDir = new File(serviceRoot.getURL().toURI()); } + grantPermissions(cl, new Permission[]{new FilePermission(workingDir.getAbsolutePath(), Strings.READ)}); Utils.logClassLoaderHierarchy(log, Level.FINE, this.getClass()); @@ -328,9 +335,9 @@ public class StarterServiceDeployer implements StarterServiceDeployerMXBean { void prepareService(ApplicationEnvironment env) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { - CodeSource serviceCodeSource = - new CodeSource(findServiceURL(env.getServiceArchive(), env.getServiceRoot()), - new Certificate[0]); + 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); @@ -339,8 +346,8 @@ public class StarterServiceDeployer implements StarterServiceDeployerMXBean { /* Create a codebase context. */ - CodebaseContext codebaseContext = - codebaseHandler.createContext(env.getServiceName()); + CodebaseContext codebaseContext + = codebaseHandler.createContext(env.getServiceName()); env.setCodebaseContext(codebaseContext); addPlatformCodebaseJars(codebaseContext); exportServiceCodebaseJars(env.getServiceRoot(), codebaseContext); @@ -421,6 +428,7 @@ public class StarterServiceDeployer implements StarterServiceDeployerMXBean { private void grantPermissions(ClassLoader cl, Permission[] perms) { try { + perms=expandUmbrella(perms); Class clazz = Class.forName(VirtualFileSystemConfiguration.class.getName(), true, cl); securityPolicy.grant(clazz, new Principal[0], perms); @@ -429,6 +437,25 @@ public class StarterServiceDeployer implements StarterServiceDeployerMXBean { } } + private static Permission[] expandUmbrella(Permission[] perms) { + PermissionCollection pc=new Permissions(); + + for (Permission p: perms) { + pc.add(p); + } + if (pc.implies(new UmbrellaGrantPermission())) { + List l = Collections.list(pc.elements()); + pc.add(new GrantPermission( + (Permission[]) l.toArray(new Permission[l.size()]))); + } + List<Permission> permList=new ArrayList<Permission>(); + + for (Enumeration<Permission> en=pc.elements(); en.hasMoreElements();) { + permList.add(en.nextElement()); + } + return permList.toArray(new Permission[0]); + } + private Object instantiateService(ClassLoader cl, String className, String[] parms) throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, InstantiationException { Class clazz = Class.forName(className, true, cl); http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java ---------------------------------------------------------------------- diff --git a/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java b/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java index 355074a..012b8ee 100644 --- a/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java +++ b/river-container-core/src/main/java/org/apache/river/container/deployer/StarterServiceLifeCycleSM.java @@ -61,6 +61,7 @@ public class StarterServiceLifeCycleSM { machine.lifeCycleProxy = (ServiceLifeCycle) machine.eventProxy; return machine.lifeCycleProxy; } + @State({Idle.class, Preparing.class, Prepared.class, Starting.class, Failed.class, Running.class, Stopping.class, DirtyShutdown.class, Idle.class}) http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/river-container-core/src/main/java/org/apache/river/container/deployer/StartupDeployer.java ---------------------------------------------------------------------- diff --git a/river-container-core/src/main/java/org/apache/river/container/deployer/StartupDeployer.java b/river-container-core/src/main/java/org/apache/river/container/deployer/StartupDeployer.java index 2a96978..26e5598 100644 --- a/river-container-core/src/main/java/org/apache/river/container/deployer/StartupDeployer.java +++ b/river-container-core/src/main/java/org/apache/river/container/deployer/StartupDeployer.java @@ -19,16 +19,21 @@ package org.apache.river.container.deployer; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.Hashtable; import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; import javax.management.MBeanRegistrationException; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectName; 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; @@ -48,28 +53,55 @@ import org.apache.river.container.Utils; */ public class StartupDeployer { - private static final Logger log = - Logger.getLogger(StartupDeployer.class.getName(), MessageNames.BUNDLE_NAME); - + private static final Logger log + = Logger.getLogger(StartupDeployer.class.getName(), MessageNames.BUNDLE_NAME); + private String deployDirectory = org.apache.river.container.Strings.DEFAULT_DEPLOY_DIRECTORY; - + @Injected(style = InjectionStyle.BY_TYPE) private FileUtility fileUtility = null; - + @Injected(style = InjectionStyle.BY_TYPE) private Context context; - + @Injected(style = InjectionStyle.BY_TYPE) private StarterServiceDeployer deployer; - + @Injected(style = InjectionStyle.BY_TYPE) private MBeanRegistrar mbeanRegistrar; - + @Name private String myName = null; - private List<ApplicationEnvironment> applicationEnvironments = - new ArrayList<ApplicationEnvironment>(); + private Map<String, DeploymentRecord> deployedServices = new HashMap<String, DeploymentRecord>(); + + private class DeploymentRecord { + + String name; + long updateTime; + FileObject fileObject; + ServiceLifeCycle serviceLifeCycle; + } + + private boolean autoDeploy = false; + + public boolean isAutoDeploy() { + return autoDeploy; + } + + public void setAutoDeploy(boolean autoDeploy) { + this.autoDeploy = autoDeploy; + } + + public int getScanInterval() { + return scanInterval; + } + + public void setScanInterval(int scanInterval) { + this.scanInterval = scanInterval; + } + + private int scanInterval = 5; public String getDeployDirectory() { return deployDirectory; @@ -79,8 +111,8 @@ public class StartupDeployer { this.deployDirectory = deployDirectory; } - FileObject deploymentDirectoryFile=null; - + FileObject deploymentDirectoryFile = null; + @Init public void init() { try { @@ -105,52 +137,139 @@ public class StartupDeployer { new Object[]{deployDirectory, fileUtility.getProfileDirectory()}); } /* - Go through the deployment directory looking for services to deploy. + Do the scan task once - this will launch all the services currently in + deploy dir. */ - List<FileObject> serviceArchives = - Utils.findChildrenWithSuffix(deploymentDirectoryFile, - org.apache.river.container.Strings.JAR); - if (serviceArchives != null) { - log.log(Level.FINE, MessageNames.FOUND_SERVICE_ARCHIVES, - new Object[]{serviceArchives.size(), deployDirectory}); - deployServiceArchives(serviceArchives); - } else { - log.log(Level.WARNING, MessageNames.FOUND_NO_SERVICE_ARCHIVES, - new Object[]{deployDirectory}); + new ScanTask().runOnce(); + if (autoDeploy) { + /* Now schedule a scan in the required scan time. */ + deployer.workManager.schedule(null, new ScanTask(), getScanInterval(), TimeUnit.SECONDS); } + } + private void registerApplication(ServiceLifeCycle deployedApp) throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { + Hashtable<String, String> props = new Hashtable<String, String>(); + props.put(org.apache.river.container.Strings.NAME, deployedApp.getName()); + ObjectName oName = new ObjectName(org.apache.river.container.Strings.CONTAINER_JMX_DOMAIN, props); + mbeanRegistrar.getMbeanServer().registerMBean( + deployedApp, oName); } - private void deployServiceArchives(List<FileObject> serviceArchives) { + private void unregisterApplication(ServiceLifeCycle deployedApp) { + try { + Hashtable<String, String> props = new Hashtable<String, String>(); + props.put(org.apache.river.container.Strings.NAME, deployedApp.getName()); + ObjectName oName = new ObjectName(org.apache.river.container.Strings.CONTAINER_JMX_DOMAIN, props); + mbeanRegistrar.getMbeanServer().unregisterMBean(oName); + } catch (Exception e) { + log.log(Level.SEVERE, MessageNames.FAILED_TO_REMOVE_MBEAN, + new Object[]{deployedApp.getName()}); + } + } + + private Map<String, DeploymentRecord> scanDeploymentArchives() throws FileSystemException { /* - Deploy those services. + Go through the deployment directory looking for services to deploy. */ - for (FileObject archiveFile : serviceArchives) { + Map<String, DeploymentRecord> deployDirListing = new HashMap<String, DeploymentRecord>(); + deploymentDirectoryFile.refresh(); + List<FileObject> serviceArchives + = Utils.findChildrenWithSuffix(deploymentDirectoryFile, + org.apache.river.container.Strings.JAR); + if (serviceArchives != null) { + log.log(Level.FINER, MessageNames.FOUND_SERVICE_ARCHIVES, + new Object[]{serviceArchives.size(), deployDirectory}); + for (FileObject serviceArchive : serviceArchives) { + DeploymentRecord rec = new DeploymentRecord(); + rec.fileObject = serviceArchive; + rec.name = serviceArchive.getName().getBaseName(); + rec.updateTime = serviceArchive.getContent().getLastModifiedTime(); + deployDirListing.put(rec.name, rec); + } + } + return deployDirListing; + } + + private class ScanTask implements Runnable { + + public void runOnce() { try { - /* Try the archive in all the deployers to see if someone can - * handle it. For now there's only one. - */ - - /* - * Create the ApplicationEnvironment for the archive. - */ - ServiceLifeCycle deployedApp=deployer.deployServiceArchive(archiveFile); - // Register it as an MBean - registerApplication(deployedApp); - deployedApp.start(); + log.log(Level.FINER, MessageNames.SCANNING_DEPLOYMENT_DIRECTORY, + new Object[]{deployDirectory}); + Map<String, DeploymentRecord> deployDirListing = scanDeploymentArchives(); + // DeployDirListing will become the deployedServices collection + synchDeployedServices(deployedServices, deployDirListing); } catch (Throwable t) { - log.log(Level.WARNING, MessageNames.FAILED_DEPLOY_SERVICE, archiveFile.toString()); - log.log(Level.WARNING, MessageNames.EXCEPTION_THROWN, Utils.stackTrace(t)); + t.printStackTrace(); } } + + public void run() { + runOnce(); + deployer.workManager.schedule(null, this, scanInterval, TimeUnit.SECONDS); + } } - - private void registerApplication(ServiceLifeCycle deployedApp) throws MalformedObjectNameException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { - Hashtable<String, String> props=new Hashtable<String,String>(); - props.put(org.apache.river.container.Strings.NAME, deployedApp.getName()); - ObjectName oName=new ObjectName(org.apache.river.container.Strings.CONTAINER_JMX_DOMAIN, props); - mbeanRegistrar.getMbeanServer().registerMBean( - deployedApp, oName); + + private void synchDeployedServices(Map<String, DeploymentRecord> currentList, + Map<String, DeploymentRecord> newList) { + // For each entry + for (DeploymentRecord rec : newList.values()) { + // If it isn't already in deployedServices, start it + DeploymentRecord current = currentList.get(rec.name); + if (current == null) { + log.log(Level.FINE, MessageNames.STARTING_SERVICE, + new Object[]{rec.name}); + currentList.put(rec.name, rec); + deployAndStart(rec); + } else if (current.updateTime != rec.updateTime) { + // If it's in deployedServices but now newer, stop and restart + log.log(Level.FINE, MessageNames.UPDATING_SERVICE, + new Object[]{rec.name}); + currentList.remove(current.name); + stopAndRemove(current); + currentList.put(rec.name, rec); + deployAndStart(rec); + } + } + // If there are any services left in deployedServices, stop them + List<DeploymentRecord> removals = new ArrayList<DeploymentRecord>(); + for (DeploymentRecord current : currentList.values()) { + if (!newList.containsKey(current.name)) { + removals.add(current); + } + } + for (DeploymentRecord current : removals) { + log.log(Level.FINE, MessageNames.STOPPING_SERVICE, + new Object[]{current.name}); + currentList.remove(current.name); + stopAndRemove(current); + } + + } + + private void deployAndStart(DeploymentRecord dr) { + try { + /* Try the archive in all the deployers to see if someone can + * handle it. For now there's only one. + */ + + /* + * Create the ApplicationEnvironment for the archive. + */ + dr.serviceLifeCycle = deployer.deployServiceArchive(dr.fileObject); + // Register it as an MBean + registerApplication(dr.serviceLifeCycle); + dr.serviceLifeCycle.start(); + } catch (Throwable t) { + log.log(Level.WARNING, MessageNames.FAILED_DEPLOY_SERVICE, dr.name); + log.log(Level.WARNING, MessageNames.EXCEPTION_THROWN, Utils.stackTrace(t)); + } + + } + + private void stopAndRemove(DeploymentRecord dr) { + dr.serviceLifeCycle.stop(); + unregisterApplication(dr.serviceLifeCycle); } } http://git-wip-us.apache.org/repos/asf/river-container/blob/60c5c7fe/river-container-core/src/main/resources/org/apache/river/container/Messages.properties ---------------------------------------------------------------------- diff --git a/river-container-core/src/main/resources/org/apache/river/container/Messages.properties b/river-container-core/src/main/resources/org/apache/river/container/Messages.properties index e80be2b..7a93455 100644 --- a/river-container-core/src/main/resources/org/apache/river/container/Messages.properties +++ b/river-container-core/src/main/resources/org/apache/river/container/Messages.properties @@ -21,6 +21,8 @@ addingClasspathEntry=Adding classpath entry ''{0}''. annotatedObjectDeployerHasUnresolvedDependencies=Initialization failed because \ one or more dependencies flagged with @Injected or @Name annotations \ are unresolved. +autoDeployerFailedInit=Automatic Starter-Service deployer has failed to initialize. +autoDeployerStarting=Automatic deployer service is starting up. badClasspathExpression=Bad Classpath Expression {0} {1}. badMemberForInjectedAnnotation=@Injected annotation must be applied to a field or\ setter method only: Member {1} on class {0} doesn't qualify. @@ -58,6 +60,8 @@ exceptionWhileStopping=Exception thrown during stop operation:\n{0} failedCleanShutdown=Application ''{0}'' failed to shutdown cleanly, so we're interrupting it. failedDeployService=Deployment of service archive at ''{0}'' failed. failedReadProperties=Failed to read one or more properties files. +failedToRemoveMBean=Failed to remove MBean for application ''{0}''. +failedToSetProperty=Failed to set property ''{0}'' on component class ''{1}'' to ''{2}''. foundNoServiceArchives=Found no service archives for deployment dir ''{0}''. foundServiceArchives=Found {0} service archives in deployment dir ''{1}''. illegalArgumentException=An operation threw an IllegalArgumentException. @@ -93,10 +97,12 @@ readingObject.annotatedMemberFound=Member ''{0}'' is annotated @Injected. readingObject.nonAnnotatedMemberFound=Member ''{0}'' is not annotated @Injected. receivedStart=Received start event while in state {0}. receivedStartWithArgs=Received start with arguments event while in state {0}. +scanningDeploymentDirectory=Scanning deployment directory ''{0}''. securityInitializationFailed=Failed to initialize security subsystem. securityInitializationSucceeded=Security Manager and Dynamic Policy successfully installed. securityInitializationWrongPolicy=After security manager setup, the wrong policy is installed: {0}. serviceParentClassloaderIs=Parent of service classloader is {0}. +setPropertyOnComponent=Property ''{0}'' on component {1}''{2}'' was set to ''{3}''. showCommandLineArguments=Command line arguments were: {0}. shutdownFailed=Application ''{0}'' has failed to shut down - there are threads still running. shutdownMethodHasParameters=A method flagged as @Shutdown must take no parameters. \ @@ -108,12 +114,15 @@ starterServiceDeployerInitialized=Starter-Service deployer named ''{0}'' complet initialization. starterServiceDeployerStarting=Starter-Service deployer named ''{0}'' is being \ initialized. +startingService=Starting service ''{0}''. startupDeployerFailedInit=Starter-Service deployer has failed to initialize. startupDeployerInitialized=Starter-Service deployer named ''{0}'' completed \ initialization. startupDeployerStarting=Starter-Service deployer named ''{0}'' is being \ initialized. +stoppingService=Stopping service ''{0}''. systemClassLoaderIs=System classloader is ''{0}'' with classpath {1}. unresolvedDependency=Object {0} has an unresolved dependent member ''{1}'' \ (name=''{2}''). unsupportedElement=Element type {0} is currently unsupported. +updatingService=Updating service ''{0}''.