You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by da...@apache.org on 2011/03/16 16:16:54 UTC

svn commit: r1082164 - in /aries/trunk/spi-fly: spi-fly-core/src/main/java/org/apache/aries/spifly/ spi-fly-static/ spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/ spi-fly-static/src/test/java/org/apache/aries/spifly/statictool/

Author: davidb
Date: Wed Mar 16 15:16:50 2011
New Revision: 1082164

URL: http://svn.apache.org/viewvc?rev=1082164&view=rev
Log:
Work in progress for Spi Fly

The beginnings of a static weaver.

Added:
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java
    aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/DirTree.java
Modified:
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java
    aries/trunk/spi-fly/spi-fly-static/pom.xml
    aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/Main.java
    aries/trunk/spi-fly/spi-fly-static/src/test/java/org/apache/aries/spifly/statictool/MainTest.java

Modified: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java?rev=1082164&r1=1082163&r2=1082164&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java (original)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java Wed Mar 16 15:16:50 2011
@@ -18,25 +18,23 @@
  */
 package org.apache.aries.spifly;
 
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.ServiceLoader;
-import java.util.Set;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
-import org.apache.aries.spifly.HeaderParser.PathElement;
 import org.apache.aries.spifly.api.SpiFlyConstants;
 import org.objectweb.asm.ClassReader;
 import org.objectweb.asm.ClassWriter;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
