river-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gtra...@apache.org
Subject [06/40] Initial import of River Container project into git.
Date Mon, 13 Jan 2014 03:35:13 GMT
http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/MBeanRegistrar.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/MBeanRegistrar.java b/river-container-core/src/main/java/org/apache/river/container/MBeanRegistrar.java
new file mode 100644
index 0000000..8d87ba3
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/MBeanRegistrar.java
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+package org.apache.river.container;
+
+import java.lang.management.ManagementFactory;
+import java.util.Hashtable;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+/**
+ *
+ * @author trasukg
+ */
+public class MBeanRegistrar implements DeploymentListener {
+
+    private MBeanServer mbeanServer=null;
+
+    public MBeanServer getMbeanServer() {
+        return mbeanServer;
+    }
+
+    public void postInit(String name, Object object) {
+        try {
+            /*
+             Just try to register it.  If it fails, that's OK.
+             */
+            Hashtable<String,String> props=new Hashtable<String, String>();
+            props.put(Strings.NAME, name);
+            ObjectName objectName=new ObjectName(Strings.CONTAINER_JMX_DOMAIN, props);
+            mbeanServer.registerMBean(object, objectName);
+        } catch(Exception ex) {
+            // Don't really care.
+        }
+    }
+
+    @Init
+    public void init() {
+        mbeanServer=ManagementFactory.getPlatformMBeanServer();
+    }
+
+    @Override
+    public void shutDown() {
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/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
new file mode 100644
index 0000000..bc4ff9f
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/MessageNames.java
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container;
+
+/*
+ TODO: Should the messages be separated into different domains based on the
+ audience?  i.e. Should we have ExceptionMessages that might be seen by users
+ and LogMessages that are primarily meant to be seen by developers? UIMessages
+ that are assumed to be used in the UI?
+
+ Advantages:
+ - Possibly better organization
+ - Internationalization/Translation efforts could be more focused.
+
+ Also, should we assign a code to each message (msg number, etc)?  That way,
+ support tools could search an email message for the exception code and
+ then generate a list of candidate support messages.
+ */
+
+/**
+ * Constants that hold message names used in the message resource bundle.
+ * @author trasukg
+ */
+public class MessageNames {
+    public static final String BUNDLE_NAME="org.apache.river.container.Messages";
+
+    public static final String
+            ADDED_PLATFORM_CODEBASE_JAR="addedPlatformCodebaseJar",
+            ADDING_CLASSPATH_ENTRY="addingClasspathEntry",
+            ANNOTATED_OBJECT_DEPLOYER_HAS_UNRESOLVED_DEPENDENCIES="annotatedObjectDeployerHasUnresolvedDependencies",
+            BAD_CLASSPATH_EXPR="badClasspathExpression",
+            BAD_MEMBER_FOR_INJECTED_ANNOTATION="badMemberForInjectedAnnotation",
+            BAD_MEMBER_FOR_NAME_ANNOTATION="badMemberForNameAnnotation",
+            BASIC_WORK_MANAGER_INITIALIZED="basicWorkManagerInitialized",
+            CALLING_MAIN="callingMain",
+            CANT_CONVERT_EXCEPTION="cantConvertException",
+            CANT_READ_START_PROPERTIES="cantReadStartProperties",
+            CIRCULAR_CLASSPATH="circularClasspath",
+            CLASSLOADER_IS="classLoaderIs",
+            CLASSPATH_UNDEFINED="classpathUndefined",
+            CLASS_SERVER_BAD_REQUEST="classServerBadRequest",
+            CLASS_SERVER_ERROR_ACCEPTING_CONNECTIONS="classServerErrorAcceptingConnections",
+            CLASS_SERVER_ESTABLISHED="classServerEstablished",
+            CLASS_SERVER_EXCEPTION_DURING_SHUTDOWN="classServerExceptionDuringShutdown",
+            CLASS_SERVER_EXCEPTION_GETTING_BYTES="classServerExceptionGettingBytes",
+            CLASS_SERVER_EXCEPTION_WRITING_RESPONSE="classServerExceptionWritingResponse",
+            CLASS_SERVER_INIT_FAILED="classServerInitFailed",
+            CLASS_SERVER_NO_CONTENT_FOUND="classServerNoContentFound",
+            CLASS_SERVER_RECEIVED_REQUEST="classServerReceivedRequest",
+            CLASS_SERVER_RECEIVED_PROBE="classServerReceivedProbe",    
+            CLASS_SERVER_REJECTED_PATH="classServerRejectedPath",
+            CLASS_SERVER_TERMINATED="classServerTerminated",
+            CODESOURCE_IS="codeSourceIs",
+            COMPLETED_SERVICE_DEPLOYMENT="completedServiceDeployment",
+            CONFIG_FILE="configFile",
+            CONFIGURED_CLASSPATH = "configuredClasspath",
+            CONTEXT_ITEM = "contextItem",
+            CREATED_THREAD="createdThread",
+            DUPLICATE_CLASSPATH="duplicateClasspath",
+            EXCEPTION_THROWN="exceptionThrown",
+            EXCEPTION_WHILE_STOPPING="exceptionWhileStopping",
+            FAILED_CLEAN_SHUTDOWN="failedCleanShutdown",
+            FAILED_DEPLOY_SERVICE="failedDeployService",
+            FAILED_READ_PROPERTIES="failedReadProperties",
+            FOUND_NO_SERVICE_ARCHIVES="foundNoServiceArchives",
+            FOUND_SERVICE_ARCHIVES="foundServiceArchives",
+            ILLEGAL_ARGUMENT_EXCEPTION="illegalArgumentException",
+            ILLEGAL_ACCESS_EXCEPTION="illegalAccessException",
+            INITIALIZATION_EXCEPTION="initializationException",
+            INTIALIZING_EVENT_TABLE="initializingEventTable",
+            INVALID_CLASSPATH_ENTRY="invalidClasspathEntry",
+            INVOCATION_TARGET_EXCEPTION="invocationTargetException",
+            INIT_METHOD_HAS_PARAMETERS="initMethodHasParameters",
+            INIT_METHOD_NOT_VOID="initMethodIsntVoid",
+            INJECT="inject",
+            MISSING_PROPERTY_ENTRY="missingPropertyEntry",
+            MISSING_SPECIAL_VALUE="missingSpecialValue",
+            N_THREADS_LEFT="nThreadsLeft",
+            NO_DEPLOYMENT_DIRECTORY="noDeploymentDirectory",
+            PARENT_CLASS_LOADER_IS="parentClassLoaderIs",
+            POLICY_DECLINED="policyDeclined",
+            PROFILE_CONFIG_EXCEPTION="profileConfigurationException",
+            PROFILE_CONFIG_LOADING="profileConfigLoading",
+            READ_PROPERTIES="readProperties",
+            READ_PROPERTIES_FILE="readPropertiesFile",
+            READING_OBJECT="readingObject",
+            READING_OBJECT_MEMBER_COUNT="readingObject.memberCount",
+            READING_OBJECT_ANNOTATED_MEMBER_FOUND="readingObject.annotatedMemberFound",
+            READING_OBJECT_NON_ANNOTATED_MEMBER_FOUND="readingObject.nonAnnotatedMemberFound",
+            RECEIVED_START="receivedStart",
+            SECURITY_INIT_FAILED="securityInitializationFailed",
+            SECURITY_INIT_SUCCEEDED="securityInitializationSucceeded",
+            SECURITY_INIT_WRONG_POLICY="securityInitializationWrongPolicy",
+            SERVICE_PARENT_CLASSLOADER_IS="serviceParentClassloaderIs",
+            SHOW_COMMAND_LINE_ARGUMENTS="showCommandLineArguments",
+            SHUTDOWN_FAILED="shutdownFailed",
+            SHUTDOWN_METHOD_HAS_PARAMETERS="shutdownMethodHasParameters",
+            SHUTDOWN_METHOD_NOT_VOID="shutdownMethodIsntVoid",
+            STARTER_SERVICE_DEPLOYER_FAILED_INIT="starterServiceDeployerFailedInit",
+            STARTER_SERVICE_DEPLOYER_INITIALIZED="starterServiceDeployerInitialized",
+            STARTER_SERVICE_DEPLOYER_STARTING="starterServiceDeployerStarting",
+            STARTUP_DEPLOYER_FAILED_INIT="startupDeployerFailedInit",
+            STARTUP_DEPLOYER_INITIALIZED="startupDeployerInitialized",
+            STARTUP_DEPLOYER_STARTING="startupDeployerStarting",
+            SYSTEM_CLASSLOADER_IS="systemClassLoaderIs",
+            UNRESOLVED_DEPENDENCY="unresolvedDependency",
+            UNSUPPORTED_ELEMENT="unsupportedElement";
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/Name.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/Name.java b/river-container-core/src/main/java/org/apache/river/container/Name.java
new file mode 100644
index 0000000..ed36876
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/Name.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ Annotation used to flag a field or setter method that should be filled in
+ with the component's name on deployment.
+ * @author trasukg
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface Name {
+
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/Names.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/Names.java b/river-container-core/src/main/java/org/apache/river/container/Names.java
new file mode 100644
index 0000000..c9c819d
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/Names.java
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+
+package org.apache.river.container;
+
+/**
+ This class holds the names of various things that are used in the container.
+ * @author trasukg
+ */
+public class Names {
+    public static final String SURROGATE_INSTALLER="surrogateInstaller";
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/Optional.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/Optional.java b/river-container-core/src/main/java/org/apache/river/container/Optional.java
new file mode 100644
index 0000000..52111da
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/Optional.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ <p>
+ Annotation to indicate that the contents of the annotated field should
+ be injected by the AnnotatedClassDeployer when the appropriate reference
+ becomes available.
+ </p>
+
+ <p>
+ By default, the injection is done by type; in other words the value is set
+ to the first thing in the context that is assignable to the target field.
+ If the annotation includes the 'name' attribute, then the injection is done
+ by name; the value is set to whatever is stored under that name in the context.
+ </p>
+
+ <p>
+ If the type of the target happens to be Context, then the context itself
+ will be injected.
+ </p>
+
+ <p>
+ An object in the context will not be injected into any other object until
+ it has been fully resolved and its initialization method has been called.
+ Nonetheless, the target object should not do anything with the injected
+ resource as part of the 'setter' method; it should initialize itself inside
+ a method flagged with the @Init annotation, which will be called when all the
+ @Injected fields/methods have been satisfied.
+ </p>
+ * @author trasukg
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD, ElementType.METHOD})
+public @interface Optional {
+    String value() default Strings.EMPTY;
+    InjectionStyle style() default InjectionStyle.DEFAULT;
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/ProfileConfigReader.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/ProfileConfigReader.java b/river-container-core/src/main/java/org/apache/river/container/ProfileConfigReader.java
new file mode 100644
index 0000000..fc95b77
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/ProfileConfigReader.java
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.river.container.config.ContainerConfig;
+import org.xml.sax.SAXException;
+
+/**
+ *
+ * @author trasukg
+ */
+public class ProfileConfigReader {
+
+    private static final Logger log =
+            Logger.getLogger(ProfileConfigReader.class.getName(), MessageNames.BUNDLE_NAME);
+
+    @Injected
+    private FileUtility fileUtility = null;
+    @Injected(style= InjectionStyle.BY_TYPE)
+    private Context context = null;
+
+    @Injected private String profile=null;
+
+    private ContainerConfig readProfileConfig() throws SAXException, JAXBException, FileNotFoundException, IOException {
+        Unmarshaller um = Bootstrap.createConfigUnmarshaller();
+        FileObject profileDir = fileUtility.getProfileDirectory();
+        FileObject configFile = profileDir.resolveFile(Strings.CONFIG_XML);
+        log.log(Level.FINE, MessageNames.CONFIG_FILE, configFile.toString());
+        InputStream is = configFile.getContent().getInputStream();
+        ContainerConfig containerConfig = (ContainerConfig) um.unmarshal(is);
+        return containerConfig;
+    }
+
+    @Injected
+    private ClassLoader containerClassLoader;
+    
+    @Init
+    public void init() {
+        try {
+            ContainerConfig profileConfig = readProfileConfig();
+            /* We use Object not ClassLoader because it might have been loaded
+             by a different classloader.
+             */
+            
+            log.log(Level.FINE, MessageNames.PROFILE_CONFIG_LOADING, 
+                    new Object[] { containerClassLoader });
+            Bootstrap.processConfiguration(profileConfig, containerClassLoader, context);
+        } catch (Exception ex) {
+            throw new ConfigurationException(ex, MessageNames.PROFILE_CONFIG_EXCEPTION, profile);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/PropertiesFileReader.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/PropertiesFileReader.java b/river-container-core/src/main/java/org/apache/river/container/PropertiesFileReader.java
new file mode 100644
index 0000000..13dc6e0
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/PropertiesFileReader.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+package org.apache.river.container;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileType;
+
+/**
+ *
+ * @author trasukg
+ */
+public class PropertiesFileReader {
+
+    private static final Logger log=
+            Logger.getLogger(PropertiesFileReader.class.getName(),
+            MessageNames.BUNDLE_NAME);
+    
+    @Injected(style=InjectionStyle.BY_TYPE)
+    Context context = null;
+    @Injected
+    FileUtility fileUtility = null;
+
+    @Init
+    public void initialize() {
+        try {
+            FileObject[] childFiles = fileUtility.getProfileDirectory().getChildren();
+            for (FileObject fo: childFiles) {
+                if (fo.getName().getBaseName().endsWith(Strings.DOT_PROPERTIES)
+                        && fo.getType()==FileType.FILE) {
+                    readPropertiesFile(fo);
+                }
+            }
+        } catch (Exception ex) {
+            throw new LocalizedRuntimeException(ex,
+                    MessageNames.BUNDLE_NAME, MessageNames.FAILED_READ_PROPERTIES);
+        }
+    }
+    
+    private void readPropertiesFile(FileObject fo) throws FileSystemException, IOException {
+        String name=fo.getName().getBaseName();
+        Properties props = getProperties(fo);
+        context.put(name, props);
+        log.log(Level.FINE, MessageNames.READ_PROPERTIES_FILE,name);
+        if (log.isLoggable(Level.FINER)) {
+            log.log(Level.FINER, MessageNames.READ_PROPERTIES, 
+                    Utils.format(props));         
+        }
+    }
+
+    public Properties getProperties(FileObject fo) throws FileSystemException, IOException {
+        InputStream is=fo.getContent().getInputStream();
+        Properties props=new Properties();
+        props.load(is);
+        return props;
+    }
+    
+
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/ServiceStarterArchiveFileFilter.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/ServiceStarterArchiveFileFilter.java b/river-container-core/src/main/java/org/apache/river/container/ServiceStarterArchiveFileFilter.java
new file mode 100644
index 0000000..be8c826
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/ServiceStarterArchiveFileFilter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+package org.apache.river.container;
+
+import java.io.File;
+import java.io.FileFilter;
+
+/**
+ *
+ * @author trasukg
+ */
+public class ServiceStarterArchiveFileFilter implements FileFilter {
+
+    public boolean accept(File pathname) {
+        if (!pathname.isFile()) {
+            return false;
+        }
+        if (! pathname.getPath().endsWith(Strings.DOT_SSAR)) {
+            return false;
+        }
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/SettableCodebaseClassLoader.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/SettableCodebaseClassLoader.java b/river-container-core/src/main/java/org/apache/river/container/SettableCodebaseClassLoader.java
new file mode 100644
index 0000000..38adc49
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/SettableCodebaseClassLoader.java
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+package org.apache.river.container;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Extends URLClassLoader to allow alteration of the codebase annotation that
+ * will be extracted from the class loader.  Essentially, allows you to use
+ * one source for the actual classpath, and then have a marshalled object
+ * return an arbitrary codebase.
+ * @author trasukg
+ */
+public class SettableCodebaseClassLoader extends URLClassLoader {
+
+    /** Stores the codebase that will be returned as the codebase annotation.
+     *
+     */
+    private URL codebaseURLs[] = new URL[0];
+
+    /**
+     * Construct using a list of urls and the parent classloader.
+     * @param urlList A list of URLs (which may point to directories or
+     * jar files) to form the classpath.
+     * @param extensionClassloader Class loader to use as parent of this
+     * class loader.
+     */
+    SettableCodebaseClassLoader(URL urlList[], ClassLoader extensionClassloader) {
+        super(urlList, extensionClassloader);
+    }
+
+    /**
+     * Get the list of URLs that are used for the codebase annotation.
+     * Note that this list is not the actual classpath (that is in the
+     * superclass).  The codebase URLs are imposed to match whatever the Jini
+     * service wants to expose as its codebase annotation.
+     * @return
+     */
+    @Override
+    public URL[] getURLs() {
+        return codebaseURLs;
+    }
+
+    /** Add a URL to this classpath.
+     */
+    @Override
+    public void addURL(URL url) {
+        URL[] currentURLS = super.getURLs();
+        for (int i = 0; i < currentURLS.length; i++) {
+            if (url.equals(currentURLS[i])) {
+                return;
+            }
+        }
+        super.addURL(url);
+    }
+
+    /**
+     * Set the codebase URLs to an arbitrary list of URLs.  These URLs form the
+     * codebase annotation for classes loaded through this classloader.
+     * For the sake of general paranoia, sets the codebase to a copy of the
+     * provided array.
+     * @param codebase
+     */
+    public void setCodebase(URL[] codebase) {
+        if (codebase == null || codebase.length==0) {
+            codebaseURLs = new URL[]{};
+            return;
+        }
+
+        codebaseURLs = new URL[codebase.length];
+        System.arraycopy(codebase, 0, codebaseURLs, 0, codebase.length);
+
+    }
+
+    static SettableCodebaseClassLoader createLoader(ClassLoader parent,
+            File commonDirectory)
+            throws MalformedURLException, IOException {
+        List urlList = new ArrayList();
+
+        if (!commonDirectory.isDirectory()) {
+            /* There's no common directory, so we'll just use an empty url list
+            to create the classloader.
+             */
+        } else {
+            /* Create based around the unpacked directory.
+             * TODO: Maybe later;  seems to me this would make a decent idea,
+             * but the Surrogate spec specifically disallows the use of the
+             * codebase attribute, and seems to require that all classes be
+             * in the root of the surrogate package jar file.
+
+
+            urlList.add(commonDirectory.toURI().toURL());
+
+            /* Add all jar files in the directory. * /
+            FileFilter filter = new JarFilter();
+            File[] jars = commonDirectory.listFiles(filter);
+            for (int i = 0; i < jars.length; i++) {
+                urlList.add(jars[i].toURI().toURL());
+            }
+            */
+
+        }
+        URL[] urlArray = new URL[urlList.size()];
+        for (int i = 0; i < urlArray.length; i++) {
+            urlArray[i] = (URL) urlList.get(i);
+        }
+        SettableCodebaseClassLoader loader = null;
+
+        loader = new SettableCodebaseClassLoader(urlArray, parent);
+        return loader;
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer listString = new StringBuffer();
+        listString.append(getClass().getName() + " [");
+        URL[] urlArray = super.getURLs();
+        for (int i = 0; i < urlArray.length; i++) {
+            listString.append(" ");
+            listString.append(urlArray[i]);
+        }
+        listString.append("], codebase [");
+        urlArray = getURLs();
+        for (int i = 0; i < urlArray.length; i++) {
+            listString.append(" ");
+            listString.append(urlArray[i]);
+        }
+        listString.append("]");
+        return listString.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/ShowContextToConsole.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/ShowContextToConsole.java b/river-container-core/src/main/java/org/apache/river/container/ShowContextToConsole.java
new file mode 100644
index 0000000..85a5e42
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/ShowContextToConsole.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+package org.apache.river.container;
+
+import java.util.ResourceBundle;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author trasukg
+ */
+public class ShowContextToConsole implements ContextListener {
+
+    private static final Logger log=Logger.getLogger(ShowContextToConsole.class.getName(), MessageNames.BUNDLE_NAME);
+
+    private Context context;
+
+    public void init() {
+
+        for(String key: context.contents.keySet()) {
+            log.log(Level.FINE,MessageNames.CONTEXT_ITEM ,
+                    new Object[] {key, context.contents.get(key)});
+        }
+    }
+
+    public void put(String name, Object o) {
+
+    }
+
+    public void remove(Object o) {
+
+    }
+
+    public void setContext(Context ctx) {
+        context=ctx;
+    }
+
+    public void initComplete() {
+        init();
+    }
+
+    @Override
+    public void shutDown() {
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/Shutdown.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/Shutdown.java b/river-container-core/src/main/java/org/apache/river/container/Shutdown.java
new file mode 100644
index 0000000..62be5b0
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/Shutdown.java
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ Identifies a method which should be called when the context is being shut down.
+ * @author trasukg
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Shutdown {
+    
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/ShutdownListener.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/ShutdownListener.java b/river-container-core/src/main/java/org/apache/river/container/ShutdownListener.java
new file mode 100644
index 0000000..5f3f529
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/ShutdownListener.java
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+package org.apache.river.container;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author trasukg
+ */
+public class ShutdownListener implements ShutdownListenerMXBean, Runnable {
+    
+    @Injected(style= InjectionStyle.BY_TYPE)
+    private Context context=null;
+    
+    public void run() {
+        synchronized(this) {
+            try {
+                wait();
+                context.shutDown();
+                System.exit(0);
+            } catch (InterruptedException ex) {
+                Logger.getLogger(ShutdownListener.class.getName()).log(Level.SEVERE, null, ex);
+            }
+        }
+    }
+
+    public void shutdown() {
+        synchronized(this) {
+            this.notifyAll();
+        }
+    }
+
+    @Init
+    public void init() {
+        new Thread(this).start();
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/ShutdownListenerMXBean.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/ShutdownListenerMXBean.java b/river-container-core/src/main/java/org/apache/river/container/ShutdownListenerMXBean.java
new file mode 100644
index 0000000..8b2ca01
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/ShutdownListenerMXBean.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+package org.apache.river.container;
+
+import javax.management.MXBean;
+
+/**
+ *
+ * @author trasukg
+ */
+@MXBean
+public interface ShutdownListenerMXBean {
+    public void shutdown();
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/Strings.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/Strings.java b/river-container-core/src/main/java/org/apache/river/container/Strings.java
new file mode 100644
index 0000000..0e7a9a0
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/Strings.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+package org.apache.river.container;
+
+/**
+ *
+ * @author trasukg
+ */
+public class Strings {
+
+    public static final String
+            ADD_URL="addURL",
+            BOOTSTRAP_CLASS_LOADER="bootstrapClassLoader",
+            CLASS_LOADERS="classLoaders",
+            COMMAND_LINE_ARGS = "commandLineArguments",
+            CONFIG_XML = "config.xml",
+            CORE_CONFIG_XML="core-config.xml",
+            CONTAINER_CLASS_LOADER="containerClassLoader",
+            CONTAINER_JMX_DOMAIN="org.apache.river.container",
+            CONTEXT_CLASS = "org.apache.river.container.Context",
+            DASH = "-",
+            DEFAULT = "default",
+            DEFAULT_DEPLOY_DIRECTORY="deploy",
+            DEFAULT_DISCOVERY_CONTEXT = "defaultDiscoveryContext",
+            DOLLAR="$",
+            DOT=".",
+            DOT_CLASS=".class",
+            DOT_JAR=".jar",
+            DOT_PROPERTIES=".properties",
+            DOT_SSAR=".ssar",
+            EMPTY = "",
+            GET_ADMIN="getAdmin",
+            FILE_UTILITY="fileUtility",
+            INIT_COMPLETE="initComplete",
+            JAR="jar",
+            LIB="lib",
+            LIB_DL="lib-dl",
+            LIFECYCLE_CLASS="com.sun.jini.start.LifeCycle",
+            MAIN="main",
+            NAME="name",
+            PLATFORM_JARS="platformJars",
+            PLATFORM_CODEBASE="platformCodebase",
+            PROFILE = "profile",
+            PROFILE_DIR="profileDirectory",
+            PUT = "put",
+            PUT_SPECIAL_ENTRY="putSpecialEntry",
+            READ="read",
+            SECURITY_POLICY="securityPolicy",
+            SPACE=" ",
+            SSAR="ssar",
+            SET = "set",
+            SET_WORKING_DIRECTORY="setWorkingDirectory",
+            SLASH="/",
+            START_CLASS="startClass",
+            START_PARAMETERS="startParameters",
+            START_PROPERTIES="start.properties",
+            STARTER_SERVICE_DEPLOYER_CONFIG="service-starter.cfg",
+            SYSTEM_CLASS_LOADER="systemClassLoader",
+            TYPE="type",
+            UNKNOWN="unknown",
+            UNNAMED="unnamed",
+            WHITESPACE_SEPARATORS=" \t\n\r",
+            WORK="work";
+
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/SurrogateInstaller.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/SurrogateInstaller.java b/river-container-core/src/main/java/org/apache/river/container/SurrogateInstaller.java
new file mode 100644
index 0000000..e180104
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/SurrogateInstaller.java
@@ -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.
+ */
+package org.apache.river.container;
+
+import org.apache.river.container.deployer.ApplicationEnvironment;
+import java.io.File;
+
+/**
+ *
+ * @author trasukg
+ */
+public class SurrogateInstaller {
+
+    /**
+    Create and install a surrogate based on a surrogate file which has
+    been unpacked into a working directory.
+    
+    <p>This seems to be a lot like installing a generic application, apart
+    from the specific ways of determining the surrogate's class, so
+    one wonders whether we might eventually make this a plain application
+    loader, and separate out the surrogate-specific items into the
+    connector.
+    </p>
+    <p>More formally then, the surrogate connector could read a surrogate
+    file, then create a plain application that represents the surrogate.
+    </p>
+    <p>In that case, the surrogate application is simply a plain application
+    that has some extra limitations placed on it (e.g. no access to
+    local resources, a more restrictive security policy, etc).
+    </p>
+    
+    @param workingDir
+     */
+    void installSurrogate(Host host, File workingDir) {
+        // Create a context for the surrogate.
+        ApplicationEnvironment appEnv = host.newApplicationEnvironment();
+
+        try {
+            /* Configure the application environment. */
+            configureApplicationEnvironment(appEnv, workingDir);
+
+            /* Startup the application environment. */
+            // Initialize the class loader with the surrogate's classes
+            /* TODO: Set the parent classloader to what? 
+            SettableCodebaseClassLoader classLoader =
+            SettableCodebaseClassLoader.createLoader(null, workingDir);
+            appEnv.setClassLoader(classLoader);
+             */
+            // Instantiate the surrogate.
+            // Try the surrogate's getCodebase method to find the codebase
+            /*
+            If codebase method returns nothing,
+            read the manifest to get the
+            codebase entries.
+             */
+            /*
+             * Setup the discovery manager for the surrogate.
+             */
+            /*
+             * Publish the surrogate's codebase jars through hosts's codebase server.
+             */
+            /* Initialize the surrogate.
+             */
+            /*
+             * In case of failure, unpublish the codebase and clean up.
+             */
+            /*
+             * Initiate liveness callbacks.
+             */
+        } catch (Exception e) {
+            /* TODO: Handle this properly! */
+            e.printStackTrace();
+        }
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    private void configureApplicationEnvironment(ApplicationEnvironment appEnv, File workingDir) {
+        /* Read the manifest to get the surrogate's class. */
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/SystemClassloaderInitializer.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/SystemClassloaderInitializer.java b/river-container-core/src/main/java/org/apache/river/container/SystemClassloaderInitializer.java
new file mode 100644
index 0000000..6654f7b
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/SystemClassloaderInitializer.java
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container;
+
+import java.net.URLClassLoader;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+    This class simply sets the systemClassLoader property in the context.
+ * @author trasukg
+ */
+public class SystemClassloaderInitializer {
+    private static final Logger log=Logger.getLogger(SystemClassloaderInitializer.class.getName(), MessageNames.BUNDLE_NAME);
+    
+    @Injected(style= InjectionStyle.BY_TYPE) private Context context=null;
+    
+    @Init
+    public void init() {
+        Map<String, ClassLoader> classLoaders=
+                (Map<String, ClassLoader>) context.get(Strings.CLASS_LOADERS);
+        ClassLoader cl=classLoaders.get(Strings.SYSTEM_CLASS_LOADER);
+        for (String id: classLoaders.keySet()) {
+            context.put(id, classLoaders.get(id));
+        }
+        String classpath=Strings.UNKNOWN;
+        if(cl instanceof URLClassLoader) {
+            URLClassLoader ucl=(URLClassLoader) cl;
+            classpath=Utils.format(ucl.getURLs());
+        }
+        log.log(Level.FINE, MessageNames.SYSTEM_CLASSLOADER_IS,
+                new Object[] {
+                    cl.toString(), classpath
+                });
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/Utils.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/Utils.java b/river-container-core/src/main/java/org/apache/river/container/Utils.java
new file mode 100644
index 0000000..7da2d6f
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/Utils.java
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+package org.apache.river.container;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileType;
+
+/**
+
+ @author trasukg
+ */
+public class Utils {
+
+    public static String format(Object array[]) {
+        if (array == null) {
+            return "null";
+        }
+        StringBuffer sb = new StringBuffer();
+        sb.append("[");
+        for (int j = 0; j < array.length; j++) {
+            if (j != 0) {
+                sb.append(", ");
+            }
+            sb.append("'");
+            sb.append(array[j].toString());
+            sb.append("'");
+        }
+        sb.append("]");
+
+        return sb.toString();
+    }
+
+    public static String format(Properties props) {
+        if (props == null) {
+            return "null";
+        }
+        StringBuffer sb = new StringBuffer();
+        sb.append("[");
+        for (Map.Entry entry : props.entrySet()) {
+            boolean first = true;
+            if (!first) {
+                sb.append(", ");
+            } else {
+                first = false;
+            }
+            sb.append(entry.getKey() + "=\"");
+            sb.append(entry.getValue());
+            sb.append("\"");
+        }
+        sb.append("]");
+
+        return sb.toString();
+    }
+
+    public static String[] splitOnWhitespace(String input) {
+        List<String> strings = new ArrayList<String>();
+        StringTokenizer tok = new StringTokenizer(Strings.WHITESPACE_SEPARATORS);
+        while (tok.hasMoreTokens()) {
+            strings.add(tok.nextToken());
+        }
+        return (String[]) strings.toArray(new String[0]);
+    }
+
+    public static List<FileObject> findChildrenWithSuffix(FileObject dir, String suffix) throws FileSystemException {
+
+        List<FileObject> ret = new ArrayList<FileObject>();
+
+        for (FileObject fo : dir.getChildren()) {
+            if (fo.getType() == FileType.FILE && fo.getName().getBaseName().endsWith(suffix)) {
+                ret.add(fo);
+            }
+        }
+        return ret;
+    }
+
+    public static void logClassLoaderHierarchy(Logger log,
+            Class cls) {
+        logClassLoaderHierarchy(log, Level.FINE, cls);
+    }
+
+    public static void logClassLoaderHierarchy(Logger log, Level level,
+            Class cls) {
+        log.log(level, MessageNames.CLASSLOADER_IS,
+                new Object[]{cls.getName(), cls.getClassLoader()});
+        ClassLoader parent = cls.getClassLoader().getParent();
+        while (parent != null) {
+            log.log(level, MessageNames.PARENT_CLASS_LOADER_IS,
+                    new Object[]{parent});
+            parent = parent.getParent();
+        }
+    }
+
+    public static void logClassLoaderHierarchy(Logger log,
+            Level level,
+            ClassLoader loader) {
+        log.log(level, MessageNames.CLASSLOADER_IS,
+                new Object[]{null, loader});
+        ClassLoader parent = loader.getParent();
+        while (parent != null) {
+            log.log(level, MessageNames.PARENT_CLASS_LOADER_IS,
+                    new Object[]{parent});
+            parent = parent.getParent();
+        }
+    }
+    
+    public static String stackTrace(Throwable t) {
+        StringWriter s=new StringWriter();
+        PrintWriter pw=new PrintWriter(s);
+        t.printStackTrace(pw);
+        return s.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/classloading/ASTNode.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/classloading/ASTNode.java b/river-container-core/src/main/java/org/apache/river/container/classloading/ASTNode.java
new file mode 100644
index 0000000..f9e9a82
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/classloading/ASTNode.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container.classloading;
+
+/**
+ *
+ * @author trasukg
+ */
+public class ASTNode extends SimpleNode {
+
+    private Object value=null;
+
+    public Object getValue() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+
+    
+    public ASTNode(int i) {
+        super(i);
+    }
+
+    public ASTNode(ClasspathExpressionParser p, int i) {
+        super(p, i);
+    }
+
+    public String getName() {
+        return ClasspathExpressionParserTreeConstants.jjtNodeName[id];
+    }
+
+    public String toString() {
+        if (id==ClasspathExpressionParserTreeConstants.JJTSYMBOL
+                || id==ClasspathExpressionParserTreeConstants.JJTSTRINGLITERAL) {
+            return getValue().toString();
+        }
+        String childList = childList();
+        if (!Strings.EMPTY.equals(childList)) {
+            return getName() + " " + childList;
+        } else {
+            return getName();
+        }
+    }
+
+    public String childList() {
+        StringBuffer sb = new StringBuffer();
+        if (jjtGetNumChildren() != 0) {
+            boolean first = true;
+            for (int i = 0; i < jjtGetNumChildren(); i++) {
+                if (!first) {
+                    sb.append(" ");
+                } else {
+                    first = false;
+                }
+                String childStr = jjtGetChild(i).toString();
+                if (childStr.indexOf(Strings.SPACE) != -1) {
+                    sb.append(Strings.LPAREN);
+                    sb.append(childStr);
+                    sb.append(Strings.RPAREN);
+                } else {
+                    sb.append(jjtGetChild(i).toString());
+                }
+            }
+        }
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/classloading/Acceptor.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/classloading/Acceptor.java b/river-container-core/src/main/java/org/apache/river/container/classloading/Acceptor.java
new file mode 100644
index 0000000..69989fc
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/classloading/Acceptor.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container.classloading;
+
+/**
+ *
+ * @author trasukg
+ */
+public interface Acceptor {
+    public boolean acceptsResource(String resourcePath);
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/classloading/AllAcceptor.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/classloading/AllAcceptor.java b/river-container-core/src/main/java/org/apache/river/container/classloading/AllAcceptor.java
new file mode 100644
index 0000000..ec60c5b
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/classloading/AllAcceptor.java
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container.classloading;
+
+/**
+ *
+ * @author trasukg
+ */
+public class AllAcceptor implements Acceptor {
+
+    public boolean acceptsClass(String className) {
+        return true;
+    }
+
+    public boolean acceptsResource(String resourcePath) {
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathEntry.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathEntry.java b/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathEntry.java
new file mode 100644
index 0000000..f0024a0
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathEntry.java
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container.classloading;
+
+import java.util.logging.Logger;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.river.container.MessageNames;
+
+/**
+ * A ClassPathEntry is used by the VirtualFileSystemClassLoader, and is a
+ combination of a ClasspathFilter and the fileObject that points to the entry's
+ jar file.  It effectively represents an entry like 'container.jar(org.apache.ABC)',
+ which would mean 'the class org.apache.ABC contained inside the jar file
+ container.jar'.  The idea is to include selected packages from a jar file on the
+ classpath,
+ * @author trasukg
+ */
+public class ClasspathEntry {
+    private ClasspathFilter classpathFilter=null;
+
+    private FileObject fileObject=null;
+
+    public ClasspathEntry(ClasspathFilter filter, FileObject fileObject) {
+        this.fileObject=fileObject;
+        this.classpathFilter=filter;
+    }
+
+    public FileObject resolveFile(String name) throws FileSystemException {
+        if ((classpathFilter.acceptsResource(name))) {
+            return fileObject.resolveFile(name);
+        }
+        return null;
+    }
+    @Override
+    public String toString() {
+        return fileObject.toString() + classpathFilter.toString();
+    }
+        
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathFilter.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathFilter.java b/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathFilter.java
new file mode 100644
index 0000000..2748674
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathFilter.java
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+package org.apache.river.container.classloading;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author trasukg
+ */
+public class ClasspathFilter {
+
+    private String jarName = null;
+
+    public void setJarName(String s) {
+        jarName = s;
+    }
+
+    public String getJarName() {
+        return jarName;
+    }
+    List<Acceptor> acceptors = new ArrayList<Acceptor>();
+
+    public List<Acceptor> getAcceptors() {
+        return acceptors;
+    }
+
+    /**
+    Returns true if this filter accepts the given resource path.
+    Resource path is in the form '/META-INF/ABC.xyz'
+    @param resourcePath
+    @return
+     */
+    public boolean acceptsResource(String resourcePath) {
+
+        for (Acceptor a : getAcceptors()) {
+            if (a.acceptsResource(resourcePath)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    public String toString() {
+        return "("+acceptorString()+")";
+    }
+    
+    private String acceptorString() {
+        StringBuilder sb=new StringBuilder();
+        boolean first=true;
+        for (Acceptor a: getAcceptors()) {
+            if (!first) {
+                sb.append(", ");
+            } else {
+                first=false;
+            }
+            sb.append(a);     
+        }
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathFilterBuilder.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathFilterBuilder.java b/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathFilterBuilder.java
new file mode 100644
index 0000000..fc2b1ce
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/classloading/ClasspathFilterBuilder.java
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+package org.apache.river.container.classloading;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+import org.apache.river.container.LocalizedRuntimeException;
+import org.apache.river.container.MessageNames;
+
+/**
+ *
+ * @author trasukg
+ */
+public class ClasspathFilterBuilder {
+
+    private static final Logger log = Logger.getLogger(ClasspathFilterBuilder.class.getName());
+
+    public List<ClasspathFilter> parseToFilters(String input) {
+        try {
+            ASTcpExpression expression = classpathExpressionFromString(input);
+            List<ClasspathFilter> filters = filtersFromClasspathExpression(expression);
+            return filters;
+        } catch (ParseException ex) {
+            throw new LocalizedRuntimeException(MessageNames.BUNDLE_NAME,
+                    MessageNames.BAD_CLASSPATH_EXPR,
+                    new Object[]{input, ex.getMessage()});
+        }
+    }
+
+    private ASTcpExpression classpathExpressionFromString(String input) throws ParseException {
+        Reader r = new StringReader(input);
+        ClasspathExpressionParser parser = new ClasspathExpressionParser(r);
+        parser.cpExpression();
+        ASTcpExpression expression = (ASTcpExpression) parser.jjtree.popNode();
+        return expression;
+    }
+
+    private List<ClasspathFilter> filtersFromClasspathExpression(ASTcpExpression expression) {
+        List<ClasspathFilter> filters = new ArrayList<ClasspathFilter>();
+        for (int i = 0; i < expression.jjtGetNumChildren(); i++) {
+            ASTcpClause clause = (ASTcpClause) expression.jjtGetChild(i);
+            ClasspathFilter cpf = makeFilter(clause);
+            filters.add(cpf);
+        }
+        return filters;
+    }
+
+    public ClasspathFilter makeFilter(ASTcpClause expression) {
+        /* First node is the jar name.  Subsequent nodes are the filter
+        conditions.
+         */
+        ClasspathFilter cpf = new ClasspathFilter();
+        cpf.setJarName(expression.jjtGetChild(0).toString());
+        for (int i = 1; i < expression.jjtGetNumChildren(); i++) {
+            Node node = expression.jjtGetChild(i);
+            if (node instanceof ASTsymbol) {
+                String resourceName = VirtualFileSystemClassLoader.classToResourceName(node.toString());
+                log.fine("Building ResourceAcceptor with string '" + resourceName + "'");
+                Acceptor acc = new ResourceAcceptor(resourceName);
+                cpf.getAcceptors().add(acc);
+            }
+            if (node instanceof ASTstringLiteral) {
+                log.fine("Building ResourceAcceptor with string '" + node.toString() + "'");
+                Acceptor acc = new ResourceAcceptor(node.toString());
+                cpf.getAcceptors().add(acc);
+            }
+        }
+        /* If there were no filter clauses, hence no acceptors, allow all
+        patterns.
+         */
+        if (cpf.getAcceptors().isEmpty()) {
+            cpf.getAcceptors().add(new AllAcceptor());
+        }
+        return cpf;
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/classloading/ResourceAcceptor.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/classloading/ResourceAcceptor.java b/river-container-core/src/main/java/org/apache/river/container/classloading/ResourceAcceptor.java
new file mode 100644
index 0000000..d848370
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/classloading/ResourceAcceptor.java
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container.classloading;
+
+import java.util.Arrays;
+import org.apache.river.container.Utils;
+
+/**
+ *
+ * @author trasukg
+ */
+public class ResourceAcceptor implements Acceptor {
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final ResourceAcceptor other = (ResourceAcceptor) obj;
+        if (!Arrays.deepEquals(this.pathSteps, other.pathSteps)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 37 * hash + Arrays.deepHashCode(this.pathSteps);
+        return hash;
+    }
+
+    String[] pathSteps=null;
+
+    public ResourceAcceptor(String resourcePath) {
+        pathSteps=resourcePath.split(Strings.SLASH);
+    }
+
+    public boolean acceptsResource(String resourcePath) {
+        /* A better programmer would use regular expressions here.
+         But then he would have two problems...
+         */
+        String[] inputPathSteps=resourcePath.split(Strings.SLASH);
+        int inputIndex=0, pathStepIndex=0;
+        for (;;) {
+            /* Hit the end of both paths at the same time. */
+            if (inputIndex==inputPathSteps.length && pathStepIndex==pathSteps.length) {
+                return true;
+            }
+            /* End of one path but not the other. */
+            if (inputIndex==inputPathSteps.length || pathStepIndex==pathSteps.length) {
+                return false;
+            }
+            if (pathSteps[pathStepIndex].equals(inputPathSteps[inputIndex])) {
+                pathStepIndex++;
+                inputIndex++;
+                continue;
+            }
+            if (Strings.STAR.equals(pathSteps[pathStepIndex])) {
+                pathStepIndex++;
+                inputIndex++;
+                continue;
+            }
+            else {
+                return false;
+            }
+        }
+    }
+
+    public String toString() {
+        return "ResourceAcceptor(" + Utils.format(pathSteps) + ")";
+    }
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/classloading/Strings.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/classloading/Strings.java b/river-container-core/src/main/java/org/apache/river/container/classloading/Strings.java
new file mode 100644
index 0000000..4b42406
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/classloading/Strings.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package org.apache.river.container.classloading;
+
+/**
+ *
+ * @author trasukg
+ */
+public class Strings {
+    public static final String
+            DOT=".",
+            DOT_CLASS=".class",
+            EMPTY="",
+            GET_NAME="getName",
+            GET_VALUE="getValue",
+            ID="id",
+            JAR="jar",
+            JJT_GET_CHILD="jjtGetChild",
+            JJT_GET_NUM_CHILDREN="jjtGetNumChildren",
+            JJTLITERAL="JJTLITERAL",
+            JJTSYMBOL="JJTSYMBOL",
+            LIB="lib",
+            LPAREN="(",
+            RPAREN=")",
+            SLASH="/",
+            SPACE=" ",
+            STAR="*";
+}

http://git-wip-us.apache.org/repos/asf/river-container/blob/9ab01093/river-container-core/src/main/java/org/apache/river/container/classloading/VirtualFileSystemClassLoader.java
----------------------------------------------------------------------
diff --git a/river-container-core/src/main/java/org/apache/river/container/classloading/VirtualFileSystemClassLoader.java b/river-container-core/src/main/java/org/apache/river/container/classloading/VirtualFileSystemClassLoader.java
new file mode 100644
index 0000000..19a6c63
--- /dev/null
+++ b/river-container-core/src/main/java/org/apache/river/container/classloading/VirtualFileSystemClassLoader.java
@@ -0,0 +1,298 @@
+/*
+ * 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.
+ */
+package org.apache.river.container.classloading;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSource;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import net.jini.security.Security;
+import org.apache.commons.vfs2.FileObject;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileUtil;
+import org.apache.river.container.LocalizedRuntimeException;
+import org.apache.river.container.MessageNames;
+
+/**
+
+ @author trasukg
+ */
+public class VirtualFileSystemClassLoader extends URLClassLoader {
+
+    private FileObject fileSystemRoot = null;
+    private List<ClasspathEntry> classpathEntries = new ArrayList<ClasspathEntry>();
+    private CodeSource codeSource = null;
+
+    public VirtualFileSystemClassLoader(FileObject fileSystemRoot, ClassLoader parent, CodeSource codeSource) {
+        super(new URL[0], parent);
+        this.fileSystemRoot = fileSystemRoot;
+        this.codeSource = codeSource;
+    }
+
+    public static String classToResourceName(String name) {
+        String resourceName = name.replace(Strings.DOT, Strings.SLASH).concat(Strings.DOT_CLASS);
+        return resourceName;
+    }
+
+    /**
+     Add the given classpath to this classloader, based on the default root
+     supplied at construction time.
+
+     @param classPath
+     */
+    public void addClassPathEntry(String classPath) {
+
+        addClassPathEntry(fileSystemRoot, classPath);
+    }
+
+    /**
+     Add the given classpath to this classloader, based on the given fileRoot.
+     The classpath can contain multiple entries, separated by colons, e.g.
+     'jsk-platform.jar:jsk-lib.jar'.<br> Each entry can either be a jar file or
+     a jar file with a list of classes that the jar file can be used to provide.
+     For instance, 'surrogate.jar(org.apache.ABC, org.apache.DEF)'.
+
+     @param fileRoot
+     @param classPath
+     */
+    public void addClassPathEntry(FileObject fileRoot, String classPath) {
+
+        try {
+            /*
+             Classpath entry is a jar file with filter expressions that can be
+             understood by ClasspathFilterBuilder.
+             */
+            /*
+             Create a nested file system from it and add it to the file objects.
+             */
+            List<ClasspathFilter> filters = new ClasspathFilterBuilder().parseToFilters(classPath);
+            addClasspathFilters(filters, fileRoot);
+        } catch (FileSystemException ex) {
+            throw new LocalizedRuntimeException(ex, MessageNames.BUNDLE_NAME, MessageNames.INVALID_CLASSPATH_ENTRY, classPath);
+        }
+    }
+
+    public void addClasspathFilters(List<ClasspathFilter> filters, FileObject fileRoot) throws FileSystemException {
+        for (ClasspathFilter filter : filters) {
+            FileObject entryObject = fileRoot.resolveFile(filter.getJarName());
+
+            FileObject entryFileSystem =
+                    fileRoot.getFileSystem().getFileSystemManager().createFileSystem(entryObject);
+            classpathEntries.add(new ClasspathEntry(filter, entryFileSystem));
+        }
+    }
+
+    /**
+     Find a resource by searching through all the classpath entries that have
+     been set up.
+
+     @param name
+     @return
+     */
+    @Override
+    public URL findResource(final String name) {
+        try {
+            return (URL) Security.doPrivileged(new PrivilegedExceptionAction<URL>() {
+
+                @Override
+                public URL run() throws Exception {
+                    FileObject fo = findResourceFileObject(name);
+                    return fo == null ? null : fo.getURL();
+                }
+            });
+
+        } catch (Exception ex) {
+            Logger.getLogger(VirtualFileSystemClassLoader.class.getName()).log(Level.SEVERE, null, ex);
+        }
+        return null;
+    }
+
+    @Override
+    public Enumeration<URL> findResources(final String name) throws IOException {
+
+        Enumeration result = (Enumeration)
+                Security.doPrivileged(new PrivilegedAction<Enumeration>() {
+
+            public Enumeration run() {
+                List<URL> urlList = new ArrayList<URL>();
+                try {
+
+                    List<FileObject> foList = findResourceFileObjects(name);
+                    for (FileObject fo : foList) {
+                        urlList.add(fo.getURL());
+                    }
+                } catch (FileSystemException ex) {
+                    Logger.getLogger(VirtualFileSystemClassLoader.class.getName()).log(Level.SEVERE, null, ex);
+                }
+                return Collections.enumeration(urlList);
+            }
+        });
+        return result;
+    }
+
+    /**
+     Find the file object for a resource by searching through all the classpath
+     entries that have been set up.
+
+     @param name
+     @return
+     */
+    public FileObject findResourceFileObject(String name) {
+        for (ClasspathEntry cpEntry : classpathEntries) {
+            try {
+                FileObject fo = cpEntry.resolveFile(name);
+                if (fo != null && fo.isReadable()) {
+                    return fo;
+                }
+            } catch (FileSystemException ex) {
+                Logger.getLogger(VirtualFileSystemClassLoader.class.getName()).log(Level.SEVERE, null, ex);
+            }
+        }
+        return null;
+    }
+
+    /**
+     Find the all the file objects for a resource by searching through all the
+     classpath entries that have been set up.
+
+     @param name
+     @return
+     */
+    public List<FileObject> findResourceFileObjects(String name) {
+        List<FileObject> foList = new ArrayList<FileObject>();
+        for (ClasspathEntry cpEntry : classpathEntries) {
+            try {
+                FileObject fo = cpEntry.resolveFile(name);
+                if (fo != null && fo.isReadable()) {
+                    foList.add(fo);
+                }
+            } catch (FileSystemException ex) {
+                Logger.getLogger(VirtualFileSystemClassLoader.class.getName()).log(Level.SEVERE, null, ex);
+            }
+        }
+        return foList;
+    }
+
+    @Override
+    protected Class<?> findClass(final String name) throws ClassNotFoundException {
+        try {
+            return (Class) Security.doPrivileged(new PrivilegedExceptionAction<Class>() {
+
+                public Class run() throws ClassNotFoundException {
+                    String resourceName = classToResourceName(name);
+                    FileObject resourceFileObject = findResourceFileObject(resourceName);
+                    if (resourceFileObject == null) {
+                        throw new ClassNotFoundException(name + "(" + resourceName + ")");
+                    }
+                    try {
+                        byte[] bytes = FileUtil.getContent(resourceFileObject);
+                        return defineClass(name, bytes, 0, bytes.length);
+                    } catch (IOException ioe) {
+                        throw new ClassNotFoundException(name, ioe);
+                    }
+
+                }
+            });
+        } catch (PrivilegedActionException ex) {
+            throw (ClassNotFoundException) ex.getException();
+        }
+    }
+
+    /**
+     Set the codebase URLs to an arbitrary list of URLs. These URLs form the
+     codebase annotation for classes loaded through this classloader. For the
+     sake of general paranoia, sets the codebase to a copy of the provided
+     array.
+
+     @param codebase
+     */
+    public void setCodebase(URL[] codebase) {
+        if (codebase == null || codebase.length == 0) {
+            codebaseURLs = new URL[]{};
+            return;
+        }
+
+        codebaseURLs = new URL[codebase.length];
+        System.arraycopy(codebase, 0, codebaseURLs, 0, codebase.length);
+
+    }
+
+    /**
+     Get the list of URLs that are used for the codebase annotation. Note that
+     this list is not the actual classpath (that is in the superclass). The
+     codebase URLs are imposed to match whatever the Jini service wants to
+     expose as its codebase annotation.
+
+     @return
+     */
+    @Override
+    public URL[] getURLs() {
+        return codebaseURLs;
+    }
+    /**
+     Stores the codebase that will be returned as the codebase annotation.
+
+     */
+    private URL codebaseURLs[] = new URL[0];
+
+    @Override
+    public String toString() {
+        StringBuffer listString = new StringBuffer();
+        listString.append(format(classpathEntries));
+
+        listString.append(", codebase [");
+        URL[] urlArray = getURLs();
+        for (int i = 0; i < urlArray.length; i++) {
+            listString.append(" ");
+            listString.append(urlArray[i]);
+        }
+        listString.append("]");
+        return listString.toString();
+    }
+
+    public static String format(List<ClasspathEntry> items) {
+        if (items == null) {
+            return "null";
+        }
+        StringBuffer sb = new StringBuffer();
+        sb.append("[");
+        boolean first = true;
+        for (Object o : items) {
+            if (!first) {
+                sb.append(", ");
+            } else {
+                first = false;
+            }
+            sb.append("'");
+            sb.append(o.toString());
+            sb.append("'");
+        }
+        sb.append("]");
+
+        return sb.toString();
+    }
+}


Mime
View raw message