first import of gridworks project broker with two implementations, a local one that uses BerkeleyDB and a cloud one that uses Google App Engine
NOTE: code has not yet been tested but it's functionally complete and compiles git-svn-id: http://google-refine.googlecode.com/svn/trunk@1009 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
0663b898e8
commit
619f914b80
14
broker/appengine/.classpath
Normal file
14
broker/appengine/.classpath
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="com.google.appengine.eclipse.core.GAE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/butterfly-trunk-r25.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/butterfly-trunk-r25-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks-server/lib/servlet-api-2.5.jar" sourcepath="/gridworks-server/lib-src/servlet-api-2.5-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/json-20100208.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/json-20100208-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/httpclient-4.0.1.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/httpclient-4.0.1-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/httpcore-4.0.1.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/httpcore-4.0.1-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/slf4j-api-1.5.6.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/slf4j-api-1.5.6-sources.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/gridworks-broker"/>
|
||||
<classpathentry kind="output" path="module/MOD-INF/classes"/>
|
||||
</classpath>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_DISABLED_BUILDER" value="com.google.appengine.eclipse.core.projectValidator"/>
|
||||
<mapAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS"/>
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
|
||||
</launchConfiguration>
|
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_DISABLED_BUILDER" value="com.google.gdt.eclipse.core.webAppProjectValidator"/>
|
||||
<mapAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS"/>
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
|
||||
</launchConfiguration>
|
43
broker/appengine/.project
Normal file
43
broker/appengine/.project
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>gridworks-appengine-broker</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/com.google.gdt.eclipse.core.webAppProjectValidator.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.google.appengine.eclipse.core.enhancerbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/com.google.appengine.eclipse.core.projectValidator.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>com.google.appengine.eclipse.core.gaeNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
@ -0,0 +1,3 @@
|
||||
#Wed May 26 15:13:15 PDT 2010
|
||||
eclipse.preferences.version=1
|
||||
validationExclusions=src/com/metaweb/gridworks/appengine/*ClientConnection*.java
|
@ -0,0 +1,5 @@
|
||||
#Wed May 26 15:11:38 PDT 2010
|
||||
eclipse.preferences.version=1
|
||||
jarsExcludedFromWebInfLib=
|
||||
warSrcDir=
|
||||
warSrcDirIsOutput=true
|
9
broker/appengine/WEB-INF/appengine-web.xml
Normal file
9
broker/appengine/WEB-INF/appengine-web.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
|
||||
<application>$APPID</application>
|
||||
<version>$VERSION</version>
|
||||
|
||||
<system-properties>
|
||||
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
|
||||
</system-properties>
|
||||
</appengine-web-app>
|
29
broker/appengine/WEB-INF/butterfly.properties
Normal file
29
broker/appengine/WEB-INF/butterfly.properties
Normal file
@ -0,0 +1,29 @@
|
||||
#
|
||||
# Butterfly Configuration
|
||||
#
|
||||
# NOTE: properties passed to the JVM using '-Dkey=value' from the command line
|
||||
# override the settings in this file.
|
||||
|
||||
# indicates the URL path where butterfly is available in the proxy URL space
|
||||
# as there is no way of knowing otherwise as this information is not
|
||||
# transferred thru the HTTP protocol or otherwise (different story if
|
||||
# the appserver is connected thru a different protocol such as AJP)
|
||||
|
||||
butterfly.url = /
|
||||
|
||||
# ---------- Miscellaneous ----------
|
||||
|
||||
#butterfly.locale.language = en
|
||||
#butterfly.locale.country = US
|
||||
#butterfly.timeZone = GMT+09:00
|
||||
|
||||
# ---------- Module ------
|
||||
|
||||
butterfly.modules.path = modules
|
||||
|
||||
butterfly.modules.wirings = WEB-INF/modules.properties
|
||||
|
||||
# ---------- Clustering ----
|
||||
|
||||
#butterfly.routing.cookie.maxage = -1
|
||||
|
16
broker/appengine/WEB-INF/jdoconfig.xml
Normal file
16
broker/appengine/WEB-INF/jdoconfig.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig">
|
||||
|
||||
<persistence-manager-factory name="transactional">
|
||||
<property name="javax.jdo.PersistenceManagerFactoryClass"
|
||||
value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
|
||||
<property name="javax.jdo.option.ConnectionURL" value="appengine"/>
|
||||
<property name="javax.jdo.option.NontransactionalRead" value="true"/>
|
||||
<property name="javax.jdo.option.NontransactionalWrite" value="false"/>
|
||||
<property name="javax.jdo.option.RetainValues" value="true"/>
|
||||
<property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/>
|
||||
</persistence-manager-factory>
|
||||
|
||||
</jdoconfig>
|
BIN
broker/appengine/WEB-INF/lib/slf4j-jdk14-1.5.6.jar
Normal file
BIN
broker/appengine/WEB-INF/lib/slf4j-jdk14-1.5.6.jar
Normal file
Binary file not shown.
13
broker/appengine/WEB-INF/logging.properties
Normal file
13
broker/appengine/WEB-INF/logging.properties
Normal file
@ -0,0 +1,13 @@
|
||||
# A default java.util.logging configuration.
|
||||
# (All App Engine logging is through java.util.logging by default).
|
||||
#
|
||||
# To use this configuration, copy it into your application's WEB-INF
|
||||
# folder and add the following to your appengine-web.xml:
|
||||
#
|
||||
# <system-properties>
|
||||
# <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
|
||||
# </system-properties>
|
||||
#
|
||||
|
||||
# Set the default logging level for all loggers
|
||||
.level = WARN
|
5
broker/appengine/WEB-INF/modules.properties
Normal file
5
broker/appengine/WEB-INF/modules.properties
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# Butterfly Modules Configuration
|
||||
#
|
||||
|
||||
appengine-broker = /
|
20
broker/appengine/WEB-INF/web.xml
Normal file
20
broker/appengine/WEB-INF/web.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!DOCTYPE web-app
|
||||
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
|
||||
|
||||
<web-app>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>Butterfly</servlet-name>
|
||||
<servlet-class>edu.mit.simile.butterfly.Butterfly</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Butterfly</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
5
broker/appengine/module/MOD-INF/module.properties
Normal file
5
broker/appengine/module/MOD-INF/module.properties
Normal file
@ -0,0 +1,5 @@
|
||||
name = appengine-broker
|
||||
extends = broker
|
||||
description = Google App Engine implementation of Gridworks Broker Module
|
||||
module-impl = com.metaweb.gridworks.broker.AppEngineGridworksBroker
|
||||
templating = false
|
@ -0,0 +1,243 @@
|
||||
package com.metaweb.gridworks.appengine;
|
||||
|
||||
import static com.google.appengine.api.urlfetch.FetchOptions.Builder.allowTruncate;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.net.ssl.SSLSession;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpConnectionMetrics;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.conn.ManagedClientConnection;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.message.BasicHttpResponse;
|
||||
import org.apache.http.params.HttpParams;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
|
||||
import com.google.appengine.api.urlfetch.HTTPHeader;
|
||||
import com.google.appengine.api.urlfetch.HTTPMethod;
|
||||
import com.google.appengine.api.urlfetch.HTTPRequest;
|
||||
import com.google.appengine.api.urlfetch.HTTPResponse;
|
||||
import com.google.appengine.api.urlfetch.URLFetchService;
|
||||
import com.google.appengine.api.urlfetch.URLFetchServiceFactory;
|
||||
|
||||
class AppEngineClientConnection implements ManagedClientConnection {
|
||||
// Managed is the composition of ConnectionReleaseTrigger,
|
||||
// HttpClientConnection, HttpConnection, HttpInetConnection
|
||||
|
||||
private HttpRoute _route;
|
||||
private Object _state;
|
||||
private boolean _reuseable;
|
||||
|
||||
public AppEngineClientConnection(HttpRoute route, Object state) {
|
||||
_route = route;
|
||||
_state = state;
|
||||
}
|
||||
|
||||
// ManagedClientConnection methods
|
||||
|
||||
public HttpRoute getRoute() {
|
||||
return _route;
|
||||
}
|
||||
|
||||
public Object getState() {
|
||||
return _state;
|
||||
}
|
||||
|
||||
public SSLSession getSSLSession() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isSecure() {
|
||||
// XXX maybe parse the url to see if it's https?
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isMarkedReusable() {
|
||||
return _reuseable;
|
||||
}
|
||||
|
||||
public void markReusable() {
|
||||
_reuseable = true;
|
||||
}
|
||||
|
||||
public void layerProtocol(HttpContext context, HttpParams params) {
|
||||
return;
|
||||
}
|
||||
|
||||
public void open(HttpRoute route, HttpContext context, HttpParams params) {
|
||||
return;
|
||||
}
|
||||
|
||||
public void setIdleDuration(long duration, TimeUnit unit) {
|
||||
return;
|
||||
}
|
||||
|
||||
public void setState(Object state) {
|
||||
_state = state;
|
||||
}
|
||||
|
||||
public void tunnelProxy(HttpHost next, boolean secure, HttpParams params) {
|
||||
return;
|
||||
}
|
||||
|
||||
public void tunnelTarget(boolean secure, HttpParams params) {
|
||||
return;
|
||||
}
|
||||
|
||||
public void unmarkReusable() {
|
||||
_reuseable = false;
|
||||
}
|
||||
|
||||
|
||||
// ConnectionReleaseTrigger methods
|
||||
|
||||
public void releaseConnection() {
|
||||
return;
|
||||
}
|
||||
|
||||
public void abortConnection() {
|
||||
return;
|
||||
}
|
||||
|
||||
// HttpClientConnection methods
|
||||
|
||||
private HTTPRequest _appengine_hrequest;
|
||||
private HTTPResponse _appengine_hresponse;
|
||||
|
||||
public void flush() {
|
||||
return;
|
||||
}
|
||||
|
||||
public boolean isResponseAvailable(int timeout) {
|
||||
// XXX possibly use Async fetcher
|
||||
return true;
|
||||
}
|
||||
|
||||
public void receiveResponseEntity(org.apache.http.HttpResponse apache_response) {
|
||||
byte[] data = _appengine_hresponse.getContent();
|
||||
|
||||
if (data != null) {
|
||||
apache_response.setEntity(new ByteArrayEntity(data));
|
||||
}
|
||||
}
|
||||
|
||||
public HttpResponse receiveResponseHeader() {
|
||||
URLFetchService ufs = URLFetchServiceFactory.getURLFetchService();
|
||||
try {
|
||||
_appengine_hresponse = ufs.fetch(_appengine_hrequest);
|
||||
} catch (java.io.IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
org.apache.http.HttpResponse apache_response =
|
||||
new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 0),
|
||||
_appengine_hresponse.getResponseCode(),
|
||||
null);
|
||||
|
||||
for (HTTPHeader h : _appengine_hresponse.getHeaders()) {
|
||||
apache_response.addHeader(h.getName(), h.getValue());
|
||||
}
|
||||
|
||||
return apache_response;
|
||||
}
|
||||
|
||||
public void sendRequestEntity(org.apache.http.HttpEntityEnclosingRequest request) {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
||||
org.apache.http.HttpEntity ent = request.getEntity();
|
||||
if (ent != null) {
|
||||
try {
|
||||
ent.writeTo(os);
|
||||
} catch (java.io.IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
_appengine_hrequest.setPayload(os.toByteArray());
|
||||
}
|
||||
|
||||
public void sendRequestHeader(org.apache.http.HttpRequest apache_request) {
|
||||
URL request_url;
|
||||
|
||||
HttpHost host = _route.getTargetHost();
|
||||
|
||||
String protocol = host.getSchemeName();
|
||||
String addr = host.getHostName();
|
||||
int port = host.getPort();
|
||||
|
||||
String path = apache_request.getRequestLine().getUri();
|
||||
|
||||
try {
|
||||
request_url = new URL(protocol, addr, port, path);
|
||||
} catch (java.net.MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
HTTPMethod method = HTTPMethod.valueOf(apache_request.getRequestLine().getMethod());
|
||||
_appengine_hrequest = new HTTPRequest(request_url, method, allowTruncate()
|
||||
.doNotFollowRedirects());
|
||||
|
||||
Header[] apache_headers = apache_request.getAllHeaders();
|
||||
for (int i = 0; i < apache_headers.length; i++) {
|
||||
Header h = apache_headers[i];
|
||||
_appengine_hrequest
|
||||
.setHeader(new HTTPHeader(h.getName(), h.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
// HttpConnection methods
|
||||
|
||||
public void close() {
|
||||
return;
|
||||
}
|
||||
|
||||
public HttpConnectionMetrics getMetrics() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getSocketTimeout() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isStale() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setSocketTimeout(int timeout) {
|
||||
return;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
return;
|
||||
}
|
||||
|
||||
// HttpInetConnection methods
|
||||
|
||||
public InetAddress getLocalAddress() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getLocalPort() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public InetAddress getRemoteAddress() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getRemotePort() {
|
||||
return -1;
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.metaweb.gridworks.appengine;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.http.conn.ClientConnectionManager;
|
||||
import org.apache.http.conn.ClientConnectionRequest;
|
||||
import org.apache.http.conn.ManagedClientConnection;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
import org.apache.http.conn.scheme.Scheme;
|
||||
import org.apache.http.conn.scheme.SchemeRegistry;
|
||||
import org.apache.http.conn.scheme.SocketFactory;
|
||||
import org.apache.http.params.HttpParams;
|
||||
|
||||
public class AppEngineClientConnectionManager implements ClientConnectionManager {
|
||||
|
||||
private SchemeRegistry schemes;
|
||||
|
||||
class NoopSocketFactory implements SocketFactory {
|
||||
public Socket connectSocket(Socket sock, String host, int port, InetAddress addr, int lport, HttpParams params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Socket createSocket() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isSecure(Socket sock) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public AppEngineClientConnectionManager() {
|
||||
SocketFactory noop_sf = new NoopSocketFactory();
|
||||
schemes = new SchemeRegistry();
|
||||
schemes.register(new Scheme("http", noop_sf, 80));
|
||||
schemes.register(new Scheme("https", noop_sf, 443));
|
||||
}
|
||||
|
||||
public void closeExpiredConnections() {
|
||||
return;
|
||||
}
|
||||
|
||||
public void closeIdleConnections(long idletime, TimeUnit tunit) {
|
||||
return;
|
||||
}
|
||||
|
||||
public ManagedClientConnection getConnection(HttpRoute route, Object state) {
|
||||
return new AppEngineClientConnection(route, state);
|
||||
}
|
||||
|
||||
public SchemeRegistry getSchemeRegistry() {
|
||||
return schemes;
|
||||
}
|
||||
|
||||
public void releaseConnection(ManagedClientConnection conn, long valid, TimeUnit tuint) {
|
||||
return;
|
||||
}
|
||||
|
||||
public ClientConnectionRequest requestConnection(final HttpRoute route, final Object state) {
|
||||
return new ClientConnectionRequest() {
|
||||
public void abortRequest() {
|
||||
return;
|
||||
}
|
||||
|
||||
public ManagedClientConnection getConnection(long idletime, TimeUnit tunit) {
|
||||
return AppEngineClientConnectionManager.this.getConnection(route, state);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
return;
|
||||
}
|
||||
}
|
@ -0,0 +1,322 @@
|
||||
package com.metaweb.gridworks.broker;
|
||||
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.jdo.JDOHelper;
|
||||
import javax.jdo.PersistenceManager;
|
||||
import javax.jdo.PersistenceManagerFactory;
|
||||
import javax.jdo.Transaction;
|
||||
import javax.jdo.annotations.IdGeneratorStrategy;
|
||||
import javax.jdo.annotations.PersistenceCapable;
|
||||
import javax.jdo.annotations.Persistent;
|
||||
import javax.jdo.annotations.PrimaryKey;
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.conn.ClientConnectionManager;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.appengine.api.datastore.Text;
|
||||
import com.metaweb.gridworks.appengine.AppEngineClientConnectionManager;
|
||||
|
||||
public class AppEngineGridworksBroker extends GridworksBroker {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger("gridworks.broker.appengine");
|
||||
|
||||
PersistenceManagerFactory pmfInstance;
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws Exception {
|
||||
super.init(config);
|
||||
|
||||
pmfInstance = JDOHelper.getPersistenceManagerFactory("transactional");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
protected HttpClient getHttpClient() {
|
||||
ClientConnectionManager cm = new AppEngineClientConnectionManager();
|
||||
return new DefaultHttpClient(cm, null);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
protected void getLock(HttpServletResponse response, String pid) throws Exception {
|
||||
PersistenceManager pm = pmfInstance.getPersistenceManager();
|
||||
|
||||
try {
|
||||
respond(response, lockToJSON(getLock(pm,pid)));
|
||||
} finally {
|
||||
pm.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void obtainLock(HttpServletResponse response, String pid, String uid) throws Exception {
|
||||
PersistenceManager pm = pmfInstance.getPersistenceManager();
|
||||
|
||||
try {
|
||||
Lock lock = getLock(pm, pid);
|
||||
if (lock == null) {
|
||||
Transaction tx = pm.currentTransaction();
|
||||
|
||||
try {
|
||||
tx.begin();
|
||||
lock = new Lock(Long.toHexString(tx.hashCode()), pid, uid);
|
||||
pm.makePersistent(lock);
|
||||
tx.commit();
|
||||
} finally {
|
||||
if (tx.isActive()) {
|
||||
tx.rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
respond(response, lockToJSON(lock));
|
||||
|
||||
} finally {
|
||||
pm.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void releaseLock(HttpServletResponse response, String pid, String uid, String lid) throws Exception {
|
||||
|
||||
PersistenceManager pm = pmfInstance.getPersistenceManager();
|
||||
|
||||
try {
|
||||
Lock lock = getLock(pm, pid);
|
||||
if (lock != null) {
|
||||
if (!lock.id.equals(lid)) {
|
||||
throw new RuntimeException("Lock id doesn't match, can't release the lock");
|
||||
}
|
||||
if (!lock.uid.equals(uid)) {
|
||||
throw new RuntimeException("User id doesn't match the lock owner, can't release the lock");
|
||||
}
|
||||
|
||||
Transaction tx = pm.currentTransaction();
|
||||
|
||||
try {
|
||||
tx.begin();
|
||||
pm.deletePersistent(lock);
|
||||
tx.commit();
|
||||
} finally {
|
||||
if (tx.isActive()) {
|
||||
tx.rollback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
respond(response, OK);
|
||||
|
||||
} finally {
|
||||
pm.close();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
|
||||
protected void startProject(HttpServletResponse response, String pid, String uid, String lid, String data) throws Exception {
|
||||
PersistenceManager pm = pmfInstance.getPersistenceManager();
|
||||
|
||||
try {
|
||||
checkLock(pm, pid, uid, lid);
|
||||
|
||||
Project project = getProject(pm, pid);
|
||||
|
||||
if (project != null) {
|
||||
throw new RuntimeException("Project '" + pid + "' already exists");
|
||||
}
|
||||
|
||||
Transaction tx = pm.currentTransaction();
|
||||
|
||||
try {
|
||||
tx.begin();
|
||||
project = new Project(pid, data);
|
||||
pm.makePersistent(project);
|
||||
tx.commit();
|
||||
} finally {
|
||||
if (tx.isActive()) {
|
||||
tx.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
respond(response, OK);
|
||||
} finally {
|
||||
pm.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void addTransformations(HttpServletResponse response, String pid, String uid, String lid, List<String> transformations) throws Exception {
|
||||
PersistenceManager pm = pmfInstance.getPersistenceManager();
|
||||
|
||||
try {
|
||||
checkLock(pm, pid, uid, lid);
|
||||
|
||||
Project project = getProject(pm, pid);
|
||||
|
||||
if (project == null) {
|
||||
throw new RuntimeException("Project '" + pid + "' not found");
|
||||
}
|
||||
|
||||
Transaction tx = pm.currentTransaction();
|
||||
|
||||
try {
|
||||
for (String s : transformations) {
|
||||
project.transformations.add(new Text(s));
|
||||
}
|
||||
tx.commit();
|
||||
} finally {
|
||||
if (tx.isActive()) {
|
||||
tx.rollback();
|
||||
}
|
||||
}
|
||||
|
||||
respond(response, OK);
|
||||
} finally {
|
||||
pm.close();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
protected void getProject(HttpServletResponse response, String pid) throws Exception {
|
||||
PersistenceManager pm = pmfInstance.getPersistenceManager();
|
||||
|
||||
try {
|
||||
Project project = getProject(pm, pid);
|
||||
|
||||
Writer w = response.getWriter();
|
||||
JSONWriter writer = new JSONWriter(w);
|
||||
writer.object();
|
||||
writer.key("data"); writer.value(project.data.toString());
|
||||
writer.key("transformations");
|
||||
writer.array();
|
||||
for (Text s : project.transformations) {
|
||||
writer.value(s.toString());
|
||||
}
|
||||
writer.endArray();
|
||||
writer.endObject();
|
||||
w.flush();
|
||||
w.close();
|
||||
} finally {
|
||||
pm.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected void getHistory(HttpServletResponse response, String pid, int tindex) throws Exception {
|
||||
PersistenceManager pm = pmfInstance.getPersistenceManager();
|
||||
|
||||
try {
|
||||
Project project = getProject(pm, pid);
|
||||
|
||||
Writer w = response.getWriter();
|
||||
JSONWriter writer = new JSONWriter(w);
|
||||
writer.object();
|
||||
writer.key("transformations");
|
||||
writer.array();
|
||||
int size = project.transformations.size();
|
||||
for (int i = tindex; i < size; i++) {
|
||||
writer.value(project.transformations.get(i).toString());
|
||||
}
|
||||
writer.endArray();
|
||||
writer.endObject();
|
||||
w.flush();
|
||||
w.close();
|
||||
} finally {
|
||||
pm.close();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
Project getProject(PersistenceManager pm, String pid) {
|
||||
Project project = pm.getObjectById(Project.class, pid);
|
||||
if (project == null) {
|
||||
throw new RuntimeException("Project '" + pid + "' is not managed by this broker");
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
@PersistenceCapable
|
||||
static class Project {
|
||||
|
||||
@PrimaryKey
|
||||
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
|
||||
String pid;
|
||||
|
||||
@Persistent
|
||||
List<Text> transformations = new ArrayList<Text>();
|
||||
|
||||
@Persistent
|
||||
Text data;
|
||||
|
||||
Project(String pid, String data) {
|
||||
this.pid = pid;
|
||||
this.data = new Text(data);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
Lock getLock(PersistenceManager pm, String pid) {
|
||||
return pm.getObjectById(Lock.class, pid);
|
||||
}
|
||||
|
||||
void checkLock(PersistenceManager pm, String pid, String uid, String lid) {
|
||||
Lock lock = getLock(pm, pid);
|
||||
|
||||
if (lock == null) {
|
||||
throw new RuntimeException("No lock was found with the given Lock id '" + lid + "', you have to have a valid lock on a project in order to start it");
|
||||
}
|
||||
|
||||
if (!lock.pid.equals(pid)) {
|
||||
throw new RuntimeException("Lock '" + lid + "' is for another project: " + pid);
|
||||
}
|
||||
|
||||
if (!lock.uid.equals(uid)) {
|
||||
throw new RuntimeException("Lock '" + lid + "' is owned by another user: " + uid);
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject lockToJSON(Lock lock) throws JSONException {
|
||||
JSONObject o = new JSONObject();
|
||||
if (lock != null) {
|
||||
o.put("lock_id", lock.id);
|
||||
o.put("project_id", lock.pid);
|
||||
o.put("user_id", lock.uid);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
@PersistenceCapable
|
||||
static class Lock {
|
||||
|
||||
@Persistent
|
||||
String id;
|
||||
|
||||
@PrimaryKey
|
||||
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
|
||||
String pid;
|
||||
|
||||
@Persistent
|
||||
String uid;
|
||||
|
||||
Lock(String id, String pid, String uid) {
|
||||
this.id = id;
|
||||
this.pid = pid;
|
||||
this.uid = uid;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
12
broker/core/.classpath
Normal file
12
broker/core/.classpath
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/butterfly-trunk-r25.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/butterfly-trunk-r25-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks-server/lib/servlet-api-2.5.jar" sourcepath="/gridworks-server/lib-src/servlet-api-2.5-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/json-20100208.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/json-20100208-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/httpclient-4.0.1.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/httpclient-4.0.1-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/httpcore-4.0.1.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/httpcore-4.0.1-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/slf4j-api-1.5.6.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/slf4j-api-1.5.6-sources.jar"/>
|
||||
<classpathentry kind="output" path="module/MOD-INF/classes"/>
|
||||
</classpath>
|
17
broker/core/.project
Normal file
17
broker/core/.project
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>gridworks-broker</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
4
broker/core/module/MOD-INF/module.properties
Normal file
4
broker/core/module/MOD-INF/module.properties
Normal file
@ -0,0 +1,4 @@
|
||||
name = broker
|
||||
description = Gridworks Broker
|
||||
module-impl = com.metaweb.gridworks.broker.GridworksBroker
|
||||
templating = false
|
@ -0,0 +1,265 @@
|
||||
|
||||
package com.metaweb.gridworks.broker;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.ResponseHandler;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.impl.client.BasicResponseHandler;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.params.CoreProtocolPNames;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import edu.mit.simile.butterfly.ButterflyModuleImpl;
|
||||
|
||||
/**
|
||||
* This class contains all the code shared by various implementations of a Gridworks Broker.
|
||||
*
|
||||
* A broker is a server used by multiple Gridworks installations to enable collaborative
|
||||
* development over the same project.
|
||||
*
|
||||
* Broker implementations differ in how they store their state but all of them are required
|
||||
* to extend this abstract class and implement the services that are called via HTTP.
|
||||
*
|
||||
*/
|
||||
public abstract class GridworksBroker extends ButterflyModuleImpl {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger("gridworks.broker");
|
||||
|
||||
static final protected String USER_INFO_URL = "http://www.freebase.com/api/service/user_info";
|
||||
static final protected String DELEGATED_OAUTH_HEADER = "X-Freebase-Credentials";
|
||||
static final protected String OAUTH_HEADER = "Authorization";
|
||||
|
||||
static protected String OK;
|
||||
|
||||
static {
|
||||
try {
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("status","ok");
|
||||
OK = o.toString();
|
||||
} catch (JSONException e) {
|
||||
// not going to happen;
|
||||
}
|
||||
}
|
||||
|
||||
protected HttpClient httpclient;
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws Exception {
|
||||
super.init(config);
|
||||
httpclient = getHttpClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
httpclient.getConnectionManager().shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(String path, HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("> process {}", path);
|
||||
} else {
|
||||
logger.info("process {}", path);
|
||||
}
|
||||
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setHeader("Content-Type", "application/json");
|
||||
|
||||
try {
|
||||
String uid = getUserId(request);
|
||||
logger.debug("uid: {}", path);
|
||||
String pid = getParameter(request, "pid");
|
||||
logger.debug("pid: {}", path);
|
||||
|
||||
// NOTE: conditionals should be ordered by call frequency estimate to (slightly) improve performance
|
||||
// we could be using a hashtable and some classes that implement the commands, but the complexity overhead
|
||||
// doesn't seem to justify the marginal benefit.
|
||||
|
||||
if ("get_lock".equals(path)) {
|
||||
getLock(response, pid);
|
||||
} else if ("obtain_lock".equals(path)) {
|
||||
obtainLock(response, pid, uid);
|
||||
} else if ("release_lock".equals(path)) {
|
||||
releaseLock(response, pid, uid, getParameter(request, "lock"));
|
||||
} else if ("history".equals(path)) {
|
||||
getHistory(response, pid, getInteger(request, "tindex"));
|
||||
} else if ("transform".equals(path)) {
|
||||
addTransformations(response, pid, uid, getParameter(request, "lock"), getList(request, "transformations"));
|
||||
} else if ("start".equals(path)) {
|
||||
startProject(response, pid, uid, getParameter(request, "lock"), getParameter(request, "data"));
|
||||
} else if ("get".equals(path)) {
|
||||
getProject(response, pid);
|
||||
}
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
logger.error("runtime error", e);
|
||||
respondError(response, e.getMessage());
|
||||
} catch (Exception e) {
|
||||
logger.error("internal error", e);
|
||||
respondException(response, e);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) logger.debug("< process {}", path);
|
||||
|
||||
return super.process(path, request, response);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
protected abstract HttpClient getHttpClient();
|
||||
|
||||
protected abstract void getLock(HttpServletResponse response, String pid) throws Exception;
|
||||
|
||||
protected abstract void obtainLock(HttpServletResponse response, String pid, String uid) throws Exception;
|
||||
|
||||
protected abstract void releaseLock(HttpServletResponse response, String pid, String uid, String lock) throws Exception;
|
||||
|
||||
protected abstract void startProject(HttpServletResponse response, String pid, String uid, String lock, String data) throws Exception;
|
||||
|
||||
protected abstract void addTransformations(HttpServletResponse response, String pid, String uid, String lock, List<String> transformations) throws Exception;
|
||||
|
||||
protected abstract void getProject(HttpServletResponse response, String pid) throws Exception;
|
||||
|
||||
protected abstract void getHistory(HttpServletResponse response, String pid, int tindex) throws Exception;
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected String getUserId(HttpServletRequest request) throws Exception {
|
||||
|
||||
String oauth = request.getHeader(DELEGATED_OAUTH_HEADER);
|
||||
if (oauth == null) {
|
||||
throw new RuntimeException("The request needs to contain the '" + DELEGATED_OAUTH_HEADER + "' header set to obtain user identity via Freebase.");
|
||||
}
|
||||
|
||||
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
|
||||
Map<String,String> params = (Map<String,String>) request.getParameterMap();
|
||||
for (Entry<String,String> e : params.entrySet()) {
|
||||
formparams.add(new BasicNameValuePair((String) e.getKey(), (String) e.getValue()));
|
||||
}
|
||||
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, "UTF-8");
|
||||
|
||||
HttpPost httpRequest = new HttpPost(USER_INFO_URL);
|
||||
httpRequest.setHeader(OAUTH_HEADER, oauth);
|
||||
httpRequest.getParams().setParameter(CoreProtocolPNames.USER_AGENT, "Gridworks Broker");
|
||||
httpRequest.setEntity(entity);
|
||||
|
||||
ResponseHandler<String> responseHandler = new BasicResponseHandler();
|
||||
String responseBody = httpclient.execute(httpRequest, responseHandler);
|
||||
JSONObject o = new JSONObject(responseBody);
|
||||
|
||||
return o.getString("username");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
static protected String getParameter(HttpServletRequest request, String name) throws ServletException {
|
||||
String param = request.getParameter(name);
|
||||
if (param == null) {
|
||||
throw new ServletException("request must come with a '" + name + "' parameter");
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
static protected List<String> getList(HttpServletRequest request, String name) throws ServletException, JSONException {
|
||||
String param = getParameter(request, name);
|
||||
JSONArray a = new JSONArray(param);
|
||||
List<String> result = new ArrayList<String>(a.length());
|
||||
for (int i = 0; i < a.length(); i++) {
|
||||
result.add(a.getString(i));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static protected int getInteger(HttpServletRequest request, String name) throws ServletException, JSONException {
|
||||
return Integer.parseInt(getParameter(request, name));
|
||||
}
|
||||
|
||||
static protected void respondError(HttpServletResponse response, String error) throws IOException, ServletException {
|
||||
|
||||
if (response == null) {
|
||||
throw new ServletException("Response object can't be null");
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("code", "error");
|
||||
o.put("message", error);
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
respond(response, o.toString());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace(response.getWriter());
|
||||
}
|
||||
}
|
||||
|
||||
static protected void respondException(HttpServletResponse response, Exception e) throws IOException, ServletException {
|
||||
|
||||
if (response == null) {
|
||||
throw new ServletException("Response object can't be null");
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject o = new JSONObject();
|
||||
o.put("code", "error");
|
||||
o.put("message", e.getMessage());
|
||||
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
e.printStackTrace(pw);
|
||||
pw.flush();
|
||||
sw.flush();
|
||||
|
||||
o.put("stack", sw.toString());
|
||||
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
respond(response, o.toString());
|
||||
} catch (JSONException e1) {
|
||||
e.printStackTrace(response.getWriter());
|
||||
}
|
||||
}
|
||||
|
||||
static protected void respond(HttpServletResponse response, JSONObject content) throws IOException, ServletException {
|
||||
if (content == null) {
|
||||
throw new ServletException("Content object can't be null");
|
||||
}
|
||||
|
||||
JSONObject o = new JSONObject();
|
||||
respond(response, o.toString());
|
||||
}
|
||||
|
||||
static protected void respond(HttpServletResponse response, String content) throws IOException, ServletException {
|
||||
if (response == null) {
|
||||
throw new ServletException("Response object can't be null");
|
||||
}
|
||||
|
||||
Writer w = response.getWriter();
|
||||
if (w != null) {
|
||||
w.write(content);
|
||||
w.flush();
|
||||
w.close();
|
||||
} else {
|
||||
throw new ServletException("response returned a null writer");
|
||||
}
|
||||
}
|
||||
}
|
14
broker/local/.classpath
Normal file
14
broker/local/.classpath
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/butterfly-trunk-r25.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/butterfly-trunk-r25-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks-server/lib/servlet-api-2.5.jar" sourcepath="/gridworks-server/lib-src/servlet-api-2.5-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/json-20100208.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/json-20100208-sources.jar"/>
|
||||
<classpathentry kind="lib" path="module/MOD-INF/lib/bdb-je-4.0.103.jar" sourcepath="module/MOD-INF/lib-src/bdb-je-4.0.103-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/httpclient-4.0.1.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/httpclient-4.0.1-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/httpcore-4.0.1.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/httpcore-4.0.1-sources.jar"/>
|
||||
<classpathentry kind="lib" path="/gridworks/webapp/WEB-INF/lib/slf4j-api-1.5.6.jar" sourcepath="/gridworks/webapp/WEB-INF/lib-src/slf4j-api-1.5.6-sources.jar"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/gridworks-broker"/>
|
||||
<classpathentry kind="output" path="module/MOD-INF/classes"/>
|
||||
</classpath>
|
17
broker/local/.project
Normal file
17
broker/local/.project
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>gridworks-local-broker</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
29
broker/local/WEB-INF/butterfly.properties
Normal file
29
broker/local/WEB-INF/butterfly.properties
Normal file
@ -0,0 +1,29 @@
|
||||
#
|
||||
# Butterfly Configuration
|
||||
#
|
||||
# NOTE: properties passed to the JVM using '-Dkey=value' from the command line
|
||||
# override the settings in this file.
|
||||
|
||||
# indicates the URL path where butterfly is available in the proxy URL space
|
||||
# as there is no way of knowing otherwise as this information is not
|
||||
# transferred thru the HTTP protocol or otherwise (different story if
|
||||
# the appserver is connected thru a different protocol such as AJP)
|
||||
|
||||
butterfly.url = /
|
||||
|
||||
# ---------- Miscellaneous ----------
|
||||
|
||||
#butterfly.locale.language = en
|
||||
#butterfly.locale.country = US
|
||||
#butterfly.timeZone = GMT+09:00
|
||||
|
||||
# ---------- Module ------
|
||||
|
||||
butterfly.modules.path = modules
|
||||
|
||||
butterfly.modules.wirings = WEB-INF/modules.properties
|
||||
|
||||
# ---------- Clustering ----
|
||||
|
||||
#butterfly.routing.cookie.maxage = -1
|
||||
|
5
broker/local/WEB-INF/modules.properties
Normal file
5
broker/local/WEB-INF/modules.properties
Normal file
@ -0,0 +1,5 @@
|
||||
#
|
||||
# Butterfly Modules Configuration
|
||||
#
|
||||
|
||||
local-broker = /
|
20
broker/local/WEB-INF/web.xml
Normal file
20
broker/local/WEB-INF/web.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!DOCTYPE web-app
|
||||
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
|
||||
"http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
|
||||
|
||||
<web-app>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>Butterfly</servlet-name>
|
||||
<servlet-class>edu.mit.simile.butterfly.Butterfly</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>Butterfly</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
67
broker/local/licenses/je.license.txt
Normal file
67
broker/local/licenses/je.license.txt
Normal file
@ -0,0 +1,67 @@
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
/*
|
||||
* Copyright (c) 2002-2010 Oracle. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Redistributions in any form must be accompanied by information on
|
||||
* how to obtain complete source code for the Oracle Berkeley DB
|
||||
* Java Edition software and any accompanying software that uses the
|
||||
* Oracle Berkeley DB Java Edition software. The source code must
|
||||
* either be included in the distribution or be available for no
|
||||
* more than the cost of distribution plus a nominal fee, and must be
|
||||
* freely redistributable under reasonable conditions. For an
|
||||
* executable file, complete source code means the source code for all
|
||||
* modules it contains. It does not include source code for modules or
|
||||
* files that typically accompany the major components of the operating
|
||||
* system on which the executable file runs.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY ORACLE ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
|
||||
* NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL ORACLE BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
/***
|
||||
* ASM: a very small and fast Java bytecode manipulation framework
|
||||
* Copyright (c) 2000-2005 INRIA, France Telecom
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the copyright holders nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
BIN
broker/local/module/MOD-INF/lib-src/bdb-je-4.0.103-sources.jar
Normal file
BIN
broker/local/module/MOD-INF/lib-src/bdb-je-4.0.103-sources.jar
Normal file
Binary file not shown.
BIN
broker/local/module/MOD-INF/lib/bdb-je-4.0.103.jar
Normal file
BIN
broker/local/module/MOD-INF/lib/bdb-je-4.0.103.jar
Normal file
Binary file not shown.
5
broker/local/module/MOD-INF/module.properties
Normal file
5
broker/local/module/MOD-INF/module.properties
Normal file
@ -0,0 +1,5 @@
|
||||
name = local-broker
|
||||
extends = broker
|
||||
description = Local implementation of Gridworks Broker Module
|
||||
module-impl = com.metaweb.gridworks.broker.LocalDBGridworksBroker
|
||||
templating = false
|
@ -0,0 +1,309 @@
|
||||
package com.metaweb.gridworks.broker;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Writer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.sleepycat.je.Environment;
|
||||
import com.sleepycat.je.EnvironmentConfig;
|
||||
import com.sleepycat.je.Transaction;
|
||||
import com.sleepycat.persist.EntityStore;
|
||||
import com.sleepycat.persist.PrimaryIndex;
|
||||
import com.sleepycat.persist.StoreConfig;
|
||||
import com.sleepycat.persist.model.Entity;
|
||||
import com.sleepycat.persist.model.PrimaryKey;
|
||||
|
||||
public class LocalGridworksBroker extends GridworksBroker {
|
||||
|
||||
protected static final Logger logger = LoggerFactory.getLogger("gridworks.broker.local");
|
||||
|
||||
Environment env;
|
||||
EntityStore projectStore;
|
||||
EntityStore lockStore;
|
||||
|
||||
PrimaryIndex<String,Project> projectById;
|
||||
PrimaryIndex<String,Lock> lockByProject;
|
||||
|
||||
protected HttpClient httpclient;
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws Exception {
|
||||
super.init(config);
|
||||
|
||||
File dataPath = new File("data"); // FIXME: data should be configurable;
|
||||
|
||||
EnvironmentConfig envConfig = new EnvironmentConfig();
|
||||
envConfig.setAllowCreate(true);
|
||||
envConfig.setTransactional(true);
|
||||
env = new Environment(dataPath, envConfig);
|
||||
|
||||
StoreConfig storeConfig = new StoreConfig();
|
||||
storeConfig.setAllowCreate(true);
|
||||
storeConfig.setTransactional(true);
|
||||
projectStore = new EntityStore(env, "ProjectsStore", storeConfig);
|
||||
lockStore = new EntityStore(env, "LockStore", storeConfig);
|
||||
|
||||
projectById = projectStore.getPrimaryIndex(String.class, Project.class);
|
||||
lockByProject = lockStore.getPrimaryIndex(String.class, Lock.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws Exception {
|
||||
super.destroy();
|
||||
|
||||
if (projectStore != null) {
|
||||
projectStore.close();
|
||||
}
|
||||
|
||||
if (lockStore != null) {
|
||||
lockStore.close();
|
||||
}
|
||||
|
||||
if (env != null) {
|
||||
env.cleanLog();
|
||||
env.close();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
protected HttpClient getHttpClient() {
|
||||
return new DefaultHttpClient();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
protected void getLock(HttpServletResponse response, String pid) throws Exception {
|
||||
respond(response, lockToJSON(getLock(pid)));
|
||||
}
|
||||
|
||||
protected void obtainLock(HttpServletResponse response, String pid, String uid) throws Exception {
|
||||
|
||||
Transaction txn = env.beginTransaction(null, null);
|
||||
|
||||
Lock lock = getLock(pid);
|
||||
if (lock == null) {
|
||||
try {
|
||||
lock = new Lock(Long.toHexString(txn.getId()), pid, uid);
|
||||
lockByProject.put(txn, lock);
|
||||
txn.commit();
|
||||
} finally {
|
||||
if (txn != null) {
|
||||
txn.abort();
|
||||
txn = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
respond(response, lockToJSON(lock));
|
||||
}
|
||||
|
||||
protected void releaseLock(HttpServletResponse response, String pid, String uid, String lid) throws Exception {
|
||||
|
||||
Transaction txn = env.beginTransaction(null, null);
|
||||
|
||||
Lock lock = getLock(pid);
|
||||
if (lock != null) {
|
||||
try {
|
||||
if (!lock.id.equals(lid)) {
|
||||
throw new RuntimeException("Lock id doesn't match, can't release the lock");
|
||||
}
|
||||
if (!lock.uid.equals(uid)) {
|
||||
throw new RuntimeException("User id doesn't match the lock owner, can't release the lock");
|
||||
}
|
||||
|
||||
lockByProject.delete(pid);
|
||||
|
||||
txn.commit();
|
||||
} finally {
|
||||
if (txn != null) {
|
||||
txn.abort();
|
||||
txn = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
respond(response, OK);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------
|
||||
|
||||
protected void startProject(HttpServletResponse response, String pid, String uid, String lid, String data) throws Exception {
|
||||
|
||||
Transaction txn = env.beginTransaction(null, null);
|
||||
|
||||
checkLock(pid, uid, lid);
|
||||
|
||||
if (projectById.contains(pid)) {
|
||||
throw new RuntimeException("Project '" + pid + "' already exists");
|
||||
}
|
||||
|
||||
try {
|
||||
projectById.put(txn, new Project(pid, data));
|
||||
txn.commit();
|
||||
} finally {
|
||||
if (txn != null) {
|
||||
txn.abort();
|
||||
txn = null;
|
||||
}
|
||||
}
|
||||
|
||||
respond(response, OK);
|
||||
}
|
||||
|
||||
protected void addTransformations(HttpServletResponse response, String pid, String uid, String lid, List<String> transformations) throws Exception {
|
||||
|
||||
Transaction txn = env.beginTransaction(null, null);
|
||||
|
||||
checkLock(pid, uid, lid);
|
||||
|
||||
Project project = getProject(pid);
|
||||
|
||||
if (project == null) {
|
||||
throw new RuntimeException("Project '" + pid + "' not found");
|
||||
}
|
||||
|
||||
try {
|
||||
project.transformations.addAll(transformations);
|
||||
txn.commit();
|
||||
} finally {
|
||||
if (txn != null) {
|
||||
txn.abort();
|
||||
txn = null;
|
||||
}
|
||||
}
|
||||
|
||||
respond(response, OK);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
protected void getProject(HttpServletResponse response, String pid) throws Exception {
|
||||
Project project = getProject(pid);
|
||||
|
||||
Writer w = response.getWriter();
|
||||
JSONWriter writer = new JSONWriter(w);
|
||||
writer.object();
|
||||
writer.key("data"); writer.value(project.data);
|
||||
writer.key("transformations");
|
||||
writer.array();
|
||||
for (String s : project.transformations) {
|
||||
writer.value(s);
|
||||
}
|
||||
writer.endArray();
|
||||
writer.endObject();
|
||||
w.flush();
|
||||
w.close();
|
||||
}
|
||||
|
||||
protected void getHistory(HttpServletResponse response, String pid, int tindex) throws Exception {
|
||||
Project project = getProject(pid);
|
||||
|
||||
Writer w = response.getWriter();
|
||||
JSONWriter writer = new JSONWriter(w);
|
||||
writer.object();
|
||||
writer.key("transformations");
|
||||
writer.array();
|
||||
int size = project.transformations.size();
|
||||
for (int i = tindex; i < size; i++) {
|
||||
writer.value(project.transformations.get(i));
|
||||
}
|
||||
writer.endArray();
|
||||
writer.endObject();
|
||||
w.flush();
|
||||
w.close();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
Project getProject(String pid) {
|
||||
Project project = projectById.get(pid);
|
||||
if (project == null) {
|
||||
throw new RuntimeException("Project '" + pid + "' is not managed by this broker");
|
||||
}
|
||||
return project;
|
||||
}
|
||||
|
||||
@Entity
|
||||
class Project {
|
||||
|
||||
@PrimaryKey
|
||||
String pid;
|
||||
|
||||
List<String> transformations = new ArrayList<String>();
|
||||
|
||||
String data;
|
||||
|
||||
Project(String pid, String data) {
|
||||
this.pid = pid;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Project() {}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
Lock getLock(String pid) {
|
||||
return lockByProject.get(pid);
|
||||
}
|
||||
|
||||
void checkLock(String pid, String uid, String lid) {
|
||||
Lock lock = getLock(pid);
|
||||
|
||||
if (lock == null) {
|
||||
throw new RuntimeException("No lock was found with the given Lock id '" + lid + "', you have to have a valid lock on a project in order to start it");
|
||||
}
|
||||
|
||||
if (!lock.pid.equals(pid)) {
|
||||
throw new RuntimeException("Lock '" + lid + "' is for another project: " + pid);
|
||||
}
|
||||
|
||||
if (!lock.uid.equals(uid)) {
|
||||
throw new RuntimeException("Lock '" + lid + "' is owned by another user: " + uid);
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject lockToJSON(Lock lock) throws JSONException {
|
||||
JSONObject o = new JSONObject();
|
||||
if (lock != null) {
|
||||
o.put("lock_id", lock.id);
|
||||
o.put("project_id", lock.pid);
|
||||
o.put("user_id", lock.uid);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
@Entity
|
||||
class Lock {
|
||||
|
||||
@PrimaryKey
|
||||
String pid;
|
||||
|
||||
String id;
|
||||
|
||||
String uid;
|
||||
|
||||
Lock(String id, String pid, String uid) {
|
||||
this.id = id;
|
||||
this.pid = pid;
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private Lock() {}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user