-import org.osgi.framework.Version;
+import org.osgi.framework.BundleEvent;
 import org.osgi.framework.hooks.weaving.WeavingHook;
 import org.osgi.framework.hooks.weaving.WovenClass;
 import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.BundleTracker;
 
 public class ClientWeavingHook implements WeavingHook {
     private final String addedImport;
+    private final Map<Bundle, WeavingData []> bundleWeavingData = new ConcurrentHashMap<Bundle, WeavingData[]>();
     
     ClientWeavingHook(BundleContext context) {
         Bundle b = context.getBundle();
@@ -46,16 +44,46 @@ public class ClientWeavingHook implement
         addedImport = Util.class.getPackage().getName() + 
             ";bundle-symbolic-name=" + bsn + 
             ";bundle-version=" + bver;
+        
+        // TODO move to activator ? and bt.close()!
+        BundleTracker<Object> bt = new BundleTracker<Object>(context, Bundle.INSTALLED, null) {
+            @Override
+            public Object addingBundle(Bundle bundle, BundleEvent event) {
+                String consumerHeader = bundle.getHeaders().get(SpiFlyConstants.SPI_CONSUMER_HEADER);
+                if (consumerHeader != null) {
+                    WeavingData[] wd = ConsumerHeaderProcessor.processHeader(consumerHeader);
+                    bundleWeavingData.put(bundle, wd);
+                    
+                    for (WeavingData w : wd) {
+                        Activator.activator.registerConsumerBundle(bundle, w.getArgRestrictions(), w.getAllowedBundles());
+                    }
+                }                    
+                
+                return super.addingBundle(bundle, event);
+            }
+
+            @Override
+            public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
+                removedBundle(bundle, event, object);
+                addingBundle(bundle, event);
+            }
+
+            @Override
+            public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
+                bundleWeavingData.remove(bundle);
+            }
+        };
+        bt.open();
     }
     
 	@Override
 	public void weave(WovenClass wovenClass) {
 	    Bundle consumerBundle = wovenClass.getBundleWiring().getBundle();
-        String consumerHeader = consumerBundle.getHeaders().get(SpiFlyConstants.SPI_CONSUMER_HEADER);
-        if (consumerHeader != null) {
+        WeavingData[] wd = bundleWeavingData.get(consumerBundle);
+        if (wd != null) {
 	        Activator.activator.log(LogService.LOG_DEBUG, "Weaving class " + wovenClass.getClassName());            
             
-            WeavingData[] wd = processHeader(consumerBundle, consumerHeader);
+//            WeavingData[] wd = ConsumerHeaderProcessor.processHeader(consumerBundle, consumerHeader);
 	        
 	        ClassReader cr = new ClassReader(wovenClass.getBytes());
 	        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
@@ -66,147 +94,4 @@ public class ClientWeavingHook implement
 	            wovenClass.getDynamicImports().add(addedImport);
 	    }			
 	}
-
-	/**
-	 * Parses headers of the following syntax:
-	 * <ul>
-	 * <li><tt>org.acme.MyClass#myMethod</tt> - apply the weaving to all overloads of <tt>myMethod()</tt> 
-	 * in <tt>MyClass</tt>
-	 * <li><tt>org.acme.MyClass#myMethod(java.lang.String, java.util.List)</tt> - apply the weaving only 
-	 * to the <tt>myMethod(String, List)</tt> overload in <tt>MyClass</tt>
-	 * <li><tt>org.acme.MyClass#myMethod()</tt> - apply the weaving only to the noarg overload of 
-	 * <tt>myMethod()</tt>
-	 * <li><b>true</b> - equivalend to <tt>java.util.ServiceLoader#load(java.lang.Class)</tt>
-	 * </ul>
-	 * Additionally, it registers the consumer's contraints with the consumer registry in the activator, if the 
-	 * consumer is only constrained to a certain set of bundles.<p/>
-	 * 
-	 * The following attributes are supported:
-	 * <ul>
-	 * <li><tt>bundle</tt> - restrict wiring to the bundle with the specifies Symbolic Name. The attribute value 
-	 * is a list of bundle identifiers separated by a '|' sign. The bundle identifier starts with the Symbolic name
-	 * and can optionally contain a version suffix. E.g. bundle=impl2:version=1.2.3 or bundle=impl2|impl4.  
-	 * <li><tt>bundleId</tt> - restrict wiring to the bundle with the specified bundle ID. Typically used when 
-	 * the service should be forceably picked up from the system bundle (<tt>bundleId=0</tt>). Multiple bundle IDs 
-	 * can be specified separated by a '|' sign. 
-	 * </ul>
-	 * 
-	 * @param consumerBundle the consuming bundle.
-	 * @param consumerHeader the <tt>SPI-Consumer</tt> header.
-	 * @return an instance of the {@link WeavingData} class.
-	 */
-    private WeavingData[] processHeader(Bundle consumerBundle, String consumerHeader) {
-        Set<WeavingData> weavingData = new HashSet<WeavingData>();
-        
-        for (PathElement element : HeaderParser.parseHeader(consumerHeader)) {
-            List<BundleDescriptor> allowedBundles = new ArrayList<BundleDescriptor>();
-            String name = element.getName().trim();
-
-            String className;
-            String methodName;
-            MethodRestriction methodRestriction;
-            
-            int hashIdx = name.indexOf('#');
-            if (hashIdx > 0) {                
-                className = name.substring(0, hashIdx);
-                int braceIdx = name.substring(hashIdx).indexOf('(');
-                if (braceIdx > 0) {
-                    methodName = name.substring(hashIdx + 1, hashIdx + braceIdx);
-                    ArgRestrictions argRestrictions = new ArgRestrictions();
-                    int closeIdx = name.substring(hashIdx).indexOf(')');
-                    if (closeIdx > 0) {
-                        String classes = name.substring(hashIdx + braceIdx + 1, hashIdx + closeIdx).trim();
-                        if (classes.length() > 0) {
-                            if (classes.indexOf('[') > 0) {
-                                int argNumber = 0;
-                                for (String s : classes.split(",")) {
-                                    int idx = s.indexOf('[');
-                                    int end = s.indexOf(']', idx);
-                                    if (idx > 0 && end > idx) {
-                                        argRestrictions.addRestriction(argNumber, s.substring(0, idx), s.substring(idx + 1, end));
-                                    } else {
-                                        argRestrictions.addRestriction(argNumber, s);
-                                    }
-                                    argNumber++;
-                                }
-                            } else {
-                                String[] classNames = classes.split(",");
-                                for (int i = 0; i < classNames.length; i++) {
-                                    argRestrictions.addRestriction(i, classNames[i]);
-                                }
-                            }
-                        } else {
-                            argRestrictions = null;
-                        }
-                    }
-                    methodRestriction = new MethodRestriction(methodName, argRestrictions);
-                } else {
-                    methodName = name.substring(hashIdx + 1);
-                    methodRestriction = new MethodRestriction(methodName);
-                }
-            } else {
-                if ("*".equalsIgnoreCase(name)) {
-                    className = ServiceLoader.class.getName();
-                    methodName = "load";
-                    ArgRestrictions argRestrictions = new ArgRestrictions();
-                    argRestrictions.addRestriction(0, Class.class.getName());
-                    methodRestriction = new MethodRestriction(methodName, argRestrictions);
-                } else {
-                    throw new IllegalArgumentException("Must at least specify class name and method name: " + name);
-                }
-            }  
-            ConsumerRestriction restriction = new ConsumerRestriction(className, methodRestriction);
-
-            Set<ConsumerRestriction> restrictions = new HashSet<ConsumerRestriction>();
-            restrictions.add(restriction);
-                
-            String bsn = element.getAttribute("bundle");
-            if (bsn != null) {
-                bsn = bsn.trim();
-                if (bsn.length() > 0) {
-                    for (String s : bsn.split("\\|")) {
-                        int colonIdx = s.indexOf(':');
-                        if (colonIdx > 0) {
-                            String sn = s.substring(0, colonIdx);
-                            String versionSfx = s.substring(colonIdx + 1);
-                            if (versionSfx.startsWith("version=")) {
-                                allowedBundles.add(new BundleDescriptor(sn, 
-                                        Version.parseVersion(versionSfx.substring("version=".length()))));
-                            } else {
-                                allowedBundles.add(new BundleDescriptor(sn));
-                            }
-                        } else {
-                            allowedBundles.add(new BundleDescriptor(s));
-                        }
-                    }
-                }
-            }
-                        
-            String bid = element.getAttribute("bundleId");
-            if (bid != null) {
-                bid = bid.trim();
-                if (bid.length() > 0) {
-                    for (String s : bid.split("\\|")) {
-                        for (Bundle b : consumerBundle.getBundleContext().getBundles()) {
-                            if (("" + b.getBundleId()).equals(s)) {                       
-                                allowedBundles.add(new BundleDescriptor(b.getSymbolicName()));
-                                break;                        
-                            }
-                        }                        
-                    }
-                }
-            }
-            
-            // TODO fix log message
-            // Activator.activator.log(LogService.LOG_INFO, "Weaving " + className + "#" + methodName + " from bundle " + 
-            //    consumerBundle.getSymbolicName() + " to " + (allowedBundles.size() == 0 ? " any provider" : allowedBundles));
-                        
-            Activator.activator.registerConsumerBundle(consumerBundle, restrictions, 
-                    allowedBundles.size() == 0 ? null : allowedBundles);           
-            String[] argClasses = restriction.getMethodRestriction(methodName).getArgClasses();
-
-            weavingData.add(new WeavingData(className, methodName, argClasses));
-        }
-        return weavingData.toArray(new WeavingData [weavingData.size()]);
-    }
 }

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java?rev=1082164&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java Wed Mar 16 15:16:50 2011
@@ -0,0 +1,174 @@
+/**
+ * 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.aries.spifly;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+import org.apache.aries.spifly.HeaderParser.PathElement;
+import org.osgi.framework.Version;
+
+public class ConsumerHeaderProcessor {
+    /**
+     * Parses headers of the following syntax:
+     * <ul>
+     * <li><tt>org.acme.MyClass#myMethod</tt> - apply the weaving to all overloads of <tt>myMethod()</tt> 
+     * in <tt>MyClass</tt>
+     * <li><tt>org.acme.MyClass#myMethod(java.lang.String, java.util.List)</tt> - apply the weaving only 
+     * to the <tt>myMethod(String, List)</tt> overload in <tt>MyClass</tt>
+     * <li><tt>org.acme.MyClass#myMethod()</tt> - apply the weaving only to the noarg overload of 
+     * <tt>myMethod()</tt>
+     * <li><b>true</b> - equivalent to <tt>java.util.ServiceLoader#load(java.lang.Class)</tt>
+     * </ul>
+     * Additionally, it registers the consumer's constraints with the consumer registry in the activator, if the 
+     * consumer is only constrained to a certain set of bundles.<p/>
+     * 
+     * The following attributes are supported:
+     * <ul>
+     * <li><tt>bundle</tt> - restrict wiring to the bundle with the specifies Symbolic Name. The attribute value 
+     * is a list of bundle identifiers separated by a '|' sign. The bundle identifier starts with the Symbolic name
+     * and can optionally contain a version suffix. E.g. bundle=impl2:version=1.2.3 or bundle=impl2|impl4.  
+     * <li><tt>bundleId</tt> - restrict wiring to the bundle with the specified bundle ID. Typically used when 
+     * the service should be forceably picked up from the system bundle (<tt>bundleId=0</tt>). Multiple bundle IDs 
+     * can be specified separated by a '|' sign. 
+     * </ul>
+     * 
+     * @param consumerBundle the consuming bundle.
+     * @param consumerHeader the <tt>SPI-Consumer</tt> header.
+     * @return an instance of the {@link WeavingData} class.
+     */
+    public static WeavingData[] processHeader(/* Bundle consumerBundle, */String consumerHeader) {
+        Set<WeavingData> weavingData = new HashSet<WeavingData>();
+        
+        for (PathElement element : HeaderParser.parseHeader(consumerHeader)) {
+            List<BundleDescriptor> allowedBundles = new ArrayList<BundleDescriptor>();
+            String name = element.getName().trim();
+
+            String className;
+            String methodName;
+            MethodRestriction methodRestriction;
+            
+            int hashIdx = name.indexOf('#');
+            if (hashIdx > 0) {                
+                className = name.substring(0, hashIdx);
+                int braceIdx = name.substring(hashIdx).indexOf('(');
+                if (braceIdx > 0) {
+                    methodName = name.substring(hashIdx + 1, hashIdx + braceIdx);
+                    ArgRestrictions argRestrictions = new ArgRestrictions();
+                    int closeIdx = name.substring(hashIdx).indexOf(')');
+                    if (closeIdx > 0) {
+                        String classes = name.substring(hashIdx + braceIdx + 1, hashIdx + closeIdx).trim();
+                        if (classes.length() > 0) {
+                            if (classes.indexOf('[') > 0) {
+                                int argNumber = 0;
+                                for (String s : classes.split(",")) {
+                                    int idx = s.indexOf('[');
+                                    int end = s.indexOf(']', idx);
+                                    if (idx > 0 && end > idx) {
+                                        argRestrictions.addRestriction(argNumber, s.substring(0, idx), s.substring(idx + 1, end));
+                                    } else {
+                                        argRestrictions.addRestriction(argNumber, s);
+                                    }
+                                    argNumber++;
+                                }
+                            } else {
+                                String[] classNames = classes.split(",");
+                                for (int i = 0; i < classNames.length; i++) {
+                                    argRestrictions.addRestriction(i, classNames[i]);
+                                }
+                            }
+                        } else {
+                            argRestrictions = null;
+                        }
+                    }
+                    methodRestriction = new MethodRestriction(methodName, argRestrictions);
+                } else {
+                    methodName = name.substring(hashIdx + 1);
+                    methodRestriction = new MethodRestriction(methodName);
+                }
+            } else {
+                if ("*".equalsIgnoreCase(name)) {
+                    className = ServiceLoader.class.getName();
+                    methodName = "load";
+                    ArgRestrictions argRestrictions = new ArgRestrictions();
+                    argRestrictions.addRestriction(0, Class.class.getName());
+                    methodRestriction = new MethodRestriction(methodName, argRestrictions);
+                } else {
+                    throw new IllegalArgumentException("Must at least specify class name and method name: " + name);
+                }
+            }  
+            ConsumerRestriction restriction = new ConsumerRestriction(className, methodRestriction);
+
+            Set<ConsumerRestriction> restrictions = new HashSet<ConsumerRestriction>();
+            restrictions.add(restriction);
+                
+            String bsn = element.getAttribute("bundle");
+            if (bsn != null) {
+                bsn = bsn.trim();
+                if (bsn.length() > 0) {
+                    for (String s : bsn.split("\\|")) {
+                        int colonIdx = s.indexOf(':');
+                        if (colonIdx > 0) {
+                            String sn = s.substring(0, colonIdx);
+                            String versionSfx = s.substring(colonIdx + 1);
+                            if (versionSfx.startsWith("version=")) {
+                                allowedBundles.add(new BundleDescriptor(sn, 
+                                        Version.parseVersion(versionSfx.substring("version=".length()))));
+                            } else {
+                                allowedBundles.add(new BundleDescriptor(sn));
+                            }
+                        } else {
+                            allowedBundles.add(new BundleDescriptor(s));
+                        }
+                    }
+                }
+            }
+                        
+            // TODO this cannot be done this way with static weaving...
+            /*
+            String bid = element.getAttribute("bundleId");
+            if (bid != null) {
+                bid = bid.trim();
+                if (bid.length() > 0) {
+                    for (String s : bid.split("\\|")) {
+                        for (Bundle b : consumerBundle.getBundleContext().getBundles()) {
+                            if (("" + b.getBundleId()).equals(s)) {                       
+                                allowedBundles.add(new BundleDescriptor(b.getSymbolicName()));
+                                break;                        
+                            }
+                        }                        
+                    }
+                }
+            }
+            */
+                                    
+//            Activator.activator.registerConsumerBundle(consumerBundle, restrictions, 
+//                    allowedBundles.size() == 0 ? null : allowedBundles);
+            // TODO this can be done in the WeavingData itself?
+            String[] argClasses = restriction.getMethodRestriction(methodName).getArgClasses();
+
+            weavingData.add(new WeavingData(className, methodName, argClasses, restrictions, 
+                    allowedBundles.size() == 0 ? null : allowedBundles));
+        }
+        return weavingData.toArray(new WeavingData [weavingData.size()]);
+    }
+}

