You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@wookie.apache.org by sc...@apache.org on 2011/06/29 17:12:41 UTC

svn commit: r1141128 - in /incubator/wookie/trunk: features/jqmobile/ src-tests/org/apache/wookie/tests/flatpack/ src-tests/org/apache/wookie/tests/functional/ src/org/apache/wookie/feature/ src/org/apache/wookie/feature/wave/ src/org/apache/wookie/fla...

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);
+      }
+    }
+  }
 }