You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by jo...@apache.org on 2007/12/12 11:33:38 UTC
svn commit: r603539 [1/3] - in /incubator/shindig/trunk: ./ java/
java/gadgets/ java/gadgets/src/ java/gadgets/src/main/
java/gadgets/src/main/java/ java/gadgets/src/main/java/org/
java/gadgets/src/main/java/org/apache/ java/gadgets/src/main/java/org/a...
Author: johnh
Date: Wed Dec 12 02:33:35 2007
New Revision: 603539
URL: http://svn.apache.org/viewvc?rev=603539&view=rev
Log:
Initial commit of Shindig Gadget Container JavaScript and Gadget Server.
Includes:
* Gadget Container JavaScript. Code embedded into an arbitrary web page
enabling it to render and manage Gadgets.
+ Support for rendering using gmodules.com or a Shindig Gadget Server
+ Basic IFPC (gadget <-> container communication) support via inclusion
of Google IFPC library
+ Simple layout management: render in DIV (StaticLayoutManager);
render multiple Gadgets left-aligned in DIV (FloatLeftLayoutManager)
+ UserPrefs support through JS interface
+ Cookie-based UserPrefs implementation
+ _IG_SetTitle container-side support
+ _IG_AdjustIframeHeight container-side support
+ Sample pages demonstrating all the above functionality
* Gadget Server (written in Java). Web server that processes Gadget
requests, parsing their spec XML, processing it through a workflow
of core and extension (<Require>/<Optional>) Features, and serializing
output for rendering the Gadget to the end user.
+ Core server implementation constructing processing workflows out of
GadgetFeature objects providing various feature support
+ GadgetFeature interface providing core extensibility mechanism for
the Gadgets platform; extenders implement GadgetFeature and register
their implementation with GadgetFeatureRegistry
+ Gadget spec XML parser
+ Simple caching API with in-memory Map-based implementations as samples
+ Remote content (eg. HTTP) fetcher interface
+ Basic java.net-based RemoteContentFetcher implementation
+ "Hangman" variable substitution support (eg. __MSG_foo__), including BIDI,
MSG (message bundles) and UP (user prefs) type variables
+ Early JavaScript-based Feature support through JsLibrary* classes
+ Content proxy including JSON support
* A handful of tests for various Server components.
* Maven build support.
This submission represents significant contributions by:
fargo@google.com, etnu@google.com, dharkness@google.com,
jyang@google.com, wangz@google.com, dcoker@google.com
Much more to come!
Added:
incubator/shindig/trunk/COPYING
incubator/shindig/trunk/NOTICE
incubator/shindig/trunk/README
incubator/shindig/trunk/java/
incubator/shindig/trunk/java/gadgets/
incubator/shindig/trunk/java/gadgets/README
incubator/shindig/trunk/java/gadgets/pom.xml
incubator/shindig/trunk/java/gadgets/src/
incubator/shindig/trunk/java/gadgets/src/main/
incubator/shindig/trunk/java/gadgets/src/main/java/
incubator/shindig/trunk/java/gadgets/src/main/java/org/
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetDataCache.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicRemoteContentFetcher.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BidiSubstituter.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/CoreJsFeature.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetContext.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetDataCache.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpec.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpecParser.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetView.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibrary.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/JsLibraryFeature.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundle.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleParser.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/MessageBundleSubstituter.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/ModuleSubstituter.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContent.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/RemoteContentFetcher.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/SpecParserException.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Substitutions.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefSubstituter.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/UserPrefs.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Utf8InputStream.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/AnalyticsFeature.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/BasicGadgetHttpServlet.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/BasicHttpContext.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/JsonpProxyServlet.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyHandler.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/http/ProxyServlet.java
incubator/shindig/trunk/java/gadgets/src/main/webapp/
incubator/shindig/trunk/java/gadgets/src/main/webapp/WEB-INF/
incubator/shindig/trunk/java/gadgets/src/main/webapp/WEB-INF/web.xml
incubator/shindig/trunk/java/gadgets/src/test/
incubator/shindig/trunk/java/gadgets/src/test/java/
incubator/shindig/trunk/java/gadgets/src/test/java/org/
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/EasyMockTestCase.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetServerTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetSpecTestFixture.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/SubstitutionsTest.java
incubator/shindig/trunk/javascript/
incubator/shindig/trunk/javascript/README
incubator/shindig/trunk/javascript/container/
incubator/shindig/trunk/javascript/container/cookies.js
incubator/shindig/trunk/javascript/container/gadgets.css
incubator/shindig/trunk/javascript/container/gadgets.js
incubator/shindig/trunk/javascript/container/ifpc_relay.html
incubator/shindig/trunk/javascript/container/json.js
incubator/shindig/trunk/javascript/container/sample1.html
incubator/shindig/trunk/javascript/container/sample2.html
incubator/shindig/trunk/javascript/container/sample3.html
incubator/shindig/trunk/javascript/container/sample4.html
Added: incubator/shindig/trunk/COPYING
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/COPYING?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/COPYING (added)
+++ incubator/shindig/trunk/COPYING Wed Dec 12 02:33:35 2007
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2007 Google Inc.
+
+ Licensed 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.
Added: incubator/shindig/trunk/NOTICE
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/NOTICE?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/NOTICE (added)
+++ incubator/shindig/trunk/NOTICE Wed Dec 12 02:33:35 2007
@@ -0,0 +1,2 @@
+This product includes software (Gadget Server, Gadget Container) developed by
+Google Inc. (http://code.google.com/)
Added: incubator/shindig/trunk/README
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/README?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/README (added)
+++ incubator/shindig/trunk/README Wed Dec 12 02:33:35 2007
@@ -0,0 +1,10 @@
+Welcome to Apache Shindig!
+
+* Read java/gadgets/README for instructions on how to start up your own
+ Shindig Gadget Server.
+
+* Read javascript/README for instructions for using the Shindig Gadget
+ Container JavaScript to enable your page to render Gadgets using
+ gmodules.com or a server started up as described above.
+
+For more information, see http://incubator.apache.org/projects/shindig.html
Added: incubator/shindig/trunk/java/gadgets/README
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/README?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/README (added)
+++ incubator/shindig/trunk/java/gadgets/README Wed Dec 12 02:33:35 2007
@@ -0,0 +1,16 @@
+Installing and Running Shindig Gadget Server
+============================================
+
+1) Install Maven 2.0 (see http://maven.apache.org)
+
+2) Make sure the JAVA_HOME environment variable is set to the location of your
+ JDK/JRE, and that the maven executable is in your PATH.
+
+3) From java/gadgets/...
+ * mvn package - Builds Gadget Server and runs tests.
+ * mvn jetty:run - Builds Gadget Server (no tests), installs Servlet at localhost:8080.
+
+4) Hit server at http://localhost:8080/gadgets/ifr?url=<gadget-url>
+ Example: http://localhost:8080/gadgets/ifr?url=http://www.labpixies.com/campaigns/todo/todo.xml
+
+For more information, see http://incubator.apache.org/projects/shindig.html
Added: incubator/shindig/trunk/java/gadgets/pom.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/pom.xml?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/pom.xml (added)
+++ incubator/shindig/trunk/java/gadgets/pom.xml Wed Dec 12 02:33:35 2007
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<!--
+
+ Licensed 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.shindig</groupId>
+ <artifactId>gadgets</artifactId>
+ <version>0.5</version>
+ <packaging>war</packaging>
+
+ <name>Shindig Gadget Server</name>
+ <url>http://shindig.apache.org</url>
+
+ <licenses>
+ <license>
+ <name>Apache 2</name>
+ <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+ </license>
+ </licenses>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.json</groupId>
+ <artifactId>json</artifactId>
+ <version>20070829</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.3</version>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <version>2.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymockclassextension</artifactId>
+ <version>2.2.2</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <finalName>gadgets</finalName>
+ <sourceDirectory>${basedir}/src/main/java</sourceDirectory>
+ <testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>
+ <outputDirectory>${basedir}/target/classes</outputDirectory>
+ <testOutputDirectory>${basedir}/target/test-classes</testOutputDirectory>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.mortbay.jetty</groupId>
+ <artifactId>maven-jetty-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetDataCache.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetDataCache.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetDataCache.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetDataCache.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,39 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import org.apache.shindig.gadgets.GadgetDataCache;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Basic implementation of an in-memory data cache, backed by a
+ * {@code HashMap}.
+ *
+ * @param <T> Type of data to store in the cache.
+ */
+public class BasicGadgetDataCache<T> implements GadgetDataCache<T> {
+ private Map<String, T> cache = new HashMap<String, T>();
+
+ /** {@inheritDoc} */
+ public T get(String key) {
+ return cache.get(key);
+ }
+
+ /** {@inheritDoc} */
+ public void put(String key, T value) {
+ cache.put(key, value);
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicRemoteContentFetcher.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicRemoteContentFetcher.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicRemoteContentFetcher.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicRemoteContentFetcher.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,73 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import org.apache.shindig.gadgets.RemoteContentFetcher;
+import org.apache.shindig.gadgets.RemoteContent;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of a {@code RemoteObjectFetcher} using standard java.net
+ * classes. Only supports HTTP fetching at present.
+ */
+public class BasicRemoteContentFetcher implements RemoteContentFetcher {
+ private static final int CONNECT_TIMEOUT_MS = 5000;
+
+ private final int maxObjSize;
+
+ /**
+ * Creates a new fetcher capable of retrieving objects {@code maxObjSize}
+ * bytes or smaller in size.
+ * @param maxObjSize Maximum size, in bytes, of object to fetch
+ */
+ public BasicRemoteContentFetcher(int maxObjSize) {
+ this.maxObjSize = maxObjSize;
+ }
+
+ /** {@inheritDoc} */
+ public RemoteContent fetch(URL url) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+ int responseCode;
+ HttpURLConnection fetcher;
+ Map<String, List<String>> headers = null;
+
+ try {
+ fetcher = (HttpURLConnection) url.openConnection();
+ fetcher.setInstanceFollowRedirects(true);
+ fetcher.setConnectTimeout(CONNECT_TIMEOUT_MS);
+
+ responseCode = fetcher.getResponseCode();
+ headers = fetcher.getHeaderFields();
+
+ byte chunk[] = new byte[8192];
+ int chunkSize;
+ InputStream in = fetcher.getInputStream();
+ while (out.size() < maxObjSize && (chunkSize = in.read(chunk)) != -1) {
+ out.write(chunk, 0, chunkSize);
+ }
+ } catch (IOException e) {
+ responseCode = 500;
+ }
+
+ return new RemoteContent(responseCode, out.toByteArray(), headers);
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BidiSubstituter.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BidiSubstituter.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BidiSubstituter.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BidiSubstituter.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,86 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Provides static hangman substitutions for bidirectional language support.
+ * Useful for generating internationalized layouts using CSS.
+ */
+public class BidiSubstituter implements GadgetFeature {
+
+ /**
+ * Fetches a message bundle spec from the {@code GadgetSpec} for the
+ * provided locale.
+ * @param spec Gadget spec from which to retrieve message bundle spec
+ * @param locale Locale of message bundle to retrieve
+ * @return The message bundle, or null if not found
+ */
+ private GadgetSpec.MessageBundle getBundle(GadgetSpec spec,
+ Locale locale) {
+ List<GadgetSpec.MessageBundle> bundles = spec.getMessageBundles();
+ for (GadgetSpec.MessageBundle bundle : bundles) {
+ if (bundle.getLocale().equals(locale)) {
+ return bundle;
+ }
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public void prepare(GadgetView spec,
+ GadgetContext context,
+ Map<String, String> params) {
+ // Nothing here.
+ }
+
+ /**
+ * Populates bidi substitutions.
+ * @param gadget Gadget object to process
+ * @param context Context in which Gadget is being processed
+ */
+ public void process(Gadget gadget,
+ GadgetContext context,
+ Map<String, String> params) {
+ Substitutions subst = gadget.getSubstitutions();
+ Locale locale = context.getLocale();
+ // Find an appropriate bundle for the ltr flag.
+ GadgetSpec.MessageBundle bundle = getBundle(gadget, locale);
+ if (null == bundle) {
+ bundle = getBundle(gadget, new Locale(locale.getLanguage(), "all"));
+ }
+ if (null == bundle) {
+ bundle = getBundle(gadget, new Locale("all", "all"));
+ }
+ boolean rtl = false;
+ if (bundle != null) {
+ rtl = bundle.isRightToLeft();
+ }
+ subst.addSubstitution(Substitutions.Type.BIDI,
+ "START_EDGE",
+ rtl ? "right" : "left");
+ subst.addSubstitution(Substitutions.Type.BIDI,
+ "END_EDGE",
+ rtl ? "left" : "right");
+ subst.addSubstitution(Substitutions.Type.BIDI,
+ "DIR",
+ rtl ? "rtl" : "ltr");
+ subst.addSubstitution(Substitutions.Type.BIDI,
+ "REVERSE_DIR",
+ rtl ? "ltr" : "rtl");
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/CoreJsFeature.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/CoreJsFeature.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/CoreJsFeature.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/CoreJsFeature.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,27 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import java.util.Map;
+
+/**
+ * Adds all core javascript dependencies that are needed by all gadgets.
+ */
+public class CoreJsFeature extends JsLibraryFeature {
+ @Override
+ public void process(Gadget gadget, GadgetContext context,
+ Map<String, String> params) {
+ gadget.addJsLibrary(JsLibrary.file("http://gmodules.com/ig/extern_js/f/CgJlbhICdXMrMAE4ACw/6gZqwg2JpuM.js"));
+ }
+}
\ No newline at end of file
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,296 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Intermediary representation of all state associated with processing
+ * of a single gadget request.
+ *
+ * This class is constructed by an immutable base {@code GadgetSpec},
+ * and is modified in parallel by a number of {@code GadgetFeature}
+ * processors, in an order defined by their dependencies, in
+ * {@code GadgetServer}.
+ *
+ * Upon completion of processing, a {@code Gadget} is serialized as appropriate
+ * to whatever output format is appropriate (eg. as gadget content in an
+ * IFRAME), potentially with post-processing such as HTML whitespace
+ * compression or HTML+JS (Caja) rewriting applied.
+ *
+ * "Hangman" variable substitutions (eg. __MSG_foo__) are performed as needed
+ * and transparently for fields that support this functionality.
+ */
+public class Gadget implements GadgetView {
+ private final ID id;
+ private final GadgetSpec baseSpec;
+ private final Substitutions substitutions;
+ private final Map<String, String> userPrefValues;
+ private final List<JsLibrary> jsLibraries;
+
+ public static class GadgetId implements GadgetView.ID {
+ private final URL url;
+ private final int moduleId;
+
+ public GadgetId(URL url, int moduleId) {
+ this.url = url;
+ this.moduleId = moduleId;
+ }
+
+ @Override
+ public boolean equals(Object comp) {
+ if (comp instanceof GadgetView.ID) {
+ GadgetView.ID id = (GadgetView.ID)comp;
+ return id.getURL() == url &&
+ id.getModuleId() == moduleId;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = (37 * result) + url.hashCode();
+ result = (37 * result) + moduleId;
+ return result;
+ }
+
+ public URL getURL() {
+ return url;
+ }
+
+ public int getModuleId() {
+ return moduleId;
+ }
+
+ public String getKey() {
+ return url.toString();
+ }
+ }
+
+ /**
+ * Create a new {@code Gadget} devoid of processing modifications.
+ * @param id Identifier used to retrieve {@code baseSpec}
+ * @param baseSpec Base (immutable) {@code GadgetSpec} on which this is based
+ */
+ public Gadget(ID id, GadgetSpec baseSpec) {
+ this.id = id;
+ this.baseSpec = baseSpec;
+ substitutions = new Substitutions();
+ userPrefValues = new HashMap<String, String>();
+ jsLibraries = new LinkedList<JsLibrary>();
+ }
+
+ /**
+ * @return Global identifier used to retrieve gadget's spec
+ */
+ public ID getId() {
+ return id;
+ }
+
+ /**
+ * @return GadgetSpec that backs this Gadget. Package scope for tests.
+ */
+ GadgetSpec getBaseSpec() {
+ return baseSpec;
+ }
+
+ /**
+ * @return Object containing all hangman substitutions applied to this gadget
+ */
+ public Substitutions getSubstitutions() {
+ return substitutions;
+ }
+
+ // GadgetSpec accessors
+
+ /**
+ * @return Gadget title with substitutions applied
+ */
+ public String getTitle() {
+ return substitutions.substitute(baseSpec.getTitle());
+ }
+
+ /**
+ * @return URL used as a target for Gadget's title link, or null if malformed
+ */
+ public URL getTitleURL() {
+ URL ret = null;
+ String urlStr = baseSpec.getTitleURL().toString();
+ try {
+ ret = new URL(substitutions.substitute(urlStr));
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ return ret;
+ }
+
+ /**
+ * @return String used to describe this Gadget in directories, with
+ * substitutions applied
+ */
+ public String getDirectoryTitle() {
+ return substitutions.substitute(baseSpec.getDirectoryTitle());
+ }
+
+ /**
+ * @return Extended description of {@code Gadget}, with substitutions applied
+ */
+ public String getDescription() {
+ return substitutions.substitute(baseSpec.getDescription());
+ }
+
+ /**
+ * @return Name of this Gadget's author as specified in its spec
+ */
+ public String getAuthor() {
+ return baseSpec.getAuthor();
+ }
+
+ /**
+ * @return E-mail address of this Gadget's author as specified in its spec
+ */
+ public String getAuthorEmail() {
+ return baseSpec.getAuthorEmail();
+ }
+
+ // TODO: make this URL?
+ public String getScreenshot() {
+ return baseSpec.getScreenshot();
+ }
+
+ // TODO: make this URL?
+ public String getThumbnail() {
+ return baseSpec.getThumbnail();
+ }
+
+ public List<MessageBundle> getMessageBundles() {
+ return new ArrayList<MessageBundle>(baseSpec.getMessageBundles());
+ }
+
+ /**
+ * @return List of all {@code FeatureSpec}s declared by this gadget
+ */
+ public Map<String, FeatureSpec> getRequires() {
+ return Collections.unmodifiableMap(baseSpec.getRequires());
+ }
+
+ /**
+ * @return All JS libraries needed to render this gadget.
+ */
+ public List<JsLibrary> getJsLibraries() {
+ return Collections.unmodifiableList(jsLibraries);
+ }
+
+ /**
+ * @param library
+ */
+ public void addJsLibrary(JsLibrary library) {
+ jsLibraries.add(library);
+ }
+
+ /**
+ * Extracts parameters for the given feature.
+ *
+ * @param gadget
+ * @param feature
+ * @return The parameters, or an empty map.
+ */
+ @SuppressWarnings("unchecked")
+ public static Map<String, String> getFeatureParams(Gadget gadget,
+ String feature) {
+ GadgetSpec.FeatureSpec spec = gadget.getRequires().get(feature);
+ if (spec == null) {
+ return Collections.EMPTY_MAP;
+ } else {
+ return spec.getParams();
+ }
+ }
+
+ /**
+ * @return List of all preload URLs declared, with substitutions applied
+ */
+ public List<String> getPreloads() {
+ List<String> ret = new LinkedList<String>();
+ for (String preload : baseSpec.getPreloads()) {
+ ret.add(substitutions.substitute(preload));
+ }
+ return ret;
+ }
+
+ /**
+ * @return List of icons defined in gadget spec
+ */
+ public List<Icon> getIcons() {
+ return Collections.unmodifiableList(baseSpec.getIcons());
+ }
+
+ /**
+ * @return List of all user pref specs defined in gadget spec
+ */
+ public List<UserPref> getUserPrefs() {
+ return Collections.unmodifiableList(baseSpec.getUserPrefs());
+ }
+
+ public Map<String, String> getUserPrefValues() {
+ return Collections.unmodifiableMap(userPrefValues);
+ }
+
+ /**
+ * @return Type of gadget to render
+ */
+ public ContentType getContentType() {
+ return baseSpec.getContentType();
+ }
+
+ /**
+ * @return URL of gadget to render of type == URL; null if malformed/missing
+ */
+ public URL getContentHref() {
+ if (getContentType() != ContentType.URL) {
+ return null;
+ }
+
+ URL ret = null;
+ String urlStr = baseSpec.getContentHref().toString();
+ try {
+ ret = new URL(substitutions.substitute(urlStr));
+ } catch (MalformedURLException e) {
+ return null;
+ }
+ return ret;
+ }
+
+ /**
+ * @return Gadget contents with all substitutions applied
+ */
+ public String getContentData() {
+ return substitutions.substitute(baseSpec.getContentData());
+ }
+
+ /**
+ * @return Copy of base spec that created this {@code Gadget}
+ */
+ public GadgetSpec copy() {
+ return baseSpec.copy();
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetContext.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetContext.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetContext.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetContext.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,51 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import java.util.Locale;
+
+/**
+ * Bundles together per-server data and helper mechanisms providing
+ * generic functionality such as retrieval of remote data and caching.
+ */
+public class GadgetContext {
+ private final RemoteContentFetcher httpFetcher;
+ public RemoteContentFetcher getHttpFetcher() {
+ return httpFetcher;
+ }
+
+ private final GadgetDataCache<MessageBundle> messageBundleCache;
+ public GadgetDataCache<MessageBundle> getMessageBundleCache() {
+ return messageBundleCache;
+ }
+
+ private final Locale locale;
+ public Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * Creates a context for the current gadget.
+ * @param httpFetcher
+ * @param messageBundleCache
+ * @param locale
+ */
+ public GadgetContext(RemoteContentFetcher httpFetcher,
+ GadgetDataCache<MessageBundle> messageBundleCache,
+ Locale locale) {
+ this.httpFetcher = httpFetcher;
+ this.messageBundleCache = messageBundleCache;
+ this.locale = locale;
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetDataCache.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetDataCache.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetDataCache.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetDataCache.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,24 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+/**
+ * Simple interface for providing a data cache for objects of type T keyed
+ * by a String.
+ * @param <T> Type of data to store in the cache
+ */
+public interface GadgetDataCache<T> {
+ public T get(String key) throws GadgetException;
+ public void put(String key, T val) throws GadgetException;
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,62 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+/**
+ * Base class for all Gadget exceptions. The bulk of the code uses
+ * this class directly, differentiating between error conditions by
+ * the Code enumeration.
+ */
+public class GadgetException extends Exception {
+ public static enum Code {
+ // Catch-all for internal errors
+ INTERNAL_SERVER_ERROR,
+
+ // General xml
+ EMPTY_XML_DOCUMENT,
+ MALFORMED_XML_DOCUMENT,
+
+ FAILED_TO_RETRIEVE_CONTENT,
+
+ UNSUPPORTED_FEATURE,
+
+ // Interface component errors.
+ MISSING_SPEC_CACHE,
+ MISSING_MESSAGE_BUNDLE_CACHE,
+ MISSING_REMOTE_OBJECT_FETCHER,
+
+ // Caja error
+ MALFORMED_FOR_SAFE_INLINING
+ }
+
+ private final Code code;
+
+ public GadgetException(Code code) {
+ this.code = code;
+ }
+
+ public GadgetException(Code code, Throwable cause) {
+ super(cause);
+ this.code = code;
+ }
+
+ public GadgetException(Code code, String msg) {
+ super(msg);
+ this.code = code;
+ }
+
+ public Code getCode() {
+ return code;
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,46 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import java.util.Map;
+
+/**
+ * Base interface providing Gadget Server's primary extensibility mechanism.
+ *
+ * During processing of a {@code Gadget}, a tree of {@code GadgetFeature}
+ * objects is constructed based on the <Require> and <Optional>
+ * tags declared in its {@code GadgetSpec}, and the dependencies registered
+ * for these in {@code GadgetFeatureRegistry}.
+ *
+ * Each {@code GadgetFeature}'s prepare method is called first - potentially
+ * in parallel with many others whose dependencies have also been satisfied.
+ * Once this has completed, its process method is called. Prepare is useful
+ * for async operations such as retrieval of a remote resource; all
+ * {@code Gadget} modifications occur in process.
+ *
+ * To extend the Gadget Server's feature set, simply implement this interface
+ * and register your class with {@code GadgetFeatureRegistry}, indicating
+ * which other {@code GadgetFeature} features are needed before yours can
+ * operate successfully.
+ *
+ * Each feature <i>must</i> be instantiable by a no-argument constructor,
+ * and will <i>always</i> be instantiated this way. As such, it is recommended
+ * not to define a constructor for a feature at all.
+ */
+public interface GadgetFeature {
+ public void prepare(GadgetView gadget, GadgetContext context,
+ Map<String, String> params) throws GadgetException;
+ public void process(Gadget gadget, GadgetContext context,
+ Map<String, String> params) throws GadgetException;
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,195 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Maintains a registry of all {@code GadgetFeature} types supported by
+ * a given Gadget Server installation.
+ *
+ * To register a feature, simply create a static initialization block in its
+ * class definition:
+ * <pre>
+ * static {
+ * GadgetFeatureRegistry.register("my.feature.name",
+ * { "my.dep1", "my.dep2" },
+ * MyFeature.class);
+ * }
+ * </pre>
+ */
+public class GadgetFeatureRegistry {
+ private static final Map<String, Entry> features =
+ new HashMap<String, Entry>();
+ private static final List<Entry> core =
+ new LinkedList<Entry>();
+
+ // Initialization of core components, providing a minimal base context
+ // in which all Gadgets operate. This set should be kept as minimal as
+ // possible. Anything added as registerCore will automatically become a
+ // dependency of every other feature.
+ static {
+ // Substitution jobs are not order-dependent, because the actual order that
+ // they are evaluated in is determined in Substitutions.java. The order
+ // defined here is not important.
+ registerCore("core.msgbundlesubst", null, MessageBundleSubstituter.class);
+ registerCore("core.bidisubst", null, BidiSubstituter.class);
+ registerCore("core.modulesubst", null, ModuleSubstituter.class);
+ registerCore("core.userprefsubst", null, UserPrefSubstituter.class);
+
+ // Core JS loading.
+ registerCore("core.js", null, CoreJsFeature.class);
+
+ // These are all satisfied by core.
+ register("setprefs", null, NoOpFeature.class);
+ }
+
+ /**
+ * Register a {@code GadgetFeature} identified by {@code name} which
+ * depends on other {@code GadgetFeature}s listed in {@code deps}
+ * completing before this one does.
+ *
+ * Names are freeform, but it is strongly suggested that they are
+ * namespaced, optionally (yet often usefully) in Java package-notation ie.
+ * 'com.google.gadgets.skins'.
+ *
+ * @param name Name of the feature to register, ideally using the conventions
+ * described
+ * @param deps List of strings indicating features on which {@code feature}
+ * depends to operate correctly, which need to process the {@code Gadget}
+ * before it does
+ * @param feature Class implementing the feature
+ */
+ public static void register(String name,
+ String[] deps,
+ Class<? extends GadgetFeature> feature) {
+ // Core entries must come first.
+ Entry entry = new Entry(name, deps, feature);
+ for (Entry coreEntry : core) {
+ entry.deps.add(coreEntry.getName());
+ }
+ features.put(name, entry);
+ validateFeatureGraph();
+ }
+
+ /**
+ * Registers a {@code GadgetFeature} which is <i>always</i> run when
+ * processing a {@code Gadget}, and on which all other features
+ * implicitly depend. Use of this mechanism should be as sparing as possible
+ * to optimize performance.
+ *
+ * @param deps
+ * @param cap
+ */
+ private static void registerCore(String name,
+ String[] deps,
+ Class<? extends GadgetFeature> cap) {
+ core.add(new Entry(name, deps, cap));
+ validateFeatureGraph();
+ }
+
+ /**
+ * Traverses the graph traversed by the registered features, validating
+ * that it comprises a directed acyclic graph in which all features'
+ * dependencies are provided.
+ *
+ * If the graph is not acyclic, it cannot be used to create a workflow. If
+ * any dependencies are missing, {@code Gadget} rendering may be incomplete.
+ */
+ private static void validateFeatureGraph() {
+ // TODO: ensure that features form a DAG and that all deps are provided
+ }
+
+ /**
+ * Attempts to retrieve all the {@code GadgetFeature} classes specified
+ * in the {@code needed} list. Those that are found are returned in
+ * {@code resultsFound}, while the names of those that are missing are
+ * populated in {@code resultsMissing}.
+ * @param needed List of names identifying features to retrieve
+ * @param resultsFound List of feature entries found
+ * @param resultsMissing List of feature identifiers that could not be found
+ * @return True if all features were retrieved
+ */
+ public static boolean getIncludedFeatures(List<String> needed,
+ List<Entry> resultsFound,
+ List<String> resultsMissing) {
+ resultsFound.clear();
+ resultsMissing.clear();
+ resultsFound.addAll(core);
+ for (String featureName : needed) {
+ Entry entry = features.get(featureName);
+ if (entry == null) {
+ resultsMissing.add(featureName);
+ }
+ resultsFound.add(entry);
+ }
+ return resultsMissing.size() == 0;
+ }
+
+ public static class NoOpFeature implements GadgetFeature {
+ public void prepare(GadgetView gadget, GadgetContext context,
+ Map<String, String> params) {
+ }
+ public void process(Gadget gadget, GadgetContext context,
+ Map<String, String> params) {
+ }
+ }
+
+ /**
+ * Ties together a {@code GadgetFeature} with its name and dependencies.
+ */
+ public static class Entry {
+ private final String name;
+ private final Set<String> deps;
+ private final Class<? extends GadgetFeature> feature;
+
+ private Entry(String name,
+ String[] deps,
+ Class<? extends GadgetFeature> feature) {
+ this.name = name;
+ this.deps = new HashSet<String>();
+ if (deps != null) {
+ this.deps.addAll(Arrays.asList(deps));
+ }
+ this.feature = feature;
+ }
+
+ /**
+ * @return Name identifier
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return List of identifiers on which feature depends
+ */
+ public List<String> getDependencies() {
+ return new LinkedList<String>(deps);
+ }
+
+ /**
+ * @return Class implementing the feature
+ */
+ public Class<? extends GadgetFeature> getFeature() {
+ return feature;
+ }
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,484 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.Future;
+
+public class GadgetServer {
+ private final Executor executor;
+ private GadgetDataCache<GadgetSpec> specCache;
+ private GadgetDataCache<MessageBundle> messageBundleCache;
+ private RemoteContentFetcher fetcher;
+
+ public GadgetServer(Executor executor) {
+ this.executor = executor;
+ }
+
+ public void setSpecCache(GadgetDataCache<GadgetSpec> specCache) {
+ this.specCache = specCache;
+ }
+
+ public void setMessageBundleCache(GadgetDataCache<MessageBundle> cache) {
+ messageBundleCache = cache;
+ }
+
+ public void setContentFetcher(RemoteContentFetcher fetcher) {
+ this.fetcher = fetcher;
+ }
+
+ public static enum RenderingContext {
+ CONTAINER, GADGET
+ }
+
+ public Gadget processGadget(Gadget.ID gadgetId,
+ UserPrefs userPrefs,
+ Locale locale,
+ RenderingContext rctx)
+ throws GadgetProcessException {
+ if (specCache == null) {
+ throw new GadgetProcessException(GadgetException.Code.MISSING_SPEC_CACHE);
+ }
+ if (messageBundleCache == null ) {
+ throw new GadgetProcessException(
+ GadgetException.Code.MISSING_MESSAGE_BUNDLE_CACHE);
+ }
+ if (fetcher == null) {
+ throw new GadgetProcessException(
+ GadgetException.Code.MISSING_REMOTE_OBJECT_FETCHER);
+ }
+
+ // Queue/tree of all jobs to be run for successful processing
+ GadgetContext gc = new GadgetContext(fetcher, messageBundleCache, locale);
+ WorkflowContext wc = new WorkflowContext(gc);
+
+ // Bootstrap tree of jobs to process
+ WorkflowDependency cacheLoadDep =
+ new WorkflowDependency(WorkflowDependency.Type.CORE, CACHE_LOAD);
+ wc.jobsToRun.addJob(new CacheLoadTask(gadgetId, specCache), cacheLoadDep);
+
+ WorkflowDependency urlFetchDep =
+ new WorkflowDependency(WorkflowDependency.Type.CORE, URL_FETCH);
+ wc.jobsToRun.addJob(new SpecLoadTask(fetcher, gadgetId, specCache),
+ urlFetchDep, cacheLoadDep);
+
+ WorkflowDependency enqueueFeatDep =
+ new WorkflowDependency(WorkflowDependency.Type.CORE, ENQUEUE_FEATURES);
+ wc.jobsToRun.addJob(new EnqueueFeaturesTask(), enqueueFeatDep, urlFetchDep);
+
+ // Instantiate CompletionService
+ CompletionService<GadgetException> processor =
+ new ExecutorCompletionService<GadgetException>(executor);
+
+ // All exceptions caught during processing
+ List<GadgetException> gadgetExceptions = new LinkedList<GadgetException>();
+
+ // Loop through queue of Callables, executing each in CompletionService
+ // whose precursors have been satisfied
+ int jobsSubmitted = 0;
+ do {
+ // Loop through all jobs, submitting to run if all deps satisfied
+ List<WorkflowJob> runThisCycle = new LinkedList<WorkflowJob>();
+ for (WorkflowJob candidate : wc.jobsToRun) {
+ if (candidate.ready(wc.depsDone)) {
+ runThisCycle.add(candidate);
+ }
+ }
+
+ // Fire off ready jobs and remove from jobsToRun list
+ for (WorkflowJob runJob : runThisCycle) {
+ processor.submit(runJob.task);
+ jobsSubmitted++;
+ wc.jobsToRun.remove(runJob);
+ }
+
+ // Wait around for at least one job to have completed.
+ // Completion of a job results in an additional dep added to wc.depsDone,
+ // thus potentially freeing up other jobs to run
+ Future<GadgetException> latestResult = null;
+ GadgetException gadgetException = null;
+ try {
+ latestResult = processor.take();
+ } catch (InterruptedException e) {
+ gadgetException = new GadgetException(
+ GadgetException.Code.INTERNAL_SERVER_ERROR, e);
+ }
+
+ // Ensure the task ran successfully
+ if (latestResult != null) {
+ try {
+ gadgetException = latestResult.get();
+ } catch (ExecutionException e) {
+ // TODO: convert into gadgetException with internal error type
+ gadgetException = new GadgetException(
+ GadgetException.Code.INTERNAL_SERVER_ERROR, e);
+ } catch (InterruptedException e) {
+ // TODO: convert into gadgetException with internal error type
+ gadgetException = new GadgetException(
+ GadgetException.Code.INTERNAL_SERVER_ERROR, e);
+ }
+ }
+
+ if (gadgetException != null) {
+ System.err.println(gadgetException.getCode().toString());
+ gadgetException.printStackTrace();
+ Throwable t = gadgetException.getCause();
+ if (t != null) {
+ t.printStackTrace();
+ }
+ // Add to list of all exceptions caught, clear jobs, and continue
+ // to aggressively catch as many exceptions as possible. Since
+ // tasks are running anyway, we may as well get their results in
+ // case it would be useful to the user.
+ gadgetExceptions.add(gadgetException);
+ wc.jobsToRun.clear();
+ jobsSubmitted = wc.depsDone.size();
+ }
+ } while(wc.jobsToRun.size() > 0 || jobsSubmitted > wc.depsDone.size());
+
+ if (gadgetExceptions.size() > 0) {
+ throw new GadgetProcessException(gadgetExceptions);
+ }
+
+ // terminate when all Callables are finished (or Exception detected?)
+ return wc.gadget;
+ }
+
+ public List<GadgetSpec.UserPref> getPrefsInfo(Gadget.ID gadgetId) {
+ return null;
+ }
+
+ private static final String CACHE_LOAD = "cache-load";
+ private static final String URL_FETCH = "url-fetch";
+ private static final String ENQUEUE_FEATURES = "enqueue-features";
+
+ private static class WorkflowJobList extends ArrayList<WorkflowJob> {
+ private final WorkflowContext wc;
+ public WorkflowJobList(WorkflowContext wc) {
+ this.wc = wc;
+ }
+
+ public void addJob(WorkflowTask task,
+ WorkflowDependency done,
+ WorkflowDependency... deps) {
+ task.setup(wc, done);
+ this.add(new WorkflowJob(task, deps));
+ }
+ }
+
+ private static class WorkflowDependency {
+ private static enum Type {
+ CORE, FEATURE_PREPARE, FEATURE_PROCESS
+ }
+
+ private final Type type;
+ private final String id;
+
+ private WorkflowDependency(Type type, String id) {
+ this.type = type;
+ this.id = id;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof WorkflowDependency) {
+ WorkflowDependency wd = (WorkflowDependency)other;
+ return type.equals(wd.type) && id.equals(wd.id);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return (type.toString() + id).hashCode();
+ }
+ }
+
+ private static class WorkflowContext {
+ private Gadget gadget;
+ private GadgetContext context;
+ private WorkflowJobList jobsToRun;
+ private final Set<WorkflowDependency> depsDone;
+
+ private WorkflowContext(GadgetContext context) {
+ this.context = context;
+ this.depsDone = new HashSet<WorkflowDependency>();
+ this.jobsToRun = new WorkflowJobList(this);
+ }
+ }
+
+ private static class WorkflowJob {
+ private final WorkflowTask task;
+ private final List<WorkflowDependency> deps;
+
+ private WorkflowJob(WorkflowTask task, WorkflowDependency... deps) {
+ this.task = task;
+ this.deps = new LinkedList<WorkflowDependency>(Arrays.asList(deps));
+ }
+
+ private boolean ready(Set<WorkflowDependency> depsDone) {
+ for (WorkflowDependency dep : deps) {
+ if (!depsDone.contains(dep)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private static class CacheLoadTask extends WorkflowTask {
+ private final GadgetView.ID gadgetId;
+ private final GadgetDataCache<GadgetSpec> specCache;
+
+ private CacheLoadTask(GadgetView.ID gadgetId,
+ GadgetDataCache<GadgetSpec> specCache) {
+ this.gadgetId = gadgetId;
+ this.specCache = specCache;
+ }
+
+ @Override
+ public void run(WorkflowContext wc) throws GadgetException {
+ GadgetSpec spec = specCache.get(gadgetId.getKey());
+ if (spec != null) {
+ wc.gadget = new Gadget(gadgetId, spec);
+ }
+ }
+ }
+
+ private static class SpecLoadTask extends WorkflowTask {
+ private final RemoteContentFetcher fetcher;
+ private final GadgetView.ID gadgetId;
+ private final GadgetDataCache<GadgetSpec> specCache;
+
+ private SpecLoadTask(RemoteContentFetcher fetcher, GadgetView.ID gadgetId,
+ GadgetDataCache<GadgetSpec> specCache) {
+ this.fetcher = fetcher;
+ this.gadgetId = gadgetId;
+ this.specCache = specCache;
+ }
+
+ @Override
+ public void run(WorkflowContext wc) throws GadgetException {
+ if (wc.gadget != null) {
+ // Already retrieved: do nothing.
+ return;
+ }
+
+ byte[] xml = fetcher.fetch(gadgetId.getURL()).getByteArray();
+ GadgetSpecParser specParser = new GadgetSpecParser();
+ GadgetSpec spec = specParser.parse(gadgetId, xml);
+ wc.gadget = new Gadget(gadgetId, spec);
+ // This isn't a separate job because if it is we'd just need another
+ // flag telling us not to store to the cache.
+ specCache.put(wc.gadget.getId().getKey(), wc.gadget.copy());
+ }
+ }
+
+ private static class EnqueueFeaturesTask extends WorkflowTask {
+ @Override
+ public void run(WorkflowContext wc) throws GadgetException {
+ List<String> needed = new LinkedList<String>();
+ Set<String> optionalNames = new HashSet<String>();
+ Map<String, GadgetSpec.FeatureSpec> requires = wc.gadget.getRequires();
+ for (Map.Entry<String, GadgetSpec.FeatureSpec> entry : requires.entrySet()) {
+ needed.add(entry.getKey());
+ if (entry.getValue().isOptional()) {
+ optionalNames.add(entry.getKey());
+ }
+ }
+
+ // Retrieve needed feature processors from registry
+ List<GadgetFeatureRegistry.Entry> resultsFound =
+ new LinkedList<GadgetFeatureRegistry.Entry>();
+ List<String> resultsMissing = new LinkedList<String>();
+ GadgetFeatureRegistry.getIncludedFeatures(needed,
+ resultsFound,
+ resultsMissing);
+
+ // Classify features this server is missing
+ List<String> missingRequired = new LinkedList<String>();
+ List<String> missingOptional = new LinkedList<String>();
+ for (String missingResult : resultsMissing) {
+ if (optionalNames.contains(missingResult)) {
+ missingOptional.add(missingResult);
+ } else {
+ missingRequired.add(missingResult);
+ }
+ }
+
+ if (missingRequired.size() > 0) {
+ // TODO: throw MissingFeaturesException, subclass of GadgetException,
+ // which is then processed at termination of the jobs loop
+ for (String missing : missingRequired) {
+ System.err.println("Unsupported: " + missing);
+ }
+ throw new GadgetException(GadgetException.Code.UNSUPPORTED_FEATURE);
+ }
+
+ if (missingOptional.size() > 0) {
+ // TODO: add custom task, dependent on nothing, adding metadata re:
+ // missing optionals to the gadget's output (satisfies HasFeature(...))
+ }
+
+ WorkflowDependency specLoadDep =
+ new WorkflowDependency(WorkflowDependency.Type.CORE, URL_FETCH);
+ for (GadgetFeatureRegistry.Entry entry : resultsFound) {
+ List<WorkflowDependency> prepareDeps =
+ new LinkedList<WorkflowDependency>();
+ List<WorkflowDependency> processDeps =
+ new LinkedList<WorkflowDependency>();
+
+ // sanity check: each depends on the spec having been loaded
+ prepareDeps.add(specLoadDep);
+
+ for (String featureDep : entry.getDependencies()) {
+ // prepare depends on all its own deps...
+ WorkflowDependency prepareNeedsDep =
+ new WorkflowDependency(WorkflowDependency.Type.FEATURE_PREPARE,
+ featureDep);
+ prepareDeps.add(prepareNeedsDep);
+
+ WorkflowDependency processNeedsDep =
+ new WorkflowDependency(WorkflowDependency.Type.FEATURE_PROCESS,
+ featureDep);
+ // Can't process until all dependencies prepare() and process()
+ // have completed.
+ processDeps.add(prepareNeedsDep);
+ processDeps.add(processNeedsDep);
+ }
+
+ // Create task for prepare and process, each with the dependency
+ // that running each satisfies
+ WorkflowDependency prepareDep =
+ new WorkflowDependency(WorkflowDependency.Type.FEATURE_PREPARE,
+ entry.getName());
+
+ // We must guarantee that process is called after prepare. This is
+ // implicitly stating that process has all of prepare's dependencies.
+ processDeps.add(prepareDep);
+ WorkflowDependency processDep =
+ new WorkflowDependency(WorkflowDependency.Type.FEATURE_PROCESS,
+ entry.getName());
+
+ // Then add a new job for each task, with appropriate execution
+ // precursors/dependencies, to the jobs queue
+ try {
+ GadgetFeature feature = entry.getFeature().newInstance();
+ wc.jobsToRun.addJob(new FeaturePrepareTask(entry.getName(), feature),
+ prepareDep,
+ prepareDeps.toArray(new WorkflowDependency[]{}));
+ wc.jobsToRun.addJob(new FeatureProcessTask(entry.getName(), feature),
+ processDep,
+ processDeps.toArray(new WorkflowDependency[]{}));
+ } catch (InstantiationException e) {
+ System.err.println("Unable to instantiate "
+ + entry.getFeature().getName());
+ } catch (IllegalAccessException e) {
+ System.err.println("Unable to call no-arg constructor for "
+ + entry.getFeature().getName());
+ }
+ }
+ }
+ }
+
+ private static class FeaturePrepareTask extends WorkflowTask {
+ private final GadgetFeature feature;
+ private final String name;
+ private FeaturePrepareTask(String name, GadgetFeature feature) {
+ this.name = name;
+ this.feature = feature;
+ }
+
+ @Override
+ public void run(WorkflowContext wc) throws GadgetException {
+ Map<String, String> params = Gadget.getFeatureParams(wc.gadget, name);
+ feature.prepare(wc.gadget, wc.context, params);
+ }
+ }
+
+ private static class FeatureProcessTask extends WorkflowTask {
+ private final GadgetFeature feature;
+ private final String name;
+ private FeatureProcessTask(String name, GadgetFeature feature) {
+ this.name = name;
+ this.feature = feature;
+ }
+
+ @Override
+ public void run(WorkflowContext wc) throws GadgetException {
+ Map<String, String> params = Gadget.getFeatureParams(wc.gadget, name);
+ feature.process(wc.gadget, wc.context, params);
+ }
+ }
+
+ private static abstract class WorkflowTask
+ implements Callable<GadgetException> {
+ // This class is mostly just an alias to Callable<GadgetException>
+ // providing a helper method for passing in context
+ public abstract void run(WorkflowContext wc) throws GadgetException;
+
+ private WorkflowContext wc;
+ private WorkflowDependency done;
+ public WorkflowTask setup(WorkflowContext wc, WorkflowDependency done) {
+ this.wc = wc;
+ this.done = done;
+ return this;
+ }
+
+ public GadgetException call() {
+ GadgetException ret = null;
+ try {
+ this.run(wc);
+ } catch (GadgetException e) {
+ ret = e;
+ } catch (Exception e) {
+ // TODO: capture Throwable cause in wrapped exception
+ ret = new GadgetException(
+ GadgetException.Code.INTERNAL_SERVER_ERROR, e);
+ } finally {
+ wc.depsDone.add(done);
+ }
+ return ret;
+ }
+ }
+
+ public static class GadgetProcessException extends Exception {
+ private final List<GadgetException> components;
+
+ public GadgetProcessException(List<GadgetException> components) {
+ this.components = components;
+ }
+
+ public GadgetProcessException(GadgetException.Code code) {
+ this.components = new ArrayList<GadgetException>();
+ this.components.add(new GadgetException(code));
+ }
+
+ public List<GadgetException> getComponents() {
+ return components;
+ }
+ }
+}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpec.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpec.java?rev=603539&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpec.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpec.java Wed Dec 12 02:33:35 2007
@@ -0,0 +1,87 @@
+/*
+ * Licensed 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.shindig.gadgets;
+
+import java.net.URL;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Represents a Gadget specification.
+ */
+public interface GadgetSpec {
+ public String getTitle();
+ public URL getTitleURL();
+ public String getDirectoryTitle();
+ public String getDescription();
+ public String getAuthor();
+ public String getAuthorEmail();
+ public String getScreenshot();
+ public String getThumbnail();
+
+ public static interface MessageBundle {
+ public Locale getLocale();
+ public URL getURL();
+ public boolean isRightToLeft();
+ }
+
+ public List<MessageBundle> getMessageBundles();
+
+ public static interface FeatureSpec {
+ public String getName();
+ public Map<String, String> getParams();
+ public boolean isOptional();
+ }
+
+ public Map<String, FeatureSpec> getRequires();
+ public List<String> getPreloads();
+
+ public static interface Icon {
+ public URL getURL();
+ public String getMode();
+ public String getType();
+ }
+
+ public List<Icon> getIcons();
+
+ public static interface UserPref {
+
+ public String getName();
+ public String getDisplayName();
+ public String getDefaultValue();
+ public boolean isRequired();
+
+ public static enum DataType {
+ STRING, HIDDEN, BOOL, ENUM, LIST, NUMBER
+ }
+
+ public DataType getDataType();
+ }
+
+ public List<UserPref> getUserPrefs();
+
+ public static enum ContentType {
+ HTML, URL
+ }
+
+ public ContentType getContentType();
+ public URL getContentHref();
+ public String getContentData();
+
+ /**
+ * @return A copy of the spec. This is NOT the same as clone().
+ */
+ public GadgetSpec copy();
+}
\ No newline at end of file
Re: svn commit: r603539 [1/3] - in /incubator/shindig/trunk: ./ java/ java/gadgets/ java/gadgets/src/ java/gadgets/src/main/ java/gadgets/src/main/java/ java/gadgets/src/main/java/org/ java/gadgets/src/main/java/org/apache/ java/gadgets/src/main/java/org/a...
Posted by Brian McCallister <br...@skife.org>.
Just skimmed the initial checkin. Looks really nice! A couple points:
javascript/cotainer/gadgets.js
* Header reads:
// Copyright 2007 Google Inc.
// All Rights Reserved.
javascript/container/cookies.js
* Header reads:
// Copyright 2006 Google Inc.
// All Rights Reserved.
javascript/container/ifpc_relay.html relies on http://www.google.com/ig/ifpc.js
* can we remove the dependency on being on the internet for local dev,
and on Google's infrastructure in particular?
* What are the license terms for that chunk of javascript?
-Brian
On Dec 12, 2007, at 2:33 AM, johnh@apache.org wrote:
> Author: johnh
> Date: Wed Dec 12 02:33:35 2007
> New Revision: 603539
>
> URL: http://svn.apache.org/viewvc?rev=603539&view=rev
> Log:
> Initial commit of Shindig Gadget Container JavaScript and Gadget
> Server.
>
> Includes:
>
> * Gadget Container JavaScript. Code embedded into an arbitrary web
> page
> enabling it to render and manage Gadgets.
> + Support for rendering using gmodules.com or a Shindig Gadget
> Server
> + Basic IFPC (gadget <-> container communication) support via
> inclusion
> of Google IFPC library
> + Simple layout management: render in DIV (StaticLayoutManager);
> render multiple Gadgets left-aligned in DIV
> (FloatLeftLayoutManager)
> + UserPrefs support through JS interface
> + Cookie-based UserPrefs implementation
> + _IG_SetTitle container-side support
> + _IG_AdjustIframeHeight container-side support
> + Sample pages demonstrating all the above functionality
>
> * Gadget Server (written in Java). Web server that processes Gadget
> requests, parsing their spec XML, processing it through a workflow
> of core and extension (<Require>/<Optional>) Features, and
> serializing
> output for rendering the Gadget to the end user.
> + Core server implementation constructing processing workflows
> out of
> GadgetFeature objects providing various feature support
> + GadgetFeature interface providing core extensibility mechanism
> for
> the Gadgets platform; extenders implement GadgetFeature and
> register
> their implementation with GadgetFeatureRegistry
> + Gadget spec XML parser
> + Simple caching API with in-memory Map-based implementations as
> samples
> + Remote content (eg. HTTP) fetcher interface
> + Basic java.net-based RemoteContentFetcher implementation
> + "Hangman" variable substitution support (eg. __MSG_foo__),
> including BIDI,
> MSG (message bundles) and UP (user prefs) type variables
> + Early JavaScript-based Feature support through JsLibrary* classes
> + Content proxy including JSON support
>
> * A handful of tests for various Server components.
>
> * Maven build support.
>
> This submission represents significant contributions by:
> fargo@google.com, etnu@google.com, dharkness@google.com,
> jyang@google.com, wangz@google.com, dcoker@google.com
>
> Much more to come!
>
>
> Added:
> incubator/shindig/trunk/COPYING
> incubator/shindig/trunk/NOTICE
> incubator/shindig/trunk/README
> incubator/shindig/trunk/java/
> incubator/shindig/trunk/java/gadgets/
> incubator/shindig/trunk/java/gadgets/README
> incubator/shindig/trunk/java/gadgets/pom.xml
> incubator/shindig/trunk/java/gadgets/src/
> incubator/shindig/trunk/java/gadgets/src/main/
> incubator/shindig/trunk/java/gadgets/src/main/java/
> incubator/shindig/trunk/java/gadgets/src/main/java/org/
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BasicGadgetDataCache.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BasicRemoteContentFetcher.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BidiSubstituter.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/CoreJsFeature.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/Gadget.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetContext.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetDataCache.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetException.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetFeature.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetFeatureRegistry.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetServer.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetSpec.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetSpecParser.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetView.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/JsLibrary.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/JsLibraryFeature.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/MessageBundle.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/MessageBundleParser.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/MessageBundleSubstituter.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/ModuleSubstituter.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/RemoteContent.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/RemoteContentFetcher.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/SpecParserException.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/Substitutions.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/UserPrefSubstituter.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/UserPrefs.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/Utf8InputStream.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/http/
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/http/AnalyticsFeature.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/http/BasicGadgetHttpServlet.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/http/BasicHttpContext.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/http/JsonpProxyServlet.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/http/ProxyHandler.java
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/http/ProxyServlet.java
> incubator/shindig/trunk/java/gadgets/src/main/webapp/
> incubator/shindig/trunk/java/gadgets/src/main/webapp/WEB-INF/
> incubator/shindig/trunk/java/gadgets/src/main/webapp/WEB-INF/
> web.xml
> incubator/shindig/trunk/java/gadgets/src/test/
> incubator/shindig/trunk/java/gadgets/src/test/java/
> incubator/shindig/trunk/java/gadgets/src/test/java/org/
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/
> shindig/
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/
> shindig/gadgets/
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/
> shindig/gadgets/EasyMockTestCase.java
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/
> shindig/gadgets/GadgetServerTest.java
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/
> shindig/gadgets/GadgetSpecTestFixture.java
> incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/
> shindig/gadgets/SubstitutionsTest.java
> incubator/shindig/trunk/javascript/
> incubator/shindig/trunk/javascript/README
> incubator/shindig/trunk/javascript/container/
> incubator/shindig/trunk/javascript/container/cookies.js
> incubator/shindig/trunk/javascript/container/gadgets.css
> incubator/shindig/trunk/javascript/container/gadgets.js
> incubator/shindig/trunk/javascript/container/ifpc_relay.html
> incubator/shindig/trunk/javascript/container/json.js
> incubator/shindig/trunk/javascript/container/sample1.html
> incubator/shindig/trunk/javascript/container/sample2.html
> incubator/shindig/trunk/javascript/container/sample3.html
> incubator/shindig/trunk/javascript/container/sample4.html
>
> Added: incubator/shindig/trunk/COPYING
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/COPYING?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/COPYING (added)
> +++ incubator/shindig/trunk/COPYING Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,201 @@
> + Apache License
> + Version 2.0, January 2004
> + http://www.apache.org/licenses/
> +
> + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
> +
> + 1. Definitions.
> +
> + "License" shall mean the terms and conditions for use,
> reproduction,
> + and distribution as defined by Sections 1 through 9 of this
> document.
> +
> + "Licensor" shall mean the copyright owner or entity
> authorized by
> + the copyright owner that is granting the License.
> +
> + "Legal Entity" shall mean the union of the acting entity and
> all
> + other entities that control, are controlled by, or are under
> common
> + control with that entity. For the purposes of this definition,
> + "control" means (i) the power, direct or indirect, to cause the
> + direction or management of such entity, whether by contract or
> + otherwise, or (ii) ownership of fifty percent (50%) or more
> of the
> + outstanding shares, or (iii) beneficial ownership of such
> entity.
> +
> + "You" (or "Your") shall mean an individual or Legal Entity
> + exercising permissions granted by this License.
> +
> + "Source" form shall mean the preferred form for making
> modifications,
> + including but not limited to software source code,
> documentation
> + source, and configuration files.
> +
> + "Object" form shall mean any form resulting from mechanical
> + transformation or translation of a Source form, including but
> + not limited to compiled object code, generated documentation,
> + and conversions to other media types.
> +
> + "Work" shall mean the work of authorship, whether in Source or
> + Object form, made available under the License, as indicated
> by a
> + copyright notice that is included in or attached to the work
> + (an example is provided in the Appendix below).
> +
> + "Derivative Works" shall mean any work, whether in Source or
> Object
> + form, that is based on (or derived from) the Work and for
> which the
> + editorial revisions, annotations, elaborations, or other
> modifications
> + represent, as a whole, an original work of authorship. For
> the purposes
> + of this License, Derivative Works shall not include works
> that remain
> + separable from, or merely link (or bind by name) to the
> interfaces of,
> + the Work and Derivative Works thereof.
> +
> + "Contribution" shall mean any work of authorship, including
> + the original version of the Work and any modifications or
> additions
> + to that Work or Derivative Works thereof, that is intentionally
> + submitted to Licensor for inclusion in the Work by the
> copyright owner
> + or by an individual or Legal Entity authorized to submit on
> behalf of
> + the copyright owner. For the purposes of this definition,
> "submitted"
> + means any form of electronic, verbal, or written
> communication sent
> + to the Licensor or its representatives, including but not
> limited to
> + communication on electronic mailing lists, source code
> control systems,
> + and issue tracking systems that are managed by, or on behalf
> of, the
> + Licensor for the purpose of discussing and improving the
> Work, but
> + excluding communication that is conspicuously marked or
> otherwise
> + designated in writing by the copyright owner as "Not a
> Contribution."
> +
> + "Contributor" shall mean Licensor and any individual or Legal
> Entity
> + on behalf of whom a Contribution has been received by
> Licensor and
> + subsequently incorporated within the Work.
> +
> + 2. Grant of Copyright License. Subject to the terms and
> conditions of
> + this License, each Contributor hereby grants to You a
> perpetual,
> + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
> + copyright license to reproduce, prepare Derivative Works of,
> + publicly display, publicly perform, sublicense, and
> distribute the
> + Work and such Derivative Works in Source or Object form.
> +
> + 3. Grant of Patent License. Subject to the terms and conditions of
> + this License, each Contributor hereby grants to You a
> perpetual,
> + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
> + (except as stated in this section) patent license to make,
> have made,
> + use, offer to sell, sell, import, and otherwise transfer the
> Work,
> + where such license applies only to those patent claims
> licensable
> + by such Contributor that are necessarily infringed by their
> + Contribution(s) alone or by combination of their
> Contribution(s)
> + with the Work to which such Contribution(s) was submitted. If
> You
> + institute patent litigation against any entity (including a
> + cross-claim or counterclaim in a lawsuit) alleging that the
> Work
> + or a Contribution incorporated within the Work constitutes
> direct
> + or contributory patent infringement, then any patent licenses
> + granted to You under this License for that Work shall terminate
> + as of the date such litigation is filed.
> +
> + 4. Redistribution. You may reproduce and distribute copies of the
> + Work or Derivative Works thereof in any medium, with or without
> + modifications, and in Source or Object form, provided that You
> + meet the following conditions:
> +
> + (a) You must give any other recipients of the Work or
> + Derivative Works a copy of this License; and
> +
> + (b) You must cause any modified files to carry prominent
> notices
> + stating that You changed the files; and
> +
> + (c) You must retain, in the Source form of any Derivative Works
> + that You distribute, all copyright, patent, trademark, and
> + attribution notices from the Source form of the Work,
> + excluding those notices that do not pertain to any part of
> + the Derivative Works; and
> +
> + (d) If the Work includes a "NOTICE" text file as part of its
> + distribution, then any Derivative Works that You
> distribute must
> + include a readable copy of the attribution notices
> contained
> + within such NOTICE file, excluding those notices that do
> not
> + pertain to any part of the Derivative Works, in at least
> one
> + of the following places: within a NOTICE text file
> distributed
> + as part of the Derivative Works; within the Source form or
> + documentation, if provided along with the Derivative
> Works; or,
> + within a display generated by the Derivative Works, if and
> + wherever such third-party notices normally appear. The
> contents
> + of the NOTICE file are for informational purposes only and
> + do not modify the License. You may add Your own attribution
> + notices within Derivative Works that You distribute,
> alongside
> + or as an addendum to the NOTICE text from the Work,
> provided
> + that such additional attribution notices cannot be
> construed
> + as modifying the License.
> +
> + You may add Your own copyright statement to Your
> modifications and
> + may provide additional or different license terms and
> conditions
> + for use, reproduction, or distribution of Your modifications,
> or
> + for any such Derivative Works as a whole, provided Your use,
> + reproduction, and distribution of the Work otherwise complies
> with
> + the conditions stated in this License.
> +
> + 5. Submission of Contributions. Unless You explicitly state
> otherwise,
> + any Contribution intentionally submitted for inclusion in the
> Work
> + by You to the Licensor shall be under the terms and
> conditions of
> + this License, without any additional terms or conditions.
> + Notwithstanding the above, nothing herein shall supersede or
> modify
> + the terms of any separate license agreement you may have
> executed
> + with Licensor regarding such Contributions.
> +
> + 6. Trademarks. This License does not grant permission to use the
> trade
> + names, trademarks, service marks, or product names of the
> Licensor,
> + except as required for reasonable and customary use in
> describing the
> + origin of the Work and reproducing the content of the NOTICE
> file.
> +
> + 7. Disclaimer of Warranty. Unless required by applicable law or
> + agreed to in writing, Licensor provides the Work (and each
> + Contributor provides its Contributions) on an "AS IS" BASIS,
> + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> + implied, including, without limitation, any warranties or
> conditions
> + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
> + PARTICULAR PURPOSE. You are solely responsible for
> determining the
> + appropriateness of using or redistributing the Work and
> assume any
> + risks associated with Your exercise of permissions under this
> License.
> +
> + 8. Limitation of Liability. In no event and under no legal theory,
> + whether in tort (including negligence), contract, or otherwise,
> + unless required by applicable law (such as deliberate and
> grossly
> + negligent acts) or agreed to in writing, shall any
> Contributor be
> + liable to You for damages, including any direct, indirect,
> special,
> + incidental, or consequential damages of any character arising
> as a
> + result of this License or out of the use or inability to use
> the
> + Work (including but not limited to damages for loss of
> goodwill,
> + work stoppage, computer failure or malfunction, or any and all
> + other commercial damages or losses), even if such Contributor
> + has been advised of the possibility of such damages.
> +
> + 9. Accepting Warranty or Additional Liability. While
> redistributing
> + the Work or Derivative Works thereof, You may choose to offer,
> + and charge a fee for, acceptance of support, warranty,
> indemnity,
> + or other liability obligations and/or rights consistent with
> this
> + License. However, in accepting such obligations, You may act
> only
> + on Your own behalf and on Your sole responsibility, not on
> behalf
> + of any other Contributor, and only if You agree to indemnify,
> + defend, and hold each Contributor harmless for any liability
> + incurred by, or claims asserted against, such Contributor by
> reason
> + of your accepting any such warranty or additional liability.
> +
> + END OF TERMS AND CONDITIONS
> +
> + APPENDIX: How to apply the Apache License to your work.
> +
> + To apply the Apache License to your work, attach the following
> + boilerplate notice, with the fields enclosed by brackets "[]"
> + replaced with your own identifying information. (Don't include
> + the brackets!) The text should be enclosed in the appropriate
> + comment syntax for the file format. We also recommend that a
> + file or class name and description of purpose be included on
> the
> + same "printed page" as the copyright notice for easier
> + identification within third-party archives.
> +
> + Copyright 2007 Google Inc.
> +
> + Licensed 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.
>
> Added: incubator/shindig/trunk/NOTICE
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/NOTICE?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/NOTICE (added)
> +++ incubator/shindig/trunk/NOTICE Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,2 @@
> +This product includes software (Gadget Server, Gadget Container)
> developed by
> +Google Inc. (http://code.google.com/)
>
> Added: incubator/shindig/trunk/README
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/README?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/README (added)
> +++ incubator/shindig/trunk/README Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,10 @@
> +Welcome to Apache Shindig!
> +
> +* Read java/gadgets/README for instructions on how to start up your
> own
> + Shindig Gadget Server.
> +
> +* Read javascript/README for instructions for using the Shindig
> Gadget
> + Container JavaScript to enable your page to render Gadgets using
> + gmodules.com or a server started up as described above.
> +
> +For more information, see http://incubator.apache.org/projects/shindig.html
>
> Added: incubator/shindig/trunk/java/gadgets/README
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/README?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/README (added)
> +++ incubator/shindig/trunk/java/gadgets/README Wed Dec 12 02:33:35
> 2007
> @@ -0,0 +1,16 @@
> +Installing and Running Shindig Gadget Server
> +============================================
> +
> +1) Install Maven 2.0 (see http://maven.apache.org)
> +
> +2) Make sure the JAVA_HOME environment variable is set to the
> location of your
> + JDK/JRE, and that the maven executable is in your PATH.
> +
> +3) From java/gadgets/...
> + * mvn package - Builds Gadget Server and runs tests.
> + * mvn jetty:run - Builds Gadget Server (no tests), installs
> Servlet at localhost:8080.
> +
> +4) Hit server at http://localhost:8080/gadgets/ifr?url=<gadget-url>
> + Example: http://localhost:8080/gadgets/ifr?url=http://www.labpixies.com/campaigns/todo/todo.xml
> +
> +For more information, see http://incubator.apache.org/projects/shindig.html
>
> Added: incubator/shindig/trunk/java/gadgets/pom.xml
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/pom.xml?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/pom.xml (added)
> +++ incubator/shindig/trunk/java/gadgets/pom.xml Wed Dec 12 02:33:35
> 2007
> @@ -0,0 +1,92 @@
> +<?xml version="1.0"?>
> +<!--
> +
> + Licensed 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.
> +
> +-->
> +<project xmlns="http://maven.apache.org/POM/4.0.0"
> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
> + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd
> ">
> +
> + <modelVersion>4.0.0</modelVersion>
> + <groupId>org.apache.shindig</groupId>
> + <artifactId>gadgets</artifactId>
> + <version>0.5</version>
> + <packaging>war</packaging>
> +
> + <name>Shindig Gadget Server</name>
> + <url>http://shindig.apache.org</url>
> +
> + <licenses>
> + <license>
> + <name>Apache 2</name>
> + <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
> + </license>
> + </licenses>
> +
> + <dependencies>
> + <dependency>
> + <groupId>org.json</groupId>
> + <artifactId>json</artifactId>
> + <version>20070829</version>
> + <scope>compile</scope>
> + </dependency>
> + <dependency>
> + <groupId>javax.servlet</groupId>
> + <artifactId>servlet-api</artifactId>
> + <version>2.3</version>
> + <scope>compile</scope>
> + </dependency>
> +
> + <dependency>
> + <groupId>junit</groupId>
> + <artifactId>junit</artifactId>
> + <version>3.8.1</version>
> + <scope>test</scope>
> + </dependency>
> + <dependency>
> + <groupId>org.easymock</groupId>
> + <artifactId>easymock</artifactId>
> + <version>2.3</version>
> + <scope>test</scope>
> + </dependency>
> + <dependency>
> + <groupId>org.easymock</groupId>
> + <artifactId>easymockclassextension</artifactId>
> + <version>2.2.2</version>
> + <scope>test</scope>
> + </dependency>
> + </dependencies>
> +
> + <build>
> + <finalName>gadgets</finalName>
> + <sourceDirectory>${basedir}/src/main/java</sourceDirectory>
> + <testSourceDirectory>${basedir}/src/test/java</
> testSourceDirectory>
> + <outputDirectory>${basedir}/target/classes</outputDirectory>
> + <testOutputDirectory>${basedir}/target/test-classes</
> testOutputDirectory>
> + <plugins>
> + <plugin>
> + <groupId>org.apache.maven.plugins</groupId>
> + <artifactId>maven-compiler-plugin</artifactId>
> + <configuration>
> + <source>1.5</source>
> + <target>1.5</target>
> + </configuration>
> + </plugin>
> + <plugin>
> + <groupId>org.mortbay.jetty</groupId>
> + <artifactId>maven-jetty-plugin</artifactId>
> + </plugin>
> + </plugins>
> + </build>
> +</project>
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BasicGadgetDataCache.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicGadgetDataCache.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BasicGadgetDataCache.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BasicGadgetDataCache.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,39 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import org.apache.shindig.gadgets.GadgetDataCache;
> +
> +import java.util.HashMap;
> +import java.util.Map;
> +
> +/**
> + * Basic implementation of an in-memory data cache, backed by a
> + * {@code HashMap}.
> + *
> + * @param <T> Type of data to store in the cache.
> + */
> +public class BasicGadgetDataCache<T> implements GadgetDataCache<T> {
> + private Map<String, T> cache = new HashMap<String, T>();
> +
> + /** {@inheritDoc} */
> + public T get(String key) {
> + return cache.get(key);
> + }
> +
> + /** {@inheritDoc} */
> + public void put(String key, T value) {
> + cache.put(key, value);
> + }
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BasicRemoteContentFetcher.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BasicRemoteContentFetcher.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BasicRemoteContentFetcher.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BasicRemoteContentFetcher.java Wed Dec 12 02:33:35
> 2007
> @@ -0,0 +1,73 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import org.apache.shindig.gadgets.RemoteContentFetcher;
> +import org.apache.shindig.gadgets.RemoteContent;
> +
> +import java.io.ByteArrayOutputStream;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.net.HttpURLConnection;
> +import java.net.URL;
> +import java.util.List;
> +import java.util.Map;
> +
> +/**
> + * Implementation of a {@code RemoteObjectFetcher} using standard
> java.net
> + * classes. Only supports HTTP fetching at present.
> + */
> +public class BasicRemoteContentFetcher implements
> RemoteContentFetcher {
> + private static final int CONNECT_TIMEOUT_MS = 5000;
> +
> + private final int maxObjSize;
> +
> + /**
> + * Creates a new fetcher capable of retrieving objects {@code
> maxObjSize}
> + * bytes or smaller in size.
> + * @param maxObjSize Maximum size, in bytes, of object to fetch
> + */
> + public BasicRemoteContentFetcher(int maxObjSize) {
> + this.maxObjSize = maxObjSize;
> + }
> +
> + /** {@inheritDoc} */
> + public RemoteContent fetch(URL url) {
> + ByteArrayOutputStream out = new ByteArrayOutputStream();
> +
> + int responseCode;
> + HttpURLConnection fetcher;
> + Map<String, List<String>> headers = null;
> +
> + try {
> + fetcher = (HttpURLConnection) url.openConnection();
> + fetcher.setInstanceFollowRedirects(true);
> + fetcher.setConnectTimeout(CONNECT_TIMEOUT_MS);
> +
> + responseCode = fetcher.getResponseCode();
> + headers = fetcher.getHeaderFields();
> +
> + byte chunk[] = new byte[8192];
> + int chunkSize;
> + InputStream in = fetcher.getInputStream();
> + while (out.size() < maxObjSize && (chunkSize =
> in.read(chunk)) != -1) {
> + out.write(chunk, 0, chunkSize);
> + }
> + } catch (IOException e) {
> + responseCode = 500;
> + }
> +
> + return new RemoteContent(responseCode, out.toByteArray(),
> headers);
> + }
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BidiSubstituter.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/BidiSubstituter.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BidiSubstituter.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/BidiSubstituter.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,86 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import java.util.List;
> +import java.util.Locale;
> +import java.util.Map;
> +
> +/**
> + * Provides static hangman substitutions for bidirectional language
> support.
> + * Useful for generating internationalized layouts using CSS.
> + */
> +public class BidiSubstituter implements GadgetFeature {
> +
> + /**
> + * Fetches a message bundle spec from the {@code GadgetSpec} for
> the
> + * provided locale.
> + * @param spec Gadget spec from which to retrieve message bundle
> spec
> + * @param locale Locale of message bundle to retrieve
> + * @return The message bundle, or null if not found
> + */
> + private GadgetSpec.MessageBundle getBundle(GadgetSpec spec,
> + Locale locale) {
> + List<GadgetSpec.MessageBundle> bundles =
> spec.getMessageBundles();
> + for (GadgetSpec.MessageBundle bundle : bundles) {
> + if (bundle.getLocale().equals(locale)) {
> + return bundle;
> + }
> + }
> + return null;
> + }
> +
> + /** {@inheritDoc} */
> + public void prepare(GadgetView spec,
> + GadgetContext context,
> + Map<String, String> params) {
> + // Nothing here.
> + }
> +
> + /**
> + * Populates bidi substitutions.
> + * @param gadget Gadget object to process
> + * @param context Context in which Gadget is being processed
> + */
> + public void process(Gadget gadget,
> + GadgetContext context,
> + Map<String, String> params) {
> + Substitutions subst = gadget.getSubstitutions();
> + Locale locale = context.getLocale();
> + // Find an appropriate bundle for the ltr flag.
> + GadgetSpec.MessageBundle bundle = getBundle(gadget, locale);
> + if (null == bundle) {
> + bundle = getBundle(gadget, new Locale(locale.getLanguage(),
> "all"));
> + }
> + if (null == bundle) {
> + bundle = getBundle(gadget, new Locale("all", "all"));
> + }
> + boolean rtl = false;
> + if (bundle != null) {
> + rtl = bundle.isRightToLeft();
> + }
> + subst.addSubstitution(Substitutions.Type.BIDI,
> + "START_EDGE",
> + rtl ? "right" : "left");
> + subst.addSubstitution(Substitutions.Type.BIDI,
> + "END_EDGE",
> + rtl ? "left" : "right");
> + subst.addSubstitution(Substitutions.Type.BIDI,
> + "DIR",
> + rtl ? "rtl" : "ltr");
> + subst.addSubstitution(Substitutions.Type.BIDI,
> + "REVERSE_DIR",
> + rtl ? "ltr" : "rtl");
> + }
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/CoreJsFeature.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/CoreJsFeature.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/CoreJsFeature.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/CoreJsFeature.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,27 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import java.util.Map;
> +
> +/**
> + * Adds all core javascript dependencies that are needed by all
> gadgets.
> + */
> +public class CoreJsFeature extends JsLibraryFeature {
> + @Override
> + public void process(Gadget gadget, GadgetContext context,
> + Map<String, String> params) {
> + gadget.addJsLibrary(JsLibrary.file("http://gmodules.com/ig/extern_js/f/CgJlbhICdXMrMAE4ACw/6gZqwg2JpuM.js
> "));
> + }
> +}
> \ No newline at end of file
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/Gadget.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/Gadget.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/Gadget.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/Gadget.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,296 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import java.net.MalformedURLException;
> +import java.net.URL;
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.HashMap;
> +import java.util.LinkedList;
> +import java.util.List;
> +import java.util.Map;
> +
> +/**
> + * Intermediary representation of all state associated with
> processing
> + * of a single gadget request.
> + *
> + * This class is constructed by an immutable base {@code GadgetSpec},
> + * and is modified in parallel by a number of {@code GadgetFeature}
> + * processors, in an order defined by their dependencies, in
> + * {@code GadgetServer}.
> + *
> + * Upon completion of processing, a {@code Gadget} is serialized as
> appropriate
> + * to whatever output format is appropriate (eg. as gadget content
> in an
> + * IFRAME), potentially with post-processing such as HTML whitespace
> + * compression or HTML+JS (Caja) rewriting applied.
> + *
> + * "Hangman" variable substitutions (eg. __MSG_foo__) are performed
> as needed
> + * and transparently for fields that support this functionality.
> + */
> +public class Gadget implements GadgetView {
> + private final ID id;
> + private final GadgetSpec baseSpec;
> + private final Substitutions substitutions;
> + private final Map<String, String> userPrefValues;
> + private final List<JsLibrary> jsLibraries;
> +
> + public static class GadgetId implements GadgetView.ID {
> + private final URL url;
> + private final int moduleId;
> +
> + public GadgetId(URL url, int moduleId) {
> + this.url = url;
> + this.moduleId = moduleId;
> + }
> +
> + @Override
> + public boolean equals(Object comp) {
> + if (comp instanceof GadgetView.ID) {
> + GadgetView.ID id = (GadgetView.ID)comp;
> + return id.getURL() == url &&
> + id.getModuleId() == moduleId;
> + } else {
> + return false;
> + }
> + }
> +
> + @Override
> + public int hashCode() {
> + int result = 17;
> + result = (37 * result) + url.hashCode();
> + result = (37 * result) + moduleId;
> + return result;
> + }
> +
> + public URL getURL() {
> + return url;
> + }
> +
> + public int getModuleId() {
> + return moduleId;
> + }
> +
> + public String getKey() {
> + return url.toString();
> + }
> + }
> +
> + /**
> + * Create a new {@code Gadget} devoid of processing modifications.
> + * @param id Identifier used to retrieve {@code baseSpec}
> + * @param baseSpec Base (immutable) {@code GadgetSpec} on which
> this is based
> + */
> + public Gadget(ID id, GadgetSpec baseSpec) {
> + this.id = id;
> + this.baseSpec = baseSpec;
> + substitutions = new Substitutions();
> + userPrefValues = new HashMap<String, String>();
> + jsLibraries = new LinkedList<JsLibrary>();
> + }
> +
> + /**
> + * @return Global identifier used to retrieve gadget's spec
> + */
> + public ID getId() {
> + return id;
> + }
> +
> + /**
> + * @return GadgetSpec that backs this Gadget. Package scope for
> tests.
> + */
> + GadgetSpec getBaseSpec() {
> + return baseSpec;
> + }
> +
> + /**
> + * @return Object containing all hangman substitutions applied to
> this gadget
> + */
> + public Substitutions getSubstitutions() {
> + return substitutions;
> + }
> +
> + // GadgetSpec accessors
> +
> + /**
> + * @return Gadget title with substitutions applied
> + */
> + public String getTitle() {
> + return substitutions.substitute(baseSpec.getTitle());
> + }
> +
> + /**
> + * @return URL used as a target for Gadget's title link, or null
> if malformed
> + */
> + public URL getTitleURL() {
> + URL ret = null;
> + String urlStr = baseSpec.getTitleURL().toString();
> + try {
> + ret = new URL(substitutions.substitute(urlStr));
> + } catch (MalformedURLException e) {
> + return null;
> + }
> + return ret;
> + }
> +
> + /**
> + * @return String used to describe this Gadget in directories, with
> + * substitutions applied
> + */
> + public String getDirectoryTitle() {
> + return substitutions.substitute(baseSpec.getDirectoryTitle());
> + }
> +
> + /**
> + * @return Extended description of {@code Gadget}, with
> substitutions applied
> + */
> + public String getDescription() {
> + return substitutions.substitute(baseSpec.getDescription());
> + }
> +
> + /**
> + * @return Name of this Gadget's author as specified in its spec
> + */
> + public String getAuthor() {
> + return baseSpec.getAuthor();
> + }
> +
> + /**
> + * @return E-mail address of this Gadget's author as specified in
> its spec
> + */
> + public String getAuthorEmail() {
> + return baseSpec.getAuthorEmail();
> + }
> +
> + // TODO: make this URL?
> + public String getScreenshot() {
> + return baseSpec.getScreenshot();
> + }
> +
> + // TODO: make this URL?
> + public String getThumbnail() {
> + return baseSpec.getThumbnail();
> + }
> +
> + public List<MessageBundle> getMessageBundles() {
> + return new
> ArrayList<MessageBundle>(baseSpec.getMessageBundles());
> + }
> +
> + /**
> + * @return List of all {@code FeatureSpec}s declared by this gadget
> + */
> + public Map<String, FeatureSpec> getRequires() {
> + return Collections.unmodifiableMap(baseSpec.getRequires());
> + }
> +
> + /**
> + * @return All JS libraries needed to render this gadget.
> + */
> + public List<JsLibrary> getJsLibraries() {
> + return Collections.unmodifiableList(jsLibraries);
> + }
> +
> + /**
> + * @param library
> + */
> + public void addJsLibrary(JsLibrary library) {
> + jsLibraries.add(library);
> + }
> +
> + /**
> + * Extracts parameters for the given feature.
> + *
> + * @param gadget
> + * @param feature
> + * @return The parameters, or an empty map.
> + */
> + @SuppressWarnings("unchecked")
> + public static Map<String, String> getFeatureParams(Gadget gadget,
> + String
> feature) {
> + GadgetSpec.FeatureSpec spec = gadget.getRequires().get(feature);
> + if (spec == null) {
> + return Collections.EMPTY_MAP;
> + } else {
> + return spec.getParams();
> + }
> + }
> +
> + /**
> + * @return List of all preload URLs declared, with substitutions
> applied
> + */
> + public List<String> getPreloads() {
> + List<String> ret = new LinkedList<String>();
> + for (String preload : baseSpec.getPreloads()) {
> + ret.add(substitutions.substitute(preload));
> + }
> + return ret;
> + }
> +
> + /**
> + * @return List of icons defined in gadget spec
> + */
> + public List<Icon> getIcons() {
> + return Collections.unmodifiableList(baseSpec.getIcons());
> + }
> +
> + /**
> + * @return List of all user pref specs defined in gadget spec
> + */
> + public List<UserPref> getUserPrefs() {
> + return Collections.unmodifiableList(baseSpec.getUserPrefs());
> + }
> +
> + public Map<String, String> getUserPrefValues() {
> + return Collections.unmodifiableMap(userPrefValues);
> + }
> +
> + /**
> + * @return Type of gadget to render
> + */
> + public ContentType getContentType() {
> + return baseSpec.getContentType();
> + }
> +
> + /**
> + * @return URL of gadget to render of type == URL; null if
> malformed/missing
> + */
> + public URL getContentHref() {
> + if (getContentType() != ContentType.URL) {
> + return null;
> + }
> +
> + URL ret = null;
> + String urlStr = baseSpec.getContentHref().toString();
> + try {
> + ret = new URL(substitutions.substitute(urlStr));
> + } catch (MalformedURLException e) {
> + return null;
> + }
> + return ret;
> + }
> +
> + /**
> + * @return Gadget contents with all substitutions applied
> + */
> + public String getContentData() {
> + return substitutions.substitute(baseSpec.getContentData());
> + }
> +
> + /**
> + * @return Copy of base spec that created this {@code Gadget}
> + */
> + public GadgetSpec copy() {
> + return baseSpec.copy();
> + }
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetContext.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetContext.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetContext.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetContext.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,51 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import java.util.Locale;
> +
> +/**
> + * Bundles together per-server data and helper mechanisms providing
> + * generic functionality such as retrieval of remote data and
> caching.
> + */
> +public class GadgetContext {
> + private final RemoteContentFetcher httpFetcher;
> + public RemoteContentFetcher getHttpFetcher() {
> + return httpFetcher;
> + }
> +
> + private final GadgetDataCache<MessageBundle> messageBundleCache;
> + public GadgetDataCache<MessageBundle> getMessageBundleCache() {
> + return messageBundleCache;
> + }
> +
> + private final Locale locale;
> + public Locale getLocale() {
> + return locale;
> + }
> +
> + /**
> + * Creates a context for the current gadget.
> + * @param httpFetcher
> + * @param messageBundleCache
> + * @param locale
> + */
> + public GadgetContext(RemoteContentFetcher httpFetcher,
> + GadgetDataCache<MessageBundle>
> messageBundleCache,
> + Locale locale) {
> + this.httpFetcher = httpFetcher;
> + this.messageBundleCache = messageBundleCache;
> + this.locale = locale;
> + }
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetDataCache.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetDataCache.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetDataCache.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetDataCache.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,24 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +/**
> + * Simple interface for providing a data cache for objects of type
> T keyed
> + * by a String.
> + * @param <T> Type of data to store in the cache
> + */
> +public interface GadgetDataCache<T> {
> + public T get(String key) throws GadgetException;
> + public void put(String key, T val) throws GadgetException;
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetException.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetException.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetException.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetException.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,62 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +/**
> + * Base class for all Gadget exceptions. The bulk of the code uses
> + * this class directly, differentiating between error conditions by
> + * the Code enumeration.
> + */
> +public class GadgetException extends Exception {
> + public static enum Code {
> + // Catch-all for internal errors
> + INTERNAL_SERVER_ERROR,
> +
> + // General xml
> + EMPTY_XML_DOCUMENT,
> + MALFORMED_XML_DOCUMENT,
> +
> + FAILED_TO_RETRIEVE_CONTENT,
> +
> + UNSUPPORTED_FEATURE,
> +
> + // Interface component errors.
> + MISSING_SPEC_CACHE,
> + MISSING_MESSAGE_BUNDLE_CACHE,
> + MISSING_REMOTE_OBJECT_FETCHER,
> +
> + // Caja error
> + MALFORMED_FOR_SAFE_INLINING
> + }
> +
> + private final Code code;
> +
> + public GadgetException(Code code) {
> + this.code = code;
> + }
> +
> + public GadgetException(Code code, Throwable cause) {
> + super(cause);
> + this.code = code;
> + }
> +
> + public GadgetException(Code code, String msg) {
> + super(msg);
> + this.code = code;
> + }
> +
> + public Code getCode() {
> + return code;
> + }
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetFeature.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeature.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetFeature.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetFeature.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,46 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import java.util.Map;
> +
> +/**
> + * Base interface providing Gadget Server's primary extensibility
> mechanism.
> + *
> + * During processing of a {@code Gadget}, a tree of {@code
> GadgetFeature}
> + * objects is constructed based on the <Require> and
> <Optional>
> + * tags declared in its {@code GadgetSpec}, and the dependencies
> registered
> + * for these in {@code GadgetFeatureRegistry}.
> + *
> + * Each {@code GadgetFeature}'s prepare method is called first -
> potentially
> + * in parallel with many others whose dependencies have also been
> satisfied.
> + * Once this has completed, its process method is called. Prepare
> is useful
> + * for async operations such as retrieval of a remote resource; all
> + * {@code Gadget} modifications occur in process.
> + *
> + * To extend the Gadget Server's feature set, simply implement this
> interface
> + * and register your class with {@code GadgetFeatureRegistry},
> indicating
> + * which other {@code GadgetFeature} features are needed before
> yours can
> + * operate successfully.
> + *
> + * Each feature <i>must</i> be instantiable by a no-argument
> constructor,
> + * and will <i>always</i> be instantiated this way. As such, it is
> recommended
> + * not to define a constructor for a feature at all.
> + */
> +public interface GadgetFeature {
> + public void prepare(GadgetView gadget, GadgetContext context,
> + Map<String, String> params) throws
> GadgetException;
> + public void process(Gadget gadget, GadgetContext context,
> + Map<String, String> params) throws
> GadgetException;
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetFeatureRegistry.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetFeatureRegistry.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetFeatureRegistry.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetFeatureRegistry.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,195 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import java.util.Arrays;
> +import java.util.HashMap;
> +import java.util.HashSet;
> +import java.util.LinkedList;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +
> +/**
> + * Maintains a registry of all {@code GadgetFeature} types
> supported by
> + * a given Gadget Server installation.
> + *
> + * To register a feature, simply create a static initialization
> block in its
> + * class definition:
> + * <pre>
> + * static {
> + * GadgetFeatureRegistry.register("my.feature.name",
> + * { "my.dep1", "my.dep2" },
> + * MyFeature.class);
> + * }
> + * </pre>
> + */
> +public class GadgetFeatureRegistry {
> + private static final Map<String, Entry> features =
> + new HashMap<String, Entry>();
> + private static final List<Entry> core =
> + new LinkedList<Entry>();
> +
> + // Initialization of core components, providing a minimal base
> context
> + // in which all Gadgets operate. This set should be kept as
> minimal as
> + // possible. Anything added as registerCore will automatically
> become a
> + // dependency of every other feature.
> + static {
> + // Substitution jobs are not order-dependent, because the
> actual order that
> + // they are evaluated in is determined in Substitutions.java.
> The order
> + // defined here is not important.
> + registerCore("core.msgbundlesubst", null,
> MessageBundleSubstituter.class);
> + registerCore("core.bidisubst", null, BidiSubstituter.class);
> + registerCore("core.modulesubst", null, ModuleSubstituter.class);
> + registerCore("core.userprefsubst", null,
> UserPrefSubstituter.class);
> +
> + // Core JS loading.
> + registerCore("core.js", null, CoreJsFeature.class);
> +
> + // These are all satisfied by core.
> + register("setprefs", null, NoOpFeature.class);
> + }
> +
> + /**
> + * Register a {@code GadgetFeature} identified by {@code name}
> which
> + * depends on other {@code GadgetFeature}s listed in {@code deps}
> + * completing before this one does.
> + *
> + * Names are freeform, but it is strongly suggested that they are
> + * namespaced, optionally (yet often usefully) in Java package-
> notation ie.
> + * 'com.google.gadgets.skins'.
> + *
> + * @param name Name of the feature to register, ideally using the
> conventions
> + * described
> + * @param deps List of strings indicating features on which
> {@code feature}
> + * depends to operate correctly, which need to process the {@code
> Gadget}
> + * before it does
> + * @param feature Class implementing the feature
> + */
> + public static void register(String name,
> + String[] deps,
> + Class<? extends GadgetFeature>
> feature) {
> + // Core entries must come first.
> + Entry entry = new Entry(name, deps, feature);
> + for (Entry coreEntry : core) {
> + entry.deps.add(coreEntry.getName());
> + }
> + features.put(name, entry);
> + validateFeatureGraph();
> + }
> +
> + /**
> + * Registers a {@code GadgetFeature} which is <i>always</i> run
> when
> + * processing a {@code Gadget}, and on which all other features
> + * implicitly depend. Use of this mechanism should be as sparing
> as possible
> + * to optimize performance.
> + *
> + * @param deps
> + * @param cap
> + */
> + private static void registerCore(String name,
> + String[] deps,
> + Class<? extends GadgetFeature>
> cap) {
> + core.add(new Entry(name, deps, cap));
> + validateFeatureGraph();
> + }
> +
> + /**
> + * Traverses the graph traversed by the registered features,
> validating
> + * that it comprises a directed acyclic graph in which all
> features'
> + * dependencies are provided.
> + *
> + * If the graph is not acyclic, it cannot be used to create a
> workflow. If
> + * any dependencies are missing, {@code Gadget} rendering may be
> incomplete.
> + */
> + private static void validateFeatureGraph() {
> + // TODO: ensure that features form a DAG and that all deps are
> provided
> + }
> +
> + /**
> + * Attempts to retrieve all the {@code GadgetFeature} classes
> specified
> + * in the {@code needed} list. Those that are found are returned in
> + * {@code resultsFound}, while the names of those that are
> missing are
> + * populated in {@code resultsMissing}.
> + * @param needed List of names identifying features to retrieve
> + * @param resultsFound List of feature entries found
> + * @param resultsMissing List of feature identifiers that could
> not be found
> + * @return True if all features were retrieved
> + */
> + public static boolean getIncludedFeatures(List<String> needed,
> + List<Entry> resultsFound,
> + List<String>
> resultsMissing) {
> + resultsFound.clear();
> + resultsMissing.clear();
> + resultsFound.addAll(core);
> + for (String featureName : needed) {
> + Entry entry = features.get(featureName);
> + if (entry == null) {
> + resultsMissing.add(featureName);
> + }
> + resultsFound.add(entry);
> + }
> + return resultsMissing.size() == 0;
> + }
> +
> + public static class NoOpFeature implements GadgetFeature {
> + public void prepare(GadgetView gadget, GadgetContext context,
> + Map<String, String> params) {
> + }
> + public void process(Gadget gadget, GadgetContext context,
> + Map<String, String> params) {
> + }
> + }
> +
> + /**
> + * Ties together a {@code GadgetFeature} with its name and
> dependencies.
> + */
> + public static class Entry {
> + private final String name;
> + private final Set<String> deps;
> + private final Class<? extends GadgetFeature> feature;
> +
> + private Entry(String name,
> + String[] deps,
> + Class<? extends GadgetFeature> feature) {
> + this.name = name;
> + this.deps = new HashSet<String>();
> + if (deps != null) {
> + this.deps.addAll(Arrays.asList(deps));
> + }
> + this.feature = feature;
> + }
> +
> + /**
> + * @return Name identifier
> + */
> + public String getName() {
> + return name;
> + }
> +
> + /**
> + * @return List of identifiers on which feature depends
> + */
> + public List<String> getDependencies() {
> + return new LinkedList<String>(deps);
> + }
> +
> + /**
> + * @return Class implementing the feature
> + */
> + public Class<? extends GadgetFeature> getFeature() {
> + return feature;
> + }
> + }
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetServer.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetServer.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetServer.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetServer.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,484 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import java.util.ArrayList;
> +import java.util.Arrays;
> +import java.util.HashSet;
> +import java.util.LinkedList;
> +import java.util.List;
> +import java.util.Locale;
> +import java.util.Map;
> +import java.util.Set;
> +import java.util.concurrent.Callable;
> +import java.util.concurrent.CompletionService;
> +import java.util.concurrent.ExecutionException;
> +import java.util.concurrent.Executor;
> +import java.util.concurrent.ExecutorCompletionService;
> +import java.util.concurrent.Future;
> +
> +public class GadgetServer {
> + private final Executor executor;
> + private GadgetDataCache<GadgetSpec> specCache;
> + private GadgetDataCache<MessageBundle> messageBundleCache;
> + private RemoteContentFetcher fetcher;
> +
> + public GadgetServer(Executor executor) {
> + this.executor = executor;
> + }
> +
> + public void setSpecCache(GadgetDataCache<GadgetSpec> specCache) {
> + this.specCache = specCache;
> + }
> +
> + public void setMessageBundleCache(GadgetDataCache<MessageBundle>
> cache) {
> + messageBundleCache = cache;
> + }
> +
> + public void setContentFetcher(RemoteContentFetcher fetcher) {
> + this.fetcher = fetcher;
> + }
> +
> + public static enum RenderingContext {
> + CONTAINER, GADGET
> + }
> +
> + public Gadget processGadget(Gadget.ID gadgetId,
> + UserPrefs userPrefs,
> + Locale locale,
> + RenderingContext rctx)
> + throws GadgetProcessException {
> + if (specCache == null) {
> + throw new
> GadgetProcessException(GadgetException.Code.MISSING_SPEC_CACHE);
> + }
> + if (messageBundleCache == null ) {
> + throw new GadgetProcessException(
> + GadgetException.Code.MISSING_MESSAGE_BUNDLE_CACHE);
> + }
> + if (fetcher == null) {
> + throw new GadgetProcessException(
> + GadgetException.Code.MISSING_REMOTE_OBJECT_FETCHER);
> + }
> +
> + // Queue/tree of all jobs to be run for successful processing
> + GadgetContext gc = new GadgetContext(fetcher,
> messageBundleCache, locale);
> + WorkflowContext wc = new WorkflowContext(gc);
> +
> + // Bootstrap tree of jobs to process
> + WorkflowDependency cacheLoadDep =
> + new WorkflowDependency(WorkflowDependency.Type.CORE,
> CACHE_LOAD);
> + wc.jobsToRun.addJob(new CacheLoadTask(gadgetId, specCache),
> cacheLoadDep);
> +
> + WorkflowDependency urlFetchDep =
> + new WorkflowDependency(WorkflowDependency.Type.CORE,
> URL_FETCH);
> + wc.jobsToRun.addJob(new SpecLoadTask(fetcher, gadgetId,
> specCache),
> + urlFetchDep, cacheLoadDep);
> +
> + WorkflowDependency enqueueFeatDep =
> + new WorkflowDependency(WorkflowDependency.Type.CORE,
> ENQUEUE_FEATURES);
> + wc.jobsToRun.addJob(new EnqueueFeaturesTask(), enqueueFeatDep,
> urlFetchDep);
> +
> + // Instantiate CompletionService
> + CompletionService<GadgetException> processor =
> + new ExecutorCompletionService<GadgetException>(executor);
> +
> + // All exceptions caught during processing
> + List<GadgetException> gadgetExceptions = new
> LinkedList<GadgetException>();
> +
> + // Loop through queue of Callables, executing each in
> CompletionService
> + // whose precursors have been satisfied
> + int jobsSubmitted = 0;
> + do {
> + // Loop through all jobs, submitting to run if all deps
> satisfied
> + List<WorkflowJob> runThisCycle = new LinkedList<WorkflowJob>();
> + for (WorkflowJob candidate : wc.jobsToRun) {
> + if (candidate.ready(wc.depsDone)) {
> + runThisCycle.add(candidate);
> + }
> + }
> +
> + // Fire off ready jobs and remove from jobsToRun list
> + for (WorkflowJob runJob : runThisCycle) {
> + processor.submit(runJob.task);
> + jobsSubmitted++;
> + wc.jobsToRun.remove(runJob);
> + }
> +
> + // Wait around for at least one job to have completed.
> + // Completion of a job results in an additional dep added to
> wc.depsDone,
> + // thus potentially freeing up other jobs to run
> + Future<GadgetException> latestResult = null;
> + GadgetException gadgetException = null;
> + try {
> + latestResult = processor.take();
> + } catch (InterruptedException e) {
> + gadgetException = new GadgetException(
> + GadgetException.Code.INTERNAL_SERVER_ERROR, e);
> + }
> +
> + // Ensure the task ran successfully
> + if (latestResult != null) {
> + try {
> + gadgetException = latestResult.get();
> + } catch (ExecutionException e) {
> + // TODO: convert into gadgetException with internal error
> type
> + gadgetException = new GadgetException(
> + GadgetException.Code.INTERNAL_SERVER_ERROR, e);
> + } catch (InterruptedException e) {
> + // TODO: convert into gadgetException with internal error
> type
> + gadgetException = new GadgetException(
> + GadgetException.Code.INTERNAL_SERVER_ERROR, e);
> + }
> + }
> +
> + if (gadgetException != null) {
> + System.err.println(gadgetException.getCode().toString());
> + gadgetException.printStackTrace();
> + Throwable t = gadgetException.getCause();
> + if (t != null) {
> + t.printStackTrace();
> + }
> + // Add to list of all exceptions caught, clear jobs, and
> continue
> + // to aggressively catch as many exceptions as possible.
> Since
> + // tasks are running anyway, we may as well get their
> results in
> + // case it would be useful to the user.
> + gadgetExceptions.add(gadgetException);
> + wc.jobsToRun.clear();
> + jobsSubmitted = wc.depsDone.size();
> + }
> + } while(wc.jobsToRun.size() > 0 || jobsSubmitted >
> wc.depsDone.size());
> +
> + if (gadgetExceptions.size() > 0) {
> + throw new GadgetProcessException(gadgetExceptions);
> + }
> +
> + // terminate when all Callables are finished (or Exception
> detected?)
> + return wc.gadget;
> + }
> +
> + public List<GadgetSpec.UserPref> getPrefsInfo(Gadget.ID gadgetId) {
> + return null;
> + }
> +
> + private static final String CACHE_LOAD = "cache-load";
> + private static final String URL_FETCH = "url-fetch";
> + private static final String ENQUEUE_FEATURES = "enqueue-features";
> +
> + private static class WorkflowJobList extends
> ArrayList<WorkflowJob> {
> + private final WorkflowContext wc;
> + public WorkflowJobList(WorkflowContext wc) {
> + this.wc = wc;
> + }
> +
> + public void addJob(WorkflowTask task,
> + WorkflowDependency done,
> + WorkflowDependency... deps) {
> + task.setup(wc, done);
> + this.add(new WorkflowJob(task, deps));
> + }
> + }
> +
> + private static class WorkflowDependency {
> + private static enum Type {
> + CORE, FEATURE_PREPARE, FEATURE_PROCESS
> + }
> +
> + private final Type type;
> + private final String id;
> +
> + private WorkflowDependency(Type type, String id) {
> + this.type = type;
> + this.id = id;
> + }
> +
> + @Override
> + public boolean equals(Object other) {
> + if (other instanceof WorkflowDependency) {
> + WorkflowDependency wd = (WorkflowDependency)other;
> + return type.equals(wd.type) && id.equals(wd.id);
> + }
> + return false;
> + }
> +
> + @Override
> + public int hashCode() {
> + return (type.toString() + id).hashCode();
> + }
> + }
> +
> + private static class WorkflowContext {
> + private Gadget gadget;
> + private GadgetContext context;
> + private WorkflowJobList jobsToRun;
> + private final Set<WorkflowDependency> depsDone;
> +
> + private WorkflowContext(GadgetContext context) {
> + this.context = context;
> + this.depsDone = new HashSet<WorkflowDependency>();
> + this.jobsToRun = new WorkflowJobList(this);
> + }
> + }
> +
> + private static class WorkflowJob {
> + private final WorkflowTask task;
> + private final List<WorkflowDependency> deps;
> +
> + private WorkflowJob(WorkflowTask task, WorkflowDependency...
> deps) {
> + this.task = task;
> + this.deps = new
> LinkedList<WorkflowDependency>(Arrays.asList(deps));
> + }
> +
> + private boolean ready(Set<WorkflowDependency> depsDone) {
> + for (WorkflowDependency dep : deps) {
> + if (!depsDone.contains(dep)) {
> + return false;
> + }
> + }
> + return true;
> + }
> + }
> +
> + private static class CacheLoadTask extends WorkflowTask {
> + private final GadgetView.ID gadgetId;
> + private final GadgetDataCache<GadgetSpec> specCache;
> +
> + private CacheLoadTask(GadgetView.ID gadgetId,
> + GadgetDataCache<GadgetSpec> specCache) {
> + this.gadgetId = gadgetId;
> + this.specCache = specCache;
> + }
> +
> + @Override
> + public void run(WorkflowContext wc) throws GadgetException {
> + GadgetSpec spec = specCache.get(gadgetId.getKey());
> + if (spec != null) {
> + wc.gadget = new Gadget(gadgetId, spec);
> + }
> + }
> + }
> +
> + private static class SpecLoadTask extends WorkflowTask {
> + private final RemoteContentFetcher fetcher;
> + private final GadgetView.ID gadgetId;
> + private final GadgetDataCache<GadgetSpec> specCache;
> +
> + private SpecLoadTask(RemoteContentFetcher fetcher,
> GadgetView.ID gadgetId,
> + GadgetDataCache<GadgetSpec> specCache) {
> + this.fetcher = fetcher;
> + this.gadgetId = gadgetId;
> + this.specCache = specCache;
> + }
> +
> + @Override
> + public void run(WorkflowContext wc) throws GadgetException {
> + if (wc.gadget != null) {
> + // Already retrieved: do nothing.
> + return;
> + }
> +
> + byte[] xml = fetcher.fetch(gadgetId.getURL()).getByteArray();
> + GadgetSpecParser specParser = new GadgetSpecParser();
> + GadgetSpec spec = specParser.parse(gadgetId, xml);
> + wc.gadget = new Gadget(gadgetId, spec);
> + // This isn't a separate job because if it is we'd just need
> another
> + // flag telling us not to store to the cache.
> + specCache.put(wc.gadget.getId().getKey(), wc.gadget.copy());
> + }
> + }
> +
> + private static class EnqueueFeaturesTask extends WorkflowTask {
> + @Override
> + public void run(WorkflowContext wc) throws GadgetException {
> + List<String> needed = new LinkedList<String>();
> + Set<String> optionalNames = new HashSet<String>();
> + Map<String, GadgetSpec.FeatureSpec> requires =
> wc.gadget.getRequires();
> + for (Map.Entry<String, GadgetSpec.FeatureSpec> entry :
> requires.entrySet()) {
> + needed.add(entry.getKey());
> + if (entry.getValue().isOptional()) {
> + optionalNames.add(entry.getKey());
> + }
> + }
> +
> + // Retrieve needed feature processors from registry
> + List<GadgetFeatureRegistry.Entry> resultsFound =
> + new LinkedList<GadgetFeatureRegistry.Entry>();
> + List<String> resultsMissing = new LinkedList<String>();
> + GadgetFeatureRegistry.getIncludedFeatures(needed,
> + resultsFound,
> + resultsMissing);
> +
> + // Classify features this server is missing
> + List<String> missingRequired = new LinkedList<String>();
> + List<String> missingOptional = new LinkedList<String>();
> + for (String missingResult : resultsMissing) {
> + if (optionalNames.contains(missingResult)) {
> + missingOptional.add(missingResult);
> + } else {
> + missingRequired.add(missingResult);
> + }
> + }
> +
> + if (missingRequired.size() > 0) {
> + // TODO: throw MissingFeaturesException, subclass of
> GadgetException,
> + // which is then processed at termination of the jobs loop
> + for (String missing : missingRequired) {
> + System.err.println("Unsupported: " + missing);
> + }
> + throw new
> GadgetException(GadgetException.Code.UNSUPPORTED_FEATURE);
> + }
> +
> + if (missingOptional.size() > 0) {
> + // TODO: add custom task, dependent on nothing, adding
> metadata re:
> + // missing optionals to the gadget's output (satisfies
> HasFeature(...))
> + }
> +
> + WorkflowDependency specLoadDep =
> + new WorkflowDependency(WorkflowDependency.Type.CORE,
> URL_FETCH);
> + for (GadgetFeatureRegistry.Entry entry : resultsFound) {
> + List<WorkflowDependency> prepareDeps =
> + new LinkedList<WorkflowDependency>();
> + List<WorkflowDependency> processDeps =
> + new LinkedList<WorkflowDependency>();
> +
> + // sanity check: each depends on the spec having been loaded
> + prepareDeps.add(specLoadDep);
> +
> + for (String featureDep : entry.getDependencies()) {
> + // prepare depends on all its own deps...
> + WorkflowDependency prepareNeedsDep =
> + new
> WorkflowDependency(WorkflowDependency.Type.FEATURE_PREPARE,
> + featureDep);
> + prepareDeps.add(prepareNeedsDep);
> +
> + WorkflowDependency processNeedsDep =
> + new
> WorkflowDependency(WorkflowDependency.Type.FEATURE_PROCESS,
> + featureDep);
> + // Can't process until all dependencies prepare() and
> process()
> + // have completed.
> + processDeps.add(prepareNeedsDep);
> + processDeps.add(processNeedsDep);
> + }
> +
> + // Create task for prepare and process, each with the
> dependency
> + // that running each satisfies
> + WorkflowDependency prepareDep =
> + new
> WorkflowDependency(WorkflowDependency.Type.FEATURE_PREPARE,
> + entry.getName());
> +
> + // We must guarantee that process is called after prepare.
> This is
> + // implicitly stating that process has all of prepare's
> dependencies.
> + processDeps.add(prepareDep);
> + WorkflowDependency processDep =
> + new
> WorkflowDependency(WorkflowDependency.Type.FEATURE_PROCESS,
> + entry.getName());
> +
> + // Then add a new job for each task, with appropriate
> execution
> + // precursors/dependencies, to the jobs queue
> + try {
> + GadgetFeature feature = entry.getFeature().newInstance();
> + wc.jobsToRun.addJob(new
> FeaturePrepareTask(entry.getName(), feature),
> + prepareDep,
> + prepareDeps.toArray(new
> WorkflowDependency[]{}));
> + wc.jobsToRun.addJob(new
> FeatureProcessTask(entry.getName(), feature),
> + processDep,
> + processDeps.toArray(new
> WorkflowDependency[]{}));
> + } catch (InstantiationException e) {
> + System.err.println("Unable to instantiate "
> + + entry.getFeature().getName());
> + } catch (IllegalAccessException e) {
> + System.err.println("Unable to call no-arg constructor for "
> + + entry.getFeature().getName());
> + }
> + }
> + }
> + }
> +
> + private static class FeaturePrepareTask extends WorkflowTask {
> + private final GadgetFeature feature;
> + private final String name;
> + private FeaturePrepareTask(String name, GadgetFeature feature) {
> + this.name = name;
> + this.feature = feature;
> + }
> +
> + @Override
> + public void run(WorkflowContext wc) throws GadgetException {
> + Map<String, String> params =
> Gadget.getFeatureParams(wc.gadget, name);
> + feature.prepare(wc.gadget, wc.context, params);
> + }
> + }
> +
> + private static class FeatureProcessTask extends WorkflowTask {
> + private final GadgetFeature feature;
> + private final String name;
> + private FeatureProcessTask(String name, GadgetFeature feature) {
> + this.name = name;
> + this.feature = feature;
> + }
> +
> + @Override
> + public void run(WorkflowContext wc) throws GadgetException {
> + Map<String, String> params =
> Gadget.getFeatureParams(wc.gadget, name);
> + feature.process(wc.gadget, wc.context, params);
> + }
> + }
> +
> + private static abstract class WorkflowTask
> + implements Callable<GadgetException> {
> + // This class is mostly just an alias to
> Callable<GadgetException>
> + // providing a helper method for passing in context
> + public abstract void run(WorkflowContext wc) throws
> GadgetException;
> +
> + private WorkflowContext wc;
> + private WorkflowDependency done;
> + public WorkflowTask setup(WorkflowContext wc,
> WorkflowDependency done) {
> + this.wc = wc;
> + this.done = done;
> + return this;
> + }
> +
> + public GadgetException call() {
> + GadgetException ret = null;
> + try {
> + this.run(wc);
> + } catch (GadgetException e) {
> + ret = e;
> + } catch (Exception e) {
> + // TODO: capture Throwable cause in wrapped exception
> + ret = new GadgetException(
> + GadgetException.Code.INTERNAL_SERVER_ERROR, e);
> + } finally {
> + wc.depsDone.add(done);
> + }
> + return ret;
> + }
> + }
> +
> + public static class GadgetProcessException extends Exception {
> + private final List<GadgetException> components;
> +
> + public GadgetProcessException(List<GadgetException> components) {
> + this.components = components;
> + }
> +
> + public GadgetProcessException(GadgetException.Code code) {
> + this.components = new ArrayList<GadgetException>();
> + this.components.add(new GadgetException(code));
> + }
> +
> + public List<GadgetException> getComponents() {
> + return components;
> + }
> + }
> +}
>
> Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetSpec.java
> URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetSpec.java?rev=603539&view=auto
> =
> =
> =
> =
> =
> =
> =
> =
> ======================================================================
> --- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetSpec.java (added)
> +++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/
> shindig/gadgets/GadgetSpec.java Wed Dec 12 02:33:35 2007
> @@ -0,0 +1,87 @@
> +/*
> + * Licensed 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.shindig.gadgets;
> +
> +import java.net.URL;
> +import java.util.List;
> +import java.util.Locale;
> +import java.util.Map;
> +
> +/**
> + * Represents a Gadget specification.
> + */
> +public interface GadgetSpec {
> + public String getTitle();
> + public URL getTitleURL();
> + public String getDirectoryTitle();
> + public String getDescription();
> + public String getAuthor();
> + public String getAuthorEmail();
> + public String getScreenshot();
> + public String getThumbnail();
> +
> + public static interface MessageBundle {
> + public Locale getLocale();
> + public URL getURL();
> + public boolean isRightToLeft();
> + }
> +
> + public List<MessageBundle> getMessageBundles();
> +
> + public static interface FeatureSpec {
> + public String getName();
> + public Map<String, String> getParams();
> + public boolean isOptional();
> + }
> +
> + public Map<String, FeatureSpec> getRequires();
> + public List<String> getPreloads();
> +
> + public static interface Icon {
> + public URL getURL();
> + public String getMode();
> + public String getType();
> + }
> +
> + public List<Icon> getIcons();
> +
> + public static interface UserPref {
> +
> + public String getName();
> + public String getDisplayName();
> + public String getDefaultValue();
> + public boolean isRequired();
> +
> + public static enum DataType {
> + STRING, HIDDEN, BOOL, ENUM, LIST, NUMBER
> + }
> +
> + public DataType getDataType();
> + }
> +
> + public List<UserPref> getUserPrefs();
> +
> + public static enum ContentType {
> + HTML, URL
> + }
> +
> + public ContentType getContentType();
> + public URL getContentHref();
> + public String getContentData();
> +
> + /**
> + * @return A copy of the spec. This is NOT the same as clone().
> + */
> + public GadgetSpec copy();
> +}
> \ No newline at end of file
>
>