Modified: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java?rev=1082164&r1=1082163&r2=1082164&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java (original)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java Wed Mar 16 15:16:50 2011
@@ -19,6 +19,8 @@
 package org.apache.aries.spifly;
 
 import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
 
 /** Contains information needed for the byte code weaver.
  */
@@ -26,6 +28,8 @@ public class WeavingData {
     private final String className;
     private final String methodName;
     private final String[] argClasses;
+    private final Set<ConsumerRestriction> argRestrictions;
+    private final List<BundleDescriptor> allowedBundles;
 
     /**
      * Constructor.
@@ -34,11 +38,16 @@ public class WeavingData {
      * @param argClasses The overload (class names of the signature) of the call
      * that needs to be woven. If <code>null</code> then all overloads of the method
      * need to be woven.
+     * @param argRestrictions 
+     * @param allowedBundles 
      */
-    public WeavingData(String className, String methodName, String[] argClasses) {
+    public WeavingData(String className, String methodName, String[] argClasses, Set<ConsumerRestriction> argRestrictions, List<BundleDescriptor> allowedBundles) {
+        // TODO can we infer argClasses from restrictions?
         this.className = className;
         this.methodName = methodName;
         this.argClasses = argClasses;
+        this.argRestrictions = argRestrictions;
+        this.allowedBundles = allowedBundles;
     }
 
     public String getClassName() {
@@ -49,10 +58,18 @@ public class WeavingData {
         return methodName;
     }
 
+    public List<BundleDescriptor> getAllowedBundles() {
+        return allowedBundles;
+    }
+
     public String[] getArgClasses() {
         return argClasses;
-    }
+    }        
 
+    public Set<ConsumerRestriction> getArgRestrictions() {
+        return argRestrictions;
+    }        
+    
     @Override
     public int hashCode() {
         final int prime = 31;
@@ -88,7 +105,5 @@ public class WeavingData {
         } else if (!methodName.equals(other.methodName))
             return false;
         return true;
-    }    
-    
-    
+    }
 }

