RC android: load bundle classes from library
authorMarkus Jung <markus.jung@samsung.com>
Tue, 16 Feb 2016 09:37:32 +0000 (18:37 +0900)
committerJungHo Kim <jhyo.kim@samsung.com>
Sat, 27 Feb 2016 02:37:14 +0000 (02:37 +0000)
This change enables the resource container to load
bundle classes from a .jar dynamically at runtime.
It extends the startContainer method in order to
return the status of a bundle, if it has been
succesfully activated or not.

Removed unnecessary white spaces.

Change-Id: I158889e4366b89b0ac825d789d476d7330894926
Signed-off-by: Markus Jung <markus.jung@samsung.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/5011
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: JungHo Kim <jhyo.kim@samsung.com>
14 files changed:
resource/docs/javadocGen.sh
service/resource-container/android/resource-container/build.gradle
service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleActivator.java
service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/BundleSoftSensorResource.java
service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsBundleInfo.java
service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceAttributes.java
service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainer.java
service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/RcsResourceContainerBundleAPI.java
service/resource-container/android/resource-container/src/main/java/org/iotivity/service/resourcecontainer/ResourceConfig.java
service/resource-container/android/resource-container/src/main/jni/JniRcsResourceContainer.cpp
service/resource-container/examples/android/AndroidBundle/app/build.gradle
service/resource-container/examples/android/RCSampleServerApp/app/src/main/assets/lib/ResourceContainerConfig.xml
service/resource-container/include/RCSBundleInfo.h
service/resource-container/src/ResourceContainerImpl.cpp

index a18252a..3bd28a6 100755 (executable)
@@ -39,9 +39,12 @@ SIMULATOR_COMMON_PKG=org.oic.simulator
 SIMULATOR_CLIENT_PKG=org.oic.simulator.clientcontroller
 SIMULATOR_SERVER_PKG=org.oic.simulator.serviceprovider
 
+RESOURCECONTAINER_PATH="../../service/resource-container/android/resource-container/src/main/java/"
+RESOURCECONTAINER_PKG=org.iotivity.service.resourcecontainer
+
 javadoc -public -splitindex -d ./Java_API -sourcepath \
-    $BASE_PATH:$RE_PATH:$TM_PATH:$RH_PATH:$EASYSETUP_PATH:$SIMULATOR_PATH \
+    $BASE_PATH:$RE_PATH:$TM_PATH:$RH_PATH:$EASYSETUP_PATH:$RESOURCECONTAINER_PATH:$SIMULATOR_PATH \
     $BASE_PKG $RE_COMMON_PKG  $RE_CLINET_PKG $RE_SERVER_PKG $TM_PKG $RH_PKG $EASYSETUP_PKG \
-    $SIMULATOR_COMMON_PKG  $SIMULATOR_CLIENT_PKG $SIMULATOR_SERVER_PKG
+    $SIMULATOR_COMMON_PKG $SIMULATOR_CLIENT_PKG $RESOURCECONTAINER_PKG $SIMULATOR_SERVER_PKG  
 
 
index ee1273e..d466538 100644 (file)
 
 apply plugin: 'com.android.library'
 
