openjpa-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Donald Woods <dwo...@apache.org>
Subject Re: svn commit: r1028093 [1/2] - JEST...
Date Wed, 27 Oct 2010 21:58:17 GMT
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
> 
> 
> 

Mime
View raw message