Modified: aries/trunk/spi-fly/spi-fly-static/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-static/pom.xml?rev=1082164&r1=1082163&r2=1082164&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-static/pom.xml (original)
+++ aries/trunk/spi-fly/spi-fly-static/pom.xml Wed Mar 16 15:16:50 2011
@@ -35,6 +35,12 @@
 
     <dependencies>
         <dependency>
+            <groupId>asm</groupId>
+            <artifactId>asm-all</artifactId>
+            <version>3.3.1</version>
+        </dependency>
+
+        <dependency>
             <groupId>org.apache.aries.spifly</groupId>
             <artifactId>org.apache.aries.spifly.core</artifactId>
             <version>${pom.version}</version>

Added: aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/DirTree.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/DirTree.java?rev=1082164&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/DirTree.java (added)
+++ aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/DirTree.java Wed Mar 16 15:16:50 2011
@@ -0,0 +1,51 @@
+/**
+ * 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.aries.spifly.statictool;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DirTree {
+    List<File> fileList = new ArrayList<File>();
+
+    public DirTree(File f) {
+        String[] names = f.list();
+
+        if (names == null) {
+            fileList.add(f);
+            return;
+        }
+
+        for (String name : names) {
+            File curFile = new File(f, name);
+
+            if (curFile.isDirectory()) {
+                fileList.addAll(new DirTree(curFile).getFiles());
+            } else {
+                fileList.add(curFile);
+            }
+        }
+        fileList.add(f);
+    }
+
+    public List<File> getFiles() {
+        return fileList;
+    }
+}

Modified: aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/Main.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/Main.java?rev=1082164&r1=1082163&r2=1082164&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/Main.java (original)
+++ aries/trunk/spi-fly/spi-fly-static/src/main/java/org/apache/aries/spifly/statictool/Main.java Wed Mar 16 15:16:50 2011
@@ -20,15 +20,23 @@ package org.apache.aries.spifly.staticto
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.jar.JarEntry;
 import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
 import java.util.jar.Manifest;
 
+import org.apache.aries.spifly.ConsumerHeaderProcessor;
 import org.apache.aries.spifly.Streams;
+import org.apache.aries.spifly.TCCLSetterVisitor;
+import org.apache.aries.spifly.WeavingData;
+import org.apache.aries.spifly.api.SpiFlyConstants;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
 
 public class Main {
     public static void usage() {
@@ -45,11 +53,45 @@ public class Main {
 
     private static void weaveJar(String jarPath) throws IOException {
         File jarFile = new File(jarPath);
-        File tempdir = new File(System.getProperty("java.io.tmpdir") + File.separator + jarFile.getName() + "_" + System.currentTimeMillis());        
-        unJar(jarFile, tempdir);
+        File tempDir = new File(System.getProperty("java.io.tmpdir") + File.separator + jarFile.getName() + "_" + System.currentTimeMillis());        
+        Manifest manifest = unJar(jarFile, tempDir);
+        String consumerHeader = manifest.getMainAttributes().getValue(SpiFlyConstants.SPI_CONSUMER_HEADER);
+        if (consumerHeader != null) {
+            weaveDir(tempDir, consumerHeader);
+            // jar(tempDir, newJar);
+        }
+        // finally - clean up
+    }
+
+    private static void weaveDir(File dir, String consumerHeader) throws IOException {
+        DirTree dt = new DirTree(dir);
+        for (File f : dt.getFiles()) {
+            if (!f.getName().endsWith(".class"))
+                continue;
+            
+            WeavingData[] wd = ConsumerHeaderProcessor.processHeader(consumerHeader);
+            InputStream is = new FileInputStream(f);
+            byte[] b;
+            try {
+                ClassReader cr = new ClassReader(is);
+                ClassWriter cw = new ClassWriter(0);
+                ClassVisitor cv = new TCCLSetterVisitor(cw, null, wd); 
+                cr.accept(cv, 0);
+                b = cw.toByteArray();
+            } finally {
+                is.close();
+            }
+            
+            OutputStream os = new FileOutputStream(f);
+            try {
+                os.write(b);                
+            } finally {
+                os.close();
+            }
+        }
     }
 
-    static void unJar(File jarFile, File tempDir) throws IOException {
+    static Manifest unJar(File jarFile, File tempDir) throws IOException {
         ensureDirectory(tempDir);
 
         JarInputStream jis = new JarInputStream(new FileInputStream(jarFile));
@@ -93,6 +135,55 @@ public class Main {
         }
         
         jis.close();
+        return manifest;
+    }
+    
+    static void jar(File jarFile, File rootFile, Manifest manifest) throws IOException {
+        JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile), manifest);
+        try {
+            addToJarRecursively(jos, rootFile.getAbsoluteFile(), rootFile.getAbsolutePath());
+        } finally {
+            jos.close();
+        }
+    }
+
+    static void addToJarRecursively(JarOutputStream jar, File source, String rootDirectory) throws IOException {                
+        String sourceName = source.getAbsolutePath().replace("\\", "/");
+        sourceName = sourceName.substring(rootDirectory.length());
+        
+        if (sourceName.startsWith("/")) {
+            sourceName = sourceName.substring(1);
+        }
+        
+        if ("META-INF/MANIFEST.MF".equals(sourceName))
+            return;
+        
+        if (source.isDirectory()) {
+            /* Is there any point in adding a directory beyond just taking up space? 
+            if (!sourceName.isEmpty()) {
+                if (!sourceName.endsWith("/")) {
+                    sourceName += "/";                        
+                }
+                JarEntry entry = new JarEntry(sourceName);
+                jar.putNextEntry(entry);
+                jar.closeEntry();
+            }
+            */
+            for (File nested : source.listFiles()) {
+                addToJarRecursively(jar, nested, rootDirectory);
+            }
+            return;
+        }
+        
+        JarEntry entry = new JarEntry(sourceName);
+        jar.putNextEntry(entry);
+        InputStream is = new FileInputStream(source);
+        try {
+            Streams.pump(is, jar);
+        } finally {
+            jar.closeEntry();
+            is.close();
+        }
     }
 
     private static void ensureDirectory(File outDir) throws IOException {

Modified: aries/trunk/spi-fly/spi-fly-static/src/test/java/org/apache/aries/spifly/statictool/MainTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-static/src/test/java/org/apache/aries/spifly/statictool/MainTest.java?rev=1082164&r1=1082163&r2=1082164&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-static/src/test/java/org/apache/aries/spifly/statictool/MainTest.java (original)
+++ aries/trunk/spi-fly/spi-fly-static/src/test/java/org/apache/aries/spifly/statictool/MainTest.java Wed Mar 16 15:16:50 2011
@@ -23,20 +23,22 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
+import java.util.jar.Manifest;
 
 import org.apache.aries.spifly.Streams;
 import org.junit.Assert;
 import org.junit.Test;
+import org.junit.internal.ArrayComparisonFailure;
 
 public class MainTest {
     @Test
-    public void testUnJar() throws Exception {
+    public void testUnJarReJar() throws Exception {
         URL jarURL = getClass().getResource("/testjar.jar");
         File jarFile = new File(jarURL.getFile());
         File tempDir = new File(System.getProperty("java.io.tmpdir") + "/testjar_" + System.currentTimeMillis());
         
         try {
-            Main.unJar(jarFile, tempDir);
+            Manifest manifest = Main.unJar(jarFile, tempDir);
             
             assertStreams(new File(tempDir, "META-INF/MANIFEST.MF"), 
                     "jar:" + jarURL + "!/META-INF/MANIFEST.MF");
@@ -51,14 +53,43 @@ public class MainTest {
                     "jar:" + jarURL + "!/dir/dir 2/b.txt");
                         
             Assert.assertTrue(new File(tempDir, "dir/dir.3").exists());
+            
+            // Create a second jar from the exploded directory
+            File copiedFile = new File(jarFile.getAbsolutePath() + ".copy");            
+            Main.jar(copiedFile, tempDir, manifest);
+            URL copyURL = copiedFile.toURI().toURL();
+            
+            assertStreams("jar:" + copyURL + "!/META-INF/MANIFEST.MF", 
+                    "jar:" + jarURL + "!/META-INF/MANIFEST.MF");
+            
+            assertStreams("jar:" + copyURL + "!/A text File with no content",
+                    "jar:" + jarURL + "!/A text File with no content");
+            assertStreams("jar:" + copyURL + "!/dir/Main.class",
+                    "jar:" + jarURL + "!/dir/Main.class");
+            assertStreams("jar:" + copyURL + "!/dir/dir 2/a.txt", 
+                    "jar:" + jarURL + "!/dir/dir 2/a.txt");
+            assertStreams("jar:" + copyURL + "!/dir/dir 2/b.txt", 
+                    "jar:" + jarURL + "!/dir/dir 2/b.txt");
         } finally {
             deleteTree(tempDir);
         }
     }
-
+    
+    
+    private void assertStreams(String url1, String url2) throws Exception {
+        InputStream is1 = new URL(url1).openStream();
+        InputStream is2 = new URL(url2).openStream();
+        assertStreams(is1, is2);
+    }
+    
     private void assertStreams(File file, String url) throws Exception {
         InputStream is1 = new FileInputStream(file);
         InputStream is2 = new URL(url).openStream();
+        assertStreams(is1, is2);
+    }
+
+    private void assertStreams(InputStream is1, InputStream is2)
+            throws IOException, ArrayComparisonFailure {
         try {
             byte[] bytes1 = Streams.suck(is1);
             byte[] bytes2 = Streams.suck(is2);