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