Author: scottbw
Date: Wed Jun 29 15:12:32 2011
New Revision: 1141128

URL: http://svn.apache.org/viewvc?rev=1141128&view=rev
Log:
Added support in Flatpack  (see WOOKIE-182) for selective "flattening" of 
features. The test cases use JQuery Mobile - so when exporting Widgets that 
require JQM the relevant resources (js, css, images) are included in the 
exported .wgt package and the <feature> element removed from config.xml. The 
general approach is that features that are Wookie-specific 
conveniences/normalization for loading common libraries (like JQM) are 
flattened on export to improve interoperability. Features are flagged for this 
treatment by including a 'flatten="true"' attribute in feature.xml.

Modified:
    incubator/wookie/trunk/features/jqmobile/feature.xml
    
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/flatpack/FlatpackFactoryTest.java
    
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java
    incubator/wookie/trunk/src/org/apache/wookie/feature/Feature.java
    incubator/wookie/trunk/src/org/apache/wookie/feature/Features.java
    incubator/wookie/trunk/src/org/apache/wookie/feature/IFeature.java
    incubator/wookie/trunk/src/org/apache/wookie/feature/wave/WaveAPIImpl.java
    incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackFactory.java
    incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackProcessor.java

Modified: incubator/wookie/trunk/features/jqmobile/feature.xml
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/features/jqmobile/feature.xml?rev=1141128&r1=1141127&r2=1141128&view=diff
==============================================================================
--- incubator/wookie/trunk/features/jqmobile/feature.xml (original)
+++ incubator/wookie/trunk/features/jqmobile/feature.xml Wed Jun 29 15:12:32 
2011
@@ -15,7 +15,7 @@
   See the License for the specific language governing permissions and
   limitations under the License.
 -->
-<feature>
+<feature flatten="true">
        <name>http://jquerymobile.com</name>
        <script src="shared/jquery-1.5.min.js"/>
        <script src="shared/jquery.mobile-1.0a4-patched.min.js"/>

Modified: 
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/flatpack/FlatpackFactoryTest.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src-tests/org/apache/wookie/tests/flatpack/FlatpackFactoryTest.java?rev=1141128&r1=1141127&r2=1141128&view=diff
==============================================================================
--- 
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/flatpack/FlatpackFactoryTest.java
 (original)
+++ 
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/flatpack/FlatpackFactoryTest.java
 Wed Jun 29 15:12:32 2011
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 
 import org.apache.wookie.beans.IPreference;
 import org.apache.wookie.beans.IWidgetInstance;
+import org.apache.wookie.feature.Features;
 import org.apache.wookie.flatpack.FlatpackFactory;
 import org.apache.wookie.w3c.W3CWidget;
 import org.apache.wookie.w3c.W3CWidgetFactory;
