You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2010/10/27 22:42:45 UTC

svn commit: r1028093 [1/2] - in /openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence: ./ jest/

Author: ppoddar
Date: Wed Oct 27 20:42:44 2010
New Revision: 1028093

URL: http://svn.apache.org/viewvc?rev=1028093&view=rev
Log:
OPENJPA-1851: First version of JEST (REST on OpenJPA)

Added:
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ServerContext.java   (with props)
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/XMLEncoder.java   (with props)
Modified:
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
    openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java

Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java?rev=1028093&r1=1028092&r2=1028093&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java (original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java Wed Oct 27 20:42:44 2010
@@ -45,10 +45,10 @@ import org.apache.openjpa.lib.util.Close
 import org.apache.openjpa.lib.util.Localizer;
 import org.apache.openjpa.persistence.criteria.CriteriaBuilderImpl;
 import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
+import org.apache.openjpa.persistence.jest.Server;
 import org.apache.openjpa.persistence.meta.MetamodelImpl;
 import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
 import org.apache.openjpa.persistence.query.QueryBuilderImpl;
-import org.apache.openjpa.util.UserException;
 
 /**
  * Implementation of {@link EntityManagerFactory} that acts as a
@@ -70,7 +70,8 @@ public class EntityManagerFactoryImpl
     private transient StoreCache _cache = null;
     private transient QueryResultCache _queryCache = null;
     private transient MetamodelImpl _metaModel;
-    
+    private transient Server _remoteAccess = null;
+
     /**
      * Default constructor provided for auto-instantiation.
      */
@@ -93,10 +94,11 @@ public class EntityManagerFactoryImpl
 
     /**
      * Delegate must be provided before use.
+     * Configures for Remote Access, if appropriate. 
      */
     public void setBrokerFactory(BrokerFactory factory) {
-        _factory = new DelegatingBrokerFactory(factory,
-            PersistenceExceptions.TRANSLATOR);
+        _factory = new DelegatingBrokerFactory(factory, PersistenceExceptions.TRANSLATOR);
+        configureRemoteAccess(getConfiguration());
     }
 
     public OpenJPAConfiguration getConfiguration() {
@@ -271,6 +273,10 @@ public class EntityManagerFactoryImpl
         if (log.isTraceEnabled()) {
             log.trace(this + ".close() invoked.");
         }
+        if (_remoteAccess != null) {
+            _remoteAccess.stop();
+            _remoteAccess = null;
+        }
         _factory.close();
     }
 
@@ -396,4 +402,40 @@ public class EntityManagerFactoryImpl
             }
         }
     }
+    
+    /**
+     * Configures this unit for remote access.
+     */
+    protected void configureRemoteAccess(OpenJPAConfiguration conf) {
+        Value value = conf.getValue("RemoteAccess");
+        if (value == null) {
+            return;
+        }
+        String props = value.getString();
+        if (props == null)
+            return;
+        try {
+            _remoteAccess = new Server();
+            _remoteAccess.setContext(this);
+            Configurations.configureInstance(_remoteAccess, conf, props);
+            conf.removeValue(value);
+            if (!_remoteAccess.start()) {
+                _remoteAccess = null;
+            }
+        } catch (Exception ex) {
+            Log log = _factory.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
+            if (log != null) {
+                log.error(_loc.get("remote-start-error"), ex);
+            }
+        }
+    }
+    
+    /**
+     * Affirms if this unit is accessible remotely.
+     */
+    public boolean allowsRemoteAccess() {
+        return _remoteAccess != null;
+    }
+    
+
 }

Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java?rev=1028093&r1=1028092&r2=1028093&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java (original)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java Wed Oct 27 20:42:44 2010
@@ -188,6 +188,7 @@ public class PersistenceProductDerivatio
         conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(),  PersistenceMetaDataFactory.class.getName());
         
         conf.addValue(new EntityManagerFactoryValue());