+task jar(type: Jar) {
+    from fileTree(dir: 'build/intermediates/classes/release')
+}
+
 android {
     compileSdkVersion 21
     buildToolsVersion "20.0.0"
index 0e0a7e7..ebfd7ce 100644 (file)
@@ -22,6 +22,10 @@ package org.iotivity.service.resourcecontainer;
 import android.content.Context;
 import java.util.List;
 
+/**
+ * Every resource bundle has to provide a bundle activator that can be called
+ * by the resource container on bundle startup.
+ */
 public abstract class BundleActivator {
     protected RcsResourceContainerBundleAPI bundleAPI;
     protected Context appContext;
index 1102740..46eb9d3 100644 (file)
@@ -27,7 +27,10 @@ import java.util.Vector;
 import android.content.Context;
 import android.util.Log;
 
-
+/**
+ * Every resource bundle has to provide a bundle activator that can be called
+ * by the resource container on bundle startup.
+ */
 public abstract class BundleSoftSensorResource extends BundleResource {
     protected HashMap<String, RcsValue> m_mapInputData;
     
index 11e3112..5d2964f 100644 (file)
@@ -34,6 +34,7 @@ public class RcsBundleInfo {
     private final String mActivatorName;
     private final String mLibraryPath;
     private final String mVersion;
+    private boolean mActivated; // not final since it might be modified for Android-specific bundles
 
     private RcsBundleInfo(String id, String path, String activatorName,
             String libraryPath, String version) {
@@ -42,13 +43,24 @@ public class RcsBundleInfo {
         mActivatorName = activatorName;
         mLibraryPath = libraryPath;
         mVersion = version;
+        mActivated = false;
+    }
+
+    private RcsBundleInfo(String id, String path, String activatorName,
+            String libraryPath, String version, boolean activated) {
+        mId = id;
+        mPath = path;
+        mActivatorName = activatorName;
+        mLibraryPath = libraryPath;
+        mVersion = version;
+        mActivated = activated;
     }
 
     /**
      * API for getting the Id of the bundle
-     * 
+     *
      * @return string - Id of the bundle
-     * 
+     *
      */
     public String getID() {
         return mId;
@@ -56,9 +68,9 @@ public class RcsBundleInfo {
 
     /**
      * API for getting the path of the bundle
-     * 
+     *
      * @return path - path of the bundle
-     * 
+     *
      */
     public String getPath() {
         return mPath;
@@ -66,9 +78,9 @@ public class RcsBundleInfo {
 
     /**
      * API for setting the Activator name for the bundle
-     * 
+     *
      * @return string - Name of the activator
-     * 
+     *
      */
     public String getActivatorName() {
         return mActivatorName;
@@ -76,9 +88,9 @@ public class RcsBundleInfo {
 
     /**
      * API for getting the library path for the bundle
-     * 
+     *
      * @return string - Library path in string form
-     * 
+     *
      */
     public String getLibraryPath() {
         return mLibraryPath;
@@ -86,12 +98,31 @@ public class RcsBundleInfo {
 
     /**
      * API for getting the version of the bundle
-     * 
+     *
      * @return string - version of the bundle
-     * 
+     *
      */
     public String getVersion() {
         return mVersion;
     }
+    
+    /**
+     * Returns the current activation status of the bundle
+     *
+     * @return boolean - bundle has been successfully loaded and started
+     *
+     */
+    public boolean isActivated() {
+        return mActivated;
+    }
 
+    /**
+     * Set the current activation status of the bundle
+     *
+     * @return boolean - bundle has been successfully loaded and started
+     *
+     */
+    protected void setActivated(boolean activated) {
+        mActivated = activated;
+    }
 }
index 453716d..87bb7e4 100644 (file)
@@ -27,8 +27,6 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
-//import org.iotivity.service.resourcecontainer.server.RcsLockedAttributes;
-
 /**
  *
  * This class represents the attributes for a resource.
@@ -43,7 +41,6 @@ public final class RcsResourceAttributes
     public RcsResourceAttributes() {
     }
 
-    
     public RcsResourceAttributes(RcsResourceAttributes attrs){
         for (final String key : attrs.keySet()) {
             mCache.put(key, attrs.get(key));
@@ -202,5 +199,4 @@ public final class RcsResourceAttributes
             mCache.put(key, attrs.get(key));
         }
     }
-
 }
index 3eaef68..9d06a86 100644 (file)
@@ -26,6 +26,7 @@
 package org.iotivity.service.resourcecontainer;
 
 import java.util.List;
+
 import java.util.Map;
 import java.util.Enumeration;
 import android.util.Log;
@@ -35,10 +36,15 @@ import java.util.Vector;
 import dalvik.system.DexFile;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import dalvik.system.PathClassLoader;
+import java.net.URLClassLoader;
 
 import java.util.Hashtable;
+import java.io.File;
+import java.net.URL;
+
+import java.lang.reflect.InvocationTargetException;
 
-// TODO null check for parameters
 /**
  * This class provides APIs for managing the container and bundles in the
  * container.
@@ -46,6 +52,7 @@ import java.util.Hashtable;
 public class RcsResourceContainer implements RcsResourceContainerBundleAPI {
 
     private static final String TAG = RcsResourceContainer.class.getSimpleName();
+
     static {
         System.loadLibrary("gnustl_shared");
         System.loadLibrary("oc_logger");
@@ -59,7 +66,7 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI {
         System.loadLibrary("rcs_container");
         System.loadLibrary("resource_container_jni");
     }
-    
+
     private Context appContext;
 
     private native void nativeStartContainer(String configFile);
@@ -114,45 +121,78 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI {
      *            information.
      *
      */
-    public void startContainer(String configFile) {        
+    public List<RcsBundleInfo> startContainer(String configFile) {
         nativeStartContainer(configFile);
-        Log.d(TAG, "startContainer");
+        Log.d(TAG, "startContainer in Java");
         List<RcsBundleInfo> bundles = listBundles();
+        Log.d(TAG, "startContainer. There are " + bundles.size() + " bundles.");
         for(RcsBundleInfo bundleInfo : bundles){
             Log.d(TAG, "bundle-id: " + bundleInfo.getID() + ", " + bundleInfo.getPath());
-            if(bundleInfo.getPath().endsWith(".apk")){
-                String packageName = bundleInfo.getPath().replace(".apk", "");
-                try{
-                    PackageManager packageManager = appContext.getPackageManager();
-                    ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0);
-                    DexFile df = new DexFile(appInfo.sourceDir);
-                    ClassLoader cl = appContext.getClassLoader();
-                    for (Enumeration<String> iter = df.entries(); iter.hasMoreElements(); ) {
-                        String classN = iter.nextElement();
-                        if (classN.contains(packageName)) {
-                            Log.d(TAG,"Class: " + classN);
-                            df.loadClass(classN, cl);
-                        }
-                    }
-                    
-                    String className = bundleInfo.getActivatorName();
-                    Log.d(TAG, "Loading activator: " + className);
-                    Class activatorClass = df.loadClass(className, cl);
-                    if(activatorClass!= null){
-                        BundleActivator activator = (BundleActivator) activatorClass.
-                                getConstructor(RcsResourceContainerBundleAPI.class, Context.class).
-                                newInstance(this, appContext);
-                        activator.activateBundle();
-                        activators.put(bundleInfo.getID(), activator);
-                    }else{
-                        Log.e(TAG, "Activator is null.");
-                    }
-                }
-                catch(Exception e){
-                    Log.e(TAG, e.getMessage(), e);
+            if(bundleInfo.getPath().endsWith(".apk")){ // load classes from standalone application
+                startBundleFromStandaloneApp(bundleInfo);
+            }else if(bundleInfo.getPath().endsWith(".jar")){ // load classes from library
+                startBundleFromJar(bundleInfo);
+            }
+        }
+        return bundles;
+    }
+
+    private void startBundleFromStandaloneApp(RcsBundleInfo bundleInfo){
+        String packageName = bundleInfo.getPath().replace(".apk", "");
+        try{
+            PackageManager packageManager = appContext.getPackageManager();
+            ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0);
+            DexFile df = new DexFile(appInfo.sourceDir);
+            ClassLoader cl = appContext.getClassLoader();
+            for (Enumeration<String> iter = df.entries(); iter.hasMoreElements(); ) {
+                String classN = iter.nextElement();
+                if (classN.contains(packageName)) {
+                    Log.d(TAG,"Class: " + classN);
+                    df.loadClass(classN, cl);
                 }
-                Log.d(TAG, "Have to register android bundle");
             }
+            String className = bundleInfo.getActivatorName();
+            Log.d(TAG, "Loading activator: " + className);
+            Class activatorClass = df.loadClass(className, cl);
+            activateBundle(activatorClass, bundleInfo);
+        }
+        catch(Exception e){
+            Log.e(TAG, e.getMessage(), e);
+        }
+        Log.d(TAG, "Have to register android bundle");
+    }
+    
+    private void startBundleFromJar(RcsBundleInfo bundleInfo){
+        try{
+            Log.e(TAG, "Loading from .jar file.");
+            
+            PathClassLoader classLoader = new PathClassLoader(bundleInfo.getPath(),
+                    RcsResourceContainer.class.getClassLoader());
+           
+            String className = bundleInfo.getActivatorName().replace('/', '.');
+            Log.d(TAG, "Loading activator: " + className);
+            Class activatorClass = Class.forName(className, true, classLoader);
+
+            activateBundle(activatorClass, bundleInfo);
+        }
+        catch(Exception e){
+            Log.e(TAG, e.getMessage(), e);
+        }
+        Log.d(TAG, "Have to register android bundle");
+    }
+    
+    private void activateBundle(Class activatorClass, RcsBundleInfo bundleInfo) throws
+        NoSuchMethodException, InstantiationException, IllegalAccessException, 
+        InvocationTargetException{
+        if(activatorClass!= null){
+            BundleActivator activator = (BundleActivator) activatorClass.
+                    getConstructor(RcsResourceContainerBundleAPI.class, Context.class).
+                    newInstance(this, appContext);
+            activator.activateBundle();
+            activators.put(bundleInfo.getID(), activator);
+            bundleInfo.setActivated(true);
+        }else{
+            Log.e(TAG, "Activator is null.");
         }
     }
 
@@ -227,49 +267,21 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI {
     public void startBundle(String bundleId) {
         Log.d(TAG, "startBundle");
         List<RcsBundleInfo> bundles = listBundles();
-       
-        for(RcsBundleInfo bundleInfo : bundles){          
+
+        for(RcsBundleInfo bundleInfo : bundles){
             if(bundleInfo.getID().equals(bundleId) && bundleInfo.getLibraryPath().endsWith(".apk")){
                 Log.d(TAG, "Have to start android bundle");
                 Log.d(TAG, "bundle-id: " + bundleInfo.getID() + ", " + bundleInfo.getPath());
                 if(bundleInfo.getPath().endsWith(".apk")){
-                    String packageName = bundleInfo.getPath().replace(".apk", "");
-                    try{
-                        PackageManager packageManager = appContext.getPackageManager();
-                        ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0);
-                        DexFile df = new DexFile(appInfo.sourceDir);
-                        ClassLoader cl = appContext.getClassLoader();
-                        for (Enumeration<String> iter = df.entries(); iter.hasMoreElements(); ) {
-                            String classN = iter.nextElement();
-                            if (classN.contains(packageName)) {
-                                Log.d(TAG,"Class: " + classN);
-                                df.loadClass(classN, cl);
-                            }
-                        }
-                        
-                        String className = bundleInfo.getActivatorName();
-                        Log.d(TAG, "Loading activator: " + className);
-                        Class activatorClass = df.loadClass(className, cl);
-                        if(activatorClass!= null){
-                            BundleActivator activator = (BundleActivator) activatorClass.
-                                    getConstructor(RcsResourceContainerBundleAPI.class, 
-                                            Context.class).
-                                    newInstance(this, appContext);
-                            activator.activateBundle();
-                        }else{
-                            Log.e(TAG, "Activator is null.");
-                        }
-                    }
-                    catch(Exception e){
-                        Log.e(TAG, e.getMessage(), e);
-                    }
-                    Log.d(TAG, "Have to register android bundle");
+                    startBundleFromStandaloneApp(bundleInfo);
+                }else if(bundleInfo.getID().equals(bundleId) &&
+                        bundleInfo.getPath().endsWith(".jar")){ // load classes from library
+                    startBundleFromJar(bundleInfo);
                 }
             }else{
                 nativeStartBundle(bundleId);
             }
         }
-      
     }
 
     /**
@@ -324,6 +336,14 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI {
         return nativeListBundleResources(bundleId);
     }
 
+    /**
+     * Registers a bundle resource
+     *
+     * @param bundleId
+     *            Id of the Bundle
+     * @param resource
+     *            resource to be registered
+     */
     public void registerResource(String bundleId, BundleResource resource){
         Log.d(TAG, "register Resource");
         // bundleResources.add(resource);
@@ -331,7 +351,15 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI {
                         resource.getURI(), resource.getResourceType(),
                         resource.getName());
     }
-    
+
+    /**
+     * Returns the bundle configuration for the resources
+     *
+     * @param bundleId
+     *            Id of the Bundle
+     *
+     * @return List<ResourceConfig> All the resource configurations for the given bundle
+     */
     public List<ResourceConfig> getConfiguredBundleResources(String bundleId) {
         Log.d(TAG, "getConfiguredBundleResource " + bundleId);
         int configuredResources = getNumberOfConfiguredResources(bundleId);
@@ -343,22 +371,42 @@ public class RcsResourceContainer implements RcsResourceContainerBundleAPI {
                 String[] resourceParams = getConfiguredResourceParams(bundleId, i);
                 ResourceConfig config = new ResourceConfig(resourceParams);
                 configs.add(config);
-
         }
         return configs;
     }
 
-    
+    /**
+     * Unregisters a bundle resource
+     *
+     * @param resource
+     *            Resource to be unregistered
+     */
     public void unregisterResource(BundleResource resource){
         Log.d(TAG, "unregister Resource");
         nativeUnregisterBundleResource(resource, resource.getURI());
     }
 
+    /**
+     * Returns the number of configured resources
+     *
+     * @param bundleId
+     *            Id of the Bundle
+     * @return number of configured resources
+     */
     public int getNumberOfConfiguredResources(String bundleId){
         Log.d(TAG, "getNumberOfConfiguredResources");
         return nativeGetNumberOfConfiguredResources(bundleId);
     }
 
+    /**
+     * Provides the configured resource parameter
+     *
+     * @param bundleId
+     *            Id of the Bundle
+     * @param resId
+                  Continuous numeric identifier within the bundle
+     * @return resource paramaters such as URI, resource type, name, etc. for the resource
+     */
     public String[] getConfiguredResourceParams(String bundleId, int resId){
         Log.d(TAG, "getConfiguredResourceParams");
         return nativeGetConfiguredResourceParams(bundleId, resId);
index 92954aa..721a7fb 100644 (file)
@@ -31,7 +31,8 @@ import java.util.Vector;
 
 /**
  * This class provides APIs for managing the container and bundles in the
- * container.
+ * container. The container provides such an interface to the bundle developer
+ * to access the configuration and to register/unregister resources.
  */
 public interface RcsResourceContainerBundleAPI{
 
index 09ef002..3bd2beb 100644 (file)
@@ -85,7 +85,8 @@ namespace
         JavaLocalString version{env, newStringObject(env, bundleInfo->getVersion()) };
 
         return env->NewObject(g_cls_RCSBundleInfo, g_ctor_RCSBundleInfo,
-                id.get(), path.get(), activatorName.get(), libraryPath.get(), version.get());
+                id.get(), path.get(), activatorName.get(), libraryPath.get(), version.get(),
+                bundleInfo->isActivated());
     }
 }
 
index 043b375..9e88223 100755 (executable)
@@ -1,11 +1,10 @@
-apply plugin: 'com.android.application'
+apply plugin: 'com.android.library'
 
 android {
     compileSdkVersion 23
     buildToolsVersion "22.0.1"
 
     defaultConfig {
-        applicationId "org.iotivity.service.sample.androidbundle"
         minSdkVersion 21
         targetSdkVersion 23
         versionCode 1
index da6e17f..493d638 100755 (executable)
@@ -37,7 +37,7 @@
             </resourceInfo>
         </resources>
     </bundle>
-    <bundle>
+    <!--<bundle>
         <id>oic.android.sample</id>
         <path>org.iotivity.service.sample.androidbundle.apk</path>
         <activator>org.iotivity.service.sample.androidbundle.SampleActivator</activator>
             </resourceInfo>
         </resources>
     </bundle>
+    <bundle>
+        <id>oic.android.sample</id>
+        <path>data/data/org.iotivity.service.sample.resourcecontainer/files/android_sample_bundle.jar</path>
+        <activator>org.iotivity.service.sample.androidbundle.SampleActivator</activator>
+        <version>1.0.0</version>
+        <resources>
+            <resourceInfo>
+                <name>LightResource1</name>
+                <resourceType>oic.r.light</resourceType>
+                <resourceUri>/android/light/1</resourceUri>
+            </resourceInfo>
+            <resourceInfo>
+                 <name>LightIntensity1</name>
+                 <resourceType>oic.r.lightintensity</resourceType>
+                 <resourceUri>/android/lightintensity/1</resourceUri>
+            </resourceInfo>
+            <resourceInfo>
+                <name>Humidity1</name>
+                <resourceType>oic.r.humidity</resourceType>
+                <resourceUri>/android/humidity/1</resourceUri>
+            </resourceInfo>
+            <resourceInfo>
+                <name>Tepmerature1</name>
+                <resourceType>oic.r.temperature</resourceType>
+                <resourceUri>/android/temperature/1</resourceUri>
+            </resourceInfo>
+            <resourceInfo>
+                <name>Gyroscope1</name>
+                <resourceType>oic.r.gyroscope</resourceType>
+                <resourceUri>/android/gyroscope/1</resourceUri>
+            </resourceInfo>
+            <resourceInfo>
+                <name>DiscomfortIndexSensor1</name>
+                <resourceType>oic.r.discomfortindex</resourceType>
+                <resourceUri>/android/discomfortindex/1</resourceUri>
+                <outputs>
+                    <output>
+                        <name>discomfortIndex</name>
+                        <type>int</type>
+                    </output>
+                    <output>
+                        <name>humidity</name>
+                        <type>double</type>
+                    </output>
+                    <output>
+                        <name>temperature</name>
+                        <type>double</type>
+                    </output>
+                </outputs>
+                <inputs>
+                    <input>
+                        <name>humidity</name>
+                        <type>double</type>
+                        <resourceType>oic.r.humidity</resourceType>
+                    </input>
+                    <input>
+                        <name>temperature</name>
+                        <type>double</type>
+                        <resourceType>oic.r.temperature</resourceType>
+                    </input>
+                </inputs>
+            </resourceInfo>
+        </resources>
+    </bundle>-->
 </container>
\ No newline at end of file
index 31d2902..323cf54 100644 (file)
@@ -82,6 +82,14 @@ namespace OIC
                 */
                 virtual const std::string &getVersion() = 0;
 
+                /**
+                * API for getting the activation status of the bundle
+                *
+                * @return activation status of the bundle
+                *
+                */
+                virtual bool isActivated() = 0;
+
                 RCSBundleInfo();
                 virtual ~RCSBundleInfo();
             protected:
index ce57b2b..62c7dda 100644 (file)
@@ -212,6 +212,11 @@ namespace OIC
                 ((BundleInfoInternal *) bundleInfo)->setJavaBundle(true);
                 ((BundleInfoInternal *) bundleInfo)->setSoBundle(false);
                 registerJavaBundle(bundleInfo);
+#else
+                // android .jar library
+                ((BundleInfoInternal *) bundleInfo)->setSoBundle(false);
+                ((BundleInfoInternal *) bundleInfo)->setJavaBundle(false);
+                registerExtBundle(bundleInfo);
 #endif
             }
             else if(has_suffix(bundleInfo->getPath(), ".so"))