@@ -48,6 +49,7 @@ public class FlatpackFactoryTest {
                download = File.createTempFile("wookie-download", "wgt");
                output = File.createTempFile("wookie-output", "tmp");
                flatpack = File.createTempFile("wookie-flatpack", "");
+               Features.loadFeatures(new File("features"), "/wookie/features");
        }
        
        /*
@@ -136,6 +138,37 @@ public class FlatpackFactoryTest {
                
        }
        
+        /**
+   * Test creating a flatpack for an instance of a widget using the factory 
defaults
+   * when the widget also uses Features
+   * @throws Exception
+   */
+  @Test
+  public void createFeatureFlatpackUsingDefaults() throws Exception{
+    // upload a new widget to test with
+    W3CWidgetFactory fac = getFactory();
+    fac.setFeatures(Features.getFeatureNames());
+    File testWidget = new File("build/widgets/freeder.wgt");
+    fac.parse(testWidget);
+    download = fac.getUnzippedWidgetDirectory(); //download is where we 
unzipped the widget
+    
+    // Create an instance of it
+    IWidgetInstance instance = new WidgetInstanceMock();
+    
+    // Flatpack it
+    FlatpackFactory flatfac = new FlatpackFactory(instance);
+    flatfac.setInputWidget(testWidget); // this is the original .wgt
+    File file = flatfac.pack(); // Get the new .wgt file
+  
+    // Test it works!
+    System.out.println("createFeatureFlatpackUsingDefaults: 
"+file.getAbsolutePath());
+    W3CWidget fpWidget = fac.parse(file);
+    assertNotNull(fpWidget);
+    // The JQM feature should have been removed from config.xml
+    assertEquals(0, fpWidget.getFeatures().size());
+    
+  }
+       
        /**
         * Test that we add preference defaults from an instance
         * @throws Exception

Modified: 
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java?rev=1141128&r1=1141127&r2=1141128&view=diff
==============================================================================
--- 
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java
 (original)
+++ 
incubator/wookie/trunk/src-tests/org/apache/wookie/tests/functional/FlatpackControllerTest.java
 Wed Jun 29 15:12:32 2011
@@ -31,6 +31,7 @@ public class FlatpackControllerTest exte
        
        private static final String TEST_FLATPACK_SERVICE_URL_VALID = 
TEST_SERVER_LOCATION+"flatpack";
        private static final String TEST_EXPORT_SERVICE_URL_VALID = 
TEST_SERVER_LOCATION+"export";
+       private static final String TEST_WIDGET_ID_JQM = 
"http://wookie.apache.org/widgets/freeder";;
        private static String test_id_key = "";
        
        @BeforeClass
@@ -41,6 +42,11 @@ public class FlatpackControllerTest exte
         client.executeMethod(post);
         test_id_key = 
post.getResponseBodyAsString().substring(post.getResponseBodyAsString().indexOf("<identifier>")+12,post.getResponseBodyAsString().indexOf("</identifier>"));
         post.releaseConnection();
+        
+        post = new PostMethod(TEST_INSTANCES_SERVICE_URL_VALID);
+        
post.setQueryString("api_key="+API_KEY_VALID+"&widgetid="+TEST_WIDGET_ID_JQM+"&userid=FPtest&shareddatakey=test");
+        client.executeMethod(post);
+        post.releaseConnection();
        }
        
        // Test that you can't get a directory listing of exported widgets
@@ -110,4 +116,28 @@ public class FlatpackControllerTest exte
                fail("post failed");
              }
          }
+        
+         @Test
+         public void getPackUsingFlattenedFeature(){
+             try {
+                 HttpClient client = new HttpClient();
+                 PostMethod post = new 
PostMethod(TEST_FLATPACK_SERVICE_URL_VALID);
+                 
post.setQueryString("api_key="+API_KEY_VALID+"&widgetid="+TEST_WIDGET_ID_JQM+"&userid=FPtest&shareddatakey=test");
+                 client.executeMethod(post);
+                 int code = post.getStatusCode();
+                 assertEquals(200,code);
+                 String url = post.getResponseBodyAsString();
+                 post.releaseConnection();
+                 
+                 // Now lets try to download it!
+                 GetMethod get = new GetMethod(url);
+                 client.executeMethod(get);
+                 code = get.getStatusCode();
+                 assertEquals(200, code);
+             }
+             catch (Exception e) {
+               e.printStackTrace();
+               fail("post failed");
+             }
+         }
 }

Modified: incubator/wookie/trunk/src/org/apache/wookie/feature/Feature.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/feature/Feature.java?rev=1141128&r1=1141127&r2=1141128&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/feature/Feature.java (original)
+++ incubator/wookie/trunk/src/org/apache/wookie/feature/Feature.java Wed Jun 
29 15:12:32 2011
@@ -25,7 +25,38 @@ public class Feature implements IFeature
   private String name;
   private String[] scripts;
   private String[] stylesheets;
+  private boolean flattenOnExport;
+  private String folder;
   
+  
+  /* (non-Javadoc)
+   * @see org.apache.wookie.feature.IFeature#getFolder()
+   */
+  public String getFolder() {
+    return folder;
+  }
+
+  /**
+   * @param folderName the folderName to set
+   */
+  public void setFolder(String folder) {
+    this.folder = folder;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.wookie.feature.IFeature#flattenOnExport()
+   */
+  public boolean flattenOnExport() {
+    return flattenOnExport;
+  }
+
+  /**
+   * @param flattenOnExport the flattenOnExport to set
+   */
+  public void setFlattenOnExport(boolean flattenOnExport) {
+    this.flattenOnExport = flattenOnExport;
+  }
+
   public Feature(String name, String[] scripts, String[] stylesheets){
     this.name = name;
     this.scripts = scripts;

Modified: incubator/wookie/trunk/src/org/apache/wookie/feature/Features.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/feature/Features.java?rev=1141128&r1=1141127&r2=1141128&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/feature/Features.java 
(original)
+++ incubator/wookie/trunk/src/org/apache/wookie/feature/Features.java Wed Jun 
29 15:12:32 2011
@@ -53,6 +53,16 @@ public class Features {
    */
   public static final File DEFAULT_FEATURE_FOLDER = new File("features");
   
+  /*
+   * The folder where deployed features live
+   */
+  private static File featuresFolder;
+  
+  public static File getFeaturesFolder(){
+    if (featuresFolder == null) return DEFAULT_FEATURE_FOLDER;
+    return featuresFolder;
+  }
+  
   /**
    * Get the currently installed features
    * @return a List of IFeature objects
@@ -92,8 +102,17 @@ public class Features {
     // Load defaults
     loadDefaultFeatures();
     
+    // Load features from file
+    loadFeatures(DEFAULT_FEATURE_FOLDER, context.getContextPath() + "/" + 
DEFAULT_FEATURE_FOLDER + "/");
+  }
+  
+  public static void loadFeatures(File theFeaturesFolder, String basePath){
+    featuresFolder = theFeaturesFolder;
+    
+    if (features == null) features = new ArrayList<IFeature>();
+    
     // Iterate over child folders of the /features folder
-    for (File folder: DEFAULT_FEATURE_FOLDER.listFiles()){
+    for (File folder: featuresFolder.listFiles()){
 
       // If the folder contains a feature.xml file, parse it and create a 
Feature object
       if (folder.isDirectory()){
@@ -101,9 +120,10 @@ public class Features {
         if (featureXml.exists() && featureXml.canRead()){
           try {
             // Create a base path for resources using the current servlet 
context and default feature folder 
-            String basePath = context.getContextPath() + "/" + 
DEFAULT_FEATURE_FOLDER + "/" + folder.getName();
+            String path = "/wookie/features/" + folder.getName();
             // Load the feature and add it to the features collection
-            IFeature feature = loadFeature(featureXml, basePath);
+            Feature feature = loadFeature(featureXml, path);
+            feature.setFolder(folder.getPath());
             features.add(feature);
             _logger.info("Installed feature:"+feature.getName());   
           } catch (Exception e) {
@@ -121,7 +141,7 @@ public class Features {
    * @return an IFeature implementation
    * @throws Exception
    */
-  private static IFeature loadFeature(File featureFile, String basePath) 
throws Exception{
+  private static Feature loadFeature(File featureFile, String basePath) throws 
Exception{
     // Parse the XML
     Document doc;
     doc = new SAXBuilder().build(featureFile);
@@ -146,7 +166,15 @@ public class Features {
       stylesheets[i] =  basePath + "/" + 
stylesheetElements.get(i).getAttributeValue("src");
     }
     // Create a Feature object and return it
-    IFeature feature = new Feature(name, scripts, stylesheets);
+    Feature feature = new Feature(name, scripts, stylesheets);
+    
+    // Set the "flatten" flag if set
+    if (doc.getRootElement().getAttributeValue("flatten")!=null){
+      if (doc.getRootElement().getAttributeValue("flatten").equals("true")){
+        ((Feature)feature).setFlattenOnExport(true);        
+      }
+    }
+    
     return feature;
   }
 

Modified: incubator/wookie/trunk/src/org/apache/wookie/feature/IFeature.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/feature/IFeature.java?rev=1141128&r1=1141127&r2=1141128&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/feature/IFeature.java 
(original)
+++ incubator/wookie/trunk/src/org/apache/wookie/feature/IFeature.java Wed Jun 
29 15:12:32 2011
@@ -34,5 +34,15 @@ public interface IFeature {
         * @return
         */
        public String[] stylesheets();
+       
+       /**
+        * @return true if this feature should be flattened (injected) on 
export, or remain in the config.xml as a <feature> element
+        */
+       public boolean flattenOnExport();
+       
+       /**
+        * @return the path to the folder containing the feature and its 
resources
+        */
+       public String getFolder();
 
 }

Modified: 
incubator/wookie/trunk/src/org/apache/wookie/feature/wave/WaveAPIImpl.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/feature/wave/WaveAPIImpl.java?rev=1141128&r1=1141127&r2=1141128&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/feature/wave/WaveAPIImpl.java 
(original)
+++ incubator/wookie/trunk/src/org/apache/wookie/feature/wave/WaveAPIImpl.java 
Wed Jun 29 15:12:32 2011
@@ -38,7 +38,21 @@ import org.directwebremoting.WebContextF
  */
 public class WaveAPIImpl implements IFeature, IWaveAPI{
 
-       public WaveAPIImpl() {
+       /* (non-Javadoc)
+   * @see org.apache.wookie.feature.IFeature#flattenOnExport()
+   */
+  public boolean flattenOnExport() {
+    return false;
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.wookie.feature.IFeature#getFolder()
+   */
+  public String getFolder() {
+    return null;
+  }
+  
+  public WaveAPIImpl() {
        }
        
        public String getName() {

Modified: 
incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackFactory.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackFactory.java?rev=1141128&r1=1141127&r2=1141128&view=diff
==============================================================================
--- incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackFactory.java 
(original)
+++ incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackFactory.java 
Wed Jun 29 15:12:32 2011
@@ -18,6 +18,7 @@ import java.io.IOException;
 
 import org.apache.wookie.beans.IPreference;
 import org.apache.wookie.beans.IWidgetInstance;
+import org.apache.wookie.feature.Features;
 import org.apache.wookie.w3c.IPreferenceEntity;
 import org.apache.wookie.w3c.W3CWidget;
 import org.apache.wookie.w3c.W3CWidgetFactory;
@@ -78,7 +79,7 @@ public class FlatpackFactory {
                        // try to locate the widget upload package from the 
WidgetInstance
                        inputWidget = new 
File(instance.getWidget().getPackagePath());
                }
-               if (parser == null) parser = DEFAULT_PARSER;
+               if (parser == null) this.setParser(DEFAULT_PARSER);
                
                // Verify the file locations we're using exist
                if (!inputWidget.exists()) throw new Exception("Input widget 
file does not exist:"+inputWidget.getPath());
@@ -184,7 +185,7 @@ public class FlatpackFactory {
         */
        public void setParser(W3CWidgetFactory factory) throws IOException{
                parser = factory;
-               parser.setStartPageProcessor(new 
FlatpackProcessor(this.instance));
+               parser.setStartPageProcessor(new FlatpackProcessor());
                parser.setLocalPath(DEFAULT_LOCAL_PATH);
        }
        
@@ -194,6 +195,7 @@ public class FlatpackFactory {
        private static W3CWidgetFactory createDefaultParser() {
                W3CWidgetFactory fac = new W3CWidgetFactory();
                fac.setLocalPath(DEFAULT_LOCAL_PATH);
+        fac.setFeatures(Features.getFeatureNames());
                try {
                        fac.setEncodings(new String[]{"UTF-8"});
                } catch (Exception e) {

Modified: 
incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackProcessor.java
URL: 
http://svn.apache.org/viewvc/incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackProcessor.java?rev=1141128&r1=1141127&r2=1141128&view=diff
==============================================================================
--- 
incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackProcessor.java 
(original)
+++ 
incubator/wookie/trunk/src/org/apache/wookie/flatpack/FlatpackProcessor.java 
Wed Jun 29 15:12:32 2011
@@ -14,9 +14,18 @@
 package org.apache.wookie.flatpack;
 
 import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
 
-import org.apache.wookie.beans.IWidgetInstance;
+import org.apache.commons.io.FileUtils;
+import org.apache.wookie.feature.Features;
+import org.apache.wookie.feature.IFeature;
+import org.apache.wookie.util.html.HtmlCleaner;
+import org.apache.wookie.util.html.IHtmlProcessor;
 import org.apache.wookie.w3c.IContentEntity;
+import org.apache.wookie.w3c.IFeatureEntity;
 import org.apache.wookie.w3c.IStartPageProcessor;
 import org.apache.wookie.w3c.W3CWidget;
 
@@ -36,15 +45,12 @@ import org.apache.wookie.w3c.W3CWidget;
  *
  */
 public class FlatpackProcessor  implements     IStartPageProcessor {
-       
-       private IWidgetInstance instance;
 
        /**
-        * Constructs a FlatpackProcessor taking a WidgetInstance as the 
constructor argument.
+        * Constructs a FlatpackProcessor 
         * @param instance
         */
-       public FlatpackProcessor(IWidgetInstance instance) {
-               this.instance = instance;
+       public FlatpackProcessor() {
        }
 
        /**
@@ -55,5 +61,82 @@ public class FlatpackProcessor  implemen
         * TODO implement
         */
        public void processStartFile(File startFile, W3CWidget 
model,IContentEntity content) throws Exception {
+          if (startFile == null) throw new Exception("Start file cannot be 
processed: file is null");
+           if (!startFile.exists()) throw new Exception("Start file cannot be 
processed:  file does not exist");
+           if (!(startFile.canWrite()&&startFile.canRead())) throw new 
Exception("Start file cannot be processed: read or write permissions missing");
+           if (model == null) throw new Exception("Start file cannot be 
processed: widget model is null");
+           IHtmlProcessor engine = new HtmlCleaner();
+           engine.setReader(new FileReader(startFile));
+           addFlattenedFeatures(startFile.getParentFile(), engine, model);
+           FileWriter writer = new FileWriter(startFile);
+           engine.process(writer);
        }
+       
+        /**
+   * Adds features to widget start file by injecting javascript and stylesheets
+   * required by each supported feature in the model.
+   * @param engine
+   * @param model
+   * @throws Exception if a feature cannot be found and instantiated for the 
widget.
+   */
+  private void addFlattenedFeatures(File widgetFolder, IHtmlProcessor engine, 
W3CWidget model) throws Exception{
+    ArrayList<IFeatureEntity> featuresToRemove = new 
ArrayList<IFeatureEntity>();
+    for (IFeatureEntity feature: model.getFeatures()){
+      for (IFeature theFeature: Features.getFeatures()){
+        if (theFeature.getName().equals(feature.getName()) && 
theFeature.flattenOnExport()){
+          addScripts(engine, theFeature);
+          addStylesheets(engine, theFeature);  
+          addResources(widgetFolder, theFeature);
+          featuresToRemove.add(feature);
+        }
+      }
+    }
+    // Remove flattened features
+    for (IFeatureEntity feature: featuresToRemove){
+      model.getFeatures().remove(feature);
+    }
+  }
+  
+  /**
+   * @param widgetFolder
+   * @param theFeature
+   * @throws IOException 
+   */
+  private void addResources(File widgetFolder, IFeature theFeature) throws 
IOException {
+    // Copy everything under the feature to the widgetfolder
+    File featureFolder = new File(theFeature.getFolder());
+    FileUtils.copyDirectoryToDirectory(featureFolder, widgetFolder);
+  }
+
+  /**
+   * Adds scripts for a given feature
+   * @param engine
+   * @param feature
+   */
+  private void addScripts(IHtmlProcessor engine, IFeature feature){
+    if (feature.scripts() != null){
+      for (String script: feature.scripts()){
+        // remove the "base" path
+        // FIXME this is fragile - consider replacing with a better solution
+        script = script.replace("/wookie/features/", "");
+        engine.injectScript(script);
+      }
+    }
+  }
+  
+  /**
+   * Adds stylesheets for a given feature
+   * @param engine
+   * @param feature
+   */
+  private void addStylesheets(IHtmlProcessor engine, IFeature feature){
+    if (feature.stylesheets() != null){
+      for (String style: feature.stylesheets()){
+        // remove the "base" path
+        // FIXME this is fragile - consider replacing with a better solution
+        style = style.replace("/wookie/features/", "");
+        engine.injectStylesheet(style);
+      }
+    }
+  }
 }


Reply via email to