+        conf.addString("RemoteAccess");
         
         conf.readLockLevel.setAlias("optimistic", String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
         conf.readLockLevel.setAlias("optimistic-force-increment", String

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+/**
+ * Abstract implementation of a stream-based response.
+ * Every response is result of a {@linkplain Request} and operates within a {@linkplain ServerContext}.
+ * This fact is enforced by the constructor argument of an abstract response.
+ * <p>
+ * Besides, this implementation provides common utility to write HTTP response or extract useful information
+ * from the request.
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public abstract class AbstractResponse extends PrintStream implements Response {
+    protected final Request _request;
+    protected final ServerContext _ctx;
+    
+    /**
+     * Construct a response for the given request and server context.
+     * 
+     * @param request the request for this response. Can be null if the response is for server error.
+     * @param ctx the processing context 
+     * @param out the output stream where the response is targeted.
+     */
+    protected AbstractResponse(Request request, ServerContext ctx, OutputStream out) {
+        super(out);
+        _request = request;
+        _ctx = ctx;
+    }
+    
+    /**
+     * Write a HTTP header to the stream.
+     * <br> 
+     * The format of HTTP header is <code>key: [value]* NEWLINE</code>
+     * 
+     * @param key the key of the header
+     * @param values one of more value of the header fields.
+     */
+    protected void printHeader(String key, String...values) {
+        if (key == null)
+            return;
+        print(key);
+        print(" :");
+        if (values == null || values.length == 0)
+            return;
+        int n = values.length-1;
+        for (int i = 0; i < n-1; i++) {
+            print(values[i]);
+            print(";");
+        }
+        println(values[n]);
+    }
+    
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+
+/**
+ * A HTTP response for something gone wrong.
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class ErrorResponse extends AbstractResponse {
+    private final Exception _error;
+    private final int _code;
+    /**
+     * Construct a response to describe a error.
+     * 
+     * @param request a request that produced this response. VCan be null to denote that the request can not
+     * be created.
+     * @param ctx the processing context
+     * @param ex the error
+     * @param code HTTP error code
+     * @param out the stream where the response is written
+     * 
+     */
+    public ErrorResponse(Request request, ServerContext ctx, Exception ex, int code, OutputStream out)  {
+        super(request, ctx, out);
+        _error = ex;
+        _code = code;
+    }
+
+    /**
+     * Writes the response. 
+     * The response is always a HTTP response with the error stack trace.
+     */
+    public void writeOut() throws Exception {
+        println("HTTP/1.1"); print(" " + _code); println("Error");
+        printHeader("Connection",  "close");
+        printHeader("Content-Type", "text/html", "charset=UTF-8");
+        println();
+        println("<html><body><pre>");
+        _error.printStackTrace(this);
+        println("Response from JEST");
+        
+        println("</pre></body></html>");
+        close();
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityNotFoundException;
+import javax.persistence.Query;
+
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.util.ApplicationIds;
+import org.apache.openjpa.util.ObjectNotFoundException;
+
+/**
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class GETRequest extends JESTRequest {
+    public Response process(ServerContext server, OutputStream out) throws Exception {
+        String action = getAction();
+        try {
+            if ("find".equals(action)) {
+                return find(server, out);
+            } else {
+                return resource(server, out);
+            }
+        } catch (Exception e) {
+            return new ErrorResponse(this, server, new RuntimeException("bad action " + action), 
+                HttpURLConnection.HTTP_BAD_REQUEST, out);
+        }
+    }
+    
+    Response find(ServerContext server, OutputStream out)  throws Exception {
+        EntityManager em = server.getPersistenceUnit().createEntityManager();
+        Map<String, String> qualifiers = getQualifiers();
+        Map<String, String> parameters = getParameters();
+        if (parameters.size() < 2)
+            throw new IllegalArgumentException("find must have at least two parameters");
+        Object[] pks = new Object[parameters.size()-1];
+        Iterator<Map.Entry<String,String>> params = parameters.entrySet().iterator();
+        String alias = null;
+        for (int i = 0; i < parameters.size(); i++) {
+            if (i == 0) {
+                alias = params.next().getKey();
+            } else {
+                pks[i-1] = params.next().getKey();
+            }
+        }
+        ClassMetaData meta = server.resolve(alias);
+        Object oid = ApplicationIds.fromPKValues(pks, meta);
+        Object pc = em.find(meta.getDescribedType(), oid); 
+        if (pc != null) {
+            OpenJPAStateManager sm = ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(pc);
+            return new JESTResponse(this, server, sm, out);
+        } else {
+            return new ErrorResponse(this, server, new EntityNotFoundException("not found!"), 
+                HttpURLConnection.HTTP_NOT_FOUND, out);
+        }
+    }
+    
+    Response query(ServerContext server, OutputStream out)  throws Exception {
+        EntityManager em = server.getPersistenceUnit().createEntityManager();
+        Map<String, String> qualifiers = getQualifiers();
+        boolean named = isBooleanQualifier("named");
+        boolean single = isBooleanQualifier("single");
+        Map<String, String> parameters = getParameters();
+        if (parameters.size() < 1)
+            throw new IllegalArgumentException("find must have at least one parameter");
+        Iterator<Map.Entry<String,String>> params = parameters.entrySet().iterator();
+        Query query = null;
+        int i = 0;
+        for (Map.Entry<String, String> param : parameters.entrySet()) {
+            if (i == 0) {
+                query = named ? em.createQuery(param.getKey()) : em.createNamedQuery(param.getKey());
+            } else {
+                query.setParameter(param.getKey(), param.getValue());
+            }
+        }
+        if (single) {
+            Object result = query.getSingleResult();
+            OpenJPAStateManager sm = ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(result);
+            return new JESTResponse(this, server, sm, out);
+        } else {
+            List<Object> result = query.getResultList();
+            return new ErrorResponse(this, server, new EntityNotFoundException("not found!"), 404, out);
+        }
+    }
+    
+    Response resource(ServerContext server, OutputStream out)  throws Exception {
+        String resource = getAction();
+        if (resource.length() == 0)
+            resource = "index.html";
+        String mimeType = getMimeType(resource);
+//        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
+        InputStream in = getClass().getResourceAsStream(resource);
+        if (in == null) {
+            return new ErrorResponse(this, server, new ObjectNotFoundException(resource), 404, out);
+        }
+        if (server.getLog().isTraceEnabled())
+            server.getLog().trace("Found resource " + resource);
+        return mimeType.startsWith("image") 
+          ? new ImageResponse(this, server, in, mimeType, out)
+          : new ResourceResponse(this, server, in, mimeType, out);
+    }
+    
+    boolean isBooleanQualifier(String key) {
+        String q = getQualifier(key);
+        return hasQualifier(key) && (q == null || "true".equals(q));
+    }
+    
+    String getMimeType(String resource) {
+        int index = resource.lastIndexOf('.');
+        String ext = (index != -1) ? resource.substring(index+1) : ""; 
+        if (ext.equalsIgnoreCase("html") || ext.equalsIgnoreCase(".html")) return "text/html";
+        if (ext.equalsIgnoreCase(".png") || ext.equalsIgnoreCase(".ico") || ext.equalsIgnoreCase("jpeg")) 
+            return "image/"+ext;
+        return "text/html";
+    }
+    
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+import javax.imageio.ImageIO;
+
+/**
+ * Sends an image as response.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class ImageResponse extends AbstractResponse {
+    private final InputStream _in;
+    private final String _mimeType;
+    
+    public ImageResponse(Request request, ServerContext ctx, InputStream resource, String mimeType,
+        OutputStream out) throws Exception {
+        super(request, ctx, out);
+        _in = resource;
+        _mimeType = mimeType;
+    }
+
+    public void writeOut() throws Exception {
+        print(_request.getProtocol()); println("200 OK");
+        printHeader("Connection",  "close");
+        printHeader("Content-Type", _mimeType);
+        println();
+        byte[] b = new byte[1024];
+        int i = 0;
+        for (int l = 0; (l = _in.read(b)) != -1;) {
+            write(b, 0, l);
+            i += l;
+        }
+        printHeader("Content-Length", ""+i);
+        _in.close();
+        close();
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+/**
+ * A request carries requisite data for a JPA operation to be performed. 
+ * The request is populated by parsing an input data stream.
+ * 
+ * 
+ * @author Pinaki Poddar
+ * 
+ */
+@SuppressWarnings("serial")
+public abstract class JESTRequest implements Request {
+    private String _method;
+    private String _protocol;
+    private String _action;
+    private String _body;
+    private LinkedHashMap<String, String> _qualifiers = new LinkedHashMap<String, String>();
+    private LinkedHashMap<String, String> _params = new LinkedHashMap<String, String>();
+    private Map<String, List<String>> _headers = new HashMap<String, List<String>>();
+    private ParseState _state;
+    private StringBuffer buf = new StringBuffer();
+    private LinkedList<Token> _stack = new LinkedList<Token>();
+    
+    public static final List<String> METHODS = Arrays.asList(new String[]{"GET","POST","PUT","DELETE"});
+    
+    /**
+     * Parse States.
+     */
+    static enum ParseState {
+        INIT, ACTION, QUALIFIER_KEY, QUALIFIER_VALUE, PARAM_KEY, PARAM_VALUE, END
+    };
+
+
+    public String getMethod() {
+        return _method;
+    }
+
+    void setMethod(String method) {
+        if (_method == null) {
+            if (method != null && METHODS.contains(method.toUpperCase())) {
+                _method = method.toUpperCase();
+            } else {
+                throw new IllegalArgumentException("Unsupported method " + method);
+            }
+        } else if (!_method.equalsIgnoreCase(method)) {
+            throw new IllegalStateException("Method can not be changed to [" + method + "]. " +
+                "Current method [" + _method + "]");
+        }
+    }
+
+    public String getProtocol() {
+        return _protocol == null ? "HTTP/1.1" : _protocol;
+    }
+
+    void setProtocol(String protocol) {
+        if (_protocol == null) {
+            if (protocol != null && protocol.toUpperCase().startsWith("HTTP")) {
+                _protocol = protocol.toUpperCase();
+            } else {
+                throw new IllegalArgumentException("Unsupported protocol " + protocol);
+            }
+        } else if (!_protocol.equalsIgnoreCase(protocol)) {
+            throw new IllegalStateException("Protocol can not be changed to [" + protocol + "]. " +
+                "Current protocol [" + _protocol + "]");
+        }
+    }
+
+    /**
+     * Sets an action. Once set, an action can not be modified.
+     * 
+     * @param action
+     */
+    private void setAction(String action) {
+        if (_action == null) {
+            _action = action;
+        } else if (!_action.equals(action)) {
+            throw new IllegalStateException("Action can not be [" + action + "]. Already set to [" + _action + "]");
+        }
+    }
+
+    public String getAction() {
+        return _action == null ? "" : _action;
+    }
+
+    public String getBody() {
+        return _body;
+    }
+
+    private void setQualifier(String key, String value) {
+        _qualifiers.put(key, value);
+    }
+
+    public String getQualifier(String key) {
+        return _qualifiers.get(key);
+    }
+
+    public Map<String, String> getQualifiers() {
+        return Collections.unmodifiableMap(_qualifiers);
+    }
+
+    public boolean hasQualifier(String key) {
+        return _qualifiers.containsKey(key);
+    }
+
+    private void setParameter(String key, String value) {
+        _params.put(key, value);
+    }
+
+    public String getParameter(String key) {
+        return _params.get(key);
+    }
+
+    public boolean hasParameter(String key) {
+        return _params.containsKey(key);
+    }
+
+    public Map<String, String> getParameters() {
+        return Collections.unmodifiableMap(_params);
+    }
+    
+    public Map.Entry<String, String> getParameter(int n) {
+        if (n >= _params.size())
+            throw new NoSuchElementException("Index " + n + " size " + _params.size());
+        int i = 0;
+        for (Map.Entry<String, String> entry : _params.entrySet()) {
+            if (i == n) {
+                return entry;
+            }
+            i++;
+        }
+        return null;
+    }
+
+    public Map<String, List<String>> getHeaders() {
+        return Collections.unmodifiableMap(_headers);
+    }
+
+    public List<String> getHeader(String key) {
+        return _headers.get(key);
+    }
+    
+    
+    public void read(List<String> lines) throws IOException {
+        parse(lines.get(0));
+        int i = 1;
+        for (; i < lines.size(); i++) {
+            String line = lines.get(i);
+            if (line.length() == 0) {
+                break;
+            } else {
+                parseHeader(line);
+            }
+        }
+        parseBody(lines.subList(i, lines.size()));
+    }
+
+    protected void parseHeader(String line) throws IOException {
+        String key = null;
+        StringBuilder token = new StringBuilder();
+        int N = line.length();
+        for (int i = 0; i < N; i++) {
+            char c = line.charAt(i);
+            if (c == ':' && key == null) {
+                key = token.toString().trim();
+                token.delete(0, token.length());
+            } else {
+                token.append(c);
+            }
+        }
+        if (key != null) {
+            _headers.put(key, Collections.singletonList(token.toString().trim()));
+        }
+    }
+
+    protected void parseBody(List<String> lines) {
+        if (lines == null || lines.isEmpty())
+            return;
+        for (String line : lines) {
+            if (_body == null) {
+                _body = line;
+            } else {
+                _body = _body + line;
+            }
+        }
+    }
+
+    /**
+     * Parses JEST stream and populates a request.
+     * 
+     */
+     protected void parse(String s) {
+            char[] chars = s.toCharArray();
+            _state = ParseState.INIT;
+            _stack.clear();
+
+            for (int i = 0; i < chars.length; i++) {
+                char ch = chars[i];
+                switch (_state) {
+                    case INIT:
+                        if (ch == '/') {
+                            transit(ParseState.ACTION);
+                        } else if (!Character.isWhitespace(ch)) {
+                            parseError(ch, i, s, true, ' ');
+                        }
+                        break;
+
+                    case ACTION:
+                        if (ch == '/') {
+                            transit(ParseState.QUALIFIER_KEY);
+                        } else if (ch == '?') {
+                            transit(ParseState.PARAM_KEY);
+                        } else {
+                            buf.append(ch);
+                        }
+                        break;
+
+                    case QUALIFIER_KEY:
+                        if (Character.isJavaIdentifierPart(ch)) {
+                            buf.append(ch);
+                        } else if (ch == '=') {
+                            transit(ParseState.QUALIFIER_VALUE);
+                        } else if (ch == '/') {
+                            transit(ParseState.QUALIFIER_KEY);
+                        } else if (ch == '?') {
+                            transit(ParseState.PARAM_KEY);
+                        } else {
+                            parseError(ch, i, s, true, '/', '?', '=');
+                        }
+                        break;
+
+                    case QUALIFIER_VALUE:
+                        if (Character.isJavaIdentifierPart(ch)) {
+                            buf.append(ch);
+                        } else if (ch == '/') {
+                            transit(ParseState.QUALIFIER_KEY);
+                        } else if (ch == '?') {
+                            transit(ParseState.PARAM_KEY);
+                        } else {
+                            parseError(ch, i, s, true, '/', '?');
+                        }
+                        break;
+
+                    case PARAM_KEY:
+                        if (Character.isJavaIdentifierPart(ch)) {
+                            buf.append(ch);
+                        } else if (ch == '=') {
+                            if (isQueryKey())
+                                buf.append(ch);
+                            else
+                                transit(ParseState.PARAM_VALUE);
+                        } else if (ch == ';') {
+                            transit(ParseState.PARAM_KEY);
+                        } else if (isQueryKey() && isQueryChar(ch)) {
+                            buf.append(ch);
+                        } else {
+                            parseError(ch, i, s, true, ';', '=');
+                        }
+                        break;
+
+                    case PARAM_VALUE:
+                        if (Character.isJavaIdentifierPart(ch)) {
+                            buf.append(ch);
+                        } else if (ch == ';') {
+                            transit(ParseState.PARAM_KEY);
+                        } else {
+                            parseError(ch, i, s, true, ';');
+                        }
+                        break;
+                    default:
+                        throw new RuntimeException("ParseError: '" + ch + "' at " + i + " in [" + s + "]. "
+                            + "Unknown state " + _state);
+                }
+            }
+            if (buf.length() > 0) {
+                transit(ParseState.END);
+            }
+        }
+
+        /**
+         * Affirms if parsing a query string.
+         */
+        private boolean isQueryKey() {
+            return "query".equals(_action) && _stack.size() == 1;
+        }
+        
+        /**
+         * Affirms if the given character is valid in a query string 
+         */
+        private boolean isQueryChar(char c) {
+            return c == ' ' || c == '.' || c == ':' || c == '?' || c == '\'';
+        }
+
+        /**
+         * Transitions to a new parse state.
+         * 
+         * @param to target parse state
+         */
+        void transit(ParseState to) {
+            String token = buf.toString();
+            switch (_state) {
+                case ACTION:
+                    setAction(token);
+                    break;
+                case QUALIFIER_KEY:
+                    setQualifier(token, null);
+                    break;
+                case QUALIFIER_VALUE:
+                    setQualifier(_stack.peekLast().getValue(), token);
+                    break;
+                case PARAM_KEY:
+                    setParameter(token, null);
+                    break;
+                case PARAM_VALUE:
+                    setParameter(_stack.peekLast().getValue(), token);
+                    break;
+
+            }
+            if (_state != ParseState.INIT && to != ParseState.END) {
+                _stack.add(new Token(_state, token));
+            }
+            buf.delete(0, buf.length());
+            _state = to;
+        }
+
+        protected void parseError(char ch, int pos, String line, boolean java, char... expected) {
+            throw new RuntimeException("ParseError: Encountered '" + ch + "' at " + pos + " in [" + line + "] while "
+                + "parsing " + _state + ". Expected " + Arrays.toString(expected) + (java ? " or Java identifer" : ""));
+
+        }
+
+        /**
+         * Token in a JEST stream.
+         * 
+         */
+        static class Token {
+            final ParseState _type;
+            final String _value;
+
+            public Token(ParseState type, String value) {
+                _type = type;
+                _value = value;
+            }
+
+            public ParseState getType() {
+                return _type;
+            }
+
+            public String getValue() {
+                return _value;
+            }
+
+            public String toString() {
+                return _value + "[" + _type + "]";
+            }
+        }
+    
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Response of a JEST Request.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class JESTResponse extends AbstractResponse {
+    private final OpenJPAStateManager _sm;
+    
+    public JESTResponse(Request request, ServerContext ctx, OpenJPAStateManager sm, OutputStream out) throws Exception {
+        super(request, ctx, out);
+        _sm = sm;
+    }
+    
+    public String getContentType() {
+        return "text/html";
+    }
+    
+    public void writeOut() throws Exception {
+        print(_request.getProtocol()); println("200 OK");
+        printHeader("Connection",  "close");
+        printHeader("Content-Type", "text/html", "charset=UTF-8");
+        println();
+        println("<html><body><pre>");
+        XMLEncoder encoder = new XMLEncoder(_ctx.getPersistenceUnit().getMetamodel());
+        Document doc = encoder.encode(_sm);
+        encoder.writeDoc(doc, this);
+        println("Response from JEST");
+        
+        println("</pre></body></html>");
+        close();
+    }
+    
+}
+
+

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,316 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.BufferedReader;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.persistence.metamodel.Attribute;
+
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+import org.apache.openjpa.meta.ValueMetaData;
+import org.apache.openjpa.persistence.meta.Members;
+import org.apache.openjpa.persistence.meta.MetamodelImpl;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Marshals a root instance and its persistent closure as JSON object.
+ * The closure is resolved against the persistence context that contains the root instance.
+ * The JSON format introduces a $id and $ref to address reference that pure JSON does not. 
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class JSONEncoder {
+    /**
+     * The element/attribute tags declared in <code>jest-instance.xsd</code> XML schema.
+     */
+    public static final String ELEMENT_NULL_REF    = "null";
+    public static final String ELEMENT_INSTANCE    = "instance";
+    public static final String ELEMENT_REF         = "ref";
+        
+    
+    private MetamodelHelper _model;
+    
+    public JSONEncoder(MetamodelImpl model) {
+        _model = new MetamodelHelper(model);
+    }
+    
+    /**
+     * Encodes the given managed instance into a new XML element as a child of the given parent node.
+     * 
+     * @param sm a managed instance, can be null.
+     * @param parent the parent node to which the new node be attached.
+     */
+    public StringBuilder encode(final OpenJPAStateManager sm) {
+        return encode(sm, new HashSet<OpenJPAStateManager>(), 0, false);
+    }
+    StringBuilder indent(StringBuilder buf, int indent) {
+        if (indent <= 0)
+            return buf;
+        char[] spaces = new char[indent*4];
+        Arrays.fill(spaces, ' ');
+        buf.insert(0, spaces);
+        return buf;
+    }
+    StringBuilder end(StringBuilder buf, char ch, int indent) {
+        char[] spaces = new char[indent*4];
+        Arrays.fill(spaces, ' ');
+        return buf.append("\r\n").append(spaces).append(ch);
+    }
+    
+    /**
+     * Encodes the closure of a persistent instance into a XML element.
+     * 
+     * @param sm the managed instance to be encoded. Can be null.
+     * @param parent the parent XML element to which the new XML element be added. Must not be null. Must be
+     * owned by a document. 
+     * @param visited the persistent instances that had been encoded already. Must not be null or immutable.
+     * 
+     * @return the new element. The element has been appended as a child to the given parent in this method.  
+     */
+    private StringBuilder encode(final OpenJPAStateManager sm, final Set<OpenJPAStateManager> visited, 
+        int indent, boolean indentPara) {
+        if (visited == null) {
+            throw new IllegalArgumentException("null closure for encoder");
+        }
+        StringBuilder root =  indent(new StringBuilder("{"), indentPara ? indent : 0);
+        if (sm == null) {
+            return root.append("null}");
+        }
+        boolean ref = !visited.add(sm);
+        if (ref) {
+            return indent(root.append(quoted("$ref")).append(": ").append(ior(sm)).append('}'), 
+                indentPara ? indent : 0);
+        } else {
+            indent(root.append(quoted("$id")).append(": ").append(ior(sm)), indentPara ? indent : 0);
+        }
+        
+        StringBuilder child = new StringBuilder();
+        BitSet loaded = sm.getLoaded();
+        StoreContext ctx = (StoreContext)sm.getGenericContext();
+        List<Attribute<?, ?>> attrs = _model.getAttributesInOrder(sm.getMetaData());
+        for (int i = 0; i < attrs.size(); child = new StringBuilder(), i++) {
+            FieldMetaData fmd = ((Members.Member<?, ?>) attrs.get(i)).fmd;
+            if (!loaded.get(fmd.getIndex())) 
+                continue;
+            Object value = sm.fetch(fmd.getIndex());
+            child.append(quoted(fmd.getName())).append(": ");
+            switch (fmd.getDeclaredTypeCode()) {
+                case JavaTypes.BOOLEAN:
+                case JavaTypes.BYTE:
+                case JavaTypes.CHAR:
+                case JavaTypes.DOUBLE:
+                case JavaTypes.FLOAT:
+                case JavaTypes.INT:
+                case JavaTypes.LONG:
+                case JavaTypes.SHORT:
+
+                case JavaTypes.BOOLEAN_OBJ:
+                case JavaTypes.BYTE_OBJ:
+                case JavaTypes.CHAR_OBJ:
+                case JavaTypes.DOUBLE_OBJ:
+                case JavaTypes.FLOAT_OBJ:
+                case JavaTypes.INT_OBJ:
+                case JavaTypes.LONG_OBJ:
+                case JavaTypes.SHORT_OBJ:
+
+                case JavaTypes.BIGDECIMAL:
+                case JavaTypes.BIGINTEGER:
+                case JavaTypes.DATE:
+                case JavaTypes.NUMBER:
+                case JavaTypes.CALENDAR:
+                case JavaTypes.LOCALE:
+                case JavaTypes.STRING:
+                case JavaTypes.ENUM:
+                         child.append(quoted(value));
+                break;
+                
+                case JavaTypes.PC:
+                    if (value == null) {
+                        child.append("null");
+                    } else {
+                        child.append(encode(ctx.getStateManager(value), visited, indent+1, false));
+                    }
+                    break;
+                    
+                case JavaTypes.ARRAY:
+                    Object[] values = (Object[])value;
+                    value = Arrays.asList(values);
+                // no break;
+                case JavaTypes.COLLECTION:
+                    if (value == null) {
+                        child.append("null");
+                        break;
+                    }
+                    child.append("[");
+                    Collection<?> members = (Collection<?>)value;
+                    boolean basic = fmd.getElement().getTypeMetaData() == null;
+                    int k = 0;
+                    for (Object o : members) {
+                        child.append("\r\n");
+                        if (o == null) {
+                            child.append(indent(new StringBuilder("null"), indent+1)); 
+                        } else {
+                            if (basic) {
+                                child.append(indent(new StringBuilder(quoted(o)), indent+1));
+                            } else {
+                                child.append(encode(ctx.getStateManager(o), visited, indent+1, true));
+                            }
+                        }
+                    }
+                    end(child, ']', indent+1);
+                    break;
+                case JavaTypes.MAP:
+                    if (value == null) {
+                        child.append("null");
+                        break;
+                    }
+                    child.append("[");
+                    Set<Map.Entry> entries = ((Map)value).entrySet();
+                    boolean basicKey   = fmd.getElement().getTypeMetaData() == null;
+                    boolean basicValue = fmd.getValue().getTypeMetaData() == null;
+                    for (Map.Entry<?,?> e : entries) {
+                        if (e.getKey() == null) {
+                            child.append("null:");
+                        } else {
+                            if (basicKey) {
+                                child.append(quoted(e.getKey())).append(":");
+                            } else {
+                                child.append(encode(ctx.getStateManager(e.getKey()), visited, indent+1, true));
+                            }
+                        }
+                        if (e.getValue() == null) {
+                            child.append("null");
+                        } else {
+                            if (basicValue) {
+                                child.append(quoted(e.getValue()));
+                            } else {
+                                child.append(encode(ctx.getStateManager(e.getValue()), visited, indent+1, false));
+                            }
+                        }
+                    }
+                    break;
+                    
+                case JavaTypes.INPUT_STREAM:
+                case JavaTypes.INPUT_READER:
+                    child = new StringBuilder(fmd.getName());
+                    if (value == null) {
+                        child.append("null");
+                    } else { 
+                        child.append(streamToString(value));
+                    }
+                    break;
+                    
+                case JavaTypes.PC_UNTYPED:
+                case JavaTypes.OBJECT:
+                case JavaTypes.OID:
+                    System.err.println("Not handled " + fmd.getName() + " of type " + fmd.getDeclaredType());
+            }
+            
+            if (child != null) {
+                root.append("\r\n");
+                root.append(indent(child, indent+1));
+                if (loaded.length()-1 != i)
+                    root.append(",");
+           }
+        }
+        return end(root, '}', indent);
+    }
+    
+    
+    String ior(OpenJPAStateManager sm) {
+        return quoted(typeOf(sm)+"-"+sm.getObjectId().toString());
+    }
+    
+    String typeOf(OpenJPAStateManager sm) {
+        return sm.getMetaData().getDescribedType().getSimpleName();
+    }
+    
+    String typeOf(Class<?> cls) {
+        return cls.getSimpleName();
+    }
+    
+    String typeOf(ClassMetaData meta) {
+        return meta.getDescribedType().getSimpleName();
+    }
+    
+    String typeOf(ValueMetaData vm) {
+        if (vm.getTypeMetaData() == null)
+            return typeOf(vm.getType()); 
+        return typeOf(vm.getTypeMetaData());
+    }
+    
+    String typeOf(FieldMetaData fmd) {
+        return fmd.getType().getSimpleName();
+    }
+    
+    
+    /**
+     * Convert the given stream (either an InutStream or a Reader) to a String
+     * to be included in CDATA section of a XML document.
+     * 
+     * @param value the field value to be converted. Can not be null 
+     * @return
+     */
+    String streamToString(Object value) {
+        Reader reader = null;
+        if (value instanceof InputStream) {
+            reader = new BufferedReader(new InputStreamReader((InputStream)value));
+        } else if (value instanceof Reader) {
+            reader = (Reader)value;
+        } else {
+            throw new RuntimeException();
+        }
+        CharArrayWriter writer = new CharArrayWriter();
+        try {
+            for (int c; (c = reader.read()) != -1;) {
+                writer.write(c);
+            }
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+        return writer.toString();
+    }
+    
+    String quoted(Object o) {
+        if (o == null) return "null";
+        if (o instanceof Number)
+            return o.toString();
+        return "\"" + o.toString() + "\"";
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.metamodel.Attribute;
+import javax.persistence.metamodel.EntityType;
+import javax.persistence.metamodel.ManagedType;
+import javax.persistence.metamodel.Metamodel;
+import javax.persistence.metamodel.SingularAttribute;
+
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.persistence.meta.MetamodelImpl;
+
+/**
+ * @author Pinaki Poddar
+ *
+ */
+public class MetamodelHelper {
+    private MetamodelImpl _model;
+    private Map<ManagedType<?>, List<Attribute<?, ?>>> _attrs = new HashMap<ManagedType<?>, List<Attribute<?,?>>>();
+    
+    public MetamodelHelper(MetamodelImpl model) {
+        _model = model;
+    }
+    
+    public List<Attribute<?,?>> getAttributesInOrder(Class<?> cls) {
+        return getAttributesInOrder(_model.managedType(cls));
+    }
+    
+    public List<Attribute<?,?>> getAttributesInOrder(ClassMetaData meta) {
+        return getAttributesInOrder(meta.getDescribedType());
+    }
+    
+    /**
+     * Gets the attributes of the given type in defined order.
+     * @param type
+     * @return
+     */
+    public List<Attribute<?,?>> getAttributesInOrder(ManagedType<?> type) {
+        List<Attribute<?,?>> attrs = _attrs.get(type);
+        if (attrs != null)
+            return attrs;
+        List<Attribute<?,?>> list = new ArrayList<Attribute<?,?>>(type.getAttributes());
+        Collections.sort(list, new AttributeComparator());
+        _attrs.put(type, list);
+        return list;
+    }
+
+    public static boolean isId(Attribute<?,?> a) {
+        if (a instanceof SingularAttribute)
+            return ((SingularAttribute<?,?>)a).isId();
+        return false;
+    }
+    
+    public static boolean isVersion(Attribute<?,?> a) {
+        if (a instanceof SingularAttribute)
+            return ((SingularAttribute<?,?>)a).isVersion();
+        return false;
+    }
+
+    public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
+        if (isId(attr))
+            return 0;
+        if (isVersion(attr))
+            return 1;
+        
+      switch (attr.getPersistentAttributeType()) {
+      case BASIC : 
+      case EMBEDDED:
+          return 2;
+      case ONE_TO_ONE: 
+      case MANY_TO_ONE:
+          return 3;
+      case ONE_TO_MANY:
+      case MANY_TO_MANY:
+      case ELEMENT_COLLECTION: return 4;
+      default: return 5;
+      }
+    }
+    
+    /**
+     * Compares attribute by their qualification.
+     * Identity 
+     * Version
+     * Basic
+     * Singular association
+     * Plural association
+     *
+     */
+    public static class AttributeComparator implements Comparator<Attribute<?,?>> {
+        @Override
+        public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
+            Integer t1 = getAttributeTypeCode(a1);
+            Integer t2 = getAttributeTypeCode(a2);
+            if (t1.equals(t2)) {
+                return a1.getName().compareTo(a2.getName());
+            } else {
+                return t1.compareTo(t2);
+            }
+        }
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A request from a remote client to a server to do something.
+ * The request arrives as stream of bytes from a remote location
+ * and if the server can interpret the protocol from the stream, 
+ * then  make a concrete request object.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public interface Request extends Serializable {
+    /**
+     * Get the HTTP verb such as GET, POST
+     * 
+     * @return uppercase string
+     */
+    String getMethod();
+    
+    /**
+     * Get the first path segment as intended persistence action such as <code>find</code> or <code>query</code>
+     *   
+     * @return lowercase action name. Can be empty.
+     */
+    String getAction();
+    
+    /**
+     * Get the protocol such as HTTP/1.1
+     * 
+     * @return upper-case string
+     */
+    String getProtocol();
+    
+    /**
+     * Get the body, if any.
+     * 
+     * @return body of the request. null if no body.
+     */
+    String getBody();
+    
+    /**
+     * Get the headers indexed by the keys.
+     * Header values are list of Strings.
+     * 
+     * @return empty map if there is no header
+     */
+    Map<String, List<String>> getHeaders();
+    
+    /**
+     * Get the header values for the given key.
+     * @param key a key
+     * @return null if no header value for the given key
+     */
+    List<String> getHeader(String key);
+    
+    /**
+     * Affirm if the the given qualifier is available in this request.
+     *  
+     * @param key case-sensitive qualifier
+     * @return true if the key is present.
+     */
+    boolean hasQualifier(String key);
+    
+    /**
+     * Gets the value for the given qualifier key.
+     * 
+     * @param key case-sensitive qualifier
+     * @return value of the qualifier. null if the key is absent.
+     */
+    String getQualifier(String key);
+    
+    /**
+     * Get all the qualifiers available in this request.
+     * 
+     * @return key-value pairs of the qualifiers. Empty map if no qualifier is present.
+     */
+    Map<String,String> getQualifiers();
+    
+    
+    /**
+     * Affirm if the the given parameter is available in this request.
+     *  
+     * @param key case-sensitive parameter
+     * @return true if the key is present.
+     */
+    boolean hasParameter(String key);
+    
+    
+    /**
+     * Gets the value for the given parameter key.
+     * 
+     * @param key case-sensitive parameter
+     * @return value of the parameter. null if the key is absent.
+     */
+    String getParameter(String key);
+    
+    /**
+     * Get all the parameters available in this request.
+     * 
+     * @return key-value pairs of the parameters. Empty map if no parameter is present.
+     */
+    Map<String,String> getParameters();
+    
+    /**
+     * Parse the request represented as a list of strings.
+     *
+     * @param lines each line of the request.
+     * @throws IOException
+     */
+    void read(List<String> lines) throws IOException;
+    
+    Response process(ServerContext server, OutputStream stream) throws Exception;
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A factory to create a specific type of request.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class RequestFactory {
+    private final String _protocol;
+    private static Map<String, RequestFactory> _registered = new HashMap<String, RequestFactory>();
+    static {
+        _registered.put("HTTP/1.0", new RequestFactory("HTTP/1.0"));
+        _registered.put("HTTP/1.1", new RequestFactory("HTTP/1.1"));
+    }
+    
+    public static void register(String protocol, RequestFactory factory) {
+        _registered.put(protocol, factory);
+    }
+    
+    private RequestFactory(String proto) {
+        _protocol = proto;
+    }
+    
+    public static RequestFactory getFactory(String protocol) {
+        return _registered.get(protocol);
+    }
+    
+    Request createRequest(String method) {
+        JESTRequest request = null;
+        if ("GET".equalsIgnoreCase(method)) {
+            request = new GETRequest();
+        } else {
+            throw new UnsupportedOperationException();
+        }
+        request.setProtocol(_protocol);
+        request.setMethod(method.toUpperCase());
+        return request;
+        
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityNotFoundException;
+
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.kernel.StoreContext;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+import org.apache.openjpa.util.ApplicationIds;
+
+/**
+ * Handles a request from a remote client.
+ * Reads the socket data.
+ * Populates the request.
+ * Determines the processor based on request method and action.
+ * Delegates to the processor.
+ * Processor generates the response.
+ * Writes the response to the stream.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class RequestHandler implements Callable<Void> {
+    private static final int SPACE = ' ';
+    private final Socket _socket;
+    private final ServerContext _server;
+    private final Log _log;
+    private static final Localizer _loc = Localizer.forPackage(RequestHandler.class);
+    
+    public RequestHandler(Socket socket, ServerContext server) {
+        _socket = socket;
+        _server = server;
+        _log = _server.getLog();
+    }
+    
+    public Void call() throws Exception {
+        Request request = null;
+        Response response = null;
+        try {
+            request = readRequest(_socket.getInputStream());
+            response = request.process(_server, _socket.getOutputStream());
+        } catch (Exception e) {
+            response = new ErrorResponse(request, _server, e, HttpURLConnection.HTTP_INTERNAL_ERROR, 
+                _socket.getOutputStream());
+        }
+        response.writeOut();
+        return null;
+    }
+    
+
+
+    /**
+     * Reads the given CR-LF delimited stream. 
+     * The first line is scanned for the method (first space-delimited String) and protocol (the last
+     * space-delimited String). Accordingly a request is created and rest of the input stream content
+     * is parsed by the request itself. 
+     * 
+     * @param input
+     * @throws IOException
+     */
+    public Request readRequest(InputStream input) throws IOException {
+        if (_log.isTraceEnabled())
+            _log.trace("Reading request from the input stream ");
+        
+        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
+        String status = reader.readLine();
+        if (_log.isInfoEnabled())
+            _log.info("Status Line [" + status + "]");
+        int spaceFirst = status.indexOf(SPACE);
+        if (spaceFirst == -1) 
+            throw new IOException("HTTP Method could not be determined from [" + status + "]");
+        int spaceLast = status.lastIndexOf(SPACE);
+        if (spaceLast == -1) 
+            throw new IOException("HTTP Protocol could not be determined from [" + status + "]");
+        String method = status.substring(0, spaceFirst);
+        String protocol = status.substring(spaceLast+1);
+        String path = status.substring(spaceFirst+1, spaceLast);
+        Request request = RequestFactory.getFactory(protocol).createRequest(method);
+        List<String> lines = new ArrayList<String>();
+        if (path.equals("/")) {
+            lines.add(path);
+        } else {
+            lines = readlines(reader);
+            lines.add(0, path);
+        }
+        if (lines.isEmpty()) {
+            throw new IOException("No CR-LF delimited lines could be read from " + input);
+        }
+        request.read(lines);
+        return request;
+    }
+    
+
+    List<String> readlines(BufferedReader reader) throws IOException {
+        List<String> buffers = new ArrayList<String>();
+        String line;
+        while ((line = reader.readLine()) != null && line.length() > 0) {
+            buffers.add(line);
+        }
+        return buffers;
+    }
+    
+    
+    public String toString() {
+        return _socket.getInetAddress()+":"+_socket.getPort();
+    }    
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+import javax.imageio.ImageIO;
+
+/**
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class ResourceResponse extends AbstractResponse {
+    private final InputStream _in;
+    private final String _mimeType;
+    public ResourceResponse(Request request, ServerContext ctx, InputStream resource, String mimeType,
+        OutputStream out) throws Exception {
+        super(request, ctx, out);
+        _in = resource;
+        _mimeType = mimeType;
+    }
+
+    public void writeOut() throws Exception {
+        print(_request.getProtocol()); println("200 OK");
+        printHeader("Connection",  "close");
+        printHeader("Content-Type", _mimeType, "charset=UTF-8");
+        println();
+        for (int c = 0; (c = _in.read()) != -1;) {
+           print((char)c);
+        }
+        _in.close();
+        close();
+    }
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.Serializable;
+
+/**
+ * @author Pinaki Poddar
+ *
+ */
+public interface Response extends Serializable {
+//    void setHeader(String key, String value);
+    void writeOut() throws Exception;
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java?rev=1028093&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java (added)
+++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java Wed Oct 27 20:42:44 2010
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.openjpa.persistence.jest;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.persistence.EntityManagerFactory;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+import org.apache.openjpa.lib.conf.Configurable;
+import org.apache.openjpa.lib.conf.Configuration;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.util.Localizer;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.meta.MetaDataRepository;
+import org.apache.openjpa.persistence.EntityManagerFactoryImpl;
+
+
+/**
+ * A server running on an independent thread that allows a remote, language-neutral client to access OpenJPA runtime.
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+public class Server implements ServerContext, Configurable, Runnable {
+    private ServerSocket _listenSocket;
+    protected ExecutorService _executors;
+    public final static int DEFAULT_PORT = 6789;
+    protected int _port = DEFAULT_PORT;
+    protected int _range = 1;
+    protected String _format = "xml";
+    protected Log _log;
+    protected Thread _thread;
+    private EntityManagerFactoryImpl _ctx;
+    private static Localizer _loc = Localizer.forPackage(Server.class);
+    
+    /**
+     * Sets the persistence unit context in which this server will serve requests.
+     * The context must be set before operation.
+     * 
+     * @param emf an implementation of OpenJPA Persistence Unit. Must not be null.
+     */
+    public void setContext(EntityManagerFactoryImpl emf) {
+        if (emf == null)
+            throw new NullPointerException();
+        _ctx = emf;
+    }
+    
+    /**
+     * Gets the persistence unit context in which this server serves requests.
+     * 
+     * @param emf an implementation of OpenJPA Persistence Unit. 
+     */
+    public EntityManagerFactoryImpl getPersistenceUnit() {
+        return _ctx;
+    }
+    
+    public Log getLog() {
+        return _log;
+    }
+    
+    /**
+     * Start the server in a daemon thread.
+     * This method is idempotent.
+     * 
+     * @return true if the server has started by this call or already running. 
+     */
+    public synchronized boolean start() {
+        try {
+            if (_thread != null)
+                return true;
+            if (createServerSocket()) {
+                _thread = new Thread(this);
+                _thread.setDaemon(true);
+                _thread.start();
+                return true;
+            }
+            return false;
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            return false;
+        }
+    }
+    
+    /**
+     * Stops the server.
+     */
+    public synchronized void stop() {
+        _thread.interrupt();
+        _thread = null;
+        _executors.shutdownNow();
+    }
+    
+    /**
+     * Sets the port in which the server will listen.
+     * 
+     * @param port a positive integer.
+     */
+    public void setPort(int port) {
+        _port = port;
+    }
+    
+    /**
+     * Gets the current port.
+     * 
+     * @return the port number. Defaults to default HTTP port.
+     */
+    public int getPort() {
+        return _port;
+    }
+    
+    /**
+     * Sets the range of ports the server will attempt at start.
+     * 
+     * @param range a positive integer.
+     */
+    public void setRange(int range) {
+        if (range > 0) 
+            _range = range;
+    }
+    
+    public void setFormat(String format) {
+        _format = format;
+    }
+    
+    public String getFormat() {
+        return _format;
+    }
+    
+    /**
+     * Sets the range of ports the server will attempt at start.
+     * @return  a positive integer. Defaults to 1.
+     */
+    public int getRange() {
+        return _range;
+    }
+    
+    public void run() {
+        _log.info(_loc.get("server-starting", this));
+        
+        _executors = Executors.newCachedThreadPool();
+        while (!Thread.interrupted()) {
+            try {
+                Socket socket = _listenSocket.accept();
+                if (_log.isTraceEnabled())
+                    _log.trace(_loc.get("server-request", socket));
+                RequestHandler request = new RequestHandler(socket, this); 
+                _executors.submit(request);
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+    
+    private boolean createServerSocket() {
+        int p = _port;
+        int p2 = p + _range;
+        Exception error = null;
+        for (; _listenSocket == null && p < p2; ) {
+            try {
+                _listenSocket = new ServerSocket(p);
+            } catch (IOException ex) {
+                p++;
+                error = ex;
+            }
+        }
+        if (_listenSocket != null) {
+            if (p != _port) {
+                _port = p;
+                _log.warn(_loc.get("server-reconfigured", _port));
+            }
+        } else {
+            if (error != null) {
+                _log.warn(_loc.get("server-failed", this, _port, error));
+            }
+        }
+        return _listenSocket != null;
+    }
+    
+    // Configurable contract
+    public void setConfiguration(Configuration conf) {
+        _log = conf.getLog("Remote");
+    }
+
+    public void startConfiguration() {
+    }
+
+    public void endConfiguration() {
+    }
+    
+    
+    // Server side utilities 
+    
+    /**
+     * Resolves the given alias to a persistent class meta data.
+     * 
+     * @exception if no meta data available for the given alias 
+     */
+    public ClassMetaData resolve(String alias) {
+        MetaDataRepository repos = _ctx.getConfiguration().getMetaDataRepositoryInstance();
+        return repos.getMetaData(alias, Thread.currentThread().getContextClassLoader(), true);
+    }
+    
+    public String toString() {
+        if (_listenSocket == null) return "JEST Server [not strated]";
+        return "JEST Server " + _listenSocket.getInetAddress()+":"+_listenSocket.getLocalPort();
+    }
+
+}

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Re: svn commit: r1028093 [1/2] - JEST...

Posted by Donald Woods <dw...@apache.org>.
Was thinking of a 2.1.0 release before the end of this year....


-Donald


On 10/27/10 6:32 PM, Jeremy Bauer wrote:
> Ditto on the concerns.  Maybe this would be a good opportunity to discuss a
> 2.1 release date?  That may help guide whether we decide to include large
> new features in 2.1 or hold off until 2.x.x and give them more time to brew.
> 
> -Jeremy
> 
> On Wed, Oct 27, 2010 at 5:11 PM, Kevin Sutter <kw...@gmail.com> wrote:
> 
>> I have to admit that I was having the same questions/concerns that Donald
>> just raised.  I thought we were still in the discussion and experimentation
>> phase of this REST work.  Is it really ready for prime time?
>>
>> Kevin
>>
>> On Wed, Oct 27, 2010 at 4:58 PM, Donald Woods <dw...@apache.org> wrote:
>>
>>> Is this really ready to drop into trunk?
>>> And do we really want it in the base openjpa.jar?
>>>
>>>
>>> -Donald
>>>
>>>
>>> On 10/27/10 4:42 PM, ppoddar@apache.org wrote:
>>>> Author: ppoddar
>>>> Date: Wed Oct 27 20:42:44 2010
>>>> New Revision: 1028093
>>>>
>>>> URL: http://svn.apache.org/viewvc?rev=1028093&view=rev
>>>> Log:
>>>> OPENJPA-1851: First version of JEST (REST on OpenJPA)
>>>>
>>>> Added:
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ServerContext.java
>>>   (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/XMLEncoder.java
>>>   (with props)
>>>> Modified:
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
>>>>
>>>> Modified:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java?rev=1028093&r1=1028092&r2=1028093&view=diff
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
>>> (original)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -45,10 +45,10 @@ import org.apache.openjpa.lib.util.Close
>>>>  import org.apache.openjpa.lib.util.Localizer;
>>>>  import org.apache.openjpa.persistence.criteria.CriteriaBuilderImpl;
>>>>  import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
>>>> +import org.apache.openjpa.persistence.jest.Server;
>>>>  import org.apache.openjpa.persistence.meta.MetamodelImpl;
>>>>  import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
>>>>  import org.apache.openjpa.persistence.query.QueryBuilderImpl;
>>>> -import org.apache.openjpa.util.UserException;
>>>>
>>>>  /**
>>>>   * Implementation of {@link EntityManagerFactory} that acts as a
>>>> @@ -70,7 +70,8 @@ public class EntityManagerFactoryImpl
>>>>      private transient StoreCache _cache = null;
>>>>      private transient QueryResultCache _queryCache = null;
>>>>      private transient MetamodelImpl _metaModel;
>>>> -
>>>> +    private transient Server _remoteAccess = null;
>>>> +
>>>>      /**
>>>>       * Default constructor provided for auto-instantiation.
>>>>       */
>>>> @@ -93,10 +94,11 @@ public class EntityManagerFactoryImpl
>>>>
>>>>      /**
>>>>       * Delegate must be provided before use.
>>>> +     * Configures for Remote Access, if appropriate.
>>>>       */
>>>>      public void setBrokerFactory(BrokerFactory factory) {
>>>> -        _factory = new DelegatingBrokerFactory(factory,
>>>> -            PersistenceExceptions.TRANSLATOR);
>>>> +        _factory = new DelegatingBrokerFactory(factory,
>>> PersistenceExceptions.TRANSLATOR);
>>>> +        configureRemoteAccess(getConfiguration());
>>>>      }
>>>>
>>>>      public OpenJPAConfiguration getConfiguration() {
>>>> @@ -271,6 +273,10 @@ public class EntityManagerFactoryImpl
>>>>          if (log.isTraceEnabled()) {
>>>>              log.trace(this + ".close() invoked.");
>>>>          }
>>>> +        if (_remoteAccess != null) {
>>>> +            _remoteAccess.stop();
>>>> +            _remoteAccess = null;
>>>> +        }
>>>>          _factory.close();
>>>>      }
>>>>
>>>> @@ -396,4 +402,40 @@ public class EntityManagerFactoryImpl
>>>>              }
>>>>          }
>>>>      }
>>>> +
>>>> +    /**
>>>> +     * Configures this unit for remote access.
>>>> +     */
>>>> +    protected void configureRemoteAccess(OpenJPAConfiguration conf) {
>>>> +        Value value = conf.getValue("RemoteAccess");
>>>> +        if (value == null) {
>>>> +            return;
>>>> +        }
>>>> +        String props = value.getString();
>>>> +        if (props == null)
>>>> +            return;
>>>> +        try {
>>>> +            _remoteAccess = new Server();
>>>> +            _remoteAccess.setContext(this);
>>>> +            Configurations.configureInstance(_remoteAccess, conf,
>>> props);
>>>> +            conf.removeValue(value);
>>>> +            if (!_remoteAccess.start()) {
>>>> +                _remoteAccess = null;
>>>> +            }
>>>> +        } catch (Exception ex) {
>>>> +            Log log =
>>> _factory.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
>>>> +            if (log != null) {
>>>> +                log.error(_loc.get("remote-start-error"), ex);
>>>> +            }
>>>> +        }
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Affirms if this unit is accessible remotely.
>>>> +     */
>>>> +    public boolean allowsRemoteAccess() {
>>>> +        return _remoteAccess != null;
>>>> +    }
>>>> +
>>>> +
>>>>  }
>>>>
>>>> Modified:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java?rev=1028093&r1=1028092&r2=1028093&view=diff
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
>>> (original)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -188,6 +188,7 @@ public class PersistenceProductDerivatio
>>>>          conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(),
>>>  PersistenceMetaDataFactory.class.getName());
>>>>
>>>>          conf.addValue(new EntityManagerFactoryValue());
>>>> +        conf.addString("RemoteAccess");
>>>>
>>>>          conf.readLockLevel.setAlias("optimistic",
>>> String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
>>>>          conf.readLockLevel.setAlias("optimistic-force-increment",
>> String
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,77 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.OutputStream;
>>>> +import java.io.PrintStream;
>>>> +
>>>> +/**
>>>> + * Abstract implementation of a stream-based response.
>>>> + * Every response is result of a {@linkplain Request} and operates
>>> within a {@linkplain ServerContext}.
>>>> + * This fact is enforced by the constructor argument of an abstract
>>> response.
>>>> + * <p>
>>>> + * Besides, this implementation provides common utility to write HTTP
>>> response or extract useful information
>>>> + * from the request.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public abstract class AbstractResponse extends PrintStream implements
>>> Response {
>>>> +    protected final Request _request;
>>>> +    protected final ServerContext _ctx;
>>>> +
>>>> +    /**
>>>> +     * Construct a response for the given request and server context.
>>>> +     *
>>>> +     * @param request the request for this response. Can be null if
>> the
>>> response is for server error.
>>>> +     * @param ctx the processing context
>>>> +     * @param out the output stream where the response is targeted.
>>>> +     */
>>>> +    protected AbstractResponse(Request request, ServerContext ctx,
>>> OutputStream out) {
>>>> +        super(out);
>>>> +        _request = request;
>>>> +        _ctx = ctx;
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Write a HTTP header to the stream.
>>>> +     * <br>
>>>> +     * The format of HTTP header is <code>key: [value]* NEWLINE</code>
>>>> +     *
>>>> +     * @param key the key of the header
>>>> +     * @param values one of more value of the header fields.
>>>> +     */
>>>> +    protected void printHeader(String key, String...values) {
>>>> +        if (key == null)
>>>> +            return;
>>>> +        print(key);
>>>> +        print(" :");
>>>> +        if (values == null || values.length == 0)
>>>> +            return;
>>>> +        int n = values.length-1;
>>>> +        for (int i = 0; i < n-1; i++) {
>>>> +            print(values[i]);
>>>> +            print(";");
>>>> +        }
>>>> +        println(values[n]);
>>>> +    }
>>>> +
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,68 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.OutputStream;
>>>> +import java.net.HttpURLConnection;
>>>> +
>>>> +/**
>>>> + * A HTTP response for something gone wrong.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class ErrorResponse extends AbstractResponse {
>>>> +    private final Exception _error;
>>>> +    private final int _code;
>>>> +    /**
>>>> +     * Construct a response to describe a error.
>>>> +     *
>>>> +     * @param request a request that produced this response. VCan be
>>> null to denote that the request can not
>>>> +     * be created.
>>>> +     * @param ctx the processing context
>>>> +     * @param ex the error
>>>> +     * @param code HTTP error code
>>>> +     * @param out the stream where the response is written
>>>> +     *
>>>> +     */
>>>> +    public ErrorResponse(Request request, ServerContext ctx, Exception
>>> ex, int code, OutputStream out)  {
>>>> +        super(request, ctx, out);
>>>> +        _error = ex;
>>>> +        _code = code;
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Writes the response.
>>>> +     * The response is always a HTTP response with the error stack
>>> trace.
>>>> +     */
>>>> +    public void writeOut() throws Exception {
>>>> +        println("HTTP/1.1"); print(" " + _code); println("Error");
>>>> +        printHeader("Connection",  "close");
>>>> +        printHeader("Content-Type", "text/html", "charset=UTF-8");
>>>> +        println();
>>>> +        println("<html><body><pre>");
>>>> +        _error.printStackTrace(this);
>>>> +        println("Response from JEST");
>>>> +
>>>> +        println("</pre></body></html>");
>>>> +        close();
>>>> +    }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,147 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.InputStream;
>>>> +import java.io.OutputStream;
>>>> +import java.net.HttpURLConnection;
>>>> +import java.util.Iterator;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +
>>>> +import javax.persistence.EntityManager;
>>>> +import javax.persistence.EntityNotFoundException;
>>>> +import javax.persistence.Query;
>>>> +
>>>> +import org.apache.openjpa.kernel.OpenJPAStateManager;
>>>> +import org.apache.openjpa.kernel.StoreContext;
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.persistence.JPAFacadeHelper;
>>>> +import org.apache.openjpa.util.ApplicationIds;
>>>> +import org.apache.openjpa.util.ObjectNotFoundException;
>>>> +
>>>> +/**
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class GETRequest extends JESTRequest {
>>>> +    public Response process(ServerContext server, OutputStream out)
>>> throws Exception {
>>>> +        String action = getAction();
>>>> +        try {
>>>> +            if ("find".equals(action)) {
>>>> +                return find(server, out);
>>>> +            } else {
>>>> +                return resource(server, out);
>>>> +            }
>>>> +        } catch (Exception e) {
>>>> +            return new ErrorResponse(this, server, new
>>> RuntimeException("bad action " + action),
>>>> +                HttpURLConnection.HTTP_BAD_REQUEST, out);
>>>> +        }
>>>> +    }
>>>> +
>>>> +    Response find(ServerContext server, OutputStream out)  throws
>>> Exception {
>>>> +        EntityManager em =
>>> server.getPersistenceUnit().createEntityManager();
>>>> +        Map<String, String> qualifiers = getQualifiers();
>>>> +        Map<String, String> parameters = getParameters();
>>>> +        if (parameters.size() < 2)
>>>> +            throw new IllegalArgumentException("find must have at
>> least
>>> two parameters");
>>>> +        Object[] pks = new Object[parameters.size()-1];
>>>> +        Iterator<Map.Entry<String,String>> params =
>>> parameters.entrySet().iterator();
>>>> +        String alias = null;
>>>> +        for (int i = 0; i < parameters.size(); i++) {
>>>> +            if (i == 0) {
>>>> +                alias = params.next().getKey();
>>>> +            } else {
>>>> +                pks[i-1] = params.next().getKey();
>>>> +            }
>>>> +        }
>>>> +        ClassMetaData meta = server.resolve(alias);
>>>> +        Object oid = ApplicationIds.fromPKValues(pks, meta);
>>>> +        Object pc = em.find(meta.getDescribedType(), oid);
>>>> +        if (pc != null) {
>>>> +            OpenJPAStateManager sm =
>>> ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(pc);
>>>> +            return new JESTResponse(this, server, sm, out);
>>>> +        } else {
>>>> +            return new ErrorResponse(this, server, new
>>> EntityNotFoundException("not found!"),
>>>> +                HttpURLConnection.HTTP_NOT_FOUND, out);
>>>> +        }
>>>> +    }
>>>> +
>>>> +    Response query(ServerContext server, OutputStream out)  throws
>>> Exception {
>>>> +        EntityManager em =
>>> server.getPersistenceUnit().createEntityManager();
>>>> +        Map<String, String> qualifiers = getQualifiers();
>>>> +        boolean named = isBooleanQualifier("named");
>>>> +        boolean single = isBooleanQualifier("single");
>>>> +        Map<String, String> parameters = getParameters();
>>>> +        if (parameters.size() < 1)
>>>> +            throw new IllegalArgumentException("find must have at
>> least
>>> one parameter");
>>>> +        Iterator<Map.Entry<String,String>> params =
>>> parameters.entrySet().iterator();
>>>> +        Query query = null;
>>>> +        int i = 0;
>>>> +        for (Map.Entry<String, String> param : parameters.entrySet())
>> {
>>>> +            if (i == 0) {
>>>> +                query = named ? em.createQuery(param.getKey()) :
>>> em.createNamedQuery(param.getKey());
>>>> +            } else {
>>>> +                query.setParameter(param.getKey(), param.getValue());
>>>> +            }
>>>> +        }
>>>> +        if (single) {
>>>> +            Object result = query.getSingleResult();
>>>> +            OpenJPAStateManager sm =
>>> ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(result);
>>>> +            return new JESTResponse(this, server, sm, out);
>>>> +        } else {
>>>> +            List<Object> result = query.getResultList();
>>>> +            return new ErrorResponse(this, server, new
>>> EntityNotFoundException("not found!"), 404, out);
>>>> +        }
>>>> +    }
>>>> +
>>>> +    Response resource(ServerContext server, OutputStream out)  throws
>>> Exception {
>>>> +        String resource = getAction();
>>>> +        if (resource.length() == 0)
>>>> +            resource = "index.html";
>>>> +        String mimeType = getMimeType(resource);
>>>> +//        InputStream in =
>>>
>> Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
>>>> +        InputStream in = getClass().getResourceAsStream(resource);
>>>> +        if (in == null) {
>>>> +            return new ErrorResponse(this, server, new
>>> ObjectNotFoundException(resource), 404, out);
>>>> +        }
>>>> +        if (server.getLog().isTraceEnabled())
>>>> +            server.getLog().trace("Found resource " + resource);
>>>> +        return mimeType.startsWith("image")
>>>> +          ? new ImageResponse(this, server, in, mimeType, out)
>>>> +          : new ResourceResponse(this, server, in, mimeType, out);
>>>> +    }
>>>> +
>>>> +    boolean isBooleanQualifier(String key) {
>>>> +        String q = getQualifier(key);
>>>> +        return hasQualifier(key) && (q == null || "true".equals(q));
>>>> +    }
>>>> +
>>>> +    String getMimeType(String resource) {
>>>> +        int index = resource.lastIndexOf('.');
>>>> +        String ext = (index != -1) ? resource.substring(index+1) : "";
>>>> +        if (ext.equalsIgnoreCase("html") ||
>>> ext.equalsIgnoreCase(".html")) return "text/html";
>>>> +        if (ext.equalsIgnoreCase(".png") ||
>> ext.equalsIgnoreCase(".ico")
>>> || ext.equalsIgnoreCase("jpeg"))
>>>> +            return "image/"+ext;
>>>> +        return "text/html";
>>>> +    }
>>>> +
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,61 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.InputStream;
>>>> +import java.io.OutputStream;
>>>> +import java.io.RandomAccessFile;
>>>> +
>>>> +import javax.imageio.ImageIO;
>>>> +
>>>> +/**
>>>> + * Sends an image as response.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class ImageResponse extends AbstractResponse {
>>>> +    private final InputStream _in;
>>>> +    private final String _mimeType;
>>>> +
>>>> +    public ImageResponse(Request request, ServerContext ctx,
>> InputStream
>>> resource, String mimeType,
>>>> +        OutputStream out) throws Exception {
>>>> +        super(request, ctx, out);
>>>> +        _in = resource;
>>>> +        _mimeType = mimeType;
>>>> +    }
>>>> +
>>>> +    public void writeOut() throws Exception {
>>>> +        print(_request.getProtocol()); println("200 OK");
>>>> +        printHeader("Connection",  "close");
>>>> +        printHeader("Content-Type", _mimeType);
>>>> +        println();
>>>> +        byte[] b = new byte[1024];
>>>> +        int i = 0;
>>>> +        for (int l = 0; (l = _in.read(b)) != -1;) {
>>>> +            write(b, 0, l);
>>>> +            i += l;
>>>> +        }
>>>> +        printHeader("Content-Length", ""+i);
>>>> +        _in.close();
>>>> +        close();
>>>> +    }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,386 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.IOException;
>>>> +import java.util.Arrays;
>>>> +import java.util.Collections;
>>>> +import java.util.HashMap;
>>>> +import java.util.LinkedHashMap;
>>>> +import java.util.LinkedList;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +import java.util.NoSuchElementException;
>>>> +
>>>> +/**
>>>> + * A request carries requisite data for a JPA operation to be
>> performed.
>>>> + * The request is populated by parsing an input data stream.
>>>> + *
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public abstract class JESTRequest implements Request {
>>>> +    private String _method;
>>>> +    private String _protocol;
>>>> +    private String _action;
>>>> +    private String _body;
>>>> +    private LinkedHashMap<String, String> _qualifiers = new
>>> LinkedHashMap<String, String>();
>>>> +    private LinkedHashMap<String, String> _params = new
>>> LinkedHashMap<String, String>();
>>>> +    private Map<String, List<String>> _headers = new HashMap<String,
>>> List<String>>();
>>>> +    private ParseState _state;
>>>> +    private StringBuffer buf = new StringBuffer();
>>>> +    private LinkedList<Token> _stack = new LinkedList<Token>();
>>>> +
>>>> +    public static final List<String> METHODS = Arrays.asList(new
>>> String[]{"GET","POST","PUT","DELETE"});
>>>> +
>>>> +    /**
>>>> +     * Parse States.
>>>> +     */
>>>> +    static enum ParseState {
>>>> +        INIT, ACTION, QUALIFIER_KEY, QUALIFIER_VALUE, PARAM_KEY,
>>> PARAM_VALUE, END
>>>> +    };
>>>> +
>>>> +
>>>> +    public String getMethod() {
>>>> +        return _method;
>>>> +    }
>>>> +
>>>> +    void setMethod(String method) {
>>>> +        if (_method == null) {
>>>> +            if (method != null &&
>>> METHODS.contains(method.toUpperCase())) {
>>>> +                _method = method.toUpperCase();
>>>> +            } else {
>>>> +                throw new IllegalArgumentException("Unsupported method
>> "
>>> + method);
>>>> +            }
>>>> +        } else if (!_method.equalsIgnoreCase(method)) {
>>>> +            throw new IllegalStateException("Method can not be changed
>>> to [" + method + "]. " +
>>>> +                "Current method [" + _method + "]");
>>>> +        }
>>>> +    }
>>>> +
>>>> +    public String getProtocol() {
>>>> +        return _protocol == null ? "HTTP/1.1" : _protocol;
>>>> +    }
>>>> +
>>>> +    void setProtocol(String protocol) {
>>>> +        if (_protocol == null) {
>>>> +            if (protocol != null &&
>>> protocol.toUpperCase().startsWith("HTTP")) {
>>>> +                _protocol = protocol.toUpperCase();
>>>> +            } else {
>>>> +                throw new IllegalArgumentException("Unsupported
>> protocol
>>> " + protocol);
>>>> +            }
>>>> +        } else if (!_protocol.equalsIgnoreCase(protocol)) {
>>>> +            throw new IllegalStateException("Protocol can not be
>> changed
>>> to [" + protocol + "]. " +
>>>> +                "Current protocol [" + _protocol + "]");
>>>> +        }
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Sets an action. Once set, an action can not be modified.
>>>> +     *
>>>> +     * @param action
>>>> +     */
>>>> +    private void setAction(String action) {
>>>> +        if (_action == null) {
>>>> +            _action = action;
>>>> +        } else if (!_action.equals(action)) {
>>>> +            throw new IllegalStateException("Action can not be [" +
>>> action + "]. Already set to [" + _action + "]");
>>>> +        }
>>>> +    }
>>>> +
>>>> +    public String getAction() {
>>>> +        return _action == null ? "" : _action;
>>>> +    }
>>>> +
>>>> +    public String getBody() {
>>>> +        return _body;
>>>> +    }
>>>> +
>>>> +    private void setQualifier(String key, String value) {
>>>> +        _qualifiers.put(key, value);
>>>> +    }
>>>> +
>>>> +    public String getQualifier(String key) {
>>>> +        return _qualifiers.get(key);
>>>> +    }
>>>> +
>>>> +    public Map<String, String> getQualifiers() {
>>>> +        return Collections.unmodifiableMap(_qualifiers);
>>>> +    }
>>>> +
>>>> +    public boolean hasQualifier(String key) {
>>>> +        return _qualifiers.containsKey(key);
>>>> +    }
>>>> +
>>>> +    private void setParameter(String key, String value) {
>>>> +        _params.put(key, value);
>>>> +    }
>>>> +
>>>> +    public String getParameter(String key) {
>>>> +        return _params.get(key);
>>>> +    }
>>>> +
>>>> +    public boolean hasParameter(String key) {
>>>> +        return _params.containsKey(key);
>>>> +    }
>>>> +
>>>> +    public Map<String, String> getParameters() {
>>>> +        return Collections.unmodifiableMap(_params);
>>>> +    }
>>>> +
>>>> +    public Map.Entry<String, String> getParameter(int n) {
>>>> +        if (n >= _params.size())
>>>> +            throw new NoSuchElementException("Index " + n + " size " +
>>> _params.size());
>>>> +        int i = 0;
>>>> +        for (Map.Entry<String, String> entry : _params.entrySet()) {
>>>> +            if (i == n) {
>>>> +                return entry;
>>>> +            }
>>>> +            i++;
>>>> +        }
>>>> +        return null;
>>>> +    }
>>>> +
>>>> +    public Map<String, List<String>> getHeaders() {
>>>> +        return Collections.unmodifiableMap(_headers);
>>>> +    }
>>>> +
>>>> +    public List<String> getHeader(String key) {
>>>> +        return _headers.get(key);
>>>> +    }
>>>> +
>>>> +
>>>> +    public void read(List<String> lines) throws IOException {
>>>> +        parse(lines.get(0));
>>>> +        int i = 1;
>>>> +        for (; i < lines.size(); i++) {
>>>> +            String line = lines.get(i);
>>>> +            if (line.length() == 0) {
>>>> +                break;
>>>> +            } else {
>>>> +                parseHeader(line);
>>>> +            }
>>>> +        }
>>>> +        parseBody(lines.subList(i, lines.size()));
>>>> +    }
>>>> +
>>>> +    protected void parseHeader(String line) throws IOException {
>>>> +        String key = null;
>>>> +        StringBuilder token = new StringBuilder();
>>>> +        int N = line.length();
>>>> +        for (int i = 0; i < N; i++) {
>>>> +            char c = line.charAt(i);
>>>> +            if (c == ':' && key == null) {
>>>> +                key = token.toString().trim();
>>>> +                token.delete(0, token.length());
>>>> +            } else {
>>>> +                token.append(c);
>>>> +            }
>>>> +        }
>>>> +        if (key != null) {
>>>> +            _headers.put(key,
>>> Collections.singletonList(token.toString().trim()));
>>>> +        }
>>>> +    }
>>>> +
>>>> +    protected void parseBody(List<String> lines) {
>>>> +        if (lines == null || lines.isEmpty())
>>>> +            return;
>>>> +        for (String line : lines) {
>>>> +            if (_body == null) {
>>>> +                _body = line;
>>>> +            } else {
>>>> +                _body = _body + line;
>>>> +            }
>>>> +        }
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Parses JEST stream and populates a request.
>>>> +     *
>>>> +     */
>>>> +     protected void parse(String s) {
>>>> +            char[] chars = s.toCharArray();
>>>> +            _state = ParseState.INIT;
>>>> +            _stack.clear();
>>>> +
>>>> +            for (int i = 0; i < chars.length; i++) {
>>>> +                char ch = chars[i];
>>>> +                switch (_state) {
>>>> +                    case INIT:
>>>> +                        if (ch == '/') {
>>>> +                            transit(ParseState.ACTION);
>>>> +                        } else if (!Character.isWhitespace(ch)) {
>>>> +                            parseError(ch, i, s, true, ' ');
>>>> +                        }
>>>> +                        break;
>>>> +
>>>> +                    case ACTION:
>>>> +                        if (ch == '/') {
>>>> +                            transit(ParseState.QUALIFIER_KEY);
>>>> +                        } else if (ch == '?') {
>>>> +                            transit(ParseState.PARAM_KEY);
>>>> +                        } else {
>>>> +                            buf.append(ch);
>>>> +                        }
>>>> +                        break;
>>>> +
>>>> +                    case QUALIFIER_KEY:
>>>> +                        if (Character.isJavaIdentifierPart(ch)) {
>>>> +                            buf.append(ch);
>>>> +                        } else if (ch == '=') {
>>>> +                            transit(ParseState.QUALIFIER_VALUE);
>>>> +                        } else if (ch == '/') {
>>>> +                            transit(ParseState.QUALIFIER_KEY);
>>>> +                        } else if (ch == '?') {
>>>> +                            transit(ParseState.PARAM_KEY);
>>>> +                        } else {
>>>> +                            parseError(ch, i, s, true, '/', '?', '=');
>>>> +                        }
>>>> +                        break;
>>>> +
>>>> +                    case QUALIFIER_VALUE:
>>>> +                        if (Character.isJavaIdentifierPart(ch)) {
>>>> +                            buf.append(ch);
>>>> +                        } else if (ch == '/') {
>>>> +                            transit(ParseState.QUALIFIER_KEY);
>>>> +                        } else if (ch == '?') {
>>>> +                            transit(ParseState.PARAM_KEY);
>>>> +                        } else {
>>>> +                            parseError(ch, i, s, true, '/', '?');
>>>> +                        }
>>>> +                        break;
>>>> +
>>>> +                    case PARAM_KEY:
>>>> +                        if (Character.isJavaIdentifierPart(ch)) {
>>>> +                            buf.append(ch);
>>>> +                        } else if (ch == '=') {
>>>> +                            if (isQueryKey())
>>>> +                                buf.append(ch);
>>>> +                            else
>>>> +                                transit(ParseState.PARAM_VALUE);
>>>> +                        } else if (ch == ';') {
>>>> +                            transit(ParseState.PARAM_KEY);
>>>> +                        } else if (isQueryKey() && isQueryChar(ch)) {
>>>> +                            buf.append(ch);
>>>> +                        } else {
>>>> +                            parseError(ch, i, s, true, ';', '=');
>>>> +                        }
>>>> +                        break;
>>>> +
>>>> +                    case PARAM_VALUE:
>>>> +                        if (Character.isJavaIdentifierPart(ch)) {
>>>> +                            buf.append(ch);
>>>> +                        } else if (ch == ';') {
>>>> +                            transit(ParseState.PARAM_KEY);
>>>> +                        } else {
>>>> +                            parseError(ch, i, s, true, ';');
>>>> +                        }
>>>> +                        break;
>>>> +                    default:
>>>> +                        throw new RuntimeException("ParseError: '" +
>> ch
>>> + "' at " + i + " in [" + s + "]. "
>>>> +                            + "Unknown state " + _state);
>>>> +                }
>>>> +            }
>>>> +            if (buf.length() > 0) {
>>>> +                transit(ParseState.END);
>>>> +            }
>>>> +        }
>>>> +
>>>> +        /**
>>>> +         * Affirms if parsing a query string.
>>>> +         */
>>>> +        private boolean isQueryKey() {
>>>> +            return "query".equals(_action) && _stack.size() == 1;
>>>> +        }
>>>> +
>>>> +        /**
>>>> +         * Affirms if the given character is valid in a query string
>>>> +         */
>>>> +        private boolean isQueryChar(char c) {
>>>> +            return c == ' ' || c == '.' || c == ':' || c == '?' || c
>> ==
>>> '\'';
>>>> +        }
>>>> +
>>>> +        /**
>>>> +         * Transitions to a new parse state.
>>>> +         *
>>>> +         * @param to target parse state
>>>> +         */
>>>> +        void transit(ParseState to) {
>>>> +            String token = buf.toString();
>>>> +            switch (_state) {
>>>> +                case ACTION:
>>>> +                    setAction(token);
>>>> +                    break;
>>>> +                case QUALIFIER_KEY:
>>>> +                    setQualifier(token, null);
>>>> +                    break;
>>>> +                case QUALIFIER_VALUE:
>>>> +                    setQualifier(_stack.peekLast().getValue(), token);
>>>> +                    break;
>>>> +                case PARAM_KEY:
>>>> +                    setParameter(token, null);
>>>> +                    break;
>>>> +                case PARAM_VALUE:
>>>> +                    setParameter(_stack.peekLast().getValue(), token);
>>>> +                    break;
>>>> +
>>>> +            }
>>>> +            if (_state != ParseState.INIT && to != ParseState.END) {
>>>> +                _stack.add(new Token(_state, token));
>>>> +            }
>>>> +            buf.delete(0, buf.length());
>>>> +            _state = to;
>>>> +        }
>>>> +
>>>> +        protected void parseError(char ch, int pos, String line,
>> boolean
>>> java, char... expected) {
>>>> +            throw new RuntimeException("ParseError: Encountered '" +
>> ch
>>> + "' at " + pos + " in [" + line + "] while "
>>>> +                + "parsing " + _state + ". Expected " +
>>> Arrays.toString(expected) + (java ? " or Java identifer" : ""));
>>>> +
>>>> +        }
>>>> +
>>>> +        /**
>>>> +         * Token in a JEST stream.
>>>> +         *
>>>> +         */
>>>> +        static class Token {
>>>> +            final ParseState _type;
>>>> +            final String _value;
>>>> +
>>>> +            public Token(ParseState type, String value) {
>>>> +                _type = type;
>>>> +                _value = value;
>>>> +            }
>>>> +
>>>> +            public ParseState getType() {
>>>> +                return _type;
>>>> +            }
>>>> +
>>>> +            public String getValue() {
>>>> +                return _value;
>>>> +            }
>>>> +
>>>> +            public String toString() {
>>>> +                return _value + "[" + _type + "]";
>>>> +            }
>>>> +        }
>>>> +
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,71 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.IOException;
>>>> +import java.io.OutputStream;
>>>> +import java.io.PrintStream;
>>>> +import java.io.PrintWriter;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +import java.util.Set;
>>>> +
>>>> +import org.apache.openjpa.kernel.OpenJPAStateManager;
>>>> +import org.apache.openjpa.persistence.JPAFacadeHelper;
>>>> +import org.w3c.dom.Document;
>>>> +import org.w3c.dom.Element;
>>>> +
>>>> +/**
>>>> + * Response of a JEST Request.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class JESTResponse extends AbstractResponse {
>>>> +    private final OpenJPAStateManager _sm;
>>>> +
>>>> +    public JESTResponse(Request request, ServerContext ctx,
>>> OpenJPAStateManager sm, OutputStream out) throws Exception {
>>>> +        super(request, ctx, out);
>>>> +        _sm = sm;
>>>> +    }
>>>> +
>>>> +    public String getContentType() {
>>>> +        return "text/html";
>>>> +    }
>>>> +
>>>> +    public void writeOut() throws Exception {
>>>> +        print(_request.getProtocol()); println("200 OK");
>>>> +        printHeader("Connection",  "close");
>>>> +        printHeader("Content-Type", "text/html", "charset=UTF-8");
>>>> +        println();
>>>> +        println("<html><body><pre>");
>>>> +        XMLEncoder encoder = new
>>> XMLEncoder(_ctx.getPersistenceUnit().getMetamodel());
>>>> +        Document doc = encoder.encode(_sm);
>>>> +        encoder.writeDoc(doc, this);
>>>> +        println("Response from JEST");
>>>> +
>>>> +        println("</pre></body></html>");
>>>> +        close();
>>>> +    }
>>>> +
>>>> +}
>>>> +
>>>> +
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,316 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.BufferedReader;
>>>> +import java.io.CharArrayWriter;
>>>> +import java.io.IOException;
>>>> +import java.io.InputStream;
>>>> +import java.io.InputStreamReader;
>>>> +import java.io.Reader;
>>>> +import java.util.Arrays;
>>>> +import java.util.BitSet;
>>>> +import java.util.Collection;
>>>> +import java.util.HashSet;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +import java.util.Set;
>>>> +
>>>> +import javax.persistence.metamodel.Attribute;
>>>> +
>>>> +import org.apache.openjpa.kernel.OpenJPAStateManager;
>>>> +import org.apache.openjpa.kernel.StoreContext;
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.meta.FieldMetaData;
>>>> +import org.apache.openjpa.meta.JavaTypes;
>>>> +import org.apache.openjpa.meta.ValueMetaData;
>>>> +import org.apache.openjpa.persistence.meta.Members;
>>>> +import org.apache.openjpa.persistence.meta.MetamodelImpl;
>>>> +import org.w3c.dom.CDATASection;
>>>> +import org.w3c.dom.Document;
>>>> +import org.w3c.dom.Element;
>>>> +
>>>> +/**
>>>> + * Marshals a root instance and its persistent closure as JSON object.
>>>> + * The closure is resolved against the persistence context that
>> contains
>>> the root instance.
>>>> + * The JSON format introduces a $id and $ref to address reference that
>>> pure JSON does not.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class JSONEncoder {
>>>> +    /**
>>>> +     * The element/attribute tags declared in
>>> <code>jest-instance.xsd</code> XML schema.
>>>> +     */
>>>> +    public static final String ELEMENT_NULL_REF    = "null";
>>>> +    public static final String ELEMENT_INSTANCE    = "instance";
>>>> +    public static final String ELEMENT_REF         = "ref";
>>>> +
>>>> +
>>>> +    private MetamodelHelper _model;
>>>> +
>>>> +    public JSONEncoder(MetamodelImpl model) {
>>>> +        _model = new MetamodelHelper(model);
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Encodes the given managed instance into a new XML element as a
>>> child of the given parent node.
>>>> +     *
>>>> +     * @param sm a managed instance, can be null.
>>>> +     * @param parent the parent node to which the new node be
>> attached.
>>>> +     */
>>>> +    public StringBuilder encode(final OpenJPAStateManager sm) {
>>>> +        return encode(sm, new HashSet<OpenJPAStateManager>(), 0,
>> false);
>>>> +    }
>>>> +    StringBuilder indent(StringBuilder buf, int indent) {
>>>> +        if (indent <= 0)
>>>> +            return buf;
>>>> +        char[] spaces = new char[indent*4];
>>>> +        Arrays.fill(spaces, ' ');
>>>> +        buf.insert(0, spaces);
>>>> +        return buf;
>>>> +    }
>>>> +    StringBuilder end(StringBuilder buf, char ch, int indent) {
>>>> +        char[] spaces = new char[indent*4];
>>>> +        Arrays.fill(spaces, ' ');
>>>> +        return buf.append("\r\n").append(spaces).append(ch);
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Encodes the closure of a persistent instance into a XML
>> element.
>>>> +     *
>>>> +     * @param sm the managed instance to be encoded. Can be null.
>>>> +     * @param parent the parent XML element to which the new XML
>> element
>>> be added. Must not be null. Must be
>>>> +     * owned by a document.
>>>> +     * @param visited the persistent instances that had been encoded
>>> already. Must not be null or immutable.
>>>> +     *
>>>> +     * @return the new element. The element has been appended as a
>> child
>>> to the given parent in this method.
>>>> +     */
>>>> +    private StringBuilder encode(final OpenJPAStateManager sm, final
>>> Set<OpenJPAStateManager> visited,
>>>> +        int indent, boolean indentPara) {
>>>> +        if (visited == null) {
>>>> +            throw new IllegalArgumentException("null closure for
>>> encoder");
>>>> +        }
>>>> +        StringBuilder root =  indent(new StringBuilder("{"),
>> indentPara
>>> ? indent : 0);
>>>> +        if (sm == null) {
>>>> +            return root.append("null}");
>>>> +        }
>>>> +        boolean ref = !visited.add(sm);
>>>> +        if (ref) {
>>>> +            return indent(root.append(quoted("$ref")).append(":
>>> ").append(ior(sm)).append('}'),
>>>> +                indentPara ? indent : 0);
>>>> +        } else {
>>>> +            indent(root.append(quoted("$id")).append(":
>>> ").append(ior(sm)), indentPara ? indent : 0);
>>>> +        }
>>>> +
>>>> +        StringBuilder child = new StringBuilder();
>>>> +        BitSet loaded = sm.getLoaded();
>>>> +        StoreContext ctx = (StoreContext)sm.getGenericContext();
>>>> +        List<Attribute<?, ?>> attrs =
>>> _model.getAttributesInOrder(sm.getMetaData());
>>>> +        for (int i = 0; i < attrs.size(); child = new StringBuilder(),
>>> i++) {
>>>> +            FieldMetaData fmd = ((Members.Member<?, ?>)
>>> attrs.get(i)).fmd;
>>>> +            if (!loaded.get(fmd.getIndex()))
>>>> +                continue;
>>>> +            Object value = sm.fetch(fmd.getIndex());
>>>> +            child.append(quoted(fmd.getName())).append(": ");
>>>> +            switch (fmd.getDeclaredTypeCode()) {
>>>> +                case JavaTypes.BOOLEAN:
>>>> +                case JavaTypes.BYTE:
>>>> +                case JavaTypes.CHAR:
>>>> +                case JavaTypes.DOUBLE:
>>>> +                case JavaTypes.FLOAT:
>>>> +                case JavaTypes.INT:
>>>> +                case JavaTypes.LONG:
>>>> +                case JavaTypes.SHORT:
>>>> +
>>>> +                case JavaTypes.BOOLEAN_OBJ:
>>>> +                case JavaTypes.BYTE_OBJ:
>>>> +                case JavaTypes.CHAR_OBJ:
>>>> +                case JavaTypes.DOUBLE_OBJ:
>>>> +                case JavaTypes.FLOAT_OBJ:
>>>> +                case JavaTypes.INT_OBJ:
>>>> +                case JavaTypes.LONG_OBJ:
>>>> +                case JavaTypes.SHORT_OBJ:
>>>> +
>>>> +                case JavaTypes.BIGDECIMAL:
>>>> +                case JavaTypes.BIGINTEGER:
>>>> +                case JavaTypes.DATE:
>>>> +                case JavaTypes.NUMBER:
>>>> +                case JavaTypes.CALENDAR:
>>>> +                case JavaTypes.LOCALE:
>>>> +                case JavaTypes.STRING:
>>>> +                case JavaTypes.ENUM:
>>>> +                         child.append(quoted(value));
>>>> +                break;
>>>> +
>>>> +                case JavaTypes.PC:
>>>> +                    if (value == null) {
>>>> +                        child.append("null");
>>>> +                    } else {
>>>> +
>>  child.append(encode(ctx.getStateManager(value),
>>> visited, indent+1, false));
>>>> +                    }
>>>> +                    break;
>>>> +
>>>> +                case JavaTypes.ARRAY:
>>>> +                    Object[] values = (Object[])value;
>>>> +                    value = Arrays.asList(values);
>>>> +                // no break;
>>>> +                case JavaTypes.COLLECTION:
>>>> +                    if (value == null) {
>>>> +                        child.append("null");
>>>> +                        break;
>>>> +                    }
>>>> +                    child.append("[");
>>>> +                    Collection<?> members = (Collection<?>)value;
>>>> +                    boolean basic = fmd.getElement().getTypeMetaData()
>>> == null;
>>>> +                    int k = 0;
>>>> +                    for (Object o : members) {
>>>> +                        child.append("\r\n");
>>>> +                        if (o == null) {
>>>> +                            child.append(indent(new
>>> StringBuilder("null"), indent+1));
>>>> +                        } else {
>>>> +                            if (basic) {
>>>> +                                child.append(indent(new
>>> StringBuilder(quoted(o)), indent+1));
>>>> +                            } else {
>>>> +
>>>  child.append(encode(ctx.getStateManager(o), visited, indent+1, true));
>>>> +                            }
>>>> +                        }
>>>> +                    }
>>>> +                    end(child, ']', indent+1);
>>>> +                    break;
>>>> +                case JavaTypes.MAP:
>>>> +                    if (value == null) {
>>>> +                        child.append("null");
>>>> +                        break;
>>>> +                    }
>>>> +                    child.append("[");
>>>> +                    Set<Map.Entry> entries = ((Map)value).entrySet();
>>>> +                    boolean basicKey   =
>>> fmd.getElement().getTypeMetaData() == null;
>>>> +                    boolean basicValue =
>>> fmd.getValue().getTypeMetaData() == null;
>>>> +                    for (Map.Entry<?,?> e : entries) {
>>>> +                        if (e.getKey() == null) {
>>>> +                            child.append("null:");
>>>> +                        } else {
>>>> +                            if (basicKey) {
>>>> +
>>>  child.append(quoted(e.getKey())).append(":");
>>>> +                            } else {
>>>> +
>>>  child.append(encode(ctx.getStateManager(e.getKey()), visited, indent+1,
>>> true));
>>>> +                            }
>>>> +                        }
>>>> +                        if (e.getValue() == null) {
>>>> +                            child.append("null");
>>>> +                        } else {
>>>> +                            if (basicValue) {
>>>> +                                child.append(quoted(e.getValue()));
>>>> +                            } else {
>>>> +
>>>  child.append(encode(ctx.getStateManager(e.getValue()), visited,
>> indent+1,
>>> false));
>>>> +                            }
>>>> +                        }
>>>> +                    }
>>>> +                    break;
>>>> +
>>>> +                case JavaTypes.INPUT_STREAM:
>>>> +                case JavaTypes.INPUT_READER:
>>>> +                    child = new StringBuilder(fmd.getName());
>>>> +                    if (value == null) {
>>>> +                        child.append("null");
>>>> +                    } else {
>>>> +                        child.append(streamToString(value));
>>>> +                    }
>>>> +                    break;
>>>> +
>>>> +                case JavaTypes.PC_UNTYPED:
>>>> +                case JavaTypes.OBJECT:
>>>> +                case JavaTypes.OID:
>>>> +                    System.err.println("Not handled " + fmd.getName()
>> +
>>> " of type " + fmd.getDeclaredType());
>>>> +            }
>>>> +
>>>> +            if (child != null) {
>>>> +                root.append("\r\n");
>>>> +                root.append(indent(child, indent+1));
>>>> +                if (loaded.length()-1 != i)
>>>> +                    root.append(",");
>>>> +           }
>>>> +        }
>>>> +        return end(root, '}', indent);
>>>> +    }
>>>> +
>>>> +
>>>> +    String ior(OpenJPAStateManager sm) {
>>>> +        return quoted(typeOf(sm)+"-"+sm.getObjectId().toString());
>>>> +    }
>>>> +
>>>> +    String typeOf(OpenJPAStateManager sm) {
>>>> +        return sm.getMetaData().getDescribedType().getSimpleName();
>>>> +    }
>>>> +
>>>> +    String typeOf(Class<?> cls) {
>>>> +        return cls.getSimpleName();
>>>> +    }
>>>> +
>>>> +    String typeOf(ClassMetaData meta) {
>>>> +        return meta.getDescribedType().getSimpleName();
>>>> +    }
>>>> +
>>>> +    String typeOf(ValueMetaData vm) {
>>>> +        if (vm.getTypeMetaData() == null)
>>>> +            return typeOf(vm.getType());
>>>> +        return typeOf(vm.getTypeMetaData());
>>>> +    }
>>>> +
>>>> +    String typeOf(FieldMetaData fmd) {
>>>> +        return fmd.getType().getSimpleName();
>>>> +    }
>>>> +
>>>> +
>>>> +    /**
>>>> +     * Convert the given stream (either an InutStream or a Reader) to
>> a
>>> String
>>>> +     * to be included in CDATA section of a XML document.
>>>> +     *
>>>> +     * @param value the field value to be converted. Can not be null
>>>> +     * @return
>>>> +     */
>>>> +    String streamToString(Object value) {
>>>> +        Reader reader = null;
>>>> +        if (value instanceof InputStream) {
>>>> +            reader = new BufferedReader(new
>>> InputStreamReader((InputStream)value));
>>>> +        } else if (value instanceof Reader) {
>>>> +            reader = (Reader)value;
>>>> +        } else {
>>>> +            throw new RuntimeException();
>>>> +        }
>>>> +        CharArrayWriter writer = new CharArrayWriter();
>>>> +        try {
>>>> +            for (int c; (c = reader.read()) != -1;) {
>>>> +                writer.write(c);
>>>> +            }
>>>> +        } catch (IOException ex) {
>>>> +            throw new RuntimeException(ex);
>>>> +        }
>>>> +        return writer.toString();
>>>> +    }
>>>> +
>>>> +    String quoted(Object o) {
>>>> +        if (o == null) return "null";
>>>> +        if (o instanceof Number)
>>>> +            return o.toString();
>>>> +        return "\"" + o.toString() + "\"";
>>>> +    }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,127 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.util.ArrayList;
>>>> +import java.util.Collections;
>>>> +import java.util.Comparator;
>>>> +import java.util.HashMap;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +
>>>> +import javax.persistence.metamodel.Attribute;
>>>> +import javax.persistence.metamodel.EntityType;
>>>> +import javax.persistence.metamodel.ManagedType;
>>>> +import javax.persistence.metamodel.Metamodel;
>>>> +import javax.persistence.metamodel.SingularAttribute;
>>>> +
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.meta.FieldMetaData;
>>>> +import org.apache.openjpa.persistence.meta.MetamodelImpl;
>>>> +
>>>> +/**
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class MetamodelHelper {
>>>> +    private MetamodelImpl _model;
>>>> +    private Map<ManagedType<?>, List<Attribute<?, ?>>> _attrs = new
>>> HashMap<ManagedType<?>, List<Attribute<?,?>>>();
>>>> +
>>>> +    public MetamodelHelper(MetamodelImpl model) {
>>>> +        _model = model;
>>>> +    }
>>>> +
>>>> +    public List<Attribute<?,?>> getAttributesInOrder(Class<?> cls) {
>>>> +        return getAttributesInOrder(_model.managedType(cls));
>>>> +    }
>>>> +
>>>> +    public List<Attribute<?,?>> getAttributesInOrder(ClassMetaData
>> meta)
>>> {
>>>> +        return getAttributesInOrder(meta.getDescribedType());
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Gets the attributes of the given type in defined order.
>>>> +     * @param type
>>>> +     * @return
>>>> +     */
>>>> +    public List<Attribute<?,?>> getAttributesInOrder(ManagedType<?>
>>> type) {
>>>> +        List<Attribute<?,?>> attrs = _attrs.get(type);
>>>> +        if (attrs != null)
>>>> +            return attrs;
>>>> +        List<Attribute<?,?>> list = new
>>> ArrayList<Attribute<?,?>>(type.getAttributes());
>>>> +        Collections.sort(list, new AttributeComparator());
>>>> +        _attrs.put(type, list);
>>>> +        return list;
>>>> +    }
>>>> +
>>>> +    public static boolean isId(Attribute<?,?> a) {
>>>> +        if (a instanceof SingularAttribute)
>>>> +            return ((SingularAttribute<?,?>)a).isId();
>>>> +        return false;
>>>> +    }
>>>> +
>>>> +    public static boolean isVersion(Attribute<?,?> a) {
>>>> +        if (a instanceof SingularAttribute)
>>>> +            return ((SingularAttribute<?,?>)a).isVersion();
>>>> +        return false;
>>>> +    }
>>>> +
>>>> +    public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
>>>> +        if (isId(attr))
>>>> +            return 0;
>>>> +        if (isVersion(attr))
>>>> +            return 1;
>>>> +
>>>> +      switch (attr.getPersistentAttributeType()) {
>>>> +      case BASIC :
>>>> +      case EMBEDDED:
>>>> +          return 2;
>>>> +      case ONE_TO_ONE:
>>>> +      case MANY_TO_ONE:
>>>> +          return 3;
>>>> +      case ONE_TO_MANY:
>>>> +      case MANY_TO_MANY:
>>>> +      case ELEMENT_COLLECTION: return 4;
>>>> +      default: return 5;
>>>> +      }
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Compares attribute by their qualification.
>>>> +     * Identity
>>>> +     * Version
>>>> +     * Basic
>>>> +     * Singular association
>>>> +     * Plural association
>>>> +     *
>>>> +     */
>>>> +    public static class AttributeComparator implements
>>> Comparator<Attribute<?,?>> {
>>>> +        @Override
>>>> +        public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
>>>> +            Integer t1 = getAttributeTypeCode(a1);
>>>> +            Integer t2 = getAttributeTypeCode(a2);
>>>> +            if (t1.equals(t2)) {
>>>> +                return a1.getName().compareTo(a2.getName());
>>>> +            } else {
>>>> +                return t1.compareTo(t2);
>>>> +            }
>>>> +        }
>>>> +    }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,138 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.IOException;
>>>> +import java.io.OutputStream;
>>>> +import java.io.Serializable;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +
>>>> +/**
>>>> + * A request from a remote client to a server to do something.
>>>> + * The request arrives as stream of bytes from a remote location
>>>> + * and if the server can interpret the protocol from the stream,
>>>> + * then  make a concrete request object.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public interface Request extends Serializable {
>>>> +    /**
>>>> +     * Get the HTTP verb such as GET, POST
>>>> +     *
>>>> +     * @return uppercase string
>>>> +     */
>>>> +    String getMethod();
>>>> +
>>>> +    /**
>>>> +     * Get the first path segment as intended persistence action such
>> as
>>> <code>find</code> or <code>query</code>
>>>> +     *
>>>> +     * @return lowercase action name. Can be empty.
>>>> +     */
>>>> +    String getAction();
>>>> +
>>>> +    /**
>>>> +     * Get the protocol such as HTTP/1.1
>>>> +     *
>>>> +     * @return upper-case string
>>>> +     */
>>>> +    String getProtocol();
>>>> +
>>>> +    /**
>>>> +     * Get the body, if any.
>>>> +     *
>>>> +     * @return body of the request. null if no body.
>>>> +     */
>>>> +    String getBody();
>>>> +
>>>> +    /**
>>>> +     * Get the headers indexed by the keys.
>>>> +     * Header values are list of Strings.
>>>> +     *
>>>> +     * @return empty map if there is no header
>>>> +     */
>>>> +    Map<String, List<String>> getHeaders();
>>>> +
>>>> +    /**
>>>> +     * Get the header values for the given key.
>>>> +     * @param key a key
>>>> +     * @return null if no header value for the given key
>>>> +     */
>>>> +    List<String> getHeader(String key);
>>>> +
>>>> +    /**
>>>> +     * Affirm if the the given qualifier is available in this request.
>>>> +     *
>>>> +     * @param key case-sensitive qualifier
>>>> +     * @return true if the key is present.
>>>> +     */
>>>> +    boolean hasQualifier(String key);
>>>> +
>>>> +    /**
>>>> +     * Gets the value for the given qualifier key.
>>>> +     *
>>>> +     * @param key case-sensitive qualifier
>>>> +     * @return value of the qualifier. null if the key is absent.
>>>> +     */
>>>> +    String getQualifier(String key);
>>>> +
>>>> +    /**
>>>> +     * Get all the qualifiers available in this request.
>>>> +     *
>>>> +     * @return key-value pairs of the qualifiers. Empty map if no
>>> qualifier is present.
>>>> +     */
>>>> +    Map<String,String> getQualifiers();
>>>> +
>>>> +
>>>> +    /**
>>>> +     * Affirm if the the given parameter is available in this request.
>>>> +     *
>>>> +     * @param key case-sensitive parameter
>>>> +     * @return true if the key is present.
>>>> +     */
>>>> +    boolean hasParameter(String key);
>>>> +
>>>> +
>>>> +    /**
>>>> +     * Gets the value for the given parameter key.
>>>> +     *
>>>> +     * @param key case-sensitive parameter
>>>> +     * @return value of the parameter. null if the key is absent.
>>>> +     */
>>>> +    String getParameter(String key);
>>>> +
>>>> +    /**
>>>> +     * Get all the parameters available in this request.
>>>> +     *
>>>> +     * @return key-value pairs of the parameters. Empty map if no
>>> parameter is present.
>>>> +     */
>>>> +    Map<String,String> getParameters();
>>>> +
>>>> +    /**
>>>> +     * Parse the request represented as a list of strings.
>>>> +     *
>>>> +     * @param lines each line of the request.
>>>> +     * @throws IOException
>>>> +     */
>>>> +    void read(List<String> lines) throws IOException;
>>>> +
>>>> +    Response process(ServerContext server, OutputStream stream) throws
>>> Exception;
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,63 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.util.HashMap;
>>>> +import java.util.Map;
>>>> +
>>>> +/**
>>>> + * A factory to create a specific type of request.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class RequestFactory {
>>>> +    private final String _protocol;
>>>> +    private static Map<String, RequestFactory> _registered = new
>>> HashMap<String, RequestFactory>();
>>>> +    static {
>>>> +        _registered.put("HTTP/1.0", new RequestFactory("HTTP/1.0"));
>>>> +        _registered.put("HTTP/1.1", new RequestFactory("HTTP/1.1"));
>>>> +    }
>>>> +
>>>> +    public static void register(String protocol, RequestFactory
>> factory)
>>> {
>>>> +        _registered.put(protocol, factory);
>>>> +    }
>>>> +
>>>> +    private RequestFactory(String proto) {
>>>> +        _protocol = proto;
>>>> +    }
>>>> +
>>>> +    public static RequestFactory getFactory(String protocol) {
>>>> +        return _registered.get(protocol);
>>>> +    }
>>>> +
>>>> +    Request createRequest(String method) {
>>>> +        JESTRequest request = null;
>>>> +        if ("GET".equalsIgnoreCase(method)) {
>>>> +            request = new GETRequest();
>>>> +        } else {
>>>> +            throw new UnsupportedOperationException();
>>>> +        }
>>>> +        request.setProtocol(_protocol);
>>>> +        request.setMethod(method.toUpperCase());
>>>> +        return request;
>>>> +
>>>> +    }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,147 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.BufferedReader;
>>>> +import java.io.IOException;
>>>> +import java.io.InputStream;
>>>> +import java.io.InputStreamReader;
>>>> +import java.io.OutputStream;
>>>> +import java.net.HttpURLConnection;
>>>> +import java.net.Socket;
>>>> +import java.net.SocketException;
>>>> +import java.util.ArrayList;
>>>> +import java.util.Collections;
>>>> +import java.util.Iterator;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +import java.util.Set;
>>>> +import java.util.concurrent.Callable;
>>>> +
>>>> +import javax.persistence.EntityManager;
>>>> +import javax.persistence.EntityNotFoundException;
>>>> +
>>>> +import org.apache.openjpa.kernel.OpenJPAStateManager;
>>>> +import org.apache.openjpa.kernel.StoreContext;
>>>> +import org.apache.openjpa.lib.log.Log;
>>>> +import org.apache.openjpa.lib.util.Localizer;
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.meta.FieldMetaData;
>>>> +import org.apache.openjpa.persistence.JPAFacadeHelper;
>>>> +import org.apache.openjpa.persistence.OpenJPAPersistence;
>>>> +import org.apache.openjpa.util.ApplicationIds;
>>>> +
>>>> +/**
>>>> + * Handles a request from a remote client.
>>>> + * Reads the socket data.
>>>> + * Populates the request.
>>>> + * Determines the processor based on request method and action.
>>>> + * Delegates to the processor.
>>>> + * Processor generates the response.
>>>> + * Writes the response to the stream.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class RequestHandler implements Callable<Void> {
>>>> +    private static final int SPACE = ' ';
>>>> +    private final Socket _socket;
>>>> +    private final ServerContext _server;
>>>> +    private final Log _log;
>>>> +    private static final Localizer _loc =
>>> Localizer.forPackage(RequestHandler.class);
>>>> +
>>>> +    public RequestHandler(Socket socket, ServerContext server) {
>>>> +        _socket = socket;
>>>> +        _server = server;
>>>> +        _log = _server.getLog();
>>>> +    }
>>>> +
>>>> +    public Void call() throws Exception {
>>>> +        Request request = null;
>>>> +        Response response = null;
>>>> +        try {
>>>> +            request = readRequest(_socket.getInputStream());
>>>> +            response = request.process(_server,
>>> _socket.getOutputStream());
>>>> +        } catch (Exception e) {
>>>> +            response = new ErrorResponse(request, _server, e,
>>> HttpURLConnection.HTTP_INTERNAL_ERROR,
>>>> +                _socket.getOutputStream());
>>>> +        }
>>>> +        response.writeOut();
>>>> +        return null;
>>>> +    }
>>>> +
>>>> +
>>>> +
>>>> +    /**
>>>> +     * Reads the given CR-LF delimited stream.
>>>> +     * The first line is scanned for the method (first space-delimited
>>> String) and protocol (the last
>>>> +     * space-delimited String). Accordingly a request is created and
>>> rest of the input stream content
>>>> +     * is parsed by the request itself.
>>>> +     *
>>>> +     * @param input
>>>> +     * @throws IOException
>>>> +     */
>>>> +    public Request readRequest(InputStream input) throws IOException {
>>>> +        if (_log.isTraceEnabled())
>>>> +            _log.trace("Reading request from the input stream ");
>>>> +
>>>> +        BufferedReader reader = new BufferedReader(new
>>> InputStreamReader(input));
>>>> +        String status = reader.readLine();
>>>> +        if (_log.isInfoEnabled())
>>>> +            _log.info("Status Line [" + status + "]");
>>>> +        int spaceFirst = status.indexOf(SPACE);
>>>> +        if (spaceFirst == -1)
>>>> +            throw new IOException("HTTP Method could not be determined
>>> from [" + status + "]");
>>>> +        int spaceLast = status.lastIndexOf(SPACE);
>>>> +        if (spaceLast == -1)
>>>> +            throw new IOException("HTTP Protocol could not be
>> determined
>>> from [" + status + "]");
>>>> +        String method = status.substring(0, spaceFirst);
>>>> +        String protocol = status.substring(spaceLast+1);
>>>> +        String path = status.substring(spaceFirst+1, spaceLast);
>>>> +        Request request =
>>> RequestFactory.getFactory(protocol).createRequest(method);
>>>> +        List<String> lines = new ArrayList<String>();
>>>> +        if (path.equals("/")) {
>>>> +            lines.add(path);
>>>> +        } else {
>>>> +            lines = readlines(reader);
>>>> +            lines.add(0, path);
>>>> +        }
>>>> +        if (lines.isEmpty()) {
>>>> +            throw new IOException("No CR-LF delimited lines could be
>>> read from " + input);
>>>> +        }
>>>> +        request.read(lines);
>>>> +        return request;
>>>> +    }
>>>> +
>>>> +
>>>> +    List<String> readlines(BufferedReader reader) throws IOException {
>>>> +        List<String> buffers = new ArrayList<String>();
>>>> +        String line;
>>>> +        while ((line = reader.readLine()) != null && line.length() >
>> 0)
>>> {
>>>> +            buffers.add(line);
>>>> +        }
>>>> +        return buffers;
>>>> +    }
>>>> +
>>>> +
>>>> +    public String toString() {
>>>> +        return _socket.getInetAddress()+":"+_socket.getPort();
>>>> +    }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,54 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.InputStream;
>>>> +import java.io.OutputStream;
>>>> +import java.io.RandomAccessFile;
>>>> +
>>>> +import javax.imageio.ImageIO;
>>>> +
>>>> +/**
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class ResourceResponse extends AbstractResponse {
>>>> +    private final InputStream _in;
>>>> +    private final String _mimeType;
>>>> +    public ResourceResponse(Request request, ServerContext ctx,
>>> InputStream resource, String mimeType,
>>>> +        OutputStream out) throws Exception {
>>>> +        super(request, ctx, out);
>>>> +        _in = resource;
>>>> +        _mimeType = mimeType;
>>>> +    }
>>>> +
>>>> +    public void writeOut() throws Exception {
>>>> +        print(_request.getProtocol()); println("200 OK");
>>>> +        printHeader("Connection",  "close");
>>>> +        printHeader("Content-Type", _mimeType, "charset=UTF-8");
>>>> +        println();
>>>> +        for (int c = 0; (c = _in.read()) != -1;) {
>>>> +           print((char)c);
>>>> +        }
>>>> +        _in.close();
>>>> +        close();
>>>> +    }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,31 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.Serializable;
>>>> +
>>>> +/**
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public interface Response extends Serializable {
>>>> +//    void setHeader(String key, String value);
>>>> +    void writeOut() throws Exception;
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,230 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements.  See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership.  The ASF licenses this file
>>>> + * to you 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.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.IOException;
>>>> +import java.net.ServerSocket;
>>>> +import java.net.Socket;
>>>> +import java.util.concurrent.ExecutorService;
>>>> +import java.util.concurrent.Executors;
>>>> +
>>>> +import javax.persistence.EntityManagerFactory;
>>>> +
>>>> +import org.apache.openjpa.conf.OpenJPAConfiguration;
>>>> +import org.apache.openjpa.lib.conf.Configurable;
>>>> +import org.apache.openjpa.lib.conf.Configuration;
>>>> +import org.apache.openjpa.lib.log.Log;
>>>> +import org.apache.openjpa.lib.util.Localizer;
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.meta.MetaDataRepository;
>>>> +import org.apache.openjpa.persistence.EntityManagerFactoryImpl;
>>>> +
>>>> +
>>>> +/**
>>>> + * A server running on an independent thread that allows a remote,
>>> language-neutral client to access OpenJPA runtime.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class Server implements ServerContext, Configurable, Runnable {
>>>> +    private ServerSocket _listenSocket;
>>>> +    protected ExecutorService _executors;
>>>> +    public final static int DEFAULT_PORT = 6789;
>>>> +    protected int _port = DEFAULT_PORT;
>>>> +    protected int _range = 1;
>>>> +    protected String _format = "xml";
>>>> +    protected Log _log;
>>>> +    protected Thread _thread;
>>>> +    private EntityManagerFactoryImpl _ctx;
>>>> +    private static Localizer _loc =
>> Localizer.forPackage(Server.class);
>>>> +
>>>> +    /**
>>>> +     * Sets the persistence unit context in which this server will
>> serve
>>> requests.
>>>> +     * The context must be set before operation.
>>>> +     *
>>>> +     * @param emf an implementation of OpenJPA Persistence Unit. Must
>>> not be null.
>>>> +     */
>>>> +    public void setContext(EntityManagerFactoryImpl emf) {
>>>> +        if (emf == null)
>>>> +            throw new NullPointerException();
>>>> +        _ctx = emf;
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Gets the persistence unit context in which this server serves
>>> requests.
>>>> +     *
>>>> +     * @param emf an implementation of OpenJPA Persistence Unit.
>>>> +     */
>>>> +    public EntityManagerFactoryImpl getPersistenceUnit() {
>>>> +        return _ctx;
>>>> +    }
>>>> +
>>>> +    public Log getLog() {
>>>> +        return _log;
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Start the server in a daemon thread.
>>>> +     * This method is idempotent.
>>>> +     *
>>>> +     * @return true if the server has started by this call or already
>>> running.
>>>> +     */
>>>> +    public synchronized boolean start() {
>>>> +        try {
>>>> +            if (_thread != null)
>>>> +                return true;
>>>> +            if (createServerSocket()) {
>>>> +                _thread = new Thread(this);
>>>> +                _thread.setDaemon(true);
>>>> +                _thread.start();
>>>> +                return true;
>>>> +            }
>>>> +            return false;
>>>> +        } catch (Exception ex) {
>>>> +            ex.printStackTrace();
>>>> +            return false;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Stops the server.
>>>> +     */
>>>> +    public synchronized void stop() {
>>>> +        _thread.interrupt();
>>>> +        _thread = null;
>>>> +        _executors.shutdownNow();
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Sets the port in which the server will listen.
>>>> +     *
>>>> +     * @param port a positive integer.
>>>> +     */
>>>> +    public void setPort(int port) {
>>>> +        _port = port;
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Gets the current port.
>>>> +     *
>>>> +     * @return the port number. Defaults to default HTTP port.
>>>> +     */
>>>> +    public int getPort() {
>>>> +        return _port;
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Sets the range of ports the server will attempt at start.
>>>> +     *
>>>> +     * @param range a positive integer.
>>>> +     */
>>>> +    public void setRange(int range) {
>>>> +        if (range > 0)
>>>> +            _range = range;
>>>> +    }
>>>> +
>>>> +    public void setFormat(String format) {
>>>> +        _format = format;
>>>> +    }
>>>> +
>>>> +    public String getFormat() {
>>>> +        return _format;
>>>> +    }
>>>> +
>>>> +    /**
>>>> +     * Sets the range of ports the server will attempt at start.
>>>> +     * @return  a positive integer. Defaults to 1.
>>>> +     */
>>>> +    public int getRange() {
>>>> +        return _range;
>>>> +    }
>>>> +
>>>> +    public void run() {
>>>> +        _log.info(_loc.get("server-starting", this));
>>>> +
>>>> +        _executors = Executors.newCachedThreadPool();
>>>> +        while (!Thread.interrupted()) {
>>>> +            try {
>>>> +                Socket socket = _listenSocket.accept();
>>>> +                if (_log.isTraceEnabled())
>>>> +                    _log.trace(_loc.get("server-request", socket));
>>>> +                RequestHandler request = new RequestHandler(socket,
>>> this);
>>>> +                _executors.submit(request);
>>>> +            } catch (IOException e) {
>>>> +                e.printStackTrace();
>>>> +            }
>>>> +        }
>>>> +    }
>>>> +
>>>> +    private boolean createServerSocket() {
>>>> +        int p = _port;
>>>> +        int p2 = p + _range;
>>>> +        Exception error = null;
>>>> +        for (; _listenSocket == null && p < p2; ) {
>>>> +            try {
>>>> +                _listenSocket = new ServerSocket(p);
>>>> +            } catch (IOException ex) {
>>>> +                p++;
>>>> +                error = ex;
>>>> +            }
>>>> +        }
>>>> +        if (_listenSocket != null) {
>>>> +            if (p != _port) {
>>>> +                _port = p;
>>>> +                _log.warn(_loc.get("server-reconfigured", _port));
>>>> +            }
>>>> +        } else {
>>>> +            if (error != null) {
>>>> +                _log.warn(_loc.get("server-failed", this, _port,
>>> error));
>>>> +            }
>>>> +        }
>>>> +        return _listenSocket != null;
>>>> +    }
>>>> +
>>>> +    // Configurable contract
>>>> +    public void setConfiguration(Configuration conf) {
>>>> +        _log = conf.getLog("Remote");
>>>> +    }
>>>> +
>>>> +    public void startConfiguration() {
>>>> +    }
>>>> +
>>>> +    public void endConfiguration() {
>>>> +    }
>>>> +
>>>> +
>>>> +    // Server side utilities
>>>> +
>>>> +    /**
>>>> +     * Resolves the given alias to a persistent class meta data.
>>>> +     *
>>>> +     * @exception if no meta data available for the given alias
>>>> +     */
>>>> +    public ClassMetaData resolve(String alias) {
>>>> +        MetaDataRepository repos =
>>> _ctx.getConfiguration().getMetaDataRepositoryInstance();
>>>> +        return repos.getMetaData(alias,
>>> Thread.currentThread().getContextClassLoader(), true);
>>>> +    }
>>>> +
>>>> +    public String toString() {
>>>> +        if (_listenSocket == null) return "JEST Server [not strated]";
>>>> +        return "JEST Server " +
>>> _listenSocket.getInetAddress()+":"+_listenSocket.getLocalPort();
>>>> +    }
>>>> +
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>>     svn:mime-type = text/plain
>>>>
>>>>
>>>>
>>>
>>
> 

Re: svn commit: r1028093 [1/2] - JEST...

Posted by Jeremy Bauer <te...@gmail.com>.
Ditto on the concerns.  Maybe this would be a good opportunity to discuss a
2.1 release date?  That may help guide whether we decide to include large
new features in 2.1 or hold off until 2.x.x and give them more time to brew.

-Jeremy

On Wed, Oct 27, 2010 at 5:11 PM, Kevin Sutter <kw...@gmail.com> wrote:

> I have to admit that I was having the same questions/concerns that Donald
> just raised.  I thought we were still in the discussion and experimentation
> phase of this REST work.  Is it really ready for prime time?
>
> Kevin
>
> On Wed, Oct 27, 2010 at 4:58 PM, Donald Woods <dw...@apache.org> wrote:
>
> > Is this really ready to drop into trunk?
> > And do we really want it in the base openjpa.jar?
> >
> >
> > -Donald
> >
> >
> > On 10/27/10 4:42 PM, ppoddar@apache.org wrote:
> > > Author: ppoddar
> > > Date: Wed Oct 27 20:42:44 2010
> > > New Revision: 1028093
> > >
> > > URL: http://svn.apache.org/viewvc?rev=1028093&view=rev
> > > Log:
> > > OPENJPA-1851: First version of JEST (REST on OpenJPA)
> > >
> > > Added:
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ServerContext.java
> >   (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/XMLEncoder.java
> >   (with props)
> > > Modified:
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > >
> > > Modified:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > (original)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -45,10 +45,10 @@ import org.apache.openjpa.lib.util.Close
> > >  import org.apache.openjpa.lib.util.Localizer;
> > >  import org.apache.openjpa.persistence.criteria.CriteriaBuilderImpl;
> > >  import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
> > > +import org.apache.openjpa.persistence.jest.Server;
> > >  import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > >  import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
> > >  import org.apache.openjpa.persistence.query.QueryBuilderImpl;
> > > -import org.apache.openjpa.util.UserException;
> > >
> > >  /**
> > >   * Implementation of {@link EntityManagerFactory} that acts as a
> > > @@ -70,7 +70,8 @@ public class EntityManagerFactoryImpl
> > >      private transient StoreCache _cache = null;
> > >      private transient QueryResultCache _queryCache = null;
> > >      private transient MetamodelImpl _metaModel;
> > > -
> > > +    private transient Server _remoteAccess = null;
> > > +
> > >      /**
> > >       * Default constructor provided for auto-instantiation.
> > >       */
> > > @@ -93,10 +94,11 @@ public class EntityManagerFactoryImpl
> > >
> > >      /**
> > >       * Delegate must be provided before use.
> > > +     * Configures for Remote Access, if appropriate.
> > >       */
> > >      public void setBrokerFactory(BrokerFactory factory) {
> > > -        _factory = new DelegatingBrokerFactory(factory,
> > > -            PersistenceExceptions.TRANSLATOR);
> > > +        _factory = new DelegatingBrokerFactory(factory,
> > PersistenceExceptions.TRANSLATOR);
> > > +        configureRemoteAccess(getConfiguration());
> > >      }
> > >
> > >      public OpenJPAConfiguration getConfiguration() {
> > > @@ -271,6 +273,10 @@ public class EntityManagerFactoryImpl
> > >          if (log.isTraceEnabled()) {
> > >              log.trace(this + ".close() invoked.");
> > >          }
> > > +        if (_remoteAccess != null) {
> > > +            _remoteAccess.stop();
> > > +            _remoteAccess = null;
> > > +        }
> > >          _factory.close();
> > >      }
> > >
> > > @@ -396,4 +402,40 @@ public class EntityManagerFactoryImpl
> > >              }
> > >          }
> > >      }
> > > +
> > > +    /**
> > > +     * Configures this unit for remote access.
> > > +     */
> > > +    protected void configureRemoteAccess(OpenJPAConfiguration conf) {
> > > +        Value value = conf.getValue("RemoteAccess");
> > > +        if (value == null) {
> > > +            return;
> > > +        }
> > > +        String props = value.getString();
> > > +        if (props == null)
> > > +            return;
> > > +        try {
> > > +            _remoteAccess = new Server();
> > > +            _remoteAccess.setContext(this);
> > > +            Configurations.configureInstance(_remoteAccess, conf,
> > props);
> > > +            conf.removeValue(value);
> > > +            if (!_remoteAccess.start()) {
> > > +                _remoteAccess = null;
> > > +            }
> > > +        } catch (Exception ex) {
> > > +            Log log =
> > _factory.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
> > > +            if (log != null) {
> > > +                log.error(_loc.get("remote-start-error"), ex);
> > > +            }
> > > +        }
> > > +    }
> > > +
> > > +    /**
> > > +     * Affirms if this unit is accessible remotely.
> > > +     */
> > > +    public boolean allowsRemoteAccess() {
> > > +        return _remoteAccess != null;
> > > +    }
> > > +
> > > +
> > >  }
> > >
> > > Modified:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > (original)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -188,6 +188,7 @@ public class PersistenceProductDerivatio
> > >          conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(),
> >  PersistenceMetaDataFactory.class.getName());
> > >
> > >          conf.addValue(new EntityManagerFactoryValue());
> > > +        conf.addString("RemoteAccess");
> > >
> > >          conf.readLockLevel.setAlias("optimistic",
> > String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
> > >          conf.readLockLevel.setAlias("optimistic-force-increment",
> String
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,77 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.OutputStream;
> > > +import java.io.PrintStream;
> > > +
> > > +/**
> > > + * Abstract implementation of a stream-based response.
> > > + * Every response is result of a {@linkplain Request} and operates
> > within a {@linkplain ServerContext}.
> > > + * This fact is enforced by the constructor argument of an abstract
> > response.
> > > + * <p>
> > > + * Besides, this implementation provides common utility to write HTTP
> > response or extract useful information
> > > + * from the request.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public abstract class AbstractResponse extends PrintStream implements
> > Response {
> > > +    protected final Request _request;
> > > +    protected final ServerContext _ctx;
> > > +
> > > +    /**
> > > +     * Construct a response for the given request and server context.
> > > +     *
> > > +     * @param request the request for this response. Can be null if
> the
> > response is for server error.
> > > +     * @param ctx the processing context
> > > +     * @param out the output stream where the response is targeted.
> > > +     */
> > > +    protected AbstractResponse(Request request, ServerContext ctx,
> > OutputStream out) {
> > > +        super(out);
> > > +        _request = request;
> > > +        _ctx = ctx;
> > > +    }
> > > +
> > > +    /**
> > > +     * Write a HTTP header to the stream.
> > > +     * <br>
> > > +     * The format of HTTP header is <code>key: [value]* NEWLINE</code>
> > > +     *
> > > +     * @param key the key of the header
> > > +     * @param values one of more value of the header fields.
> > > +     */
> > > +    protected void printHeader(String key, String...values) {
> > > +        if (key == null)
> > > +            return;
> > > +        print(key);
> > > +        print(" :");
> > > +        if (values == null || values.length == 0)
> > > +            return;
> > > +        int n = values.length-1;
> > > +        for (int i = 0; i < n-1; i++) {
> > > +            print(values[i]);
> > > +            print(";");
> > > +        }
> > > +        println(values[n]);
> > > +    }
> > > +
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,68 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.OutputStream;
> > > +import java.net.HttpURLConnection;
> > > +
> > > +/**
> > > + * A HTTP response for something gone wrong.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class ErrorResponse extends AbstractResponse {
> > > +    private final Exception _error;
> > > +    private final int _code;
> > > +    /**
> > > +     * Construct a response to describe a error.
> > > +     *
> > > +     * @param request a request that produced this response. VCan be
> > null to denote that the request can not
> > > +     * be created.
> > > +     * @param ctx the processing context
> > > +     * @param ex the error
> > > +     * @param code HTTP error code
> > > +     * @param out the stream where the response is written
> > > +     *
> > > +     */
> > > +    public ErrorResponse(Request request, ServerContext ctx, Exception
> > ex, int code, OutputStream out)  {
> > > +        super(request, ctx, out);
> > > +        _error = ex;
> > > +        _code = code;
> > > +    }
> > > +
> > > +    /**
> > > +     * Writes the response.
> > > +     * The response is always a HTTP response with the error stack
> > trace.
> > > +     */
> > > +    public void writeOut() throws Exception {
> > > +        println("HTTP/1.1"); print(" " + _code); println("Error");
> > > +        printHeader("Connection",  "close");
> > > +        printHeader("Content-Type", "text/html", "charset=UTF-8");
> > > +        println();
> > > +        println("<html><body><pre>");
> > > +        _error.printStackTrace(this);
> > > +        println("Response from JEST");
> > > +
> > > +        println("</pre></body></html>");
> > > +        close();
> > > +    }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,147 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.InputStream;
> > > +import java.io.OutputStream;
> > > +import java.net.HttpURLConnection;
> > > +import java.util.Iterator;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +
> > > +import javax.persistence.EntityManager;
> > > +import javax.persistence.EntityNotFoundException;
> > > +import javax.persistence.Query;
> > > +
> > > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > > +import org.apache.openjpa.kernel.StoreContext;
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > > +import org.apache.openjpa.util.ApplicationIds;
> > > +import org.apache.openjpa.util.ObjectNotFoundException;
> > > +
> > > +/**
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class GETRequest extends JESTRequest {
> > > +    public Response process(ServerContext server, OutputStream out)
> > throws Exception {
> > > +        String action = getAction();
> > > +        try {
> > > +            if ("find".equals(action)) {
> > > +                return find(server, out);
> > > +            } else {
> > > +                return resource(server, out);
> > > +            }
> > > +        } catch (Exception e) {
> > > +            return new ErrorResponse(this, server, new
> > RuntimeException("bad action " + action),
> > > +                HttpURLConnection.HTTP_BAD_REQUEST, out);
> > > +        }
> > > +    }
> > > +
> > > +    Response find(ServerContext server, OutputStream out)  throws
> > Exception {
> > > +        EntityManager em =
> > server.getPersistenceUnit().createEntityManager();
> > > +        Map<String, String> qualifiers = getQualifiers();
> > > +        Map<String, String> parameters = getParameters();
> > > +        if (parameters.size() < 2)
> > > +            throw new IllegalArgumentException("find must have at
> least
> > two parameters");
> > > +        Object[] pks = new Object[parameters.size()-1];
> > > +        Iterator<Map.Entry<String,String>> params =
> > parameters.entrySet().iterator();
> > > +        String alias = null;
> > > +        for (int i = 0; i < parameters.size(); i++) {
> > > +            if (i == 0) {
> > > +                alias = params.next().getKey();
> > > +            } else {
> > > +                pks[i-1] = params.next().getKey();
> > > +            }
> > > +        }
> > > +        ClassMetaData meta = server.resolve(alias);
> > > +        Object oid = ApplicationIds.fromPKValues(pks, meta);
> > > +        Object pc = em.find(meta.getDescribedType(), oid);
> > > +        if (pc != null) {
> > > +            OpenJPAStateManager sm =
> > ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(pc);
> > > +            return new JESTResponse(this, server, sm, out);
> > > +        } else {
> > > +            return new ErrorResponse(this, server, new
> > EntityNotFoundException("not found!"),
> > > +                HttpURLConnection.HTTP_NOT_FOUND, out);
> > > +        }
> > > +    }
> > > +
> > > +    Response query(ServerContext server, OutputStream out)  throws
> > Exception {
> > > +        EntityManager em =
> > server.getPersistenceUnit().createEntityManager();
> > > +        Map<String, String> qualifiers = getQualifiers();
> > > +        boolean named = isBooleanQualifier("named");
> > > +        boolean single = isBooleanQualifier("single");
> > > +        Map<String, String> parameters = getParameters();
> > > +        if (parameters.size() < 1)
> > > +            throw new IllegalArgumentException("find must have at
> least
> > one parameter");
> > > +        Iterator<Map.Entry<String,String>> params =
> > parameters.entrySet().iterator();
> > > +        Query query = null;
> > > +        int i = 0;
> > > +        for (Map.Entry<String, String> param : parameters.entrySet())
> {
> > > +            if (i == 0) {
> > > +                query = named ? em.createQuery(param.getKey()) :
> > em.createNamedQuery(param.getKey());
> > > +            } else {
> > > +                query.setParameter(param.getKey(), param.getValue());
> > > +            }
> > > +        }
> > > +        if (single) {
> > > +            Object result = query.getSingleResult();
> > > +            OpenJPAStateManager sm =
> > ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(result);
> > > +            return new JESTResponse(this, server, sm, out);
> > > +        } else {
> > > +            List<Object> result = query.getResultList();
> > > +            return new ErrorResponse(this, server, new
> > EntityNotFoundException("not found!"), 404, out);
> > > +        }
> > > +    }
> > > +
> > > +    Response resource(ServerContext server, OutputStream out)  throws
> > Exception {
> > > +        String resource = getAction();
> > > +        if (resource.length() == 0)
> > > +            resource = "index.html";
> > > +        String mimeType = getMimeType(resource);
> > > +//        InputStream in =
> >
> Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
> > > +        InputStream in = getClass().getResourceAsStream(resource);
> > > +        if (in == null) {
> > > +            return new ErrorResponse(this, server, new
> > ObjectNotFoundException(resource), 404, out);
> > > +        }
> > > +        if (server.getLog().isTraceEnabled())
> > > +            server.getLog().trace("Found resource " + resource);
> > > +        return mimeType.startsWith("image")
> > > +          ? new ImageResponse(this, server, in, mimeType, out)
> > > +          : new ResourceResponse(this, server, in, mimeType, out);
> > > +    }
> > > +
> > > +    boolean isBooleanQualifier(String key) {
> > > +        String q = getQualifier(key);
> > > +        return hasQualifier(key) && (q == null || "true".equals(q));
> > > +    }
> > > +
> > > +    String getMimeType(String resource) {
> > > +        int index = resource.lastIndexOf('.');
> > > +        String ext = (index != -1) ? resource.substring(index+1) : "";
> > > +        if (ext.equalsIgnoreCase("html") ||
> > ext.equalsIgnoreCase(".html")) return "text/html";
> > > +        if (ext.equalsIgnoreCase(".png") ||
> ext.equalsIgnoreCase(".ico")
> > || ext.equalsIgnoreCase("jpeg"))
> > > +            return "image/"+ext;
> > > +        return "text/html";
> > > +    }
> > > +
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,61 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.InputStream;
> > > +import java.io.OutputStream;
> > > +import java.io.RandomAccessFile;
> > > +
> > > +import javax.imageio.ImageIO;
> > > +
> > > +/**
> > > + * Sends an image as response.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class ImageResponse extends AbstractResponse {
> > > +    private final InputStream _in;
> > > +    private final String _mimeType;
> > > +
> > > +    public ImageResponse(Request request, ServerContext ctx,
> InputStream
> > resource, String mimeType,
> > > +        OutputStream out) throws Exception {
> > > +        super(request, ctx, out);
> > > +        _in = resource;
> > > +        _mimeType = mimeType;
> > > +    }
> > > +
> > > +    public void writeOut() throws Exception {
> > > +        print(_request.getProtocol()); println("200 OK");
> > > +        printHeader("Connection",  "close");
> > > +        printHeader("Content-Type", _mimeType);
> > > +        println();
> > > +        byte[] b = new byte[1024];
> > > +        int i = 0;
> > > +        for (int l = 0; (l = _in.read(b)) != -1;) {
> > > +            write(b, 0, l);
> > > +            i += l;
> > > +        }
> > > +        printHeader("Content-Length", ""+i);
> > > +        _in.close();
> > > +        close();
> > > +    }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,386 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.IOException;
> > > +import java.util.Arrays;
> > > +import java.util.Collections;
> > > +import java.util.HashMap;
> > > +import java.util.LinkedHashMap;
> > > +import java.util.LinkedList;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +import java.util.NoSuchElementException;
> > > +
> > > +/**
> > > + * A request carries requisite data for a JPA operation to be
> performed.
> > > + * The request is populated by parsing an input data stream.
> > > + *
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public abstract class JESTRequest implements Request {
> > > +    private String _method;
> > > +    private String _protocol;
> > > +    private String _action;
> > > +    private String _body;
> > > +    private LinkedHashMap<String, String> _qualifiers = new
> > LinkedHashMap<String, String>();
> > > +    private LinkedHashMap<String, String> _params = new
> > LinkedHashMap<String, String>();
> > > +    private Map<String, List<String>> _headers = new HashMap<String,
> > List<String>>();
> > > +    private ParseState _state;
> > > +    private StringBuffer buf = new StringBuffer();
> > > +    private LinkedList<Token> _stack = new LinkedList<Token>();
> > > +
> > > +    public static final List<String> METHODS = Arrays.asList(new
> > String[]{"GET","POST","PUT","DELETE"});
> > > +
> > > +    /**
> > > +     * Parse States.
> > > +     */
> > > +    static enum ParseState {
> > > +        INIT, ACTION, QUALIFIER_KEY, QUALIFIER_VALUE, PARAM_KEY,
> > PARAM_VALUE, END
> > > +    };
> > > +
> > > +
> > > +    public String getMethod() {
> > > +        return _method;
> > > +    }
> > > +
> > > +    void setMethod(String method) {
> > > +        if (_method == null) {
> > > +            if (method != null &&
> > METHODS.contains(method.toUpperCase())) {
> > > +                _method = method.toUpperCase();
> > > +            } else {
> > > +                throw new IllegalArgumentException("Unsupported method
> "
> > + method);
> > > +            }
> > > +        } else if (!_method.equalsIgnoreCase(method)) {
> > > +            throw new IllegalStateException("Method can not be changed
> > to [" + method + "]. " +
> > > +                "Current method [" + _method + "]");
> > > +        }
> > > +    }
> > > +
> > > +    public String getProtocol() {
> > > +        return _protocol == null ? "HTTP/1.1" : _protocol;
> > > +    }
> > > +
> > > +    void setProtocol(String protocol) {
> > > +        if (_protocol == null) {
> > > +            if (protocol != null &&
> > protocol.toUpperCase().startsWith("HTTP")) {
> > > +                _protocol = protocol.toUpperCase();
> > > +            } else {
> > > +                throw new IllegalArgumentException("Unsupported
> protocol
> > " + protocol);
> > > +            }
> > > +        } else if (!_protocol.equalsIgnoreCase(protocol)) {
> > > +            throw new IllegalStateException("Protocol can not be
> changed
> > to [" + protocol + "]. " +
> > > +                "Current protocol [" + _protocol + "]");
> > > +        }
> > > +    }
> > > +
> > > +    /**
> > > +     * Sets an action. Once set, an action can not be modified.
> > > +     *
> > > +     * @param action
> > > +     */
> > > +    private void setAction(String action) {
> > > +        if (_action == null) {
> > > +            _action = action;
> > > +        } else if (!_action.equals(action)) {
> > > +            throw new IllegalStateException("Action can not be [" +
> > action + "]. Already set to [" + _action + "]");
> > > +        }
> > > +    }
> > > +
> > > +    public String getAction() {
> > > +        return _action == null ? "" : _action;
> > > +    }
> > > +
> > > +    public String getBody() {
> > > +        return _body;
> > > +    }
> > > +
> > > +    private void setQualifier(String key, String value) {
> > > +        _qualifiers.put(key, value);
> > > +    }
> > > +
> > > +    public String getQualifier(String key) {
> > > +        return _qualifiers.get(key);
> > > +    }
> > > +
> > > +    public Map<String, String> getQualifiers() {
> > > +        return Collections.unmodifiableMap(_qualifiers);
> > > +    }
> > > +
> > > +    public boolean hasQualifier(String key) {
> > > +        return _qualifiers.containsKey(key);
> > > +    }
> > > +
> > > +    private void setParameter(String key, String value) {
> > > +        _params.put(key, value);
> > > +    }
> > > +
> > > +    public String getParameter(String key) {
> > > +        return _params.get(key);
> > > +    }
> > > +
> > > +    public boolean hasParameter(String key) {
> > > +        return _params.containsKey(key);
> > > +    }
> > > +
> > > +    public Map<String, String> getParameters() {
> > > +        return Collections.unmodifiableMap(_params);
> > > +    }
> > > +
> > > +    public Map.Entry<String, String> getParameter(int n) {
> > > +        if (n >= _params.size())
> > > +            throw new NoSuchElementException("Index " + n + " size " +
> > _params.size());
> > > +        int i = 0;
> > > +        for (Map.Entry<String, String> entry : _params.entrySet()) {
> > > +            if (i == n) {
> > > +                return entry;
> > > +            }
> > > +            i++;
> > > +        }
> > > +        return null;
> > > +    }
> > > +
> > > +    public Map<String, List<String>> getHeaders() {
> > > +        return Collections.unmodifiableMap(_headers);
> > > +    }
> > > +
> > > +    public List<String> getHeader(String key) {
> > > +        return _headers.get(key);
> > > +    }
> > > +
> > > +
> > > +    public void read(List<String> lines) throws IOException {
> > > +        parse(lines.get(0));
> > > +        int i = 1;
> > > +        for (; i < lines.size(); i++) {
> > > +            String line = lines.get(i);
> > > +            if (line.length() == 0) {
> > > +                break;
> > > +            } else {
> > > +                parseHeader(line);
> > > +            }
> > > +        }
> > > +        parseBody(lines.subList(i, lines.size()));
> > > +    }
> > > +
> > > +    protected void parseHeader(String line) throws IOException {
> > > +        String key = null;
> > > +        StringBuilder token = new StringBuilder();
> > > +        int N = line.length();
> > > +        for (int i = 0; i < N; i++) {
> > > +            char c = line.charAt(i);
> > > +            if (c == ':' && key == null) {
> > > +                key = token.toString().trim();
> > > +                token.delete(0, token.length());
> > > +            } else {
> > > +                token.append(c);
> > > +            }
> > > +        }
> > > +        if (key != null) {
> > > +            _headers.put(key,
> > Collections.singletonList(token.toString().trim()));
> > > +        }
> > > +    }
> > > +
> > > +    protected void parseBody(List<String> lines) {
> > > +        if (lines == null || lines.isEmpty())
> > > +            return;
> > > +        for (String line : lines) {
> > > +            if (_body == null) {
> > > +                _body = line;
> > > +            } else {
> > > +                _body = _body + line;
> > > +            }
> > > +        }
> > > +    }
> > > +
> > > +    /**
> > > +     * Parses JEST stream and populates a request.
> > > +     *
> > > +     */
> > > +     protected void parse(String s) {
> > > +            char[] chars = s.toCharArray();
> > > +            _state = ParseState.INIT;
> > > +            _stack.clear();
> > > +
> > > +            for (int i = 0; i < chars.length; i++) {
> > > +                char ch = chars[i];
> > > +                switch (_state) {
> > > +                    case INIT:
> > > +                        if (ch == '/') {
> > > +                            transit(ParseState.ACTION);
> > > +                        } else if (!Character.isWhitespace(ch)) {
> > > +                            parseError(ch, i, s, true, ' ');
> > > +                        }
> > > +                        break;
> > > +
> > > +                    case ACTION:
> > > +                        if (ch == '/') {
> > > +                            transit(ParseState.QUALIFIER_KEY);
> > > +                        } else if (ch == '?') {
> > > +                            transit(ParseState.PARAM_KEY);
> > > +                        } else {
> > > +                            buf.append(ch);
> > > +                        }
> > > +                        break;
> > > +
> > > +                    case QUALIFIER_KEY:
> > > +                        if (Character.isJavaIdentifierPart(ch)) {
> > > +                            buf.append(ch);
> > > +                        } else if (ch == '=') {
> > > +                            transit(ParseState.QUALIFIER_VALUE);
> > > +                        } else if (ch == '/') {
> > > +                            transit(ParseState.QUALIFIER_KEY);
> > > +                        } else if (ch == '?') {
> > > +                            transit(ParseState.PARAM_KEY);
> > > +                        } else {
> > > +                            parseError(ch, i, s, true, '/', '?', '=');
> > > +                        }
> > > +                        break;
> > > +
> > > +                    case QUALIFIER_VALUE:
> > > +                        if (Character.isJavaIdentifierPart(ch)) {
> > > +                            buf.append(ch);
> > > +                        } else if (ch == '/') {
> > > +                            transit(ParseState.QUALIFIER_KEY);
> > > +                        } else if (ch == '?') {
> > > +                            transit(ParseState.PARAM_KEY);
> > > +                        } else {
> > > +                            parseError(ch, i, s, true, '/', '?');
> > > +                        }
> > > +                        break;
> > > +
> > > +                    case PARAM_KEY:
> > > +                        if (Character.isJavaIdentifierPart(ch)) {
> > > +                            buf.append(ch);
> > > +                        } else if (ch == '=') {
> > > +                            if (isQueryKey())
> > > +                                buf.append(ch);
> > > +                            else
> > > +                                transit(ParseState.PARAM_VALUE);
> > > +                        } else if (ch == ';') {
> > > +                            transit(ParseState.PARAM_KEY);
> > > +                        } else if (isQueryKey() && isQueryChar(ch)) {
> > > +                            buf.append(ch);
> > > +                        } else {
> > > +                            parseError(ch, i, s, true, ';', '=');
> > > +                        }
> > > +                        break;
> > > +
> > > +                    case PARAM_VALUE:
> > > +                        if (Character.isJavaIdentifierPart(ch)) {
> > > +                            buf.append(ch);
> > > +                        } else if (ch == ';') {
> > > +                            transit(ParseState.PARAM_KEY);
> > > +                        } else {
> > > +                            parseError(ch, i, s, true, ';');
> > > +                        }
> > > +                        break;
> > > +                    default:
> > > +                        throw new RuntimeException("ParseError: '" +
> ch
> > + "' at " + i + " in [" + s + "]. "
> > > +                            + "Unknown state " + _state);
> > > +                }
> > > +            }
> > > +            if (buf.length() > 0) {
> > > +                transit(ParseState.END);
> > > +            }
> > > +        }
> > > +
> > > +        /**
> > > +         * Affirms if parsing a query string.
> > > +         */
> > > +        private boolean isQueryKey() {
> > > +            return "query".equals(_action) && _stack.size() == 1;
> > > +        }
> > > +
> > > +        /**
> > > +         * Affirms if the given character is valid in a query string
> > > +         */
> > > +        private boolean isQueryChar(char c) {
> > > +            return c == ' ' || c == '.' || c == ':' || c == '?' || c
> ==
> > '\'';
> > > +        }
> > > +
> > > +        /**
> > > +         * Transitions to a new parse state.
> > > +         *
> > > +         * @param to target parse state
> > > +         */
> > > +        void transit(ParseState to) {
> > > +            String token = buf.toString();
> > > +            switch (_state) {
> > > +                case ACTION:
> > > +                    setAction(token);
> > > +                    break;
> > > +                case QUALIFIER_KEY:
> > > +                    setQualifier(token, null);
> > > +                    break;
> > > +                case QUALIFIER_VALUE:
> > > +                    setQualifier(_stack.peekLast().getValue(), token);
> > > +                    break;
> > > +                case PARAM_KEY:
> > > +                    setParameter(token, null);
> > > +                    break;
> > > +                case PARAM_VALUE:
> > > +                    setParameter(_stack.peekLast().getValue(), token);
> > > +                    break;
> > > +
> > > +            }
> > > +            if (_state != ParseState.INIT && to != ParseState.END) {
> > > +                _stack.add(new Token(_state, token));
> > > +            }
> > > +            buf.delete(0, buf.length());
> > > +            _state = to;
> > > +        }
> > > +
> > > +        protected void parseError(char ch, int pos, String line,
> boolean
> > java, char... expected) {
> > > +            throw new RuntimeException("ParseError: Encountered '" +
> ch
> > + "' at " + pos + " in [" + line + "] while "
> > > +                + "parsing " + _state + ". Expected " +
> > Arrays.toString(expected) + (java ? " or Java identifer" : ""));
> > > +
> > > +        }
> > > +
> > > +        /**
> > > +         * Token in a JEST stream.
> > > +         *
> > > +         */
> > > +        static class Token {
> > > +            final ParseState _type;
> > > +            final String _value;
> > > +
> > > +            public Token(ParseState type, String value) {
> > > +                _type = type;
> > > +                _value = value;
> > > +            }
> > > +
> > > +            public ParseState getType() {
> > > +                return _type;
> > > +            }
> > > +
> > > +            public String getValue() {
> > > +                return _value;
> > > +            }
> > > +
> > > +            public String toString() {
> > > +                return _value + "[" + _type + "]";
> > > +            }
> > > +        }
> > > +
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,71 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.IOException;
> > > +import java.io.OutputStream;
> > > +import java.io.PrintStream;
> > > +import java.io.PrintWriter;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +import java.util.Set;
> > > +
> > > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > > +import org.w3c.dom.Document;
> > > +import org.w3c.dom.Element;
> > > +
> > > +/**
> > > + * Response of a JEST Request.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class JESTResponse extends AbstractResponse {
> > > +    private final OpenJPAStateManager _sm;
> > > +
> > > +    public JESTResponse(Request request, ServerContext ctx,
> > OpenJPAStateManager sm, OutputStream out) throws Exception {
> > > +        super(request, ctx, out);
> > > +        _sm = sm;
> > > +    }
> > > +
> > > +    public String getContentType() {
> > > +        return "text/html";
> > > +    }
> > > +
> > > +    public void writeOut() throws Exception {
> > > +        print(_request.getProtocol()); println("200 OK");
> > > +        printHeader("Connection",  "close");
> > > +        printHeader("Content-Type", "text/html", "charset=UTF-8");
> > > +        println();
> > > +        println("<html><body><pre>");
> > > +        XMLEncoder encoder = new
> > XMLEncoder(_ctx.getPersistenceUnit().getMetamodel());
> > > +        Document doc = encoder.encode(_sm);
> > > +        encoder.writeDoc(doc, this);
> > > +        println("Response from JEST");
> > > +
> > > +        println("</pre></body></html>");
> > > +        close();
> > > +    }
> > > +
> > > +}
> > > +
> > > +
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,316 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.BufferedReader;
> > > +import java.io.CharArrayWriter;
> > > +import java.io.IOException;
> > > +import java.io.InputStream;
> > > +import java.io.InputStreamReader;
> > > +import java.io.Reader;
> > > +import java.util.Arrays;
> > > +import java.util.BitSet;
> > > +import java.util.Collection;
> > > +import java.util.HashSet;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +import java.util.Set;
> > > +
> > > +import javax.persistence.metamodel.Attribute;
> > > +
> > > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > > +import org.apache.openjpa.kernel.StoreContext;
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.meta.FieldMetaData;
> > > +import org.apache.openjpa.meta.JavaTypes;
> > > +import org.apache.openjpa.meta.ValueMetaData;
> > > +import org.apache.openjpa.persistence.meta.Members;
> > > +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > > +import org.w3c.dom.CDATASection;
> > > +import org.w3c.dom.Document;
> > > +import org.w3c.dom.Element;
> > > +
> > > +/**
> > > + * Marshals a root instance and its persistent closure as JSON object.
> > > + * The closure is resolved against the persistence context that
> contains
> > the root instance.
> > > + * The JSON format introduces a $id and $ref to address reference that
> > pure JSON does not.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class JSONEncoder {
> > > +    /**
> > > +     * The element/attribute tags declared in
> > <code>jest-instance.xsd</code> XML schema.
> > > +     */
> > > +    public static final String ELEMENT_NULL_REF    = "null";
> > > +    public static final String ELEMENT_INSTANCE    = "instance";
> > > +    public static final String ELEMENT_REF         = "ref";
> > > +
> > > +
> > > +    private MetamodelHelper _model;
> > > +
> > > +    public JSONEncoder(MetamodelImpl model) {
> > > +        _model = new MetamodelHelper(model);
> > > +    }
> > > +
> > > +    /**
> > > +     * Encodes the given managed instance into a new XML element as a
> > child of the given parent node.
> > > +     *
> > > +     * @param sm a managed instance, can be null.
> > > +     * @param parent the parent node to which the new node be
> attached.
> > > +     */
> > > +    public StringBuilder encode(final OpenJPAStateManager sm) {
> > > +        return encode(sm, new HashSet<OpenJPAStateManager>(), 0,
> false);
> > > +    }
> > > +    StringBuilder indent(StringBuilder buf, int indent) {
> > > +        if (indent <= 0)
> > > +            return buf;
> > > +        char[] spaces = new char[indent*4];
> > > +        Arrays.fill(spaces, ' ');
> > > +        buf.insert(0, spaces);
> > > +        return buf;
> > > +    }
> > > +    StringBuilder end(StringBuilder buf, char ch, int indent) {
> > > +        char[] spaces = new char[indent*4];
> > > +        Arrays.fill(spaces, ' ');
> > > +        return buf.append("\r\n").append(spaces).append(ch);
> > > +    }
> > > +
> > > +    /**
> > > +     * Encodes the closure of a persistent instance into a XML
> element.
> > > +     *
> > > +     * @param sm the managed instance to be encoded. Can be null.
> > > +     * @param parent the parent XML element to which the new XML
> element
> > be added. Must not be null. Must be
> > > +     * owned by a document.
> > > +     * @param visited the persistent instances that had been encoded
> > already. Must not be null or immutable.
> > > +     *
> > > +     * @return the new element. The element has been appended as a
> child
> > to the given parent in this method.
> > > +     */
> > > +    private StringBuilder encode(final OpenJPAStateManager sm, final
> > Set<OpenJPAStateManager> visited,
> > > +        int indent, boolean indentPara) {
> > > +        if (visited == null) {
> > > +            throw new IllegalArgumentException("null closure for
> > encoder");
> > > +        }
> > > +        StringBuilder root =  indent(new StringBuilder("{"),
> indentPara
> > ? indent : 0);
> > > +        if (sm == null) {
> > > +            return root.append("null}");
> > > +        }
> > > +        boolean ref = !visited.add(sm);
> > > +        if (ref) {
> > > +            return indent(root.append(quoted("$ref")).append(":
> > ").append(ior(sm)).append('}'),
> > > +                indentPara ? indent : 0);
> > > +        } else {
> > > +            indent(root.append(quoted("$id")).append(":
> > ").append(ior(sm)), indentPara ? indent : 0);
> > > +        }
> > > +
> > > +        StringBuilder child = new StringBuilder();
> > > +        BitSet loaded = sm.getLoaded();
> > > +        StoreContext ctx = (StoreContext)sm.getGenericContext();
> > > +        List<Attribute<?, ?>> attrs =
> > _model.getAttributesInOrder(sm.getMetaData());
> > > +        for (int i = 0; i < attrs.size(); child = new StringBuilder(),
> > i++) {
> > > +            FieldMetaData fmd = ((Members.Member<?, ?>)
> > attrs.get(i)).fmd;
> > > +            if (!loaded.get(fmd.getIndex()))
> > > +                continue;
> > > +            Object value = sm.fetch(fmd.getIndex());
> > > +            child.append(quoted(fmd.getName())).append(": ");
> > > +            switch (fmd.getDeclaredTypeCode()) {
> > > +                case JavaTypes.BOOLEAN:
> > > +                case JavaTypes.BYTE:
> > > +                case JavaTypes.CHAR:
> > > +                case JavaTypes.DOUBLE:
> > > +                case JavaTypes.FLOAT:
> > > +                case JavaTypes.INT:
> > > +                case JavaTypes.LONG:
> > > +                case JavaTypes.SHORT:
> > > +
> > > +                case JavaTypes.BOOLEAN_OBJ:
> > > +                case JavaTypes.BYTE_OBJ:
> > > +                case JavaTypes.CHAR_OBJ:
> > > +                case JavaTypes.DOUBLE_OBJ:
> > > +                case JavaTypes.FLOAT_OBJ:
> > > +                case JavaTypes.INT_OBJ:
> > > +                case JavaTypes.LONG_OBJ:
> > > +                case JavaTypes.SHORT_OBJ:
> > > +
> > > +                case JavaTypes.BIGDECIMAL:
> > > +                case JavaTypes.BIGINTEGER:
> > > +                case JavaTypes.DATE:
> > > +                case JavaTypes.NUMBER:
> > > +                case JavaTypes.CALENDAR:
> > > +                case JavaTypes.LOCALE:
> > > +                case JavaTypes.STRING:
> > > +                case JavaTypes.ENUM:
> > > +                         child.append(quoted(value));
> > > +                break;
> > > +
> > > +                case JavaTypes.PC:
> > > +                    if (value == null) {
> > > +                        child.append("null");
> > > +                    } else {
> > > +
>  child.append(encode(ctx.getStateManager(value),
> > visited, indent+1, false));
> > > +                    }
> > > +                    break;
> > > +
> > > +                case JavaTypes.ARRAY:
> > > +                    Object[] values = (Object[])value;
> > > +                    value = Arrays.asList(values);
> > > +                // no break;
> > > +                case JavaTypes.COLLECTION:
> > > +                    if (value == null) {
> > > +                        child.append("null");
> > > +                        break;
> > > +                    }
> > > +                    child.append("[");
> > > +                    Collection<?> members = (Collection<?>)value;
> > > +                    boolean basic = fmd.getElement().getTypeMetaData()
> > == null;
> > > +                    int k = 0;
> > > +                    for (Object o : members) {
> > > +                        child.append("\r\n");
> > > +                        if (o == null) {
> > > +                            child.append(indent(new
> > StringBuilder("null"), indent+1));
> > > +                        } else {
> > > +                            if (basic) {
> > > +                                child.append(indent(new
> > StringBuilder(quoted(o)), indent+1));
> > > +                            } else {
> > > +
> >  child.append(encode(ctx.getStateManager(o), visited, indent+1, true));
> > > +                            }
> > > +                        }
> > > +                    }
> > > +                    end(child, ']', indent+1);
> > > +                    break;
> > > +                case JavaTypes.MAP:
> > > +                    if (value == null) {
> > > +                        child.append("null");
> > > +                        break;
> > > +                    }
> > > +                    child.append("[");
> > > +                    Set<Map.Entry> entries = ((Map)value).entrySet();
> > > +                    boolean basicKey   =
> > fmd.getElement().getTypeMetaData() == null;
> > > +                    boolean basicValue =
> > fmd.getValue().getTypeMetaData() == null;
> > > +                    for (Map.Entry<?,?> e : entries) {
> > > +                        if (e.getKey() == null) {
> > > +                            child.append("null:");
> > > +                        } else {
> > > +                            if (basicKey) {
> > > +
> >  child.append(quoted(e.getKey())).append(":");
> > > +                            } else {
> > > +
> >  child.append(encode(ctx.getStateManager(e.getKey()), visited, indent+1,
> > true));
> > > +                            }
> > > +                        }
> > > +                        if (e.getValue() == null) {
> > > +                            child.append("null");
> > > +                        } else {
> > > +                            if (basicValue) {
> > > +                                child.append(quoted(e.getValue()));
> > > +                            } else {
> > > +
> >  child.append(encode(ctx.getStateManager(e.getValue()), visited,
> indent+1,
> > false));
> > > +                            }
> > > +                        }
> > > +                    }
> > > +                    break;
> > > +
> > > +                case JavaTypes.INPUT_STREAM:
> > > +                case JavaTypes.INPUT_READER:
> > > +                    child = new StringBuilder(fmd.getName());
> > > +                    if (value == null) {
> > > +                        child.append("null");
> > > +                    } else {
> > > +                        child.append(streamToString(value));
> > > +                    }
> > > +                    break;
> > > +
> > > +                case JavaTypes.PC_UNTYPED:
> > > +                case JavaTypes.OBJECT:
> > > +                case JavaTypes.OID:
> > > +                    System.err.println("Not handled " + fmd.getName()
> +
> > " of type " + fmd.getDeclaredType());
> > > +            }
> > > +
> > > +            if (child != null) {
> > > +                root.append("\r\n");
> > > +                root.append(indent(child, indent+1));
> > > +                if (loaded.length()-1 != i)
> > > +                    root.append(",");
> > > +           }
> > > +        }
> > > +        return end(root, '}', indent);
> > > +    }
> > > +
> > > +
> > > +    String ior(OpenJPAStateManager sm) {
> > > +        return quoted(typeOf(sm)+"-"+sm.getObjectId().toString());
> > > +    }
> > > +
> > > +    String typeOf(OpenJPAStateManager sm) {
> > > +        return sm.getMetaData().getDescribedType().getSimpleName();
> > > +    }
> > > +
> > > +    String typeOf(Class<?> cls) {
> > > +        return cls.getSimpleName();
> > > +    }
> > > +
> > > +    String typeOf(ClassMetaData meta) {
> > > +        return meta.getDescribedType().getSimpleName();
> > > +    }
> > > +
> > > +    String typeOf(ValueMetaData vm) {
> > > +        if (vm.getTypeMetaData() == null)
> > > +            return typeOf(vm.getType());
> > > +        return typeOf(vm.getTypeMetaData());
> > > +    }
> > > +
> > > +    String typeOf(FieldMetaData fmd) {
> > > +        return fmd.getType().getSimpleName();
> > > +    }
> > > +
> > > +
> > > +    /**
> > > +     * Convert the given stream (either an InutStream or a Reader) to
> a
> > String
> > > +     * to be included in CDATA section of a XML document.
> > > +     *
> > > +     * @param value the field value to be converted. Can not be null
> > > +     * @return
> > > +     */
> > > +    String streamToString(Object value) {
> > > +        Reader reader = null;
> > > +        if (value instanceof InputStream) {
> > > +            reader = new BufferedReader(new
> > InputStreamReader((InputStream)value));
> > > +        } else if (value instanceof Reader) {
> > > +            reader = (Reader)value;
> > > +        } else {
> > > +            throw new RuntimeException();
> > > +        }
> > > +        CharArrayWriter writer = new CharArrayWriter();
> > > +        try {
> > > +            for (int c; (c = reader.read()) != -1;) {
> > > +                writer.write(c);
> > > +            }
> > > +        } catch (IOException ex) {
> > > +            throw new RuntimeException(ex);
> > > +        }
> > > +        return writer.toString();
> > > +    }
> > > +
> > > +    String quoted(Object o) {
> > > +        if (o == null) return "null";
> > > +        if (o instanceof Number)
> > > +            return o.toString();
> > > +        return "\"" + o.toString() + "\"";
> > > +    }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,127 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.util.ArrayList;
> > > +import java.util.Collections;
> > > +import java.util.Comparator;
> > > +import java.util.HashMap;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +
> > > +import javax.persistence.metamodel.Attribute;
> > > +import javax.persistence.metamodel.EntityType;
> > > +import javax.persistence.metamodel.ManagedType;
> > > +import javax.persistence.metamodel.Metamodel;
> > > +import javax.persistence.metamodel.SingularAttribute;
> > > +
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.meta.FieldMetaData;
> > > +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > > +
> > > +/**
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class MetamodelHelper {
> > > +    private MetamodelImpl _model;
> > > +    private Map<ManagedType<?>, List<Attribute<?, ?>>> _attrs = new
> > HashMap<ManagedType<?>, List<Attribute<?,?>>>();
> > > +
> > > +    public MetamodelHelper(MetamodelImpl model) {
> > > +        _model = model;
> > > +    }
> > > +
> > > +    public List<Attribute<?,?>> getAttributesInOrder(Class<?> cls) {
> > > +        return getAttributesInOrder(_model.managedType(cls));
> > > +    }
> > > +
> > > +    public List<Attribute<?,?>> getAttributesInOrder(ClassMetaData
> meta)
> > {
> > > +        return getAttributesInOrder(meta.getDescribedType());
> > > +    }
> > > +
> > > +    /**
> > > +     * Gets the attributes of the given type in defined order.
> > > +     * @param type
> > > +     * @return
> > > +     */
> > > +    public List<Attribute<?,?>> getAttributesInOrder(ManagedType<?>
> > type) {
> > > +        List<Attribute<?,?>> attrs = _attrs.get(type);
> > > +        if (attrs != null)
> > > +            return attrs;
> > > +        List<Attribute<?,?>> list = new
> > ArrayList<Attribute<?,?>>(type.getAttributes());
> > > +        Collections.sort(list, new AttributeComparator());
> > > +        _attrs.put(type, list);
> > > +        return list;
> > > +    }
> > > +
> > > +    public static boolean isId(Attribute<?,?> a) {
> > > +        if (a instanceof SingularAttribute)
> > > +            return ((SingularAttribute<?,?>)a).isId();
> > > +        return false;
> > > +    }
> > > +
> > > +    public static boolean isVersion(Attribute<?,?> a) {
> > > +        if (a instanceof SingularAttribute)
> > > +            return ((SingularAttribute<?,?>)a).isVersion();
> > > +        return false;
> > > +    }
> > > +
> > > +    public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
> > > +        if (isId(attr))
> > > +            return 0;
> > > +        if (isVersion(attr))
> > > +            return 1;
> > > +
> > > +      switch (attr.getPersistentAttributeType()) {
> > > +      case BASIC :
> > > +      case EMBEDDED:
> > > +          return 2;
> > > +      case ONE_TO_ONE:
> > > +      case MANY_TO_ONE:
> > > +          return 3;
> > > +      case ONE_TO_MANY:
> > > +      case MANY_TO_MANY:
> > > +      case ELEMENT_COLLECTION: return 4;
> > > +      default: return 5;
> > > +      }
> > > +    }
> > > +
> > > +    /**
> > > +     * Compares attribute by their qualification.
> > > +     * Identity
> > > +     * Version
> > > +     * Basic
> > > +     * Singular association
> > > +     * Plural association
> > > +     *
> > > +     */
> > > +    public static class AttributeComparator implements
> > Comparator<Attribute<?,?>> {
> > > +        @Override
> > > +        public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
> > > +            Integer t1 = getAttributeTypeCode(a1);
> > > +            Integer t2 = getAttributeTypeCode(a2);
> > > +            if (t1.equals(t2)) {
> > > +                return a1.getName().compareTo(a2.getName());
> > > +            } else {
> > > +                return t1.compareTo(t2);
> > > +            }
> > > +        }
> > > +    }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,138 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.IOException;
> > > +import java.io.OutputStream;
> > > +import java.io.Serializable;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +
> > > +/**
> > > + * A request from a remote client to a server to do something.
> > > + * The request arrives as stream of bytes from a remote location
> > > + * and if the server can interpret the protocol from the stream,
> > > + * then  make a concrete request object.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public interface Request extends Serializable {
> > > +    /**
> > > +     * Get the HTTP verb such as GET, POST
> > > +     *
> > > +     * @return uppercase string
> > > +     */
> > > +    String getMethod();
> > > +
> > > +    /**
> > > +     * Get the first path segment as intended persistence action such
> as
> > <code>find</code> or <code>query</code>
> > > +     *
> > > +     * @return lowercase action name. Can be empty.
> > > +     */
> > > +    String getAction();
> > > +
> > > +    /**
> > > +     * Get the protocol such as HTTP/1.1
> > > +     *
> > > +     * @return upper-case string
> > > +     */
> > > +    String getProtocol();
> > > +
> > > +    /**
> > > +     * Get the body, if any.
> > > +     *
> > > +     * @return body of the request. null if no body.
> > > +     */
> > > +    String getBody();
> > > +
> > > +    /**
> > > +     * Get the headers indexed by the keys.
> > > +     * Header values are list of Strings.
> > > +     *
> > > +     * @return empty map if there is no header
> > > +     */
> > > +    Map<String, List<String>> getHeaders();
> > > +
> > > +    /**
> > > +     * Get the header values for the given key.
> > > +     * @param key a key
> > > +     * @return null if no header value for the given key
> > > +     */
> > > +    List<String> getHeader(String key);
> > > +
> > > +    /**
> > > +     * Affirm if the the given qualifier is available in this request.
> > > +     *
> > > +     * @param key case-sensitive qualifier
> > > +     * @return true if the key is present.
> > > +     */
> > > +    boolean hasQualifier(String key);
> > > +
> > > +    /**
> > > +     * Gets the value for the given qualifier key.
> > > +     *
> > > +     * @param key case-sensitive qualifier
> > > +     * @return value of the qualifier. null if the key is absent.
> > > +     */
> > > +    String getQualifier(String key);
> > > +
> > > +    /**
> > > +     * Get all the qualifiers available in this request.
> > > +     *
> > > +     * @return key-value pairs of the qualifiers. Empty map if no
> > qualifier is present.
> > > +     */
> > > +    Map<String,String> getQualifiers();
> > > +
> > > +
> > > +    /**
> > > +     * Affirm if the the given parameter is available in this request.
> > > +     *
> > > +     * @param key case-sensitive parameter
> > > +     * @return true if the key is present.
> > > +     */
> > > +    boolean hasParameter(String key);
> > > +
> > > +
> > > +    /**
> > > +     * Gets the value for the given parameter key.
> > > +     *
> > > +     * @param key case-sensitive parameter
> > > +     * @return value of the parameter. null if the key is absent.
> > > +     */
> > > +    String getParameter(String key);
> > > +
> > > +    /**
> > > +     * Get all the parameters available in this request.
> > > +     *
> > > +     * @return key-value pairs of the parameters. Empty map if no
> > parameter is present.
> > > +     */
> > > +    Map<String,String> getParameters();
> > > +
> > > +    /**
> > > +     * Parse the request represented as a list of strings.
> > > +     *
> > > +     * @param lines each line of the request.
> > > +     * @throws IOException
> > > +     */
> > > +    void read(List<String> lines) throws IOException;
> > > +
> > > +    Response process(ServerContext server, OutputStream stream) throws
> > Exception;
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,63 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.util.HashMap;
> > > +import java.util.Map;
> > > +
> > > +/**
> > > + * A factory to create a specific type of request.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class RequestFactory {
> > > +    private final String _protocol;
> > > +    private static Map<String, RequestFactory> _registered = new
> > HashMap<String, RequestFactory>();
> > > +    static {
> > > +        _registered.put("HTTP/1.0", new RequestFactory("HTTP/1.0"));
> > > +        _registered.put("HTTP/1.1", new RequestFactory("HTTP/1.1"));
> > > +    }
> > > +
> > > +    public static void register(String protocol, RequestFactory
> factory)
> > {
> > > +        _registered.put(protocol, factory);
> > > +    }
> > > +
> > > +    private RequestFactory(String proto) {
> > > +        _protocol = proto;
> > > +    }
> > > +
> > > +    public static RequestFactory getFactory(String protocol) {
> > > +        return _registered.get(protocol);
> > > +    }
> > > +
> > > +    Request createRequest(String method) {
> > > +        JESTRequest request = null;
> > > +        if ("GET".equalsIgnoreCase(method)) {
> > > +            request = new GETRequest();
> > > +        } else {
> > > +            throw new UnsupportedOperationException();
> > > +        }
> > > +        request.setProtocol(_protocol);
> > > +        request.setMethod(method.toUpperCase());
> > > +        return request;
> > > +
> > > +    }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,147 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.BufferedReader;
> > > +import java.io.IOException;
> > > +import java.io.InputStream;
> > > +import java.io.InputStreamReader;
> > > +import java.io.OutputStream;
> > > +import java.net.HttpURLConnection;
> > > +import java.net.Socket;
> > > +import java.net.SocketException;
> > > +import java.util.ArrayList;
> > > +import java.util.Collections;
> > > +import java.util.Iterator;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +import java.util.Set;
> > > +import java.util.concurrent.Callable;
> > > +
> > > +import javax.persistence.EntityManager;
> > > +import javax.persistence.EntityNotFoundException;
> > > +
> > > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > > +import org.apache.openjpa.kernel.StoreContext;
> > > +import org.apache.openjpa.lib.log.Log;
> > > +import org.apache.openjpa.lib.util.Localizer;
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.meta.FieldMetaData;
> > > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > > +import org.apache.openjpa.persistence.OpenJPAPersistence;
> > > +import org.apache.openjpa.util.ApplicationIds;
> > > +
> > > +/**
> > > + * Handles a request from a remote client.
> > > + * Reads the socket data.
> > > + * Populates the request.
> > > + * Determines the processor based on request method and action.
> > > + * Delegates to the processor.
> > > + * Processor generates the response.
> > > + * Writes the response to the stream.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class RequestHandler implements Callable<Void> {
> > > +    private static final int SPACE = ' ';
> > > +    private final Socket _socket;
> > > +    private final ServerContext _server;
> > > +    private final Log _log;
> > > +    private static final Localizer _loc =
> > Localizer.forPackage(RequestHandler.class);
> > > +
> > > +    public RequestHandler(Socket socket, ServerContext server) {
> > > +        _socket = socket;
> > > +        _server = server;
> > > +        _log = _server.getLog();
> > > +    }
> > > +
> > > +    public Void call() throws Exception {
> > > +        Request request = null;
> > > +        Response response = null;
> > > +        try {
> > > +            request = readRequest(_socket.getInputStream());
> > > +            response = request.process(_server,
> > _socket.getOutputStream());
> > > +        } catch (Exception e) {
> > > +            response = new ErrorResponse(request, _server, e,
> > HttpURLConnection.HTTP_INTERNAL_ERROR,
> > > +                _socket.getOutputStream());
> > > +        }
> > > +        response.writeOut();
> > > +        return null;
> > > +    }
> > > +
> > > +
> > > +
> > > +    /**
> > > +     * Reads the given CR-LF delimited stream.
> > > +     * The first line is scanned for the method (first space-delimited
> > String) and protocol (the last
> > > +     * space-delimited String). Accordingly a request is created and
> > rest of the input stream content
> > > +     * is parsed by the request itself.
> > > +     *
> > > +     * @param input
> > > +     * @throws IOException
> > > +     */
> > > +    public Request readRequest(InputStream input) throws IOException {
> > > +        if (_log.isTraceEnabled())
> > > +            _log.trace("Reading request from the input stream ");
> > > +
> > > +        BufferedReader reader = new BufferedReader(new
> > InputStreamReader(input));
> > > +        String status = reader.readLine();
> > > +        if (_log.isInfoEnabled())
> > > +            _log.info("Status Line [" + status + "]");
> > > +        int spaceFirst = status.indexOf(SPACE);
> > > +        if (spaceFirst == -1)
> > > +            throw new IOException("HTTP Method could not be determined
> > from [" + status + "]");
> > > +        int spaceLast = status.lastIndexOf(SPACE);
> > > +        if (spaceLast == -1)
> > > +            throw new IOException("HTTP Protocol could not be
> determined
> > from [" + status + "]");
> > > +        String method = status.substring(0, spaceFirst);
> > > +        String protocol = status.substring(spaceLast+1);
> > > +        String path = status.substring(spaceFirst+1, spaceLast);
> > > +        Request request =
> > RequestFactory.getFactory(protocol).createRequest(method);
> > > +        List<String> lines = new ArrayList<String>();
> > > +        if (path.equals("/")) {
> > > +            lines.add(path);
> > > +        } else {
> > > +            lines = readlines(reader);
> > > +            lines.add(0, path);
> > > +        }
> > > +        if (lines.isEmpty()) {
> > > +            throw new IOException("No CR-LF delimited lines could be
> > read from " + input);
> > > +        }
> > > +        request.read(lines);
> > > +        return request;
> > > +    }
> > > +
> > > +
> > > +    List<String> readlines(BufferedReader reader) throws IOException {
> > > +        List<String> buffers = new ArrayList<String>();
> > > +        String line;
> > > +        while ((line = reader.readLine()) != null && line.length() >
> 0)
> > {
> > > +            buffers.add(line);
> > > +        }
> > > +        return buffers;
> > > +    }
> > > +
> > > +
> > > +    public String toString() {
> > > +        return _socket.getInetAddress()+":"+_socket.getPort();
> > > +    }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,54 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.InputStream;
> > > +import java.io.OutputStream;
> > > +import java.io.RandomAccessFile;
> > > +
> > > +import javax.imageio.ImageIO;
> > > +
> > > +/**
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class ResourceResponse extends AbstractResponse {
> > > +    private final InputStream _in;
> > > +    private final String _mimeType;
> > > +    public ResourceResponse(Request request, ServerContext ctx,
> > InputStream resource, String mimeType,
> > > +        OutputStream out) throws Exception {
> > > +        super(request, ctx, out);
> > > +        _in = resource;
> > > +        _mimeType = mimeType;
> > > +    }
> > > +
> > > +    public void writeOut() throws Exception {
> > > +        print(_request.getProtocol()); println("200 OK");
> > > +        printHeader("Connection",  "close");
> > > +        printHeader("Content-Type", _mimeType, "charset=UTF-8");
> > > +        println();
> > > +        for (int c = 0; (c = _in.read()) != -1;) {
> > > +           print((char)c);
> > > +        }
> > > +        _in.close();
> > > +        close();
> > > +    }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,31 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.Serializable;
> > > +
> > > +/**
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public interface Response extends Serializable {
> > > +//    void setHeader(String key, String value);
> > > +    void writeOut() throws Exception;
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,230 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements.  See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership.  The ASF licenses this file
> > > + * to you 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.openjpa.persistence.jest;
> > > +
> > > +import java.io.IOException;
> > > +import java.net.ServerSocket;
> > > +import java.net.Socket;
> > > +import java.util.concurrent.ExecutorService;
> > > +import java.util.concurrent.Executors;
> > > +
> > > +import javax.persistence.EntityManagerFactory;
> > > +
> > > +import org.apache.openjpa.conf.OpenJPAConfiguration;
> > > +import org.apache.openjpa.lib.conf.Configurable;
> > > +import org.apache.openjpa.lib.conf.Configuration;
> > > +import org.apache.openjpa.lib.log.Log;
> > > +import org.apache.openjpa.lib.util.Localizer;
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.meta.MetaDataRepository;
> > > +import org.apache.openjpa.persistence.EntityManagerFactoryImpl;
> > > +
> > > +
> > > +/**
> > > + * A server running on an independent thread that allows a remote,
> > language-neutral client to access OpenJPA runtime.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class Server implements ServerContext, Configurable, Runnable {
> > > +    private ServerSocket _listenSocket;
> > > +    protected ExecutorService _executors;
> > > +    public final static int DEFAULT_PORT = 6789;
> > > +    protected int _port = DEFAULT_PORT;
> > > +    protected int _range = 1;
> > > +    protected String _format = "xml";
> > > +    protected Log _log;
> > > +    protected Thread _thread;
> > > +    private EntityManagerFactoryImpl _ctx;
> > > +    private static Localizer _loc =
> Localizer.forPackage(Server.class);
> > > +
> > > +    /**
> > > +     * Sets the persistence unit context in which this server will
> serve
> > requests.
> > > +     * The context must be set before operation.
> > > +     *
> > > +     * @param emf an implementation of OpenJPA Persistence Unit. Must
> > not be null.
> > > +     */
> > > +    public void setContext(EntityManagerFactoryImpl emf) {
> > > +        if (emf == null)
> > > +            throw new NullPointerException();
> > > +        _ctx = emf;
> > > +    }
> > > +
> > > +    /**
> > > +     * Gets the persistence unit context in which this server serves
> > requests.
> > > +     *
> > > +     * @param emf an implementation of OpenJPA Persistence Unit.
> > > +     */
> > > +    public EntityManagerFactoryImpl getPersistenceUnit() {
> > > +        return _ctx;
> > > +    }
> > > +
> > > +    public Log getLog() {
> > > +        return _log;
> > > +    }
> > > +
> > > +    /**
> > > +     * Start the server in a daemon thread.
> > > +     * This method is idempotent.
> > > +     *
> > > +     * @return true if the server has started by this call or already
> > running.
> > > +     */
> > > +    public synchronized boolean start() {
> > > +        try {
> > > +            if (_thread != null)
> > > +                return true;
> > > +            if (createServerSocket()) {
> > > +                _thread = new Thread(this);
> > > +                _thread.setDaemon(true);
> > > +                _thread.start();
> > > +                return true;
> > > +            }
> > > +            return false;
> > > +        } catch (Exception ex) {
> > > +            ex.printStackTrace();
> > > +            return false;
> > > +        }
> > > +    }
> > > +
> > > +    /**
> > > +     * Stops the server.
> > > +     */
> > > +    public synchronized void stop() {
> > > +        _thread.interrupt();
> > > +        _thread = null;
> > > +        _executors.shutdownNow();
> > > +    }
> > > +
> > > +    /**
> > > +     * Sets the port in which the server will listen.
> > > +     *
> > > +     * @param port a positive integer.
> > > +     */
> > > +    public void setPort(int port) {
> > > +        _port = port;
> > > +    }
> > > +
> > > +    /**
> > > +     * Gets the current port.
> > > +     *
> > > +     * @return the port number. Defaults to default HTTP port.
> > > +     */
> > > +    public int getPort() {
> > > +        return _port;
> > > +    }
> > > +
> > > +    /**
> > > +     * Sets the range of ports the server will attempt at start.
> > > +     *
> > > +     * @param range a positive integer.
> > > +     */
> > > +    public void setRange(int range) {
> > > +        if (range > 0)
> > > +            _range = range;
> > > +    }
> > > +
> > > +    public void setFormat(String format) {
> > > +        _format = format;
> > > +    }
> > > +
> > > +    public String getFormat() {
> > > +        return _format;
> > > +    }
> > > +
> > > +    /**
> > > +     * Sets the range of ports the server will attempt at start.
> > > +     * @return  a positive integer. Defaults to 1.
> > > +     */
> > > +    public int getRange() {
> > > +        return _range;
> > > +    }
> > > +
> > > +    public void run() {
> > > +        _log.info(_loc.get("server-starting", this));
> > > +
> > > +        _executors = Executors.newCachedThreadPool();
> > > +        while (!Thread.interrupted()) {
> > > +            try {
> > > +                Socket socket = _listenSocket.accept();
> > > +                if (_log.isTraceEnabled())
> > > +                    _log.trace(_loc.get("server-request", socket));
> > > +                RequestHandler request = new RequestHandler(socket,
> > this);
> > > +                _executors.submit(request);
> > > +            } catch (IOException e) {
> > > +                e.printStackTrace();
> > > +            }
> > > +        }
> > > +    }
> > > +
> > > +    private boolean createServerSocket() {
> > > +        int p = _port;
> > > +        int p2 = p + _range;
> > > +        Exception error = null;
> > > +        for (; _listenSocket == null && p < p2; ) {
> > > +            try {
> > > +                _listenSocket = new ServerSocket(p);
> > > +            } catch (IOException ex) {
> > > +                p++;
> > > +                error = ex;
> > > +            }
> > > +        }
> > > +        if (_listenSocket != null) {
> > > +            if (p != _port) {
> > > +                _port = p;
> > > +                _log.warn(_loc.get("server-reconfigured", _port));
> > > +            }
> > > +        } else {
> > > +            if (error != null) {
> > > +                _log.warn(_loc.get("server-failed", this, _port,
> > error));
> > > +            }
> > > +        }
> > > +        return _listenSocket != null;
> > > +    }
> > > +
> > > +    // Configurable contract
> > > +    public void setConfiguration(Configuration conf) {
> > > +        _log = conf.getLog("Remote");
> > > +    }
> > > +
> > > +    public void startConfiguration() {
> > > +    }
> > > +
> > > +    public void endConfiguration() {
> > > +    }
> > > +
> > > +
> > > +    // Server side utilities
> > > +
> > > +    /**
> > > +     * Resolves the given alias to a persistent class meta data.
> > > +     *
> > > +     * @exception if no meta data available for the given alias
> > > +     */
> > > +    public ClassMetaData resolve(String alias) {
> > > +        MetaDataRepository repos =
> > _ctx.getConfiguration().getMetaDataRepositoryInstance();
> > > +        return repos.getMetaData(alias,
> > Thread.currentThread().getContextClassLoader(), true);
> > > +    }
> > > +
> > > +    public String toString() {
> > > +        if (_listenSocket == null) return "JEST Server [not strated]";
> > > +        return "JEST Server " +
> > _listenSocket.getInetAddress()+":"+_listenSocket.getLocalPort();
> > > +    }
> > > +
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > >
> >
> ------------------------------------------------------------------------------
> > >     svn:mime-type = text/plain
> > >
> > >
> > >
> >
>

Re: svn commit: r1028093 [1/2] - JEST...

Posted by Kevin Sutter <kw...@gmail.com>.
I have to admit that I was having the same questions/concerns that Donald
just raised.  I thought we were still in the discussion and experimentation
phase of this REST work.  Is it really ready for prime time?

Kevin

On Wed, Oct 27, 2010 at 4:58 PM, Donald Woods <dw...@apache.org> wrote:

> Is this really ready to drop into trunk?
> And do we really want it in the base openjpa.jar?
>
>
> -Donald
>
>
> On 10/27/10 4:42 PM, ppoddar@apache.org wrote:
> > Author: ppoddar
> > Date: Wed Oct 27 20:42:44 2010
> > New Revision: 1028093
> >
> > URL: http://svn.apache.org/viewvc?rev=1028093&view=rev
> > Log:
> > OPENJPA-1851: First version of JEST (REST on OpenJPA)
> >
> > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ServerContext.java
>   (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/XMLEncoder.java
>   (with props)
> > Modified:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> >
> > Modified:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> (original)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> Wed Oct 27 20:42:44 2010
> > @@ -45,10 +45,10 @@ import org.apache.openjpa.lib.util.Close
> >  import org.apache.openjpa.lib.util.Localizer;
> >  import org.apache.openjpa.persistence.criteria.CriteriaBuilderImpl;
> >  import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
> > +import org.apache.openjpa.persistence.jest.Server;
> >  import org.apache.openjpa.persistence.meta.MetamodelImpl;
> >  import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
> >  import org.apache.openjpa.persistence.query.QueryBuilderImpl;
> > -import org.apache.openjpa.util.UserException;
> >
> >  /**
> >   * Implementation of {@link EntityManagerFactory} that acts as a
> > @@ -70,7 +70,8 @@ public class EntityManagerFactoryImpl
> >      private transient StoreCache _cache = null;
> >      private transient QueryResultCache _queryCache = null;
> >      private transient MetamodelImpl _metaModel;
> > -
> > +    private transient Server _remoteAccess = null;
> > +
> >      /**
> >       * Default constructor provided for auto-instantiation.
> >       */
> > @@ -93,10 +94,11 @@ public class EntityManagerFactoryImpl
> >
> >      /**
> >       * Delegate must be provided before use.
> > +     * Configures for Remote Access, if appropriate.
> >       */
> >      public void setBrokerFactory(BrokerFactory factory) {
> > -        _factory = new DelegatingBrokerFactory(factory,
> > -            PersistenceExceptions.TRANSLATOR);
> > +        _factory = new DelegatingBrokerFactory(factory,
> PersistenceExceptions.TRANSLATOR);
> > +        configureRemoteAccess(getConfiguration());
> >      }
> >
> >      public OpenJPAConfiguration getConfiguration() {
> > @@ -271,6 +273,10 @@ public class EntityManagerFactoryImpl
> >          if (log.isTraceEnabled()) {
> >              log.trace(this + ".close() invoked.");
> >          }
> > +        if (_remoteAccess != null) {
> > +            _remoteAccess.stop();
> > +            _remoteAccess = null;
> > +        }
> >          _factory.close();
> >      }
> >
> > @@ -396,4 +402,40 @@ public class EntityManagerFactoryImpl
> >              }
> >          }
> >      }
> > +
> > +    /**
> > +     * Configures this unit for remote access.
> > +     */
> > +    protected void configureRemoteAccess(OpenJPAConfiguration conf) {
> > +        Value value = conf.getValue("RemoteAccess");
> > +        if (value == null) {
> > +            return;
> > +        }
> > +        String props = value.getString();
> > +        if (props == null)
> > +            return;
> > +        try {
> > +            _remoteAccess = new Server();
> > +            _remoteAccess.setContext(this);
> > +            Configurations.configureInstance(_remoteAccess, conf,
> props);
> > +            conf.removeValue(value);
> > +            if (!_remoteAccess.start()) {
> > +                _remoteAccess = null;
> > +            }
> > +        } catch (Exception ex) {
> > +            Log log =
> _factory.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
> > +            if (log != null) {
> > +                log.error(_loc.get("remote-start-error"), ex);
> > +            }
> > +        }
> > +    }
> > +
> > +    /**
> > +     * Affirms if this unit is accessible remotely.
> > +     */
> > +    public boolean allowsRemoteAccess() {
> > +        return _remoteAccess != null;
> > +    }
> > +
> > +
> >  }
> >
> > Modified:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> (original)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> Wed Oct 27 20:42:44 2010
> > @@ -188,6 +188,7 @@ public class PersistenceProductDerivatio
> >          conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(),
>  PersistenceMetaDataFactory.class.getName());
> >
> >          conf.addValue(new EntityManagerFactoryValue());
> > +        conf.addString("RemoteAccess");
> >
> >          conf.readLockLevel.setAlias("optimistic",
> String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
> >          conf.readLockLevel.setAlias("optimistic-force-increment", String
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,77 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.OutputStream;
> > +import java.io.PrintStream;
> > +
> > +/**
> > + * Abstract implementation of a stream-based response.
> > + * Every response is result of a {@linkplain Request} and operates
> within a {@linkplain ServerContext}.
> > + * This fact is enforced by the constructor argument of an abstract
> response.
> > + * <p>
> > + * Besides, this implementation provides common utility to write HTTP
> response or extract useful information
> > + * from the request.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public abstract class AbstractResponse extends PrintStream implements
> Response {
> > +    protected final Request _request;
> > +    protected final ServerContext _ctx;
> > +
> > +    /**
> > +     * Construct a response for the given request and server context.
> > +     *
> > +     * @param request the request for this response. Can be null if the
> response is for server error.
> > +     * @param ctx the processing context
> > +     * @param out the output stream where the response is targeted.
> > +     */
> > +    protected AbstractResponse(Request request, ServerContext ctx,
> OutputStream out) {
> > +        super(out);
> > +        _request = request;
> > +        _ctx = ctx;
> > +    }
> > +
> > +    /**
> > +     * Write a HTTP header to the stream.
> > +     * <br>
> > +     * The format of HTTP header is <code>key: [value]* NEWLINE</code>
> > +     *
> > +     * @param key the key of the header
> > +     * @param values one of more value of the header fields.
> > +     */
> > +    protected void printHeader(String key, String...values) {
> > +        if (key == null)
> > +            return;
> > +        print(key);
> > +        print(" :");
> > +        if (values == null || values.length == 0)
> > +            return;
> > +        int n = values.length-1;
> > +        for (int i = 0; i < n-1; i++) {
> > +            print(values[i]);
> > +            print(";");
> > +        }
> > +        println(values[n]);
> > +    }
> > +
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,68 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.OutputStream;
> > +import java.net.HttpURLConnection;
> > +
> > +/**
> > + * A HTTP response for something gone wrong.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class ErrorResponse extends AbstractResponse {
> > +    private final Exception _error;
> > +    private final int _code;
> > +    /**
> > +     * Construct a response to describe a error.
> > +     *
> > +     * @param request a request that produced this response. VCan be
> null to denote that the request can not
> > +     * be created.
> > +     * @param ctx the processing context
> > +     * @param ex the error
> > +     * @param code HTTP error code
> > +     * @param out the stream where the response is written
> > +     *
> > +     */
> > +    public ErrorResponse(Request request, ServerContext ctx, Exception
> ex, int code, OutputStream out)  {
> > +        super(request, ctx, out);
> > +        _error = ex;
> > +        _code = code;
> > +    }
> > +
> > +    /**
> > +     * Writes the response.
> > +     * The response is always a HTTP response with the error stack
> trace.
> > +     */
> > +    public void writeOut() throws Exception {
> > +        println("HTTP/1.1"); print(" " + _code); println("Error");
> > +        printHeader("Connection",  "close");
> > +        printHeader("Content-Type", "text/html", "charset=UTF-8");
> > +        println();
> > +        println("<html><body><pre>");
> > +        _error.printStackTrace(this);
> > +        println("Response from JEST");
> > +
> > +        println("</pre></body></html>");
> > +        close();
> > +    }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,147 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.InputStream;
> > +import java.io.OutputStream;
> > +import java.net.HttpURLConnection;
> > +import java.util.Iterator;
> > +import java.util.List;
> > +import java.util.Map;
> > +
> > +import javax.persistence.EntityManager;
> > +import javax.persistence.EntityNotFoundException;
> > +import javax.persistence.Query;
> > +
> > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > +import org.apache.openjpa.kernel.StoreContext;
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > +import org.apache.openjpa.util.ApplicationIds;
> > +import org.apache.openjpa.util.ObjectNotFoundException;
> > +
> > +/**
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class GETRequest extends JESTRequest {
> > +    public Response process(ServerContext server, OutputStream out)
> throws Exception {
> > +        String action = getAction();
> > +        try {
> > +            if ("find".equals(action)) {
> > +                return find(server, out);
> > +            } else {
> > +                return resource(server, out);
> > +            }
> > +        } catch (Exception e) {
> > +            return new ErrorResponse(this, server, new
> RuntimeException("bad action " + action),
> > +                HttpURLConnection.HTTP_BAD_REQUEST, out);
> > +        }
> > +    }
> > +
> > +    Response find(ServerContext server, OutputStream out)  throws
> Exception {
> > +        EntityManager em =
> server.getPersistenceUnit().createEntityManager();
> > +        Map<String, String> qualifiers = getQualifiers();
> > +        Map<String, String> parameters = getParameters();
> > +        if (parameters.size() < 2)
> > +            throw new IllegalArgumentException("find must have at least
> two parameters");
> > +        Object[] pks = new Object[parameters.size()-1];
> > +        Iterator<Map.Entry<String,String>> params =
> parameters.entrySet().iterator();
> > +        String alias = null;
> > +        for (int i = 0; i < parameters.size(); i++) {
> > +            if (i == 0) {
> > +                alias = params.next().getKey();
> > +            } else {
> > +                pks[i-1] = params.next().getKey();
> > +            }
> > +        }
> > +        ClassMetaData meta = server.resolve(alias);
> > +        Object oid = ApplicationIds.fromPKValues(pks, meta);
> > +        Object pc = em.find(meta.getDescribedType(), oid);
> > +        if (pc != null) {
> > +            OpenJPAStateManager sm =
> ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(pc);
> > +            return new JESTResponse(this, server, sm, out);
> > +        } else {
> > +            return new ErrorResponse(this, server, new
> EntityNotFoundException("not found!"),
> > +                HttpURLConnection.HTTP_NOT_FOUND, out);
> > +        }
> > +    }
> > +
> > +    Response query(ServerContext server, OutputStream out)  throws
> Exception {
> > +        EntityManager em =
> server.getPersistenceUnit().createEntityManager();
> > +        Map<String, String> qualifiers = getQualifiers();
> > +        boolean named = isBooleanQualifier("named");
> > +        boolean single = isBooleanQualifier("single");
> > +        Map<String, String> parameters = getParameters();
> > +        if (parameters.size() < 1)
> > +            throw new IllegalArgumentException("find must have at least
> one parameter");
> > +        Iterator<Map.Entry<String,String>> params =
> parameters.entrySet().iterator();
> > +        Query query = null;
> > +        int i = 0;
> > +        for (Map.Entry<String, String> param : parameters.entrySet()) {
> > +            if (i == 0) {
> > +                query = named ? em.createQuery(param.getKey()) :
> em.createNamedQuery(param.getKey());
> > +            } else {
> > +                query.setParameter(param.getKey(), param.getValue());
> > +            }
> > +        }
> > +        if (single) {
> > +            Object result = query.getSingleResult();
> > +            OpenJPAStateManager sm =
> ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(result);
> > +            return new JESTResponse(this, server, sm, out);
> > +        } else {
> > +            List<Object> result = query.getResultList();
> > +            return new ErrorResponse(this, server, new
> EntityNotFoundException("not found!"), 404, out);
> > +        }
> > +    }
> > +
> > +    Response resource(ServerContext server, OutputStream out)  throws
> Exception {
> > +        String resource = getAction();
> > +        if (resource.length() == 0)
> > +            resource = "index.html";
> > +        String mimeType = getMimeType(resource);
> > +//        InputStream in =
> Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
> > +        InputStream in = getClass().getResourceAsStream(resource);
> > +        if (in == null) {
> > +            return new ErrorResponse(this, server, new
> ObjectNotFoundException(resource), 404, out);
> > +        }
> > +        if (server.getLog().isTraceEnabled())
> > +            server.getLog().trace("Found resource " + resource);
> > +        return mimeType.startsWith("image")
> > +          ? new ImageResponse(this, server, in, mimeType, out)
> > +          : new ResourceResponse(this, server, in, mimeType, out);
> > +    }
> > +
> > +    boolean isBooleanQualifier(String key) {
> > +        String q = getQualifier(key);
> > +        return hasQualifier(key) && (q == null || "true".equals(q));
> > +    }
> > +
> > +    String getMimeType(String resource) {
> > +        int index = resource.lastIndexOf('.');
> > +        String ext = (index != -1) ? resource.substring(index+1) : "";
> > +        if (ext.equalsIgnoreCase("html") ||
> ext.equalsIgnoreCase(".html")) return "text/html";
> > +        if (ext.equalsIgnoreCase(".png") || ext.equalsIgnoreCase(".ico")
> || ext.equalsIgnoreCase("jpeg"))
> > +            return "image/"+ext;
> > +        return "text/html";
> > +    }
> > +
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,61 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.InputStream;
> > +import java.io.OutputStream;
> > +import java.io.RandomAccessFile;
> > +
> > +import javax.imageio.ImageIO;
> > +
> > +/**
> > + * Sends an image as response.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class ImageResponse extends AbstractResponse {
> > +    private final InputStream _in;
> > +    private final String _mimeType;
> > +
> > +    public ImageResponse(Request request, ServerContext ctx, InputStream
> resource, String mimeType,
> > +        OutputStream out) throws Exception {
> > +        super(request, ctx, out);
> > +        _in = resource;
> > +        _mimeType = mimeType;
> > +    }
> > +
> > +    public void writeOut() throws Exception {
> > +        print(_request.getProtocol()); println("200 OK");
> > +        printHeader("Connection",  "close");
> > +        printHeader("Content-Type", _mimeType);
> > +        println();
> > +        byte[] b = new byte[1024];
> > +        int i = 0;
> > +        for (int l = 0; (l = _in.read(b)) != -1;) {
> > +            write(b, 0, l);
> > +            i += l;
> > +        }
> > +        printHeader("Content-Length", ""+i);
> > +        _in.close();
> > +        close();
> > +    }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,386 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.IOException;
> > +import java.util.Arrays;
> > +import java.util.Collections;
> > +import java.util.HashMap;
> > +import java.util.LinkedHashMap;
> > +import java.util.LinkedList;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.NoSuchElementException;
> > +
> > +/**
> > + * A request carries requisite data for a JPA operation to be performed.
> > + * The request is populated by parsing an input data stream.
> > + *
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public abstract class JESTRequest implements Request {
> > +    private String _method;
> > +    private String _protocol;
> > +    private String _action;
> > +    private String _body;
> > +    private LinkedHashMap<String, String> _qualifiers = new
> LinkedHashMap<String, String>();
> > +    private LinkedHashMap<String, String> _params = new
> LinkedHashMap<String, String>();
> > +    private Map<String, List<String>> _headers = new HashMap<String,
> List<String>>();
> > +    private ParseState _state;
> > +    private StringBuffer buf = new StringBuffer();
> > +    private LinkedList<Token> _stack = new LinkedList<Token>();
> > +
> > +    public static final List<String> METHODS = Arrays.asList(new
> String[]{"GET","POST","PUT","DELETE"});
> > +
> > +    /**
> > +     * Parse States.
> > +     */
> > +    static enum ParseState {
> > +        INIT, ACTION, QUALIFIER_KEY, QUALIFIER_VALUE, PARAM_KEY,
> PARAM_VALUE, END
> > +    };
> > +
> > +
> > +    public String getMethod() {
> > +        return _method;
> > +    }
> > +
> > +    void setMethod(String method) {
> > +        if (_method == null) {
> > +            if (method != null &&
> METHODS.contains(method.toUpperCase())) {
> > +                _method = method.toUpperCase();
> > +            } else {
> > +                throw new IllegalArgumentException("Unsupported method "
> + method);
> > +            }
> > +        } else if (!_method.equalsIgnoreCase(method)) {
> > +            throw new IllegalStateException("Method can not be changed
> to [" + method + "]. " +
> > +                "Current method [" + _method + "]");
> > +        }
> > +    }
> > +
> > +    public String getProtocol() {
> > +        return _protocol == null ? "HTTP/1.1" : _protocol;
> > +    }
> > +
> > +    void setProtocol(String protocol) {
> > +        if (_protocol == null) {
> > +            if (protocol != null &&
> protocol.toUpperCase().startsWith("HTTP")) {
> > +                _protocol = protocol.toUpperCase();
> > +            } else {
> > +                throw new IllegalArgumentException("Unsupported protocol
> " + protocol);
> > +            }
> > +        } else if (!_protocol.equalsIgnoreCase(protocol)) {
> > +            throw new IllegalStateException("Protocol can not be changed
> to [" + protocol + "]. " +
> > +                "Current protocol [" + _protocol + "]");
> > +        }
> > +    }
> > +
> > +    /**
> > +     * Sets an action. Once set, an action can not be modified.
> > +     *
> > +     * @param action
> > +     */
> > +    private void setAction(String action) {
> > +        if (_action == null) {
> > +            _action = action;
> > +        } else if (!_action.equals(action)) {
> > +            throw new IllegalStateException("Action can not be [" +
> action + "]. Already set to [" + _action + "]");
> > +        }
> > +    }
> > +
> > +    public String getAction() {
> > +        return _action == null ? "" : _action;
> > +    }
> > +
> > +    public String getBody() {
> > +        return _body;
> > +    }
> > +
> > +    private void setQualifier(String key, String value) {
> > +        _qualifiers.put(key, value);
> > +    }
> > +
> > +    public String getQualifier(String key) {
> > +        return _qualifiers.get(key);
> > +    }
> > +
> > +    public Map<String, String> getQualifiers() {
> > +        return Collections.unmodifiableMap(_qualifiers);
> > +    }
> > +
> > +    public boolean hasQualifier(String key) {
> > +        return _qualifiers.containsKey(key);
> > +    }
> > +
> > +    private void setParameter(String key, String value) {
> > +        _params.put(key, value);
> > +    }
> > +
> > +    public String getParameter(String key) {
> > +        return _params.get(key);
> > +    }
> > +
> > +    public boolean hasParameter(String key) {
> > +        return _params.containsKey(key);
> > +    }
> > +
> > +    public Map<String, String> getParameters() {
> > +        return Collections.unmodifiableMap(_params);
> > +    }
> > +
> > +    public Map.Entry<String, String> getParameter(int n) {
> > +        if (n >= _params.size())
> > +            throw new NoSuchElementException("Index " + n + " size " +
> _params.size());
> > +        int i = 0;
> > +        for (Map.Entry<String, String> entry : _params.entrySet()) {
> > +            if (i == n) {
> > +                return entry;
> > +            }
> > +            i++;
> > +        }
> > +        return null;
> > +    }
> > +
> > +    public Map<String, List<String>> getHeaders() {
> > +        return Collections.unmodifiableMap(_headers);
> > +    }
> > +
> > +    public List<String> getHeader(String key) {
> > +        return _headers.get(key);
> > +    }
> > +
> > +
> > +    public void read(List<String> lines) throws IOException {
> > +        parse(lines.get(0));
> > +        int i = 1;
> > +        for (; i < lines.size(); i++) {
> > +            String line = lines.get(i);
> > +            if (line.length() == 0) {
> > +                break;
> > +            } else {
> > +                parseHeader(line);
> > +            }
> > +        }
> > +        parseBody(lines.subList(i, lines.size()));
> > +    }
> > +
> > +    protected void parseHeader(String line) throws IOException {
> > +        String key = null;
> > +        StringBuilder token = new StringBuilder();
> > +        int N = line.length();
> > +        for (int i = 0; i < N; i++) {
> > +            char c = line.charAt(i);
> > +            if (c == ':' && key == null) {
> > +                key = token.toString().trim();
> > +                token.delete(0, token.length());
> > +            } else {
> > +                token.append(c);
> > +            }
> > +        }
> > +        if (key != null) {
> > +            _headers.put(key,
> Collections.singletonList(token.toString().trim()));
> > +        }
> > +    }
> > +
> > +    protected void parseBody(List<String> lines) {
> > +        if (lines == null || lines.isEmpty())
> > +            return;
> > +        for (String line : lines) {
> > +            if (_body == null) {
> > +                _body = line;
> > +            } else {
> > +                _body = _body + line;
> > +            }
> > +        }
> > +    }
> > +
> > +    /**
> > +     * Parses JEST stream and populates a request.
> > +     *
> > +     */
> > +     protected void parse(String s) {
> > +            char[] chars = s.toCharArray();
> > +            _state = ParseState.INIT;
> > +            _stack.clear();
> > +
> > +            for (int i = 0; i < chars.length; i++) {
> > +                char ch = chars[i];
> > +                switch (_state) {
> > +                    case INIT:
> > +                        if (ch == '/') {
> > +                            transit(ParseState.ACTION);
> > +                        } else if (!Character.isWhitespace(ch)) {
> > +                            parseError(ch, i, s, true, ' ');
> > +                        }
> > +                        break;
> > +
> > +                    case ACTION:
> > +                        if (ch == '/') {
> > +                            transit(ParseState.QUALIFIER_KEY);
> > +                        } else if (ch == '?') {
> > +                            transit(ParseState.PARAM_KEY);
> > +                        } else {
> > +                            buf.append(ch);
> > +                        }
> > +                        break;
> > +
> > +                    case QUALIFIER_KEY:
> > +                        if (Character.isJavaIdentifierPart(ch)) {
> > +                            buf.append(ch);
> > +                        } else if (ch == '=') {
> > +                            transit(ParseState.QUALIFIER_VALUE);
> > +                        } else if (ch == '/') {
> > +                            transit(ParseState.QUALIFIER_KEY);
> > +                        } else if (ch == '?') {
> > +                            transit(ParseState.PARAM_KEY);
> > +                        } else {
> > +                            parseError(ch, i, s, true, '/', '?', '=');
> > +                        }
> > +                        break;
> > +
> > +                    case QUALIFIER_VALUE:
> > +                        if (Character.isJavaIdentifierPart(ch)) {
> > +                            buf.append(ch);
> > +                        } else if (ch == '/') {
> > +                            transit(ParseState.QUALIFIER_KEY);
> > +                        } else if (ch == '?') {
> > +                            transit(ParseState.PARAM_KEY);
> > +                        } else {
> > +                            parseError(ch, i, s, true, '/', '?');
> > +                        }
> > +                        break;
> > +
> > +                    case PARAM_KEY:
> > +                        if (Character.isJavaIdentifierPart(ch)) {
> > +                            buf.append(ch);
> > +                        } else if (ch == '=') {
> > +                            if (isQueryKey())
> > +                                buf.append(ch);
> > +                            else
> > +                                transit(ParseState.PARAM_VALUE);
> > +                        } else if (ch == ';') {
> > +                            transit(ParseState.PARAM_KEY);
> > +                        } else if (isQueryKey() && isQueryChar(ch)) {
> > +                            buf.append(ch);
> > +                        } else {
> > +                            parseError(ch, i, s, true, ';', '=');
> > +                        }
> > +                        break;
> > +
> > +                    case PARAM_VALUE:
> > +                        if (Character.isJavaIdentifierPart(ch)) {
> > +                            buf.append(ch);
> > +                        } else if (ch == ';') {
> > +                            transit(ParseState.PARAM_KEY);
> > +                        } else {
> > +                            parseError(ch, i, s, true, ';');
> > +                        }
> > +                        break;
> > +                    default:
> > +                        throw new RuntimeException("ParseError: '" + ch
> + "' at " + i + " in [" + s + "]. "
> > +                            + "Unknown state " + _state);
> > +                }
> > +            }
> > +            if (buf.length() > 0) {
> > +                transit(ParseState.END);
> > +            }
> > +        }
> > +
> > +        /**
> > +         * Affirms if parsing a query string.
> > +         */
> > +        private boolean isQueryKey() {
> > +            return "query".equals(_action) && _stack.size() == 1;
> > +        }
> > +
> > +        /**
> > +         * Affirms if the given character is valid in a query string
> > +         */
> > +        private boolean isQueryChar(char c) {
> > +            return c == ' ' || c == '.' || c == ':' || c == '?' || c ==
> '\'';
> > +        }
> > +
> > +        /**
> > +         * Transitions to a new parse state.
> > +         *
> > +         * @param to target parse state
> > +         */
> > +        void transit(ParseState to) {
> > +            String token = buf.toString();
> > +            switch (_state) {
> > +                case ACTION:
> > +                    setAction(token);
> > +                    break;
> > +                case QUALIFIER_KEY:
> > +                    setQualifier(token, null);
> > +                    break;
> > +                case QUALIFIER_VALUE:
> > +                    setQualifier(_stack.peekLast().getValue(), token);
> > +                    break;
> > +                case PARAM_KEY:
> > +                    setParameter(token, null);
> > +                    break;
> > +                case PARAM_VALUE:
> > +                    setParameter(_stack.peekLast().getValue(), token);
> > +                    break;
> > +
> > +            }
> > +            if (_state != ParseState.INIT && to != ParseState.END) {
> > +                _stack.add(new Token(_state, token));
> > +            }
> > +            buf.delete(0, buf.length());
> > +            _state = to;
> > +        }
> > +
> > +        protected void parseError(char ch, int pos, String line, boolean
> java, char... expected) {
> > +            throw new RuntimeException("ParseError: Encountered '" + ch
> + "' at " + pos + " in [" + line + "] while "
> > +                + "parsing " + _state + ". Expected " +
> Arrays.toString(expected) + (java ? " or Java identifer" : ""));
> > +
> > +        }
> > +
> > +        /**
> > +         * Token in a JEST stream.
> > +         *
> > +         */
> > +        static class Token {
> > +            final ParseState _type;
> > +            final String _value;
> > +
> > +            public Token(ParseState type, String value) {
> > +                _type = type;
> > +                _value = value;
> > +            }
> > +
> > +            public ParseState getType() {
> > +                return _type;
> > +            }
> > +
> > +            public String getValue() {
> > +                return _value;
> > +            }
> > +
> > +            public String toString() {
> > +                return _value + "[" + _type + "]";
> > +            }
> > +        }
> > +
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,71 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.IOException;
> > +import java.io.OutputStream;
> > +import java.io.PrintStream;
> > +import java.io.PrintWriter;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.Set;
> > +
> > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > +import org.w3c.dom.Document;
> > +import org.w3c.dom.Element;
> > +
> > +/**
> > + * Response of a JEST Request.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class JESTResponse extends AbstractResponse {
> > +    private final OpenJPAStateManager _sm;
> > +
> > +    public JESTResponse(Request request, ServerContext ctx,
> OpenJPAStateManager sm, OutputStream out) throws Exception {
> > +        super(request, ctx, out);
> > +        _sm = sm;
> > +    }
> > +
> > +    public String getContentType() {
> > +        return "text/html";
> > +    }
> > +
> > +    public void writeOut() throws Exception {
> > +        print(_request.getProtocol()); println("200 OK");
> > +        printHeader("Connection",  "close");
> > +        printHeader("Content-Type", "text/html", "charset=UTF-8");
> > +        println();
> > +        println("<html><body><pre>");
> > +        XMLEncoder encoder = new
> XMLEncoder(_ctx.getPersistenceUnit().getMetamodel());
> > +        Document doc = encoder.encode(_sm);
> > +        encoder.writeDoc(doc, this);
> > +        println("Response from JEST");
> > +
> > +        println("</pre></body></html>");
> > +        close();
> > +    }
> > +
> > +}
> > +
> > +
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,316 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.BufferedReader;
> > +import java.io.CharArrayWriter;
> > +import java.io.IOException;
> > +import java.io.InputStream;
> > +import java.io.InputStreamReader;
> > +import java.io.Reader;
> > +import java.util.Arrays;
> > +import java.util.BitSet;
> > +import java.util.Collection;
> > +import java.util.HashSet;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.Set;
> > +
> > +import javax.persistence.metamodel.Attribute;
> > +
> > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > +import org.apache.openjpa.kernel.StoreContext;
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.meta.FieldMetaData;
> > +import org.apache.openjpa.meta.JavaTypes;
> > +import org.apache.openjpa.meta.ValueMetaData;
> > +import org.apache.openjpa.persistence.meta.Members;
> > +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > +import org.w3c.dom.CDATASection;
> > +import org.w3c.dom.Document;
> > +import org.w3c.dom.Element;
> > +
> > +/**
> > + * Marshals a root instance and its persistent closure as JSON object.
> > + * The closure is resolved against the persistence context that contains
> the root instance.
> > + * The JSON format introduces a $id and $ref to address reference that
> pure JSON does not.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class JSONEncoder {
> > +    /**
> > +     * The element/attribute tags declared in
> <code>jest-instance.xsd</code> XML schema.
> > +     */
> > +    public static final String ELEMENT_NULL_REF    = "null";
> > +    public static final String ELEMENT_INSTANCE    = "instance";
> > +    public static final String ELEMENT_REF         = "ref";
> > +
> > +
> > +    private MetamodelHelper _model;
> > +
> > +    public JSONEncoder(MetamodelImpl model) {
> > +        _model = new MetamodelHelper(model);
> > +    }
> > +
> > +    /**
> > +     * Encodes the given managed instance into a new XML element as a
> child of the given parent node.
> > +     *
> > +     * @param sm a managed instance, can be null.
> > +     * @param parent the parent node to which the new node be attached.
> > +     */
> > +    public StringBuilder encode(final OpenJPAStateManager sm) {
> > +        return encode(sm, new HashSet<OpenJPAStateManager>(), 0, false);
> > +    }
> > +    StringBuilder indent(StringBuilder buf, int indent) {
> > +        if (indent <= 0)
> > +            return buf;
> > +        char[] spaces = new char[indent*4];
> > +        Arrays.fill(spaces, ' ');
> > +        buf.insert(0, spaces);
> > +        return buf;
> > +    }
> > +    StringBuilder end(StringBuilder buf, char ch, int indent) {
> > +        char[] spaces = new char[indent*4];
> > +        Arrays.fill(spaces, ' ');
> > +        return buf.append("\r\n").append(spaces).append(ch);
> > +    }
> > +
> > +    /**
> > +     * Encodes the closure of a persistent instance into a XML element.
> > +     *
> > +     * @param sm the managed instance to be encoded. Can be null.
> > +     * @param parent the parent XML element to which the new XML element
> be added. Must not be null. Must be
> > +     * owned by a document.
> > +     * @param visited the persistent instances that had been encoded
> already. Must not be null or immutable.
> > +     *
> > +     * @return the new element. The element has been appended as a child
> to the given parent in this method.
> > +     */
> > +    private StringBuilder encode(final OpenJPAStateManager sm, final
> Set<OpenJPAStateManager> visited,
> > +        int indent, boolean indentPara) {
> > +        if (visited == null) {
> > +            throw new IllegalArgumentException("null closure for
> encoder");
> > +        }
> > +        StringBuilder root =  indent(new StringBuilder("{"), indentPara
> ? indent : 0);
> > +        if (sm == null) {
> > +            return root.append("null}");
> > +        }
> > +        boolean ref = !visited.add(sm);
> > +        if (ref) {
> > +            return indent(root.append(quoted("$ref")).append(":
> ").append(ior(sm)).append('}'),
> > +                indentPara ? indent : 0);
> > +        } else {
> > +            indent(root.append(quoted("$id")).append(":
> ").append(ior(sm)), indentPara ? indent : 0);
> > +        }
> > +
> > +        StringBuilder child = new StringBuilder();
> > +        BitSet loaded = sm.getLoaded();
> > +        StoreContext ctx = (StoreContext)sm.getGenericContext();
> > +        List<Attribute<?, ?>> attrs =
> _model.getAttributesInOrder(sm.getMetaData());
> > +        for (int i = 0; i < attrs.size(); child = new StringBuilder(),
> i++) {
> > +            FieldMetaData fmd = ((Members.Member<?, ?>)
> attrs.get(i)).fmd;
> > +            if (!loaded.get(fmd.getIndex()))
> > +                continue;
> > +            Object value = sm.fetch(fmd.getIndex());
> > +            child.append(quoted(fmd.getName())).append(": ");
> > +            switch (fmd.getDeclaredTypeCode()) {
> > +                case JavaTypes.BOOLEAN:
> > +                case JavaTypes.BYTE:
> > +                case JavaTypes.CHAR:
> > +                case JavaTypes.DOUBLE:
> > +                case JavaTypes.FLOAT:
> > +                case JavaTypes.INT:
> > +                case JavaTypes.LONG:
> > +                case JavaTypes.SHORT:
> > +
> > +                case JavaTypes.BOOLEAN_OBJ:
> > +                case JavaTypes.BYTE_OBJ:
> > +                case JavaTypes.CHAR_OBJ:
> > +                case JavaTypes.DOUBLE_OBJ:
> > +                case JavaTypes.FLOAT_OBJ:
> > +                case JavaTypes.INT_OBJ:
> > +                case JavaTypes.LONG_OBJ:
> > +                case JavaTypes.SHORT_OBJ:
> > +
> > +                case JavaTypes.BIGDECIMAL:
> > +                case JavaTypes.BIGINTEGER:
> > +                case JavaTypes.DATE:
> > +                case JavaTypes.NUMBER:
> > +                case JavaTypes.CALENDAR:
> > +                case JavaTypes.LOCALE:
> > +                case JavaTypes.STRING:
> > +                case JavaTypes.ENUM:
> > +                         child.append(quoted(value));
> > +                break;
> > +
> > +                case JavaTypes.PC:
> > +                    if (value == null) {
> > +                        child.append("null");
> > +                    } else {
> > +                        child.append(encode(ctx.getStateManager(value),
> visited, indent+1, false));
> > +                    }
> > +                    break;
> > +
> > +                case JavaTypes.ARRAY:
> > +                    Object[] values = (Object[])value;
> > +                    value = Arrays.asList(values);
> > +                // no break;
> > +                case JavaTypes.COLLECTION:
> > +                    if (value == null) {
> > +                        child.append("null");
> > +                        break;
> > +                    }
> > +                    child.append("[");
> > +                    Collection<?> members = (Collection<?>)value;
> > +                    boolean basic = fmd.getElement().getTypeMetaData()
> == null;
> > +                    int k = 0;
> > +                    for (Object o : members) {
> > +                        child.append("\r\n");
> > +                        if (o == null) {
> > +                            child.append(indent(new
> StringBuilder("null"), indent+1));
> > +                        } else {
> > +                            if (basic) {
> > +                                child.append(indent(new
> StringBuilder(quoted(o)), indent+1));
> > +                            } else {
> > +
>  child.append(encode(ctx.getStateManager(o), visited, indent+1, true));
> > +                            }
> > +                        }
> > +                    }
> > +                    end(child, ']', indent+1);
> > +                    break;
> > +                case JavaTypes.MAP:
> > +                    if (value == null) {
> > +                        child.append("null");
> > +                        break;
> > +                    }
> > +                    child.append("[");
> > +                    Set<Map.Entry> entries = ((Map)value).entrySet();
> > +                    boolean basicKey   =
> fmd.getElement().getTypeMetaData() == null;
> > +                    boolean basicValue =
> fmd.getValue().getTypeMetaData() == null;
> > +                    for (Map.Entry<?,?> e : entries) {
> > +                        if (e.getKey() == null) {
> > +                            child.append("null:");
> > +                        } else {
> > +                            if (basicKey) {
> > +
>  child.append(quoted(e.getKey())).append(":");
> > +                            } else {
> > +
>  child.append(encode(ctx.getStateManager(e.getKey()), visited, indent+1,
> true));
> > +                            }
> > +                        }
> > +                        if (e.getValue() == null) {
> > +                            child.append("null");
> > +                        } else {
> > +                            if (basicValue) {
> > +                                child.append(quoted(e.getValue()));
> > +                            } else {
> > +
>  child.append(encode(ctx.getStateManager(e.getValue()), visited, indent+1,
> false));
> > +                            }
> > +                        }
> > +                    }
> > +                    break;
> > +
> > +                case JavaTypes.INPUT_STREAM:
> > +                case JavaTypes.INPUT_READER:
> > +                    child = new StringBuilder(fmd.getName());
> > +                    if (value == null) {
> > +                        child.append("null");
> > +                    } else {
> > +                        child.append(streamToString(value));
> > +                    }
> > +                    break;
> > +
> > +                case JavaTypes.PC_UNTYPED:
> > +                case JavaTypes.OBJECT:
> > +                case JavaTypes.OID:
> > +                    System.err.println("Not handled " + fmd.getName() +
> " of type " + fmd.getDeclaredType());
> > +            }
> > +
> > +            if (child != null) {
> > +                root.append("\r\n");
> > +                root.append(indent(child, indent+1));
> > +                if (loaded.length()-1 != i)
> > +                    root.append(",");
> > +           }
> > +        }
> > +        return end(root, '}', indent);
> > +    }
> > +
> > +
> > +    String ior(OpenJPAStateManager sm) {
> > +        return quoted(typeOf(sm)+"-"+sm.getObjectId().toString());
> > +    }
> > +
> > +    String typeOf(OpenJPAStateManager sm) {
> > +        return sm.getMetaData().getDescribedType().getSimpleName();
> > +    }
> > +
> > +    String typeOf(Class<?> cls) {
> > +        return cls.getSimpleName();
> > +    }
> > +
> > +    String typeOf(ClassMetaData meta) {
> > +        return meta.getDescribedType().getSimpleName();
> > +    }
> > +
> > +    String typeOf(ValueMetaData vm) {
> > +        if (vm.getTypeMetaData() == null)
> > +            return typeOf(vm.getType());
> > +        return typeOf(vm.getTypeMetaData());
> > +    }
> > +
> > +    String typeOf(FieldMetaData fmd) {
> > +        return fmd.getType().getSimpleName();
> > +    }
> > +
> > +
> > +    /**
> > +     * Convert the given stream (either an InutStream or a Reader) to a
> String
> > +     * to be included in CDATA section of a XML document.
> > +     *
> > +     * @param value the field value to be converted. Can not be null
> > +     * @return
> > +     */
> > +    String streamToString(Object value) {
> > +        Reader reader = null;
> > +        if (value instanceof InputStream) {
> > +            reader = new BufferedReader(new
> InputStreamReader((InputStream)value));
> > +        } else if (value instanceof Reader) {
> > +            reader = (Reader)value;
> > +        } else {
> > +            throw new RuntimeException();
> > +        }
> > +        CharArrayWriter writer = new CharArrayWriter();
> > +        try {
> > +            for (int c; (c = reader.read()) != -1;) {
> > +                writer.write(c);
> > +            }
> > +        } catch (IOException ex) {
> > +            throw new RuntimeException(ex);
> > +        }
> > +        return writer.toString();
> > +    }
> > +
> > +    String quoted(Object o) {
> > +        if (o == null) return "null";
> > +        if (o instanceof Number)
> > +            return o.toString();
> > +        return "\"" + o.toString() + "\"";
> > +    }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,127 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.util.ArrayList;
> > +import java.util.Collections;
> > +import java.util.Comparator;
> > +import java.util.HashMap;
> > +import java.util.List;
> > +import java.util.Map;
> > +
> > +import javax.persistence.metamodel.Attribute;
> > +import javax.persistence.metamodel.EntityType;
> > +import javax.persistence.metamodel.ManagedType;
> > +import javax.persistence.metamodel.Metamodel;
> > +import javax.persistence.metamodel.SingularAttribute;
> > +
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.meta.FieldMetaData;
> > +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > +
> > +/**
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class MetamodelHelper {
> > +    private MetamodelImpl _model;
> > +    private Map<ManagedType<?>, List<Attribute<?, ?>>> _attrs = new
> HashMap<ManagedType<?>, List<Attribute<?,?>>>();
> > +
> > +    public MetamodelHelper(MetamodelImpl model) {
> > +        _model = model;
> > +    }
> > +
> > +    public List<Attribute<?,?>> getAttributesInOrder(Class<?> cls) {
> > +        return getAttributesInOrder(_model.managedType(cls));
> > +    }
> > +
> > +    public List<Attribute<?,?>> getAttributesInOrder(ClassMetaData meta)
> {
> > +        return getAttributesInOrder(meta.getDescribedType());
> > +    }
> > +
> > +    /**
> > +     * Gets the attributes of the given type in defined order.
> > +     * @param type
> > +     * @return
> > +     */
> > +    public List<Attribute<?,?>> getAttributesInOrder(ManagedType<?>
> type) {
> > +        List<Attribute<?,?>> attrs = _attrs.get(type);
> > +        if (attrs != null)
> > +            return attrs;
> > +        List<Attribute<?,?>> list = new
> ArrayList<Attribute<?,?>>(type.getAttributes());
> > +        Collections.sort(list, new AttributeComparator());
> > +        _attrs.put(type, list);
> > +        return list;
> > +    }
> > +
> > +    public static boolean isId(Attribute<?,?> a) {
> > +        if (a instanceof SingularAttribute)
> > +            return ((SingularAttribute<?,?>)a).isId();
> > +        return false;
> > +    }
> > +
> > +    public static boolean isVersion(Attribute<?,?> a) {
> > +        if (a instanceof SingularAttribute)
> > +            return ((SingularAttribute<?,?>)a).isVersion();
> > +        return false;
> > +    }
> > +
> > +    public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
> > +        if (isId(attr))
> > +            return 0;
> > +        if (isVersion(attr))
> > +            return 1;
> > +
> > +      switch (attr.getPersistentAttributeType()) {
> > +      case BASIC :
> > +      case EMBEDDED:
> > +          return 2;
> > +      case ONE_TO_ONE:
> > +      case MANY_TO_ONE:
> > +          return 3;
> > +      case ONE_TO_MANY:
> > +      case MANY_TO_MANY:
> > +      case ELEMENT_COLLECTION: return 4;
> > +      default: return 5;
> > +      }
> > +    }
> > +
> > +    /**
> > +     * Compares attribute by their qualification.
> > +     * Identity
> > +     * Version
> > +     * Basic
> > +     * Singular association
> > +     * Plural association
> > +     *
> > +     */
> > +    public static class AttributeComparator implements
> Comparator<Attribute<?,?>> {
> > +        @Override
> > +        public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
> > +            Integer t1 = getAttributeTypeCode(a1);
> > +            Integer t2 = getAttributeTypeCode(a2);
> > +            if (t1.equals(t2)) {
> > +                return a1.getName().compareTo(a2.getName());
> > +            } else {
> > +                return t1.compareTo(t2);
> > +            }
> > +        }
> > +    }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,138 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.IOException;
> > +import java.io.OutputStream;
> > +import java.io.Serializable;
> > +import java.util.List;
> > +import java.util.Map;
> > +
> > +/**
> > + * A request from a remote client to a server to do something.
> > + * The request arrives as stream of bytes from a remote location
> > + * and if the server can interpret the protocol from the stream,
> > + * then  make a concrete request object.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public interface Request extends Serializable {
> > +    /**
> > +     * Get the HTTP verb such as GET, POST
> > +     *
> > +     * @return uppercase string
> > +     */
> > +    String getMethod();
> > +
> > +    /**
> > +     * Get the first path segment as intended persistence action such as
> <code>find</code> or <code>query</code>
> > +     *
> > +     * @return lowercase action name. Can be empty.
> > +     */
> > +    String getAction();
> > +
> > +    /**
> > +     * Get the protocol such as HTTP/1.1
> > +     *
> > +     * @return upper-case string
> > +     */
> > +    String getProtocol();
> > +
> > +    /**
> > +     * Get the body, if any.
> > +     *
> > +     * @return body of the request. null if no body.
> > +     */
> > +    String getBody();
> > +
> > +    /**
> > +     * Get the headers indexed by the keys.
> > +     * Header values are list of Strings.
> > +     *
> > +     * @return empty map if there is no header
> > +     */
> > +    Map<String, List<String>> getHeaders();
> > +
> > +    /**
> > +     * Get the header values for the given key.
> > +     * @param key a key
> > +     * @return null if no header value for the given key
> > +     */
> > +    List<String> getHeader(String key);
> > +
> > +    /**
> > +     * Affirm if the the given qualifier is available in this request.
> > +     *
> > +     * @param key case-sensitive qualifier
> > +     * @return true if the key is present.
> > +     */
> > +    boolean hasQualifier(String key);
> > +
> > +    /**
> > +     * Gets the value for the given qualifier key.
> > +     *
> > +     * @param key case-sensitive qualifier
> > +     * @return value of the qualifier. null if the key is absent.
> > +     */
> > +    String getQualifier(String key);
> > +
> > +    /**
> > +     * Get all the qualifiers available in this request.
> > +     *
> > +     * @return key-value pairs of the qualifiers. Empty map if no
> qualifier is present.
> > +     */
> > +    Map<String,String> getQualifiers();
> > +
> > +
> > +    /**
> > +     * Affirm if the the given parameter is available in this request.
> > +     *
> > +     * @param key case-sensitive parameter
> > +     * @return true if the key is present.
> > +     */
> > +    boolean hasParameter(String key);
> > +
> > +
> > +    /**
> > +     * Gets the value for the given parameter key.
> > +     *
> > +     * @param key case-sensitive parameter
> > +     * @return value of the parameter. null if the key is absent.
> > +     */
> > +    String getParameter(String key);
> > +
> > +    /**
> > +     * Get all the parameters available in this request.
> > +     *
> > +     * @return key-value pairs of the parameters. Empty map if no
> parameter is present.
> > +     */
> > +    Map<String,String> getParameters();
> > +
> > +    /**
> > +     * Parse the request represented as a list of strings.
> > +     *
> > +     * @param lines each line of the request.
> > +     * @throws IOException
> > +     */
> > +    void read(List<String> lines) throws IOException;
> > +
> > +    Response process(ServerContext server, OutputStream stream) throws
> Exception;
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,63 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.util.HashMap;
> > +import java.util.Map;
> > +
> > +/**
> > + * A factory to create a specific type of request.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class RequestFactory {
> > +    private final String _protocol;
> > +    private static Map<String, RequestFactory> _registered = new
> HashMap<String, RequestFactory>();
> > +    static {
> > +        _registered.put("HTTP/1.0", new RequestFactory("HTTP/1.0"));
> > +        _registered.put("HTTP/1.1", new RequestFactory("HTTP/1.1"));
> > +    }
> > +
> > +    public static void register(String protocol, RequestFactory factory)
> {
> > +        _registered.put(protocol, factory);
> > +    }
> > +
> > +    private RequestFactory(String proto) {
> > +        _protocol = proto;
> > +    }
> > +
> > +    public static RequestFactory getFactory(String protocol) {
> > +        return _registered.get(protocol);
> > +    }
> > +
> > +    Request createRequest(String method) {
> > +        JESTRequest request = null;
> > +        if ("GET".equalsIgnoreCase(method)) {
> > +            request = new GETRequest();
> > +        } else {
> > +            throw new UnsupportedOperationException();
> > +        }
> > +        request.setProtocol(_protocol);
> > +        request.setMethod(method.toUpperCase());
> > +        return request;
> > +
> > +    }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,147 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.BufferedReader;
> > +import java.io.IOException;
> > +import java.io.InputStream;
> > +import java.io.InputStreamReader;
> > +import java.io.OutputStream;
> > +import java.net.HttpURLConnection;
> > +import java.net.Socket;
> > +import java.net.SocketException;
> > +import java.util.ArrayList;
> > +import java.util.Collections;
> > +import java.util.Iterator;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.Set;
> > +import java.util.concurrent.Callable;
> > +
> > +import javax.persistence.EntityManager;
> > +import javax.persistence.EntityNotFoundException;
> > +
> > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > +import org.apache.openjpa.kernel.StoreContext;
> > +import org.apache.openjpa.lib.log.Log;
> > +import org.apache.openjpa.lib.util.Localizer;
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.meta.FieldMetaData;
> > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > +import org.apache.openjpa.persistence.OpenJPAPersistence;
> > +import org.apache.openjpa.util.ApplicationIds;
> > +
> > +/**
> > + * Handles a request from a remote client.
> > + * Reads the socket data.
> > + * Populates the request.
> > + * Determines the processor based on request method and action.
> > + * Delegates to the processor.
> > + * Processor generates the response.
> > + * Writes the response to the stream.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class RequestHandler implements Callable<Void> {
> > +    private static final int SPACE = ' ';
> > +    private final Socket _socket;
> > +    private final ServerContext _server;
> > +    private final Log _log;
> > +    private static final Localizer _loc =
> Localizer.forPackage(RequestHandler.class);
> > +
> > +    public RequestHandler(Socket socket, ServerContext server) {
> > +        _socket = socket;
> > +        _server = server;
> > +        _log = _server.getLog();
> > +    }
> > +
> > +    public Void call() throws Exception {
> > +        Request request = null;
> > +        Response response = null;
> > +        try {
> > +            request = readRequest(_socket.getInputStream());
> > +            response = request.process(_server,
> _socket.getOutputStream());
> > +        } catch (Exception e) {
> > +            response = new ErrorResponse(request, _server, e,
> HttpURLConnection.HTTP_INTERNAL_ERROR,
> > +                _socket.getOutputStream());
> > +        }
> > +        response.writeOut();
> > +        return null;
> > +    }
> > +
> > +
> > +
> > +    /**
> > +     * Reads the given CR-LF delimited stream.
> > +     * The first line is scanned for the method (first space-delimited
> String) and protocol (the last
> > +     * space-delimited String). Accordingly a request is created and
> rest of the input stream content
> > +     * is parsed by the request itself.
> > +     *
> > +     * @param input
> > +     * @throws IOException
> > +     */
> > +    public Request readRequest(InputStream input) throws IOException {
> > +        if (_log.isTraceEnabled())
> > +            _log.trace("Reading request from the input stream ");
> > +
> > +        BufferedReader reader = new BufferedReader(new
> InputStreamReader(input));
> > +        String status = reader.readLine();
> > +        if (_log.isInfoEnabled())
> > +            _log.info("Status Line [" + status + "]");
> > +        int spaceFirst = status.indexOf(SPACE);
> > +        if (spaceFirst == -1)
> > +            throw new IOException("HTTP Method could not be determined
> from [" + status + "]");
> > +        int spaceLast = status.lastIndexOf(SPACE);
> > +        if (spaceLast == -1)
> > +            throw new IOException("HTTP Protocol could not be determined
> from [" + status + "]");
> > +        String method = status.substring(0, spaceFirst);
> > +        String protocol = status.substring(spaceLast+1);
> > +        String path = status.substring(spaceFirst+1, spaceLast);
> > +        Request request =
> RequestFactory.getFactory(protocol).createRequest(method);
> > +        List<String> lines = new ArrayList<String>();
> > +        if (path.equals("/")) {
> > +            lines.add(path);
> > +        } else {
> > +            lines = readlines(reader);
> > +            lines.add(0, path);
> > +        }
> > +        if (lines.isEmpty()) {
> > +            throw new IOException("No CR-LF delimited lines could be
> read from " + input);
> > +        }
> > +        request.read(lines);
> > +        return request;
> > +    }
> > +
> > +
> > +    List<String> readlines(BufferedReader reader) throws IOException {
> > +        List<String> buffers = new ArrayList<String>();
> > +        String line;
> > +        while ((line = reader.readLine()) != null && line.length() > 0)
> {
> > +            buffers.add(line);
> > +        }
> > +        return buffers;
> > +    }
> > +
> > +
> > +    public String toString() {
> > +        return _socket.getInetAddress()+":"+_socket.getPort();
> > +    }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,54 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.InputStream;
> > +import java.io.OutputStream;
> > +import java.io.RandomAccessFile;
> > +
> > +import javax.imageio.ImageIO;
> > +
> > +/**
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class ResourceResponse extends AbstractResponse {
> > +    private final InputStream _in;
> > +    private final String _mimeType;
> > +    public ResourceResponse(Request request, ServerContext ctx,
> InputStream resource, String mimeType,
> > +        OutputStream out) throws Exception {
> > +        super(request, ctx, out);
> > +        _in = resource;
> > +        _mimeType = mimeType;
> > +    }
> > +
> > +    public void writeOut() throws Exception {
> > +        print(_request.getProtocol()); println("200 OK");
> > +        printHeader("Connection",  "close");
> > +        printHeader("Content-Type", _mimeType, "charset=UTF-8");
> > +        println();
> > +        for (int c = 0; (c = _in.read()) != -1;) {
> > +           print((char)c);
> > +        }
> > +        _in.close();
> > +        close();
> > +    }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,31 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.Serializable;
> > +
> > +/**
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public interface Response extends Serializable {
> > +//    void setHeader(String key, String value);
> > +    void writeOut() throws Exception;
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,230 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements.  See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership.  The ASF licenses this file
> > + * to you 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.openjpa.persistence.jest;
> > +
> > +import java.io.IOException;
> > +import java.net.ServerSocket;
> > +import java.net.Socket;
> > +import java.util.concurrent.ExecutorService;
> > +import java.util.concurrent.Executors;
> > +
> > +import javax.persistence.EntityManagerFactory;
> > +
> > +import org.apache.openjpa.conf.OpenJPAConfiguration;
> > +import org.apache.openjpa.lib.conf.Configurable;
> > +import org.apache.openjpa.lib.conf.Configuration;
> > +import org.apache.openjpa.lib.log.Log;
> > +import org.apache.openjpa.lib.util.Localizer;
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.meta.MetaDataRepository;
> > +import org.apache.openjpa.persistence.EntityManagerFactoryImpl;
> > +
> > +
> > +/**
> > + * A server running on an independent thread that allows a remote,
> language-neutral client to access OpenJPA runtime.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class Server implements ServerContext, Configurable, Runnable {
> > +    private ServerSocket _listenSocket;
> > +    protected ExecutorService _executors;
> > +    public final static int DEFAULT_PORT = 6789;
> > +    protected int _port = DEFAULT_PORT;
> > +    protected int _range = 1;
> > +    protected String _format = "xml";
> > +    protected Log _log;
> > +    protected Thread _thread;
> > +    private EntityManagerFactoryImpl _ctx;
> > +    private static Localizer _loc = Localizer.forPackage(Server.class);
> > +
> > +    /**
> > +     * Sets the persistence unit context in which this server will serve
> requests.
> > +     * The context must be set before operation.
> > +     *
> > +     * @param emf an implementation of OpenJPA Persistence Unit. Must
> not be null.
> > +     */
> > +    public void setContext(EntityManagerFactoryImpl emf) {
> > +        if (emf == null)
> > +            throw new NullPointerException();
> > +        _ctx = emf;
> > +    }
> > +
> > +    /**
> > +     * Gets the persistence unit context in which this server serves
> requests.
> > +     *
> > +     * @param emf an implementation of OpenJPA Persistence Unit.
> > +     */
> > +    public EntityManagerFactoryImpl getPersistenceUnit() {
> > +        return _ctx;
> > +    }
> > +
> > +    public Log getLog() {
> > +        return _log;
> > +    }
> > +
> > +    /**
> > +     * Start the server in a daemon thread.
> > +     * This method is idempotent.
> > +     *
> > +     * @return true if the server has started by this call or already
> running.
> > +     */
> > +    public synchronized boolean start() {
> > +        try {
> > +            if (_thread != null)
> > +                return true;
> > +            if (createServerSocket()) {
> > +                _thread = new Thread(this);
> > +                _thread.setDaemon(true);
> > +                _thread.start();
> > +                return true;
> > +            }
> > +            return false;
> > +        } catch (Exception ex) {
> > +            ex.printStackTrace();
> > +            return false;
> > +        }
> > +    }
> > +
> > +    /**
> > +     * Stops the server.
> > +     */
> > +    public synchronized void stop() {
> > +        _thread.interrupt();
> > +        _thread = null;
> > +        _executors.shutdownNow();
> > +    }
> > +
> > +    /**
> > +     * Sets the port in which the server will listen.
> > +     *
> > +     * @param port a positive integer.
> > +     */
> > +    public void setPort(int port) {
> > +        _port = port;
> > +    }
> > +
> > +    /**
> > +     * Gets the current port.
> > +     *
> > +     * @return the port number. Defaults to default HTTP port.
> > +     */
> > +    public int getPort() {
> > +        return _port;
> > +    }
> > +
> > +    /**
> > +     * Sets the range of ports the server will attempt at start.
> > +     *
> > +     * @param range a positive integer.
> > +     */
> > +    public void setRange(int range) {
> > +        if (range > 0)
> > +            _range = range;
> > +    }
> > +
> > +    public void setFormat(String format) {
> > +        _format = format;
> > +    }
> > +
> > +    public String getFormat() {
> > +        return _format;
> > +    }
> > +
> > +    /**
> > +     * Sets the range of ports the server will attempt at start.
> > +     * @return  a positive integer. Defaults to 1.
> > +     */
> > +    public int getRange() {
> > +        return _range;
> > +    }
> > +
> > +    public void run() {
> > +        _log.info(_loc.get("server-starting", this));
> > +
> > +        _executors = Executors.newCachedThreadPool();
> > +        while (!Thread.interrupted()) {
> > +            try {
> > +                Socket socket = _listenSocket.accept();
> > +                if (_log.isTraceEnabled())
> > +                    _log.trace(_loc.get("server-request", socket));
> > +                RequestHandler request = new RequestHandler(socket,
> this);
> > +                _executors.submit(request);
> > +            } catch (IOException e) {
> > +                e.printStackTrace();
> > +            }
> > +        }
> > +    }
> > +
> > +    private boolean createServerSocket() {
> > +        int p = _port;
> > +        int p2 = p + _range;
> > +        Exception error = null;
> > +        for (; _listenSocket == null && p < p2; ) {
> > +            try {
> > +                _listenSocket = new ServerSocket(p);
> > +            } catch (IOException ex) {
> > +                p++;
> > +                error = ex;
> > +            }
> > +        }
> > +        if (_listenSocket != null) {
> > +            if (p != _port) {
> > +                _port = p;
> > +                _log.warn(_loc.get("server-reconfigured", _port));
> > +            }
> > +        } else {
> > +            if (error != null) {
> > +                _log.warn(_loc.get("server-failed", this, _port,
> error));
> > +            }
> > +        }
> > +        return _listenSocket != null;
> > +    }
> > +
> > +    // Configurable contract
> > +    public void setConfiguration(Configuration conf) {
> > +        _log = conf.getLog("Remote");
> > +    }
> > +
> > +    public void startConfiguration() {
> > +    }
> > +
> > +    public void endConfiguration() {
> > +    }
> > +
> > +
> > +    // Server side utilities
> > +
> > +    /**
> > +     * Resolves the given alias to a persistent class meta data.
> > +     *
> > +     * @exception if no meta data available for the given alias
> > +     */
> > +    public ClassMetaData resolve(String alias) {
> > +        MetaDataRepository repos =
> _ctx.getConfiguration().getMetaDataRepositoryInstance();
> > +        return repos.getMetaData(alias,
> Thread.currentThread().getContextClassLoader(), true);
> > +    }
> > +
> > +    public String toString() {
> > +        if (_listenSocket == null) return "JEST Server [not strated]";
> > +        return "JEST Server " +
> _listenSocket.getInetAddress()+":"+_listenSocket.getLocalPort();
> > +    }
> > +
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> >
> ------------------------------------------------------------------------------
> >     svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> >
> ------------------------------------------------------------------------------
> >     svn:mime-type = text/plain
> >
> >
> >
>

Re: svn commit: r1028093 [1/2] - JEST...

Posted by Donald Woods <dw...@apache.org>.
Is this really ready to drop into trunk?
And do we really want it in the base openjpa.jar?


-Donald


On 10/27/10 4:42 PM, ppoddar@apache.org wrote:
> Author: ppoddar
> Date: Wed Oct 27 20:42:44 2010
> New Revision: 1028093
> 
> URL: http://svn.apache.org/viewvc?rev=1028093&view=rev
> Log:
> OPENJPA-1851: First version of JEST (REST on OpenJPA)
> 
> Added:
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ServerContext.java   (with props)
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/XMLEncoder.java   (with props)
> Modified:
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> 
> Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java (original)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java Wed Oct 27 20:42:44 2010
> @@ -45,10 +45,10 @@ import org.apache.openjpa.lib.util.Close
>  import org.apache.openjpa.lib.util.Localizer;
>  import org.apache.openjpa.persistence.criteria.CriteriaBuilderImpl;
>  import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
> +import org.apache.openjpa.persistence.jest.Server;
>  import org.apache.openjpa.persistence.meta.MetamodelImpl;
>  import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
>  import org.apache.openjpa.persistence.query.QueryBuilderImpl;
> -import org.apache.openjpa.util.UserException;
>  
>  /**
>   * Implementation of {@link EntityManagerFactory} that acts as a
> @@ -70,7 +70,8 @@ public class EntityManagerFactoryImpl
>      private transient StoreCache _cache = null;
>      private transient QueryResultCache _queryCache = null;
>      private transient MetamodelImpl _metaModel;
> -    
> +    private transient Server _remoteAccess = null;
> +
>      /**
>       * Default constructor provided for auto-instantiation.
>       */
> @@ -93,10 +94,11 @@ public class EntityManagerFactoryImpl
>  
>      /**
>       * Delegate must be provided before use.
> +     * Configures for Remote Access, if appropriate. 
>       */
>      public void setBrokerFactory(BrokerFactory factory) {
> -        _factory = new DelegatingBrokerFactory(factory,
> -            PersistenceExceptions.TRANSLATOR);
> +        _factory = new DelegatingBrokerFactory(factory, PersistenceExceptions.TRANSLATOR);
> +        configureRemoteAccess(getConfiguration());
>      }
>  
>      public OpenJPAConfiguration getConfiguration() {
> @@ -271,6 +273,10 @@ public class EntityManagerFactoryImpl
>          if (log.isTraceEnabled()) {
>              log.trace(this + ".close() invoked.");
>          }
> +        if (_remoteAccess != null) {
> +            _remoteAccess.stop();
> +            _remoteAccess = null;
> +        }
>          _factory.close();
>      }
>  
> @@ -396,4 +402,40 @@ public class EntityManagerFactoryImpl
>              }
>          }
>      }
> +    
> +    /**
> +     * Configures this unit for remote access.
> +     */
> +    protected void configureRemoteAccess(OpenJPAConfiguration conf) {
> +        Value value = conf.getValue("RemoteAccess");
> +        if (value == null) {
> +            return;
> +        }
> +        String props = value.getString();
> +        if (props == null)
> +            return;
> +        try {
> +            _remoteAccess = new Server();
> +            _remoteAccess.setContext(this);
> +            Configurations.configureInstance(_remoteAccess, conf, props);
> +            conf.removeValue(value);
> +            if (!_remoteAccess.start()) {
> +                _remoteAccess = null;
> +            }
> +        } catch (Exception ex) {
> +            Log log = _factory.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
> +            if (log != null) {
> +                log.error(_loc.get("remote-start-error"), ex);
> +            }
> +        }
> +    }
> +    
> +    /**
> +     * Affirms if this unit is accessible remotely.
> +     */
> +    public boolean allowsRemoteAccess() {
> +        return _remoteAccess != null;
> +    }
> +    
> +
>  }
> 
> Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java (original)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java Wed Oct 27 20:42:44 2010
> @@ -188,6 +188,7 @@ public class PersistenceProductDerivatio
>          conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(),  PersistenceMetaDataFactory.class.getName());
>          
>          conf.addValue(new EntityManagerFactoryValue());
> +        conf.addString("RemoteAccess");
>          
>          conf.readLockLevel.setAlias("optimistic", String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
>          conf.readLockLevel.setAlias("optimistic-force-increment", String
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,77 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.OutputStream;
> +import java.io.PrintStream;
> +
> +/**
> + * Abstract implementation of a stream-based response.
> + * Every response is result of a {@linkplain Request} and operates within a {@linkplain ServerContext}.
> + * This fact is enforced by the constructor argument of an abstract response.
> + * <p>
> + * Besides, this implementation provides common utility to write HTTP response or extract useful information
> + * from the request.
> + *  
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public abstract class AbstractResponse extends PrintStream implements Response {
> +    protected final Request _request;
> +    protected final ServerContext _ctx;
> +    
> +    /**
> +     * Construct a response for the given request and server context.
> +     * 
> +     * @param request the request for this response. Can be null if the response is for server error.
> +     * @param ctx the processing context 
> +     * @param out the output stream where the response is targeted.
> +     */
> +    protected AbstractResponse(Request request, ServerContext ctx, OutputStream out) {
> +        super(out);
> +        _request = request;
> +        _ctx = ctx;
> +    }
> +    
> +    /**
> +     * Write a HTTP header to the stream.
> +     * <br> 
> +     * The format of HTTP header is <code>key: [value]* NEWLINE</code>
> +     * 
> +     * @param key the key of the header
> +     * @param values one of more value of the header fields.
> +     */
> +    protected void printHeader(String key, String...values) {
> +        if (key == null)
> +            return;
> +        print(key);
> +        print(" :");
> +        if (values == null || values.length == 0)
> +            return;
> +        int n = values.length-1;
> +        for (int i = 0; i < n-1; i++) {
> +            print(values[i]);
> +            print(";");
> +        }
> +        println(values[n]);
> +    }
> +    
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,68 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.OutputStream;
> +import java.net.HttpURLConnection;
> +
> +/**
> + * A HTTP response for something gone wrong.
> + *  
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class ErrorResponse extends AbstractResponse {
> +    private final Exception _error;
> +    private final int _code;
> +    /**
> +     * Construct a response to describe a error.
> +     * 
> +     * @param request a request that produced this response. VCan be null to denote that the request can not
> +     * be created.
> +     * @param ctx the processing context
> +     * @param ex the error
> +     * @param code HTTP error code
> +     * @param out the stream where the response is written
> +     * 
> +     */
> +    public ErrorResponse(Request request, ServerContext ctx, Exception ex, int code, OutputStream out)  {
> +        super(request, ctx, out);
> +        _error = ex;
> +        _code = code;
> +    }
> +
> +    /**
> +     * Writes the response. 
> +     * The response is always a HTTP response with the error stack trace.
> +     */
> +    public void writeOut() throws Exception {
> +        println("HTTP/1.1"); print(" " + _code); println("Error");
> +        printHeader("Connection",  "close");
> +        printHeader("Content-Type", "text/html", "charset=UTF-8");
> +        println();
> +        println("<html><body><pre>");
> +        _error.printStackTrace(this);
> +        println("Response from JEST");
> +        
> +        println("</pre></body></html>");
> +        close();
> +    }
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,147 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.InputStream;
> +import java.io.OutputStream;
> +import java.net.HttpURLConnection;
> +import java.util.Iterator;
> +import java.util.List;
> +import java.util.Map;
> +
> +import javax.persistence.EntityManager;
> +import javax.persistence.EntityNotFoundException;
> +import javax.persistence.Query;
> +
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
> +import org.apache.openjpa.kernel.StoreContext;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.persistence.JPAFacadeHelper;
> +import org.apache.openjpa.util.ApplicationIds;
> +import org.apache.openjpa.util.ObjectNotFoundException;
> +
> +/**
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class GETRequest extends JESTRequest {
> +    public Response process(ServerContext server, OutputStream out) throws Exception {
> +        String action = getAction();
> +        try {
> +            if ("find".equals(action)) {
> +                return find(server, out);
> +            } else {
> +                return resource(server, out);
> +            }
> +        } catch (Exception e) {
> +            return new ErrorResponse(this, server, new RuntimeException("bad action " + action), 
> +                HttpURLConnection.HTTP_BAD_REQUEST, out);
> +        }
> +    }
> +    
> +    Response find(ServerContext server, OutputStream out)  throws Exception {
> +        EntityManager em = server.getPersistenceUnit().createEntityManager();
> +        Map<String, String> qualifiers = getQualifiers();
> +        Map<String, String> parameters = getParameters();
> +        if (parameters.size() < 2)
> +            throw new IllegalArgumentException("find must have at least two parameters");
> +        Object[] pks = new Object[parameters.size()-1];
> +        Iterator<Map.Entry<String,String>> params = parameters.entrySet().iterator();
> +        String alias = null;
> +        for (int i = 0; i < parameters.size(); i++) {
> +            if (i == 0) {
> +                alias = params.next().getKey();
> +            } else {
> +                pks[i-1] = params.next().getKey();
> +            }
> +        }
> +        ClassMetaData meta = server.resolve(alias);
> +        Object oid = ApplicationIds.fromPKValues(pks, meta);
> +        Object pc = em.find(meta.getDescribedType(), oid); 
> +        if (pc != null) {
> +            OpenJPAStateManager sm = ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(pc);
> +            return new JESTResponse(this, server, sm, out);
> +        } else {
> +            return new ErrorResponse(this, server, new EntityNotFoundException("not found!"), 
> +                HttpURLConnection.HTTP_NOT_FOUND, out);
> +        }
> +    }
> +    
> +    Response query(ServerContext server, OutputStream out)  throws Exception {
> +        EntityManager em = server.getPersistenceUnit().createEntityManager();
> +        Map<String, String> qualifiers = getQualifiers();
> +        boolean named = isBooleanQualifier("named");
> +        boolean single = isBooleanQualifier("single");
> +        Map<String, String> parameters = getParameters();
> +        if (parameters.size() < 1)
> +            throw new IllegalArgumentException("find must have at least one parameter");
> +        Iterator<Map.Entry<String,String>> params = parameters.entrySet().iterator();
> +        Query query = null;
> +        int i = 0;
> +        for (Map.Entry<String, String> param : parameters.entrySet()) {
> +            if (i == 0) {
> +                query = named ? em.createQuery(param.getKey()) : em.createNamedQuery(param.getKey());
> +            } else {
> +                query.setParameter(param.getKey(), param.getValue());
> +            }
> +        }
> +        if (single) {
> +            Object result = query.getSingleResult();
> +            OpenJPAStateManager sm = ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(result);
> +            return new JESTResponse(this, server, sm, out);
> +        } else {
> +            List<Object> result = query.getResultList();
> +            return new ErrorResponse(this, server, new EntityNotFoundException("not found!"), 404, out);
> +        }
> +    }
> +    
> +    Response resource(ServerContext server, OutputStream out)  throws Exception {
> +        String resource = getAction();
> +        if (resource.length() == 0)
> +            resource = "index.html";
> +        String mimeType = getMimeType(resource);
> +//        InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
> +        InputStream in = getClass().getResourceAsStream(resource);
> +        if (in == null) {
> +            return new ErrorResponse(this, server, new ObjectNotFoundException(resource), 404, out);
> +        }
> +        if (server.getLog().isTraceEnabled())
> +            server.getLog().trace("Found resource " + resource);
> +        return mimeType.startsWith("image") 
> +          ? new ImageResponse(this, server, in, mimeType, out)
> +          : new ResourceResponse(this, server, in, mimeType, out);
> +    }
> +    
> +    boolean isBooleanQualifier(String key) {
> +        String q = getQualifier(key);
> +        return hasQualifier(key) && (q == null || "true".equals(q));
> +    }
> +    
> +    String getMimeType(String resource) {
> +        int index = resource.lastIndexOf('.');
> +        String ext = (index != -1) ? resource.substring(index+1) : ""; 
> +        if (ext.equalsIgnoreCase("html") || ext.equalsIgnoreCase(".html")) return "text/html";
> +        if (ext.equalsIgnoreCase(".png") || ext.equalsIgnoreCase(".ico") || ext.equalsIgnoreCase("jpeg")) 
> +            return "image/"+ext;
> +        return "text/html";
> +    }
> +    
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,61 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.InputStream;
> +import java.io.OutputStream;
> +import java.io.RandomAccessFile;
> +
> +import javax.imageio.ImageIO;
> +
> +/**
> + * Sends an image as response.
> + * 
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class ImageResponse extends AbstractResponse {
> +    private final InputStream _in;
> +    private final String _mimeType;
> +    
> +    public ImageResponse(Request request, ServerContext ctx, InputStream resource, String mimeType,
> +        OutputStream out) throws Exception {
> +        super(request, ctx, out);
> +        _in = resource;
> +        _mimeType = mimeType;
> +    }
> +
> +    public void writeOut() throws Exception {
> +        print(_request.getProtocol()); println("200 OK");
> +        printHeader("Connection",  "close");
> +        printHeader("Content-Type", _mimeType);
> +        println();
> +        byte[] b = new byte[1024];
> +        int i = 0;
> +        for (int l = 0; (l = _in.read(b)) != -1;) {
> +            write(b, 0, l);
> +            i += l;
> +        }
> +        printHeader("Content-Length", ""+i);
> +        _in.close();
> +        close();
> +    }
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,386 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.IOException;
> +import java.util.Arrays;
> +import java.util.Collections;
> +import java.util.HashMap;
> +import java.util.LinkedHashMap;
> +import java.util.LinkedList;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.NoSuchElementException;
> +
> +/**
> + * A request carries requisite data for a JPA operation to be performed. 
> + * The request is populated by parsing an input data stream.
> + * 
> + * 
> + * @author Pinaki Poddar
> + * 
> + */
> +@SuppressWarnings("serial")
> +public abstract class JESTRequest implements Request {
> +    private String _method;
> +    private String _protocol;
> +    private String _action;
> +    private String _body;
> +    private LinkedHashMap<String, String> _qualifiers = new LinkedHashMap<String, String>();
> +    private LinkedHashMap<String, String> _params = new LinkedHashMap<String, String>();
> +    private Map<String, List<String>> _headers = new HashMap<String, List<String>>();
> +    private ParseState _state;
> +    private StringBuffer buf = new StringBuffer();
> +    private LinkedList<Token> _stack = new LinkedList<Token>();
> +    
> +    public static final List<String> METHODS = Arrays.asList(new String[]{"GET","POST","PUT","DELETE"});
> +    
> +    /**
> +     * Parse States.
> +     */
> +    static enum ParseState {
> +        INIT, ACTION, QUALIFIER_KEY, QUALIFIER_VALUE, PARAM_KEY, PARAM_VALUE, END
> +    };
> +
> +
> +    public String getMethod() {
> +        return _method;
> +    }
> +
> +    void setMethod(String method) {
> +        if (_method == null) {
> +            if (method != null && METHODS.contains(method.toUpperCase())) {
> +                _method = method.toUpperCase();
> +            } else {
> +                throw new IllegalArgumentException("Unsupported method " + method);
> +            }
> +        } else if (!_method.equalsIgnoreCase(method)) {
> +            throw new IllegalStateException("Method can not be changed to [" + method + "]. " +
> +                "Current method [" + _method + "]");
> +        }
> +    }
> +
> +    public String getProtocol() {
> +        return _protocol == null ? "HTTP/1.1" : _protocol;
> +    }
> +
> +    void setProtocol(String protocol) {
> +        if (_protocol == null) {
> +            if (protocol != null && protocol.toUpperCase().startsWith("HTTP")) {
> +                _protocol = protocol.toUpperCase();
> +            } else {
> +                throw new IllegalArgumentException("Unsupported protocol " + protocol);
> +            }
> +        } else if (!_protocol.equalsIgnoreCase(protocol)) {
> +            throw new IllegalStateException("Protocol can not be changed to [" + protocol + "]. " +
> +                "Current protocol [" + _protocol + "]");
> +        }
> +    }
> +
> +    /**
> +     * Sets an action. Once set, an action can not be modified.
> +     * 
> +     * @param action
> +     */
> +    private void setAction(String action) {
> +        if (_action == null) {
> +            _action = action;
> +        } else if (!_action.equals(action)) {
> +            throw new IllegalStateException("Action can not be [" + action + "]. Already set to [" + _action + "]");
> +        }
> +    }
> +
> +    public String getAction() {
> +        return _action == null ? "" : _action;
> +    }
> +
> +    public String getBody() {
> +        return _body;
> +    }
> +
> +    private void setQualifier(String key, String value) {
> +        _qualifiers.put(key, value);
> +    }
> +
> +    public String getQualifier(String key) {
> +        return _qualifiers.get(key);
> +    }
> +
> +    public Map<String, String> getQualifiers() {
> +        return Collections.unmodifiableMap(_qualifiers);
> +    }
> +
> +    public boolean hasQualifier(String key) {
> +        return _qualifiers.containsKey(key);
> +    }
> +
> +    private void setParameter(String key, String value) {
> +        _params.put(key, value);
> +    }
> +
> +    public String getParameter(String key) {
> +        return _params.get(key);
> +    }
> +
> +    public boolean hasParameter(String key) {
> +        return _params.containsKey(key);
> +    }
> +
> +    public Map<String, String> getParameters() {
> +        return Collections.unmodifiableMap(_params);
> +    }
> +    
> +    public Map.Entry<String, String> getParameter(int n) {
> +        if (n >= _params.size())
> +            throw new NoSuchElementException("Index " + n + " size " + _params.size());
> +        int i = 0;
> +        for (Map.Entry<String, String> entry : _params.entrySet()) {
> +            if (i == n) {
> +                return entry;
> +            }
> +            i++;
> +        }
> +        return null;
> +    }
> +
> +    public Map<String, List<String>> getHeaders() {
> +        return Collections.unmodifiableMap(_headers);
> +    }
> +
> +    public List<String> getHeader(String key) {
> +        return _headers.get(key);
> +    }
> +    
> +    
> +    public void read(List<String> lines) throws IOException {
> +        parse(lines.get(0));
> +        int i = 1;
> +        for (; i < lines.size(); i++) {
> +            String line = lines.get(i);
> +            if (line.length() == 0) {
> +                break;
> +            } else {
> +                parseHeader(line);
> +            }
> +        }
> +        parseBody(lines.subList(i, lines.size()));
> +    }
> +
> +    protected void parseHeader(String line) throws IOException {
> +        String key = null;
> +        StringBuilder token = new StringBuilder();
> +        int N = line.length();
> +        for (int i = 0; i < N; i++) {
> +            char c = line.charAt(i);
> +            if (c == ':' && key == null) {
> +                key = token.toString().trim();
> +                token.delete(0, token.length());
> +            } else {
> +                token.append(c);
> +            }
> +        }
> +        if (key != null) {
> +            _headers.put(key, Collections.singletonList(token.toString().trim()));
> +        }
> +    }
> +
> +    protected void parseBody(List<String> lines) {
> +        if (lines == null || lines.isEmpty())
> +            return;
> +        for (String line : lines) {
> +            if (_body == null) {
> +                _body = line;
> +            } else {
> +                _body = _body + line;
> +            }
> +        }
> +    }
> +
> +    /**
> +     * Parses JEST stream and populates a request.
> +     * 
> +     */
> +     protected void parse(String s) {
> +            char[] chars = s.toCharArray();
> +            _state = ParseState.INIT;
> +            _stack.clear();
> +
> +            for (int i = 0; i < chars.length; i++) {
> +                char ch = chars[i];
> +                switch (_state) {
> +                    case INIT:
> +                        if (ch == '/') {
> +                            transit(ParseState.ACTION);
> +                        } else if (!Character.isWhitespace(ch)) {
> +                            parseError(ch, i, s, true, ' ');
> +                        }
> +                        break;
> +
> +                    case ACTION:
> +                        if (ch == '/') {
> +                            transit(ParseState.QUALIFIER_KEY);
> +                        } else if (ch == '?') {
> +                            transit(ParseState.PARAM_KEY);
> +                        } else {
> +                            buf.append(ch);
> +                        }
> +                        break;
> +
> +                    case QUALIFIER_KEY:
> +                        if (Character.isJavaIdentifierPart(ch)) {
> +                            buf.append(ch);
> +                        } else if (ch == '=') {
> +                            transit(ParseState.QUALIFIER_VALUE);
> +                        } else if (ch == '/') {
> +                            transit(ParseState.QUALIFIER_KEY);
> +                        } else if (ch == '?') {
> +                            transit(ParseState.PARAM_KEY);
> +                        } else {
> +                            parseError(ch, i, s, true, '/', '?', '=');
> +                        }
> +                        break;
> +
> +                    case QUALIFIER_VALUE:
> +                        if (Character.isJavaIdentifierPart(ch)) {
> +                            buf.append(ch);
> +                        } else if (ch == '/') {
> +                            transit(ParseState.QUALIFIER_KEY);
> +                        } else if (ch == '?') {
> +                            transit(ParseState.PARAM_KEY);
> +                        } else {
> +                            parseError(ch, i, s, true, '/', '?');
> +                        }
> +                        break;
> +
> +                    case PARAM_KEY:
> +                        if (Character.isJavaIdentifierPart(ch)) {
> +                            buf.append(ch);
> +                        } else if (ch == '=') {
> +                            if (isQueryKey())
> +                                buf.append(ch);
> +                            else
> +                                transit(ParseState.PARAM_VALUE);
> +                        } else if (ch == ';') {
> +                            transit(ParseState.PARAM_KEY);
> +                        } else if (isQueryKey() && isQueryChar(ch)) {
> +                            buf.append(ch);
> +                        } else {
> +                            parseError(ch, i, s, true, ';', '=');
> +                        }
> +                        break;
> +
> +                    case PARAM_VALUE:
> +                        if (Character.isJavaIdentifierPart(ch)) {
> +                            buf.append(ch);
> +                        } else if (ch == ';') {
> +                            transit(ParseState.PARAM_KEY);
> +                        } else {
> +                            parseError(ch, i, s, true, ';');
> +                        }
> +                        break;
> +                    default:
> +                        throw new RuntimeException("ParseError: '" + ch + "' at " + i + " in [" + s + "]. "
> +                            + "Unknown state " + _state);
> +                }
> +            }
> +            if (buf.length() > 0) {
> +                transit(ParseState.END);
> +            }
> +        }
> +
> +        /**
> +         * Affirms if parsing a query string.
> +         */
> +        private boolean isQueryKey() {
> +            return "query".equals(_action) && _stack.size() == 1;
> +        }
> +        
> +        /**
> +         * Affirms if the given character is valid in a query string 
> +         */
> +        private boolean isQueryChar(char c) {
> +            return c == ' ' || c == '.' || c == ':' || c == '?' || c == '\'';
> +        }
> +
> +        /**
> +         * Transitions to a new parse state.
> +         * 
> +         * @param to target parse state
> +         */
> +        void transit(ParseState to) {
> +            String token = buf.toString();
> +            switch (_state) {
> +                case ACTION:
> +                    setAction(token);
> +                    break;
> +                case QUALIFIER_KEY:
> +                    setQualifier(token, null);
> +                    break;
> +                case QUALIFIER_VALUE:
> +                    setQualifier(_stack.peekLast().getValue(), token);
> +                    break;
> +                case PARAM_KEY:
> +                    setParameter(token, null);
> +                    break;
> +                case PARAM_VALUE:
> +                    setParameter(_stack.peekLast().getValue(), token);
> +                    break;
> +
> +            }
> +            if (_state != ParseState.INIT && to != ParseState.END) {
> +                _stack.add(new Token(_state, token));
> +            }
> +            buf.delete(0, buf.length());
> +            _state = to;
> +        }
> +
> +        protected void parseError(char ch, int pos, String line, boolean java, char... expected) {
> +            throw new RuntimeException("ParseError: Encountered '" + ch + "' at " + pos + " in [" + line + "] while "
> +                + "parsing " + _state + ". Expected " + Arrays.toString(expected) + (java ? " or Java identifer" : ""));
> +
> +        }
> +
> +        /**
> +         * Token in a JEST stream.
> +         * 
> +         */
> +        static class Token {
> +            final ParseState _type;
> +            final String _value;
> +
> +            public Token(ParseState type, String value) {
> +                _type = type;
> +                _value = value;
> +            }
> +
> +            public ParseState getType() {
> +                return _type;
> +            }
> +
> +            public String getValue() {
> +                return _value;
> +            }
> +
> +            public String toString() {
> +                return _value + "[" + _type + "]";
> +            }
> +        }
> +    
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,71 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.IOException;
> +import java.io.OutputStream;
> +import java.io.PrintStream;
> +import java.io.PrintWriter;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
> +import org.apache.openjpa.persistence.JPAFacadeHelper;
> +import org.w3c.dom.Document;
> +import org.w3c.dom.Element;
> +
> +/**
> + * Response of a JEST Request.
> + * 
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class JESTResponse extends AbstractResponse {
> +    private final OpenJPAStateManager _sm;
> +    
> +    public JESTResponse(Request request, ServerContext ctx, OpenJPAStateManager sm, OutputStream out) throws Exception {
> +        super(request, ctx, out);
> +        _sm = sm;
> +    }
> +    
> +    public String getContentType() {
> +        return "text/html";
> +    }
> +    
> +    public void writeOut() throws Exception {
> +        print(_request.getProtocol()); println("200 OK");
> +        printHeader("Connection",  "close");
> +        printHeader("Content-Type", "text/html", "charset=UTF-8");
> +        println();
> +        println("<html><body><pre>");
> +        XMLEncoder encoder = new XMLEncoder(_ctx.getPersistenceUnit().getMetamodel());
> +        Document doc = encoder.encode(_sm);
> +        encoder.writeDoc(doc, this);
> +        println("Response from JEST");
> +        
> +        println("</pre></body></html>");
> +        close();
> +    }
> +    
> +}
> +
> +
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,316 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.BufferedReader;
> +import java.io.CharArrayWriter;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.io.InputStreamReader;
> +import java.io.Reader;
> +import java.util.Arrays;
> +import java.util.BitSet;
> +import java.util.Collection;
> +import java.util.HashSet;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +
> +import javax.persistence.metamodel.Attribute;
> +
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
> +import org.apache.openjpa.kernel.StoreContext;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.FieldMetaData;
> +import org.apache.openjpa.meta.JavaTypes;
> +import org.apache.openjpa.meta.ValueMetaData;
> +import org.apache.openjpa.persistence.meta.Members;
> +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> +import org.w3c.dom.CDATASection;
> +import org.w3c.dom.Document;
> +import org.w3c.dom.Element;
> +
> +/**
> + * Marshals a root instance and its persistent closure as JSON object.
> + * The closure is resolved against the persistence context that contains the root instance.
> + * The JSON format introduces a $id and $ref to address reference that pure JSON does not. 
> + * 
> + * @author Pinaki Poddar
> + *
> + */
> +public class JSONEncoder {
> +    /**
> +     * The element/attribute tags declared in <code>jest-instance.xsd</code> XML schema.
> +     */
> +    public static final String ELEMENT_NULL_REF    = "null";
> +    public static final String ELEMENT_INSTANCE    = "instance";
> +    public static final String ELEMENT_REF         = "ref";
> +        
> +    
> +    private MetamodelHelper _model;
> +    
> +    public JSONEncoder(MetamodelImpl model) {
> +        _model = new MetamodelHelper(model);
> +    }
> +    
> +    /**
> +     * Encodes the given managed instance into a new XML element as a child of the given parent node.
> +     * 
> +     * @param sm a managed instance, can be null.
> +     * @param parent the parent node to which the new node be attached.
> +     */
> +    public StringBuilder encode(final OpenJPAStateManager sm) {
> +        return encode(sm, new HashSet<OpenJPAStateManager>(), 0, false);
> +    }
> +    StringBuilder indent(StringBuilder buf, int indent) {
> +        if (indent <= 0)
> +            return buf;
> +        char[] spaces = new char[indent*4];
> +        Arrays.fill(spaces, ' ');
> +        buf.insert(0, spaces);
> +        return buf;
> +    }
> +    StringBuilder end(StringBuilder buf, char ch, int indent) {
> +        char[] spaces = new char[indent*4];
> +        Arrays.fill(spaces, ' ');
> +        return buf.append("\r\n").append(spaces).append(ch);
> +    }
> +    
> +    /**
> +     * Encodes the closure of a persistent instance into a XML element.
> +     * 
> +     * @param sm the managed instance to be encoded. Can be null.
> +     * @param parent the parent XML element to which the new XML element be added. Must not be null. Must be
> +     * owned by a document. 
> +     * @param visited the persistent instances that had been encoded already. Must not be null or immutable.
> +     * 
> +     * @return the new element. The element has been appended as a child to the given parent in this method.  
> +     */
> +    private StringBuilder encode(final OpenJPAStateManager sm, final Set<OpenJPAStateManager> visited, 
> +        int indent, boolean indentPara) {
> +        if (visited == null) {
> +            throw new IllegalArgumentException("null closure for encoder");
> +        }
> +        StringBuilder root =  indent(new StringBuilder("{"), indentPara ? indent : 0);
> +        if (sm == null) {
> +            return root.append("null}");
> +        }
> +        boolean ref = !visited.add(sm);
> +        if (ref) {
> +            return indent(root.append(quoted("$ref")).append(": ").append(ior(sm)).append('}'), 
> +                indentPara ? indent : 0);
> +        } else {
> +            indent(root.append(quoted("$id")).append(": ").append(ior(sm)), indentPara ? indent : 0);
> +        }
> +        
> +        StringBuilder child = new StringBuilder();
> +        BitSet loaded = sm.getLoaded();
> +        StoreContext ctx = (StoreContext)sm.getGenericContext();
> +        List<Attribute<?, ?>> attrs = _model.getAttributesInOrder(sm.getMetaData());
> +        for (int i = 0; i < attrs.size(); child = new StringBuilder(), i++) {
> +            FieldMetaData fmd = ((Members.Member<?, ?>) attrs.get(i)).fmd;
> +            if (!loaded.get(fmd.getIndex())) 
> +                continue;
> +            Object value = sm.fetch(fmd.getIndex());
> +            child.append(quoted(fmd.getName())).append(": ");
> +            switch (fmd.getDeclaredTypeCode()) {
> +                case JavaTypes.BOOLEAN:
> +                case JavaTypes.BYTE:
> +                case JavaTypes.CHAR:
> +                case JavaTypes.DOUBLE:
> +                case JavaTypes.FLOAT:
> +                case JavaTypes.INT:
> +                case JavaTypes.LONG:
> +                case JavaTypes.SHORT:
> +
> +                case JavaTypes.BOOLEAN_OBJ:
> +                case JavaTypes.BYTE_OBJ:
> +                case JavaTypes.CHAR_OBJ:
> +                case JavaTypes.DOUBLE_OBJ:
> +                case JavaTypes.FLOAT_OBJ:
> +                case JavaTypes.INT_OBJ:
> +                case JavaTypes.LONG_OBJ:
> +                case JavaTypes.SHORT_OBJ:
> +
> +                case JavaTypes.BIGDECIMAL:
> +                case JavaTypes.BIGINTEGER:
> +                case JavaTypes.DATE:
> +                case JavaTypes.NUMBER:
> +                case JavaTypes.CALENDAR:
> +                case JavaTypes.LOCALE:
> +                case JavaTypes.STRING:
> +                case JavaTypes.ENUM:
> +                         child.append(quoted(value));
> +                break;
> +                
> +                case JavaTypes.PC:
> +                    if (value == null) {
> +                        child.append("null");
> +                    } else {
> +                        child.append(encode(ctx.getStateManager(value), visited, indent+1, false));
> +                    }
> +                    break;
> +                    
> +                case JavaTypes.ARRAY:
> +                    Object[] values = (Object[])value;
> +                    value = Arrays.asList(values);
> +                // no break;
> +                case JavaTypes.COLLECTION:
> +                    if (value == null) {
> +                        child.append("null");
> +                        break;
> +                    }
> +                    child.append("[");
> +                    Collection<?> members = (Collection<?>)value;
> +                    boolean basic = fmd.getElement().getTypeMetaData() == null;
> +                    int k = 0;
> +                    for (Object o : members) {
> +                        child.append("\r\n");
> +                        if (o == null) {
> +                            child.append(indent(new StringBuilder("null"), indent+1)); 
> +                        } else {
> +                            if (basic) {
> +                                child.append(indent(new StringBuilder(quoted(o)), indent+1));
> +                            } else {
> +                                child.append(encode(ctx.getStateManager(o), visited, indent+1, true));
> +                            }
> +                        }
> +                    }
> +                    end(child, ']', indent+1);
> +                    break;
> +                case JavaTypes.MAP:
> +                    if (value == null) {
> +                        child.append("null");
> +                        break;
> +                    }
> +                    child.append("[");
> +                    Set<Map.Entry> entries = ((Map)value).entrySet();
> +                    boolean basicKey   = fmd.getElement().getTypeMetaData() == null;
> +                    boolean basicValue = fmd.getValue().getTypeMetaData() == null;
> +                    for (Map.Entry<?,?> e : entries) {
> +                        if (e.getKey() == null) {
> +                            child.append("null:");
> +                        } else {
> +                            if (basicKey) {
> +                                child.append(quoted(e.getKey())).append(":");
> +                            } else {
> +                                child.append(encode(ctx.getStateManager(e.getKey()), visited, indent+1, true));
> +                            }
> +                        }
> +                        if (e.getValue() == null) {
> +                            child.append("null");
> +                        } else {
> +                            if (basicValue) {
> +                                child.append(quoted(e.getValue()));
> +                            } else {
> +                                child.append(encode(ctx.getStateManager(e.getValue()), visited, indent+1, false));
> +                            }
> +                        }
> +                    }
> +                    break;
> +                    
> +                case JavaTypes.INPUT_STREAM:
> +                case JavaTypes.INPUT_READER:
> +                    child = new StringBuilder(fmd.getName());
> +                    if (value == null) {
> +                        child.append("null");
> +                    } else { 
> +                        child.append(streamToString(value));
> +                    }
> +                    break;
> +                    
> +                case JavaTypes.PC_UNTYPED:
> +                case JavaTypes.OBJECT:
> +                case JavaTypes.OID:
> +                    System.err.println("Not handled " + fmd.getName() + " of type " + fmd.getDeclaredType());
> +            }
> +            
> +            if (child != null) {
> +                root.append("\r\n");
> +                root.append(indent(child, indent+1));
> +                if (loaded.length()-1 != i)
> +                    root.append(",");
> +           }
> +        }
> +        return end(root, '}', indent);
> +    }
> +    
> +    
> +    String ior(OpenJPAStateManager sm) {
> +        return quoted(typeOf(sm)+"-"+sm.getObjectId().toString());
> +    }
> +    
> +    String typeOf(OpenJPAStateManager sm) {
> +        return sm.getMetaData().getDescribedType().getSimpleName();
> +    }
> +    
> +    String typeOf(Class<?> cls) {
> +        return cls.getSimpleName();
> +    }
> +    
> +    String typeOf(ClassMetaData meta) {
> +        return meta.getDescribedType().getSimpleName();
> +    }
> +    
> +    String typeOf(ValueMetaData vm) {
> +        if (vm.getTypeMetaData() == null)
> +            return typeOf(vm.getType()); 
> +        return typeOf(vm.getTypeMetaData());
> +    }
> +    
> +    String typeOf(FieldMetaData fmd) {
> +        return fmd.getType().getSimpleName();
> +    }
> +    
> +    
> +    /**
> +     * Convert the given stream (either an InutStream or a Reader) to a String
> +     * to be included in CDATA section of a XML document.
> +     * 
> +     * @param value the field value to be converted. Can not be null 
> +     * @return
> +     */
> +    String streamToString(Object value) {
> +        Reader reader = null;
> +        if (value instanceof InputStream) {
> +            reader = new BufferedReader(new InputStreamReader((InputStream)value));
> +        } else if (value instanceof Reader) {
> +            reader = (Reader)value;
> +        } else {
> +            throw new RuntimeException();
> +        }
> +        CharArrayWriter writer = new CharArrayWriter();
> +        try {
> +            for (int c; (c = reader.read()) != -1;) {
> +                writer.write(c);
> +            }
> +        } catch (IOException ex) {
> +            throw new RuntimeException(ex);
> +        }
> +        return writer.toString();
> +    }
> +    
> +    String quoted(Object o) {
> +        if (o == null) return "null";
> +        if (o instanceof Number)
> +            return o.toString();
> +        return "\"" + o.toString() + "\"";
> +    }
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,127 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.Comparator;
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.Map;
> +
> +import javax.persistence.metamodel.Attribute;
> +import javax.persistence.metamodel.EntityType;
> +import javax.persistence.metamodel.ManagedType;
> +import javax.persistence.metamodel.Metamodel;
> +import javax.persistence.metamodel.SingularAttribute;
> +
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.FieldMetaData;
> +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> +
> +/**
> + * @author Pinaki Poddar
> + *
> + */
> +public class MetamodelHelper {
> +    private MetamodelImpl _model;
> +    private Map<ManagedType<?>, List<Attribute<?, ?>>> _attrs = new HashMap<ManagedType<?>, List<Attribute<?,?>>>();
> +    
> +    public MetamodelHelper(MetamodelImpl model) {
> +        _model = model;
> +    }
> +    
> +    public List<Attribute<?,?>> getAttributesInOrder(Class<?> cls) {
> +        return getAttributesInOrder(_model.managedType(cls));
> +    }
> +    
> +    public List<Attribute<?,?>> getAttributesInOrder(ClassMetaData meta) {
> +        return getAttributesInOrder(meta.getDescribedType());
> +    }
> +    
> +    /**
> +     * Gets the attributes of the given type in defined order.
> +     * @param type
> +     * @return
> +     */
> +    public List<Attribute<?,?>> getAttributesInOrder(ManagedType<?> type) {
> +        List<Attribute<?,?>> attrs = _attrs.get(type);
> +        if (attrs != null)
> +            return attrs;
> +        List<Attribute<?,?>> list = new ArrayList<Attribute<?,?>>(type.getAttributes());
> +        Collections.sort(list, new AttributeComparator());
> +        _attrs.put(type, list);
> +        return list;
> +    }
> +
> +    public static boolean isId(Attribute<?,?> a) {
> +        if (a instanceof SingularAttribute)
> +            return ((SingularAttribute<?,?>)a).isId();
> +        return false;
> +    }
> +    
> +    public static boolean isVersion(Attribute<?,?> a) {
> +        if (a instanceof SingularAttribute)
> +            return ((SingularAttribute<?,?>)a).isVersion();
> +        return false;
> +    }
> +
> +    public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
> +        if (isId(attr))
> +            return 0;
> +        if (isVersion(attr))
> +            return 1;
> +        
> +      switch (attr.getPersistentAttributeType()) {
> +      case BASIC : 
> +      case EMBEDDED:
> +          return 2;
> +      case ONE_TO_ONE: 
> +      case MANY_TO_ONE:
> +          return 3;
> +      case ONE_TO_MANY:
> +      case MANY_TO_MANY:
> +      case ELEMENT_COLLECTION: return 4;
> +      default: return 5;
> +      }
> +    }
> +    
> +    /**
> +     * Compares attribute by their qualification.
> +     * Identity 
> +     * Version
> +     * Basic
> +     * Singular association
> +     * Plural association
> +     *
> +     */
> +    public static class AttributeComparator implements Comparator<Attribute<?,?>> {
> +        @Override
> +        public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
> +            Integer t1 = getAttributeTypeCode(a1);
> +            Integer t2 = getAttributeTypeCode(a2);
> +            if (t1.equals(t2)) {
> +                return a1.getName().compareTo(a2.getName());
> +            } else {
> +                return t1.compareTo(t2);
> +            }
> +        }
> +    }
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,138 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.IOException;
> +import java.io.OutputStream;
> +import java.io.Serializable;
> +import java.util.List;
> +import java.util.Map;
> +
> +/**
> + * A request from a remote client to a server to do something.
> + * The request arrives as stream of bytes from a remote location
> + * and if the server can interpret the protocol from the stream, 
> + * then  make a concrete request object.
> + * 
> + * @author Pinaki Poddar
> + *
> + */
> +public interface Request extends Serializable {
> +    /**
> +     * Get the HTTP verb such as GET, POST
> +     * 
> +     * @return uppercase string
> +     */
> +    String getMethod();
> +    
> +    /**
> +     * Get the first path segment as intended persistence action such as <code>find</code> or <code>query</code>
> +     *   
> +     * @return lowercase action name. Can be empty.
> +     */
> +    String getAction();
> +    
> +    /**
> +     * Get the protocol such as HTTP/1.1
> +     * 
> +     * @return upper-case string
> +     */
> +    String getProtocol();
> +    
> +    /**
> +     * Get the body, if any.
> +     * 
> +     * @return body of the request. null if no body.
> +     */
> +    String getBody();
> +    
> +    /**
> +     * Get the headers indexed by the keys.
> +     * Header values are list of Strings.
> +     * 
> +     * @return empty map if there is no header
> +     */
> +    Map<String, List<String>> getHeaders();
> +    
> +    /**
> +     * Get the header values for the given key.
> +     * @param key a key
> +     * @return null if no header value for the given key
> +     */
> +    List<String> getHeader(String key);
> +    
> +    /**
> +     * Affirm if the the given qualifier is available in this request.
> +     *  
> +     * @param key case-sensitive qualifier
> +     * @return true if the key is present.
> +     */
> +    boolean hasQualifier(String key);
> +    
> +    /**
> +     * Gets the value for the given qualifier key.
> +     * 
> +     * @param key case-sensitive qualifier
> +     * @return value of the qualifier. null if the key is absent.
> +     */
> +    String getQualifier(String key);
> +    
> +    /**
> +     * Get all the qualifiers available in this request.
> +     * 
> +     * @return key-value pairs of the qualifiers. Empty map if no qualifier is present.
> +     */
> +    Map<String,String> getQualifiers();
> +    
> +    
> +    /**
> +     * Affirm if the the given parameter is available in this request.
> +     *  
> +     * @param key case-sensitive parameter
> +     * @return true if the key is present.
> +     */
> +    boolean hasParameter(String key);
> +    
> +    
> +    /**
> +     * Gets the value for the given parameter key.
> +     * 
> +     * @param key case-sensitive parameter
> +     * @return value of the parameter. null if the key is absent.
> +     */
> +    String getParameter(String key);
> +    
> +    /**
> +     * Get all the parameters available in this request.
> +     * 
> +     * @return key-value pairs of the parameters. Empty map if no parameter is present.
> +     */
> +    Map<String,String> getParameters();
> +    
> +    /**
> +     * Parse the request represented as a list of strings.
> +     *
> +     * @param lines each line of the request.
> +     * @throws IOException
> +     */
> +    void read(List<String> lines) throws IOException;
> +    
> +    Response process(ServerContext server, OutputStream stream) throws Exception;
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,63 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.util.HashMap;
> +import java.util.Map;
> +
> +/**
> + * A factory to create a specific type of request.
> + * 
> + * @author Pinaki Poddar
> + *
> + */
> +public class RequestFactory {
> +    private final String _protocol;
> +    private static Map<String, RequestFactory> _registered = new HashMap<String, RequestFactory>();
> +    static {
> +        _registered.put("HTTP/1.0", new RequestFactory("HTTP/1.0"));
> +        _registered.put("HTTP/1.1", new RequestFactory("HTTP/1.1"));
> +    }
> +    
> +    public static void register(String protocol, RequestFactory factory) {
> +        _registered.put(protocol, factory);
> +    }
> +    
> +    private RequestFactory(String proto) {
> +        _protocol = proto;
> +    }
> +    
> +    public static RequestFactory getFactory(String protocol) {
> +        return _registered.get(protocol);
> +    }
> +    
> +    Request createRequest(String method) {
> +        JESTRequest request = null;
> +        if ("GET".equalsIgnoreCase(method)) {
> +            request = new GETRequest();
> +        } else {
> +            throw new UnsupportedOperationException();
> +        }
> +        request.setProtocol(_protocol);
> +        request.setMethod(method.toUpperCase());
> +        return request;
> +        
> +    }
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,147 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.BufferedReader;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.io.InputStreamReader;
> +import java.io.OutputStream;
> +import java.net.HttpURLConnection;
> +import java.net.Socket;
> +import java.net.SocketException;
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.Iterator;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +import java.util.concurrent.Callable;
> +
> +import javax.persistence.EntityManager;
> +import javax.persistence.EntityNotFoundException;
> +
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
> +import org.apache.openjpa.kernel.StoreContext;
> +import org.apache.openjpa.lib.log.Log;
> +import org.apache.openjpa.lib.util.Localizer;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.FieldMetaData;
> +import org.apache.openjpa.persistence.JPAFacadeHelper;
> +import org.apache.openjpa.persistence.OpenJPAPersistence;
> +import org.apache.openjpa.util.ApplicationIds;
> +
> +/**
> + * Handles a request from a remote client.
> + * Reads the socket data.
> + * Populates the request.
> + * Determines the processor based on request method and action.
> + * Delegates to the processor.
> + * Processor generates the response.
> + * Writes the response to the stream.
> + * 
> + * @author Pinaki Poddar
> + *
> + */
> +public class RequestHandler implements Callable<Void> {
> +    private static final int SPACE = ' ';
> +    private final Socket _socket;
> +    private final ServerContext _server;
> +    private final Log _log;
> +    private static final Localizer _loc = Localizer.forPackage(RequestHandler.class);
> +    
> +    public RequestHandler(Socket socket, ServerContext server) {
> +        _socket = socket;
> +        _server = server;
> +        _log = _server.getLog();
> +    }
> +    
> +    public Void call() throws Exception {
> +        Request request = null;
> +        Response response = null;
> +        try {
> +            request = readRequest(_socket.getInputStream());
> +            response = request.process(_server, _socket.getOutputStream());
> +        } catch (Exception e) {
> +            response = new ErrorResponse(request, _server, e, HttpURLConnection.HTTP_INTERNAL_ERROR, 
> +                _socket.getOutputStream());
> +        }
> +        response.writeOut();
> +        return null;
> +    }
> +    
> +
> +
> +    /**
> +     * Reads the given CR-LF delimited stream. 
> +     * The first line is scanned for the method (first space-delimited String) and protocol (the last
> +     * space-delimited String). Accordingly a request is created and rest of the input stream content
> +     * is parsed by the request itself. 
> +     * 
> +     * @param input
> +     * @throws IOException
> +     */
> +    public Request readRequest(InputStream input) throws IOException {
> +        if (_log.isTraceEnabled())
> +            _log.trace("Reading request from the input stream ");
> +        
> +        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
> +        String status = reader.readLine();
> +        if (_log.isInfoEnabled())
> +            _log.info("Status Line [" + status + "]");
> +        int spaceFirst = status.indexOf(SPACE);
> +        if (spaceFirst == -1) 
> +            throw new IOException("HTTP Method could not be determined from [" + status + "]");
> +        int spaceLast = status.lastIndexOf(SPACE);
> +        if (spaceLast == -1) 
> +            throw new IOException("HTTP Protocol could not be determined from [" + status + "]");
> +        String method = status.substring(0, spaceFirst);
> +        String protocol = status.substring(spaceLast+1);
> +        String path = status.substring(spaceFirst+1, spaceLast);
> +        Request request = RequestFactory.getFactory(protocol).createRequest(method);
> +        List<String> lines = new ArrayList<String>();
> +        if (path.equals("/")) {
> +            lines.add(path);
> +        } else {
> +            lines = readlines(reader);
> +            lines.add(0, path);
> +        }
> +        if (lines.isEmpty()) {
> +            throw new IOException("No CR-LF delimited lines could be read from " + input);
> +        }
> +        request.read(lines);
> +        return request;
> +    }
> +    
> +
> +    List<String> readlines(BufferedReader reader) throws IOException {
> +        List<String> buffers = new ArrayList<String>();
> +        String line;
> +        while ((line = reader.readLine()) != null && line.length() > 0) {
> +            buffers.add(line);
> +        }
> +        return buffers;
> +    }
> +    
> +    
> +    public String toString() {
> +        return _socket.getInetAddress()+":"+_socket.getPort();
> +    }    
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,54 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.InputStream;
> +import java.io.OutputStream;
> +import java.io.RandomAccessFile;
> +
> +import javax.imageio.ImageIO;
> +
> +/**
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class ResourceResponse extends AbstractResponse {
> +    private final InputStream _in;
> +    private final String _mimeType;
> +    public ResourceResponse(Request request, ServerContext ctx, InputStream resource, String mimeType,
> +        OutputStream out) throws Exception {
> +        super(request, ctx, out);
> +        _in = resource;
> +        _mimeType = mimeType;
> +    }
> +
> +    public void writeOut() throws Exception {
> +        print(_request.getProtocol()); println("200 OK");
> +        printHeader("Connection",  "close");
> +        printHeader("Content-Type", _mimeType, "charset=UTF-8");
> +        println();
> +        for (int c = 0; (c = _in.read()) != -1;) {
> +           print((char)c);
> +        }
> +        _in.close();
> +        close();
> +    }
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,31 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.Serializable;
> +
> +/**
> + * @author Pinaki Poddar
> + *
> + */
> +public interface Response extends Serializable {
> +//    void setHeader(String key, String value);
> +    void writeOut() throws Exception;
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,230 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you 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.openjpa.persistence.jest;
> +
> +import java.io.IOException;
> +import java.net.ServerSocket;
> +import java.net.Socket;
> +import java.util.concurrent.ExecutorService;
> +import java.util.concurrent.Executors;
> +
> +import javax.persistence.EntityManagerFactory;
> +
> +import org.apache.openjpa.conf.OpenJPAConfiguration;
> +import org.apache.openjpa.lib.conf.Configurable;
> +import org.apache.openjpa.lib.conf.Configuration;
> +import org.apache.openjpa.lib.log.Log;
> +import org.apache.openjpa.lib.util.Localizer;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.MetaDataRepository;
> +import org.apache.openjpa.persistence.EntityManagerFactoryImpl;
> +
> +
> +/**
> + * A server running on an independent thread that allows a remote, language-neutral client to access OpenJPA runtime.
> + *  
> + * @author Pinaki Poddar
> + *
> + */
> +public class Server implements ServerContext, Configurable, Runnable {
> +    private ServerSocket _listenSocket;
> +    protected ExecutorService _executors;
> +    public final static int DEFAULT_PORT = 6789;
> +    protected int _port = DEFAULT_PORT;
> +    protected int _range = 1;
> +    protected String _format = "xml";
> +    protected Log _log;
> +    protected Thread _thread;
> +    private EntityManagerFactoryImpl _ctx;
> +    private static Localizer _loc = Localizer.forPackage(Server.class);
> +    
> +    /**
> +     * Sets the persistence unit context in which this server will serve requests.
> +     * The context must be set before operation.
> +     * 
> +     * @param emf an implementation of OpenJPA Persistence Unit. Must not be null.
> +     */
> +    public void setContext(EntityManagerFactoryImpl emf) {
> +        if (emf == null)
> +            throw new NullPointerException();
> +        _ctx = emf;
> +    }
> +    
> +    /**
> +     * Gets the persistence unit context in which this server serves requests.
> +     * 
> +     * @param emf an implementation of OpenJPA Persistence Unit. 
> +     */
> +    public EntityManagerFactoryImpl getPersistenceUnit() {
> +        return _ctx;
> +    }
> +    
> +    public Log getLog() {
> +        return _log;
> +    }
> +    
> +    /**
> +     * Start the server in a daemon thread.
> +     * This method is idempotent.
> +     * 
> +     * @return true if the server has started by this call or already running. 
> +     */
> +    public synchronized boolean start() {
> +        try {
> +            if (_thread != null)
> +                return true;
> +            if (createServerSocket()) {
> +                _thread = new Thread(this);
> +                _thread.setDaemon(true);
> +                _thread.start();
> +                return true;
> +            }
> +            return false;
> +        } catch (Exception ex) {
> +            ex.printStackTrace();
> +            return false;
> +        }
> +    }
> +    
> +    /**
> +     * Stops the server.
> +     */
> +    public synchronized void stop() {
> +        _thread.interrupt();
> +        _thread = null;
> +        _executors.shutdownNow();
> +    }
> +    
> +    /**
> +     * Sets the port in which the server will listen.
> +     * 
> +     * @param port a positive integer.
> +     */
> +    public void setPort(int port) {
> +        _port = port;
> +    }
> +    
> +    /**
> +     * Gets the current port.
> +     * 
> +     * @return the port number. Defaults to default HTTP port.
> +     */
> +    public int getPort() {
> +        return _port;
> +    }
> +    
> +    /**
> +     * Sets the range of ports the server will attempt at start.
> +     * 
> +     * @param range a positive integer.
> +     */
> +    public void setRange(int range) {
> +        if (range > 0) 
> +            _range = range;
> +    }
> +    
> +    public void setFormat(String format) {
> +        _format = format;
> +    }
> +    
> +    public String getFormat() {
> +        return _format;
> +    }
> +    
> +    /**
> +     * Sets the range of ports the server will attempt at start.
> +     * @return  a positive integer. Defaults to 1.
> +     */
> +    public int getRange() {
> +        return _range;
> +    }
> +    
> +    public void run() {
> +        _log.info(_loc.get("server-starting", this));
> +        
> +        _executors = Executors.newCachedThreadPool();
> +        while (!Thread.interrupted()) {
> +            try {
> +                Socket socket = _listenSocket.accept();
> +                if (_log.isTraceEnabled())
> +                    _log.trace(_loc.get("server-request", socket));
> +                RequestHandler request = new RequestHandler(socket, this); 
> +                _executors.submit(request);
> +            } catch (IOException e) {
> +                e.printStackTrace();
> +            }
> +        }
> +    }
> +    
> +    private boolean createServerSocket() {
> +        int p = _port;
> +        int p2 = p + _range;
> +        Exception error = null;
> +        for (; _listenSocket == null && p < p2; ) {
> +            try {
> +                _listenSocket = new ServerSocket(p);
> +            } catch (IOException ex) {
> +                p++;
> +                error = ex;
> +            }
> +        }
> +        if (_listenSocket != null) {
> +            if (p != _port) {
> +                _port = p;
> +                _log.warn(_loc.get("server-reconfigured", _port));
> +            }
> +        } else {
> +            if (error != null) {
> +                _log.warn(_loc.get("server-failed", this, _port, error));
> +            }
> +        }
> +        return _listenSocket != null;
> +    }
> +    
> +    // Configurable contract
> +    public void setConfiguration(Configuration conf) {
> +        _log = conf.getLog("Remote");
> +    }
> +
> +    public void startConfiguration() {
> +    }
> +
> +    public void endConfiguration() {
> +    }
> +    
> +    
> +    // Server side utilities 
> +    
> +    /**
> +     * Resolves the given alias to a persistent class meta data.
> +     * 
> +     * @exception if no meta data available for the given alias 
> +     */
> +    public ClassMetaData resolve(String alias) {
> +        MetaDataRepository repos = _ctx.getConfiguration().getMetaDataRepositoryInstance();
> +        return repos.getMetaData(alias, Thread.currentThread().getContextClassLoader(), true);
> +    }
> +    
> +    public String toString() {
> +        if (_listenSocket == null) return "JEST Server [not strated]";
> +        return "JEST Server " + _listenSocket.getInetAddress()+":"+_listenSocket.getLocalPort();
> +    }
> +
> +}
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> ------------------------------------------------------------------------------
>     svn:eol-style = native
> 
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> ------------------------------------------------------------------------------
>     svn:mime-type = text/plain
> 
> 
>