You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@openjpa.apache.org by Donald Woods <dw...@apache.org> on 2010/10/27 23:58:17 UTC
Re: svn commit: r1028093 [1/2] - JEST...
Is this really ready to drop into trunk?
And do we really want it in the base openjpa.jar?
-Donald
On 10/27/10 4:42 PM, ppoddar@apache.org wrote:
> Author: ppoddar
> Date: Wed Oct 27 20:42:44 2010
> New Revision: 1028093
>
> URL: http://svn.apache.org/viewvc?rev=1028093&view=rev
> Log:
> OPENJPA-1851: First version of JEST (REST on OpenJPA)
>
> Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ServerContext.java (with props)
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/XMLEncoder.java (with props)
> Modified:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
>
> Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java (original)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java Wed Oct 27 20:42:44 2010
> @@ -45,10 +45,10 @@ import org.apache.openjpa.lib.util.Close
> import org.apache.openjpa.lib.util.Localizer;
> import org.apache.openjpa.persistence.criteria.CriteriaBuilderImpl;
> import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
> +import org.apache.openjpa.persistence.jest.Server;
> import org.apache.openjpa.persistence.meta.MetamodelImpl;
> import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
> import org.apache.openjpa.persistence.query.QueryBuilderImpl;
> -import org.apache.openjpa.util.UserException;
>
> /**
> * Implementation of {@link EntityManagerFactory} that acts as a
> @@ -70,7 +70,8 @@ public class EntityManagerFactoryImpl
> private transient StoreCache _cache = null;
> private transient QueryResultCache _queryCache = null;
> private transient MetamodelImpl _metaModel;
> -
> + private transient Server _remoteAccess = null;
> +
> /**
> * Default constructor provided for auto-instantiation.
> */
> @@ -93,10 +94,11 @@ public class EntityManagerFactoryImpl
>
> /**
> * Delegate must be provided before use.
> + * Configures for Remote Access, if appropriate.
> */
> public void setBrokerFactory(BrokerFactory factory) {
> - _factory = new DelegatingBrokerFactory(factory,
> - PersistenceExceptions.TRANSLATOR);
> + _factory = new DelegatingBrokerFactory(factory, PersistenceExceptions.TRANSLATOR);
> + configureRemoteAccess(getConfiguration());
> }
>
> public OpenJPAConfiguration getConfiguration() {
> @@ -271,6 +273,10 @@ public class EntityManagerFactoryImpl
> if (log.isTraceEnabled()) {
> log.trace(this + ".close() invoked.");
> }
> + if (_remoteAccess != null) {
> + _remoteAccess.stop();
> + _remoteAccess = null;
> + }
> _factory.close();
> }
>
> @@ -396,4 +402,40 @@ public class EntityManagerFactoryImpl
> }
> }
> }
> +
> + /**
> + * Configures this unit for remote access.
> + */
> + protected void configureRemoteAccess(OpenJPAConfiguration conf) {
> + Value value = conf.getValue("RemoteAccess");
> + if (value == null) {
> + return;
> + }
> + String props = value.getString();
> + if (props == null)
> + return;
> + try {
> + _remoteAccess = new Server();
> + _remoteAccess.setContext(this);
> + Configurations.configureInstance(_remoteAccess, conf, props);
> + conf.removeValue(value);
> + if (!_remoteAccess.start()) {
> + _remoteAccess = null;
> + }
> + } catch (Exception ex) {
> + Log log = _factory.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
> + if (log != null) {
> + log.error(_loc.get("remote-start-error"), ex);
> + }
> + }
> + }
> +
> + /**
> + * Affirms if this unit is accessible remotely.
> + */
> + public boolean allowsRemoteAccess() {
> + return _remoteAccess != null;
> + }
> +
> +
> }
>
> Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java (original)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java Wed Oct 27 20:42:44 2010
> @@ -188,6 +188,7 @@ public class PersistenceProductDerivatio
> conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(), PersistenceMetaDataFactory.class.getName());
>
> conf.addValue(new EntityManagerFactoryValue());
> + conf.addString("RemoteAccess");
>
> conf.readLockLevel.setAlias("optimistic", String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
> conf.readLockLevel.setAlias("optimistic-force-increment", String
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,77 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.OutputStream;
> +import java.io.PrintStream;
> +
> +/**
> + * Abstract implementation of a stream-based response.
> + * Every response is result of a {@linkplain Request} and operates within a {@linkplain ServerContext}.
> + * This fact is enforced by the constructor argument of an abstract response.
> + * <p>
> + * Besides, this implementation provides common utility to write HTTP response or extract useful information
> + * from the request.
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public abstract class AbstractResponse extends PrintStream implements Response {
> + protected final Request _request;
> + protected final ServerContext _ctx;
> +
> + /**
> + * Construct a response for the given request and server context.
> + *
> + * @param request the request for this response. Can be null if the response is for server error.
> + * @param ctx the processing context
> + * @param out the output stream where the response is targeted.
> + */
> + protected AbstractResponse(Request request, ServerContext ctx, OutputStream out) {
> + super(out);
> + _request = request;
> + _ctx = ctx;
> + }
> +
> + /**
> + * Write a HTTP header to the stream.
> + * <br>
> + * The format of HTTP header is <code>key: [value]* NEWLINE</code>
> + *
> + * @param key the key of the header
> + * @param values one of more value of the header fields.
> + */
> + protected void printHeader(String key, String...values) {
> + if (key == null)
> + return;
> + print(key);
> + print(" :");
> + if (values == null || values.length == 0)
> + return;
> + int n = values.length-1;
> + for (int i = 0; i < n-1; i++) {
> + print(values[i]);
> + print(";");
> + }
> + println(values[n]);
> + }
> +
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,68 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.OutputStream;
> +import java.net.HttpURLConnection;
> +
> +/**
> + * A HTTP response for something gone wrong.
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class ErrorResponse extends AbstractResponse {
> + private final Exception _error;
> + private final int _code;
> + /**
> + * Construct a response to describe a error.
> + *
> + * @param request a request that produced this response. VCan be null to denote that the request can not
> + * be created.
> + * @param ctx the processing context
> + * @param ex the error
> + * @param code HTTP error code
> + * @param out the stream where the response is written
> + *
> + */
> + public ErrorResponse(Request request, ServerContext ctx, Exception ex, int code, OutputStream out) {
> + super(request, ctx, out);
> + _error = ex;
> + _code = code;
> + }
> +
> + /**
> + * Writes the response.
> + * The response is always a HTTP response with the error stack trace.
> + */
> + public void writeOut() throws Exception {
> + println("HTTP/1.1"); print(" " + _code); println("Error");
> + printHeader("Connection", "close");
> + printHeader("Content-Type", "text/html", "charset=UTF-8");
> + println();
> + println("<html><body><pre>");
> + _error.printStackTrace(this);
> + println("Response from JEST");
> +
> + println("</pre></body></html>");
> + close();
> + }
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,147 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.InputStream;
> +import java.io.OutputStream;
> +import java.net.HttpURLConnection;
> +import java.util.Iterator;
> +import java.util.List;
> +import java.util.Map;
> +
> +import javax.persistence.EntityManager;
> +import javax.persistence.EntityNotFoundException;
> +import javax.persistence.Query;
> +
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
> +import org.apache.openjpa.kernel.StoreContext;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.persistence.JPAFacadeHelper;
> +import org.apache.openjpa.util.ApplicationIds;
> +import org.apache.openjpa.util.ObjectNotFoundException;
> +
> +/**
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class GETRequest extends JESTRequest {
> + public Response process(ServerContext server, OutputStream out) throws Exception {
> + String action = getAction();
> + try {
> + if ("find".equals(action)) {
> + return find(server, out);
> + } else {
> + return resource(server, out);
> + }
> + } catch (Exception e) {
> + return new ErrorResponse(this, server, new RuntimeException("bad action " + action),
> + HttpURLConnection.HTTP_BAD_REQUEST, out);
> + }
> + }
> +
> + Response find(ServerContext server, OutputStream out) throws Exception {
> + EntityManager em = server.getPersistenceUnit().createEntityManager();
> + Map<String, String> qualifiers = getQualifiers();
> + Map<String, String> parameters = getParameters();
> + if (parameters.size() < 2)
> + throw new IllegalArgumentException("find must have at least two parameters");
> + Object[] pks = new Object[parameters.size()-1];
> + Iterator<Map.Entry<String,String>> params = parameters.entrySet().iterator();
> + String alias = null;
> + for (int i = 0; i < parameters.size(); i++) {
> + if (i == 0) {
> + alias = params.next().getKey();
> + } else {
> + pks[i-1] = params.next().getKey();
> + }
> + }
> + ClassMetaData meta = server.resolve(alias);
> + Object oid = ApplicationIds.fromPKValues(pks, meta);
> + Object pc = em.find(meta.getDescribedType(), oid);
> + if (pc != null) {
> + OpenJPAStateManager sm = ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(pc);
> + return new JESTResponse(this, server, sm, out);
> + } else {
> + return new ErrorResponse(this, server, new EntityNotFoundException("not found!"),
> + HttpURLConnection.HTTP_NOT_FOUND, out);
> + }
> + }
> +
> + Response query(ServerContext server, OutputStream out) throws Exception {
> + EntityManager em = server.getPersistenceUnit().createEntityManager();
> + Map<String, String> qualifiers = getQualifiers();
> + boolean named = isBooleanQualifier("named");
> + boolean single = isBooleanQualifier("single");
> + Map<String, String> parameters = getParameters();
> + if (parameters.size() < 1)
> + throw new IllegalArgumentException("find must have at least one parameter");
> + Iterator<Map.Entry<String,String>> params = parameters.entrySet().iterator();
> + Query query = null;
> + int i = 0;
> + for (Map.Entry<String, String> param : parameters.entrySet()) {
> + if (i == 0) {
> + query = named ? em.createQuery(param.getKey()) : em.createNamedQuery(param.getKey());
> + } else {
> + query.setParameter(param.getKey(), param.getValue());
> + }
> + }
> + if (single) {
> + Object result = query.getSingleResult();
> + OpenJPAStateManager sm = ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(result);
> + return new JESTResponse(this, server, sm, out);
> + } else {
> + List<Object> result = query.getResultList();
> + return new ErrorResponse(this, server, new EntityNotFoundException("not found!"), 404, out);
> + }
> + }
> +
> + Response resource(ServerContext server, OutputStream out) throws Exception {
> + String resource = getAction();
> + if (resource.length() == 0)
> + resource = "index.html";
> + String mimeType = getMimeType(resource);
> +// InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
> + InputStream in = getClass().getResourceAsStream(resource);
> + if (in == null) {
> + return new ErrorResponse(this, server, new ObjectNotFoundException(resource), 404, out);
> + }
> + if (server.getLog().isTraceEnabled())
> + server.getLog().trace("Found resource " + resource);
> + return mimeType.startsWith("image")
> + ? new ImageResponse(this, server, in, mimeType, out)
> + : new ResourceResponse(this, server, in, mimeType, out);
> + }
> +
> + boolean isBooleanQualifier(String key) {
> + String q = getQualifier(key);
> + return hasQualifier(key) && (q == null || "true".equals(q));
> + }
> +
> + String getMimeType(String resource) {
> + int index = resource.lastIndexOf('.');
> + String ext = (index != -1) ? resource.substring(index+1) : "";
> + if (ext.equalsIgnoreCase("html") || ext.equalsIgnoreCase(".html")) return "text/html";
> + if (ext.equalsIgnoreCase(".png") || ext.equalsIgnoreCase(".ico") || ext.equalsIgnoreCase("jpeg"))
> + return "image/"+ext;
> + return "text/html";
> + }
> +
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,61 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.InputStream;
> +import java.io.OutputStream;
> +import java.io.RandomAccessFile;
> +
> +import javax.imageio.ImageIO;
> +
> +/**
> + * Sends an image as response.
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class ImageResponse extends AbstractResponse {
> + private final InputStream _in;
> + private final String _mimeType;
> +
> + public ImageResponse(Request request, ServerContext ctx, InputStream resource, String mimeType,
> + OutputStream out) throws Exception {
> + super(request, ctx, out);
> + _in = resource;
> + _mimeType = mimeType;
> + }
> +
> + public void writeOut() throws Exception {
> + print(_request.getProtocol()); println("200 OK");
> + printHeader("Connection", "close");
> + printHeader("Content-Type", _mimeType);
> + println();
> + byte[] b = new byte[1024];
> + int i = 0;
> + for (int l = 0; (l = _in.read(b)) != -1;) {
> + write(b, 0, l);
> + i += l;
> + }
> + printHeader("Content-Length", ""+i);
> + _in.close();
> + close();
> + }
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,386 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.IOException;
> +import java.util.Arrays;
> +import java.util.Collections;
> +import java.util.HashMap;
> +import java.util.LinkedHashMap;
> +import java.util.LinkedList;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.NoSuchElementException;
> +
> +/**
> + * A request carries requisite data for a JPA operation to be performed.
> + * The request is populated by parsing an input data stream.
> + *
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public abstract class JESTRequest implements Request {
> + private String _method;
> + private String _protocol;
> + private String _action;
> + private String _body;
> + private LinkedHashMap<String, String> _qualifiers = new LinkedHashMap<String, String>();
> + private LinkedHashMap<String, String> _params = new LinkedHashMap<String, String>();
> + private Map<String, List<String>> _headers = new HashMap<String, List<String>>();
> + private ParseState _state;
> + private StringBuffer buf = new StringBuffer();
> + private LinkedList<Token> _stack = new LinkedList<Token>();
> +
> + public static final List<String> METHODS = Arrays.asList(new String[]{"GET","POST","PUT","DELETE"});
> +
> + /**
> + * Parse States.
> + */
> + static enum ParseState {
> + INIT, ACTION, QUALIFIER_KEY, QUALIFIER_VALUE, PARAM_KEY, PARAM_VALUE, END
> + };
> +
> +
> + public String getMethod() {
> + return _method;
> + }
> +
> + void setMethod(String method) {
> + if (_method == null) {
> + if (method != null && METHODS.contains(method.toUpperCase())) {
> + _method = method.toUpperCase();
> + } else {
> + throw new IllegalArgumentException("Unsupported method " + method);
> + }
> + } else if (!_method.equalsIgnoreCase(method)) {
> + throw new IllegalStateException("Method can not be changed to [" + method + "]. " +
> + "Current method [" + _method + "]");
> + }
> + }
> +
> + public String getProtocol() {
> + return _protocol == null ? "HTTP/1.1" : _protocol;
> + }
> +
> + void setProtocol(String protocol) {
> + if (_protocol == null) {
> + if (protocol != null && protocol.toUpperCase().startsWith("HTTP")) {
> + _protocol = protocol.toUpperCase();
> + } else {
> + throw new IllegalArgumentException("Unsupported protocol " + protocol);
> + }
> + } else if (!_protocol.equalsIgnoreCase(protocol)) {
> + throw new IllegalStateException("Protocol can not be changed to [" + protocol + "]. " +
> + "Current protocol [" + _protocol + "]");
> + }
> + }
> +
> + /**
> + * Sets an action. Once set, an action can not be modified.
> + *
> + * @param action
> + */
> + private void setAction(String action) {
> + if (_action == null) {
> + _action = action;
> + } else if (!_action.equals(action)) {
> + throw new IllegalStateException("Action can not be [" + action + "]. Already set to [" + _action + "]");
> + }
> + }
> +
> + public String getAction() {
> + return _action == null ? "" : _action;
> + }
> +
> + public String getBody() {
> + return _body;
> + }
> +
> + private void setQualifier(String key, String value) {
> + _qualifiers.put(key, value);
> + }
> +
> + public String getQualifier(String key) {
> + return _qualifiers.get(key);
> + }
> +
> + public Map<String, String> getQualifiers() {
> + return Collections.unmodifiableMap(_qualifiers);
> + }
> +
> + public boolean hasQualifier(String key) {
> + return _qualifiers.containsKey(key);
> + }
> +
> + private void setParameter(String key, String value) {
> + _params.put(key, value);
> + }
> +
> + public String getParameter(String key) {
> + return _params.get(key);
> + }
> +
> + public boolean hasParameter(String key) {
> + return _params.containsKey(key);
> + }
> +
> + public Map<String, String> getParameters() {
> + return Collections.unmodifiableMap(_params);
> + }
> +
> + public Map.Entry<String, String> getParameter(int n) {
> + if (n >= _params.size())
> + throw new NoSuchElementException("Index " + n + " size " + _params.size());
> + int i = 0;
> + for (Map.Entry<String, String> entry : _params.entrySet()) {
> + if (i == n) {
> + return entry;
> + }
> + i++;
> + }
> + return null;
> + }
> +
> + public Map<String, List<String>> getHeaders() {
> + return Collections.unmodifiableMap(_headers);
> + }
> +
> + public List<String> getHeader(String key) {
> + return _headers.get(key);
> + }
> +
> +
> + public void read(List<String> lines) throws IOException {
> + parse(lines.get(0));
> + int i = 1;
> + for (; i < lines.size(); i++) {
> + String line = lines.get(i);
> + if (line.length() == 0) {
> + break;
> + } else {
> + parseHeader(line);
> + }
> + }
> + parseBody(lines.subList(i, lines.size()));
> + }
> +
> + protected void parseHeader(String line) throws IOException {
> + String key = null;
> + StringBuilder token = new StringBuilder();
> + int N = line.length();
> + for (int i = 0; i < N; i++) {
> + char c = line.charAt(i);
> + if (c == ':' && key == null) {
> + key = token.toString().trim();
> + token.delete(0, token.length());
> + } else {
> + token.append(c);
> + }
> + }
> + if (key != null) {
> + _headers.put(key, Collections.singletonList(token.toString().trim()));
> + }
> + }
> +
> + protected void parseBody(List<String> lines) {
> + if (lines == null || lines.isEmpty())
> + return;
> + for (String line : lines) {
> + if (_body == null) {
> + _body = line;
> + } else {
> + _body = _body + line;
> + }
> + }
> + }
> +
> + /**
> + * Parses JEST stream and populates a request.
> + *
> + */
> + protected void parse(String s) {
> + char[] chars = s.toCharArray();
> + _state = ParseState.INIT;
> + _stack.clear();
> +
> + for (int i = 0; i < chars.length; i++) {
> + char ch = chars[i];
> + switch (_state) {
> + case INIT:
> + if (ch == '/') {
> + transit(ParseState.ACTION);
> + } else if (!Character.isWhitespace(ch)) {
> + parseError(ch, i, s, true, ' ');
> + }
> + break;
> +
> + case ACTION:
> + if (ch == '/') {
> + transit(ParseState.QUALIFIER_KEY);
> + } else if (ch == '?') {
> + transit(ParseState.PARAM_KEY);
> + } else {
> + buf.append(ch);
> + }
> + break;
> +
> + case QUALIFIER_KEY:
> + if (Character.isJavaIdentifierPart(ch)) {
> + buf.append(ch);
> + } else if (ch == '=') {
> + transit(ParseState.QUALIFIER_VALUE);
> + } else if (ch == '/') {
> + transit(ParseState.QUALIFIER_KEY);
> + } else if (ch == '?') {
> + transit(ParseState.PARAM_KEY);
> + } else {
> + parseError(ch, i, s, true, '/', '?', '=');
> + }
> + break;
> +
> + case QUALIFIER_VALUE:
> + if (Character.isJavaIdentifierPart(ch)) {
> + buf.append(ch);
> + } else if (ch == '/') {
> + transit(ParseState.QUALIFIER_KEY);
> + } else if (ch == '?') {
> + transit(ParseState.PARAM_KEY);
> + } else {
> + parseError(ch, i, s, true, '/', '?');
> + }
> + break;
> +
> + case PARAM_KEY:
> + if (Character.isJavaIdentifierPart(ch)) {
> + buf.append(ch);
> + } else if (ch == '=') {
> + if (isQueryKey())
> + buf.append(ch);
> + else
> + transit(ParseState.PARAM_VALUE);
> + } else if (ch == ';') {
> + transit(ParseState.PARAM_KEY);
> + } else if (isQueryKey() && isQueryChar(ch)) {
> + buf.append(ch);
> + } else {
> + parseError(ch, i, s, true, ';', '=');
> + }
> + break;
> +
> + case PARAM_VALUE:
> + if (Character.isJavaIdentifierPart(ch)) {
> + buf.append(ch);
> + } else if (ch == ';') {
> + transit(ParseState.PARAM_KEY);
> + } else {
> + parseError(ch, i, s, true, ';');
> + }
> + break;
> + default:
> + throw new RuntimeException("ParseError: '" + ch + "' at " + i + " in [" + s + "]. "
> + + "Unknown state " + _state);
> + }
> + }
> + if (buf.length() > 0) {
> + transit(ParseState.END);
> + }
> + }
> +
> + /**
> + * Affirms if parsing a query string.
> + */
> + private boolean isQueryKey() {
> + return "query".equals(_action) && _stack.size() == 1;
> + }
> +
> + /**
> + * Affirms if the given character is valid in a query string
> + */
> + private boolean isQueryChar(char c) {
> + return c == ' ' || c == '.' || c == ':' || c == '?' || c == '\'';
> + }
> +
> + /**
> + * Transitions to a new parse state.
> + *
> + * @param to target parse state
> + */
> + void transit(ParseState to) {
> + String token = buf.toString();
> + switch (_state) {
> + case ACTION:
> + setAction(token);
> + break;
> + case QUALIFIER_KEY:
> + setQualifier(token, null);
> + break;
> + case QUALIFIER_VALUE:
> + setQualifier(_stack.peekLast().getValue(), token);
> + break;
> + case PARAM_KEY:
> + setParameter(token, null);
> + break;
> + case PARAM_VALUE:
> + setParameter(_stack.peekLast().getValue(), token);
> + break;
> +
> + }
> + if (_state != ParseState.INIT && to != ParseState.END) {
> + _stack.add(new Token(_state, token));
> + }
> + buf.delete(0, buf.length());
> + _state = to;
> + }
> +
> + protected void parseError(char ch, int pos, String line, boolean java, char... expected) {
> + throw new RuntimeException("ParseError: Encountered '" + ch + "' at " + pos + " in [" + line + "] while "
> + + "parsing " + _state + ". Expected " + Arrays.toString(expected) + (java ? " or Java identifer" : ""));
> +
> + }
> +
> + /**
> + * Token in a JEST stream.
> + *
> + */
> + static class Token {
> + final ParseState _type;
> + final String _value;
> +
> + public Token(ParseState type, String value) {
> + _type = type;
> + _value = value;
> + }
> +
> + public ParseState getType() {
> + return _type;
> + }
> +
> + public String getValue() {
> + return _value;
> + }
> +
> + public String toString() {
> + return _value + "[" + _type + "]";
> + }
> + }
> +
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,71 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.IOException;
> +import java.io.OutputStream;
> +import java.io.PrintStream;
> +import java.io.PrintWriter;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
> +import org.apache.openjpa.persistence.JPAFacadeHelper;
> +import org.w3c.dom.Document;
> +import org.w3c.dom.Element;
> +
> +/**
> + * Response of a JEST Request.
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class JESTResponse extends AbstractResponse {
> + private final OpenJPAStateManager _sm;
> +
> + public JESTResponse(Request request, ServerContext ctx, OpenJPAStateManager sm, OutputStream out) throws Exception {
> + super(request, ctx, out);
> + _sm = sm;
> + }
> +
> + public String getContentType() {
> + return "text/html";
> + }
> +
> + public void writeOut() throws Exception {
> + print(_request.getProtocol()); println("200 OK");
> + printHeader("Connection", "close");
> + printHeader("Content-Type", "text/html", "charset=UTF-8");
> + println();
> + println("<html><body><pre>");
> + XMLEncoder encoder = new XMLEncoder(_ctx.getPersistenceUnit().getMetamodel());
> + Document doc = encoder.encode(_sm);
> + encoder.writeDoc(doc, this);
> + println("Response from JEST");
> +
> + println("</pre></body></html>");
> + close();
> + }
> +
> +}
> +
> +
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,316 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.BufferedReader;
> +import java.io.CharArrayWriter;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.io.InputStreamReader;
> +import java.io.Reader;
> +import java.util.Arrays;
> +import java.util.BitSet;
> +import java.util.Collection;
> +import java.util.HashSet;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +
> +import javax.persistence.metamodel.Attribute;
> +
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
> +import org.apache.openjpa.kernel.StoreContext;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.FieldMetaData;
> +import org.apache.openjpa.meta.JavaTypes;
> +import org.apache.openjpa.meta.ValueMetaData;
> +import org.apache.openjpa.persistence.meta.Members;
> +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> +import org.w3c.dom.CDATASection;
> +import org.w3c.dom.Document;
> +import org.w3c.dom.Element;
> +
> +/**
> + * Marshals a root instance and its persistent closure as JSON object.
> + * The closure is resolved against the persistence context that contains the root instance.
> + * The JSON format introduces a $id and $ref to address reference that pure JSON does not.
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +public class JSONEncoder {
> + /**
> + * The element/attribute tags declared in <code>jest-instance.xsd</code> XML schema.
> + */
> + public static final String ELEMENT_NULL_REF = "null";
> + public static final String ELEMENT_INSTANCE = "instance";
> + public static final String ELEMENT_REF = "ref";
> +
> +
> + private MetamodelHelper _model;
> +
> + public JSONEncoder(MetamodelImpl model) {
> + _model = new MetamodelHelper(model);
> + }
> +
> + /**
> + * Encodes the given managed instance into a new XML element as a child of the given parent node.
> + *
> + * @param sm a managed instance, can be null.
> + * @param parent the parent node to which the new node be attached.
> + */
> + public StringBuilder encode(final OpenJPAStateManager sm) {
> + return encode(sm, new HashSet<OpenJPAStateManager>(), 0, false);
> + }
> + StringBuilder indent(StringBuilder buf, int indent) {
> + if (indent <= 0)
> + return buf;
> + char[] spaces = new char[indent*4];
> + Arrays.fill(spaces, ' ');
> + buf.insert(0, spaces);
> + return buf;
> + }
> + StringBuilder end(StringBuilder buf, char ch, int indent) {
> + char[] spaces = new char[indent*4];
> + Arrays.fill(spaces, ' ');
> + return buf.append("\r\n").append(spaces).append(ch);
> + }
> +
> + /**
> + * Encodes the closure of a persistent instance into a XML element.
> + *
> + * @param sm the managed instance to be encoded. Can be null.
> + * @param parent the parent XML element to which the new XML element be added. Must not be null. Must be
> + * owned by a document.
> + * @param visited the persistent instances that had been encoded already. Must not be null or immutable.
> + *
> + * @return the new element. The element has been appended as a child to the given parent in this method.
> + */
> + private StringBuilder encode(final OpenJPAStateManager sm, final Set<OpenJPAStateManager> visited,
> + int indent, boolean indentPara) {
> + if (visited == null) {
> + throw new IllegalArgumentException("null closure for encoder");
> + }
> + StringBuilder root = indent(new StringBuilder("{"), indentPara ? indent : 0);
> + if (sm == null) {
> + return root.append("null}");
> + }
> + boolean ref = !visited.add(sm);
> + if (ref) {
> + return indent(root.append(quoted("$ref")).append(": ").append(ior(sm)).append('}'),
> + indentPara ? indent : 0);
> + } else {
> + indent(root.append(quoted("$id")).append(": ").append(ior(sm)), indentPara ? indent : 0);
> + }
> +
> + StringBuilder child = new StringBuilder();
> + BitSet loaded = sm.getLoaded();
> + StoreContext ctx = (StoreContext)sm.getGenericContext();
> + List<Attribute<?, ?>> attrs = _model.getAttributesInOrder(sm.getMetaData());
> + for (int i = 0; i < attrs.size(); child = new StringBuilder(), i++) {
> + FieldMetaData fmd = ((Members.Member<?, ?>) attrs.get(i)).fmd;
> + if (!loaded.get(fmd.getIndex()))
> + continue;
> + Object value = sm.fetch(fmd.getIndex());
> + child.append(quoted(fmd.getName())).append(": ");
> + switch (fmd.getDeclaredTypeCode()) {
> + case JavaTypes.BOOLEAN:
> + case JavaTypes.BYTE:
> + case JavaTypes.CHAR:
> + case JavaTypes.DOUBLE:
> + case JavaTypes.FLOAT:
> + case JavaTypes.INT:
> + case JavaTypes.LONG:
> + case JavaTypes.SHORT:
> +
> + case JavaTypes.BOOLEAN_OBJ:
> + case JavaTypes.BYTE_OBJ:
> + case JavaTypes.CHAR_OBJ:
> + case JavaTypes.DOUBLE_OBJ:
> + case JavaTypes.FLOAT_OBJ:
> + case JavaTypes.INT_OBJ:
> + case JavaTypes.LONG_OBJ:
> + case JavaTypes.SHORT_OBJ:
> +
> + case JavaTypes.BIGDECIMAL:
> + case JavaTypes.BIGINTEGER:
> + case JavaTypes.DATE:
> + case JavaTypes.NUMBER:
> + case JavaTypes.CALENDAR:
> + case JavaTypes.LOCALE:
> + case JavaTypes.STRING:
> + case JavaTypes.ENUM:
> + child.append(quoted(value));
> + break;
> +
> + case JavaTypes.PC:
> + if (value == null) {
> + child.append("null");
> + } else {
> + child.append(encode(ctx.getStateManager(value), visited, indent+1, false));
> + }
> + break;
> +
> + case JavaTypes.ARRAY:
> + Object[] values = (Object[])value;
> + value = Arrays.asList(values);
> + // no break;
> + case JavaTypes.COLLECTION:
> + if (value == null) {
> + child.append("null");
> + break;
> + }
> + child.append("[");
> + Collection<?> members = (Collection<?>)value;
> + boolean basic = fmd.getElement().getTypeMetaData() == null;
> + int k = 0;
> + for (Object o : members) {
> + child.append("\r\n");
> + if (o == null) {
> + child.append(indent(new StringBuilder("null"), indent+1));
> + } else {
> + if (basic) {
> + child.append(indent(new StringBuilder(quoted(o)), indent+1));
> + } else {
> + child.append(encode(ctx.getStateManager(o), visited, indent+1, true));
> + }
> + }
> + }
> + end(child, ']', indent+1);
> + break;
> + case JavaTypes.MAP:
> + if (value == null) {
> + child.append("null");
> + break;
> + }
> + child.append("[");
> + Set<Map.Entry> entries = ((Map)value).entrySet();
> + boolean basicKey = fmd.getElement().getTypeMetaData() == null;
> + boolean basicValue = fmd.getValue().getTypeMetaData() == null;
> + for (Map.Entry<?,?> e : entries) {
> + if (e.getKey() == null) {
> + child.append("null:");
> + } else {
> + if (basicKey) {
> + child.append(quoted(e.getKey())).append(":");
> + } else {
> + child.append(encode(ctx.getStateManager(e.getKey()), visited, indent+1, true));
> + }
> + }
> + if (e.getValue() == null) {
> + child.append("null");
> + } else {
> + if (basicValue) {
> + child.append(quoted(e.getValue()));
> + } else {
> + child.append(encode(ctx.getStateManager(e.getValue()), visited, indent+1, false));
> + }
> + }
> + }
> + break;
> +
> + case JavaTypes.INPUT_STREAM:
> + case JavaTypes.INPUT_READER:
> + child = new StringBuilder(fmd.getName());
> + if (value == null) {
> + child.append("null");
> + } else {
> + child.append(streamToString(value));
> + }
> + break;
> +
> + case JavaTypes.PC_UNTYPED:
> + case JavaTypes.OBJECT:
> + case JavaTypes.OID:
> + System.err.println("Not handled " + fmd.getName() + " of type " + fmd.getDeclaredType());
> + }
> +
> + if (child != null) {
> + root.append("\r\n");
> + root.append(indent(child, indent+1));
> + if (loaded.length()-1 != i)
> + root.append(",");
> + }
> + }
> + return end(root, '}', indent);
> + }
> +
> +
> + String ior(OpenJPAStateManager sm) {
> + return quoted(typeOf(sm)+"-"+sm.getObjectId().toString());
> + }
> +
> + String typeOf(OpenJPAStateManager sm) {
> + return sm.getMetaData().getDescribedType().getSimpleName();
> + }
> +
> + String typeOf(Class<?> cls) {
> + return cls.getSimpleName();
> + }
> +
> + String typeOf(ClassMetaData meta) {
> + return meta.getDescribedType().getSimpleName();
> + }
> +
> + String typeOf(ValueMetaData vm) {
> + if (vm.getTypeMetaData() == null)
> + return typeOf(vm.getType());
> + return typeOf(vm.getTypeMetaData());
> + }
> +
> + String typeOf(FieldMetaData fmd) {
> + return fmd.getType().getSimpleName();
> + }
> +
> +
> + /**
> + * Convert the given stream (either an InutStream or a Reader) to a String
> + * to be included in CDATA section of a XML document.
> + *
> + * @param value the field value to be converted. Can not be null
> + * @return
> + */
> + String streamToString(Object value) {
> + Reader reader = null;
> + if (value instanceof InputStream) {
> + reader = new BufferedReader(new InputStreamReader((InputStream)value));
> + } else if (value instanceof Reader) {
> + reader = (Reader)value;
> + } else {
> + throw new RuntimeException();
> + }
> + CharArrayWriter writer = new CharArrayWriter();
> + try {
> + for (int c; (c = reader.read()) != -1;) {
> + writer.write(c);
> + }
> + } catch (IOException ex) {
> + throw new RuntimeException(ex);
> + }
> + return writer.toString();
> + }
> +
> + String quoted(Object o) {
> + if (o == null) return "null";
> + if (o instanceof Number)
> + return o.toString();
> + return "\"" + o.toString() + "\"";
> + }
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,127 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.Comparator;
> +import java.util.HashMap;
> +import java.util.List;
> +import java.util.Map;
> +
> +import javax.persistence.metamodel.Attribute;
> +import javax.persistence.metamodel.EntityType;
> +import javax.persistence.metamodel.ManagedType;
> +import javax.persistence.metamodel.Metamodel;
> +import javax.persistence.metamodel.SingularAttribute;
> +
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.FieldMetaData;
> +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> +
> +/**
> + * @author Pinaki Poddar
> + *
> + */
> +public class MetamodelHelper {
> + private MetamodelImpl _model;
> + private Map<ManagedType<?>, List<Attribute<?, ?>>> _attrs = new HashMap<ManagedType<?>, List<Attribute<?,?>>>();
> +
> + public MetamodelHelper(MetamodelImpl model) {
> + _model = model;
> + }
> +
> + public List<Attribute<?,?>> getAttributesInOrder(Class<?> cls) {
> + return getAttributesInOrder(_model.managedType(cls));
> + }
> +
> + public List<Attribute<?,?>> getAttributesInOrder(ClassMetaData meta) {
> + return getAttributesInOrder(meta.getDescribedType());
> + }
> +
> + /**
> + * Gets the attributes of the given type in defined order.
> + * @param type
> + * @return
> + */
> + public List<Attribute<?,?>> getAttributesInOrder(ManagedType<?> type) {
> + List<Attribute<?,?>> attrs = _attrs.get(type);
> + if (attrs != null)
> + return attrs;
> + List<Attribute<?,?>> list = new ArrayList<Attribute<?,?>>(type.getAttributes());
> + Collections.sort(list, new AttributeComparator());
> + _attrs.put(type, list);
> + return list;
> + }
> +
> + public static boolean isId(Attribute<?,?> a) {
> + if (a instanceof SingularAttribute)
> + return ((SingularAttribute<?,?>)a).isId();
> + return false;
> + }
> +
> + public static boolean isVersion(Attribute<?,?> a) {
> + if (a instanceof SingularAttribute)
> + return ((SingularAttribute<?,?>)a).isVersion();
> + return false;
> + }
> +
> + public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
> + if (isId(attr))
> + return 0;
> + if (isVersion(attr))
> + return 1;
> +
> + switch (attr.getPersistentAttributeType()) {
> + case BASIC :
> + case EMBEDDED:
> + return 2;
> + case ONE_TO_ONE:
> + case MANY_TO_ONE:
> + return 3;
> + case ONE_TO_MANY:
> + case MANY_TO_MANY:
> + case ELEMENT_COLLECTION: return 4;
> + default: return 5;
> + }
> + }
> +
> + /**
> + * Compares attribute by their qualification.
> + * Identity
> + * Version
> + * Basic
> + * Singular association
> + * Plural association
> + *
> + */
> + public static class AttributeComparator implements Comparator<Attribute<?,?>> {
> + @Override
> + public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
> + Integer t1 = getAttributeTypeCode(a1);
> + Integer t2 = getAttributeTypeCode(a2);
> + if (t1.equals(t2)) {
> + return a1.getName().compareTo(a2.getName());
> + } else {
> + return t1.compareTo(t2);
> + }
> + }
> + }
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,138 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.IOException;
> +import java.io.OutputStream;
> +import java.io.Serializable;
> +import java.util.List;
> +import java.util.Map;
> +
> +/**
> + * A request from a remote client to a server to do something.
> + * The request arrives as stream of bytes from a remote location
> + * and if the server can interpret the protocol from the stream,
> + * then make a concrete request object.
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +public interface Request extends Serializable {
> + /**
> + * Get the HTTP verb such as GET, POST
> + *
> + * @return uppercase string
> + */
> + String getMethod();
> +
> + /**
> + * Get the first path segment as intended persistence action such as <code>find</code> or <code>query</code>
> + *
> + * @return lowercase action name. Can be empty.
> + */
> + String getAction();
> +
> + /**
> + * Get the protocol such as HTTP/1.1
> + *
> + * @return upper-case string
> + */
> + String getProtocol();
> +
> + /**
> + * Get the body, if any.
> + *
> + * @return body of the request. null if no body.
> + */
> + String getBody();
> +
> + /**
> + * Get the headers indexed by the keys.
> + * Header values are list of Strings.
> + *
> + * @return empty map if there is no header
> + */
> + Map<String, List<String>> getHeaders();
> +
> + /**
> + * Get the header values for the given key.
> + * @param key a key
> + * @return null if no header value for the given key
> + */
> + List<String> getHeader(String key);
> +
> + /**
> + * Affirm if the the given qualifier is available in this request.
> + *
> + * @param key case-sensitive qualifier
> + * @return true if the key is present.
> + */
> + boolean hasQualifier(String key);
> +
> + /**
> + * Gets the value for the given qualifier key.
> + *
> + * @param key case-sensitive qualifier
> + * @return value of the qualifier. null if the key is absent.
> + */
> + String getQualifier(String key);
> +
> + /**
> + * Get all the qualifiers available in this request.
> + *
> + * @return key-value pairs of the qualifiers. Empty map if no qualifier is present.
> + */
> + Map<String,String> getQualifiers();
> +
> +
> + /**
> + * Affirm if the the given parameter is available in this request.
> + *
> + * @param key case-sensitive parameter
> + * @return true if the key is present.
> + */
> + boolean hasParameter(String key);
> +
> +
> + /**
> + * Gets the value for the given parameter key.
> + *
> + * @param key case-sensitive parameter
> + * @return value of the parameter. null if the key is absent.
> + */
> + String getParameter(String key);
> +
> + /**
> + * Get all the parameters available in this request.
> + *
> + * @return key-value pairs of the parameters. Empty map if no parameter is present.
> + */
> + Map<String,String> getParameters();
> +
> + /**
> + * Parse the request represented as a list of strings.
> + *
> + * @param lines each line of the request.
> + * @throws IOException
> + */
> + void read(List<String> lines) throws IOException;
> +
> + Response process(ServerContext server, OutputStream stream) throws Exception;
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,63 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.util.HashMap;
> +import java.util.Map;
> +
> +/**
> + * A factory to create a specific type of request.
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +public class RequestFactory {
> + private final String _protocol;
> + private static Map<String, RequestFactory> _registered = new HashMap<String, RequestFactory>();
> + static {
> + _registered.put("HTTP/1.0", new RequestFactory("HTTP/1.0"));
> + _registered.put("HTTP/1.1", new RequestFactory("HTTP/1.1"));
> + }
> +
> + public static void register(String protocol, RequestFactory factory) {
> + _registered.put(protocol, factory);
> + }
> +
> + private RequestFactory(String proto) {
> + _protocol = proto;
> + }
> +
> + public static RequestFactory getFactory(String protocol) {
> + return _registered.get(protocol);
> + }
> +
> + Request createRequest(String method) {
> + JESTRequest request = null;
> + if ("GET".equalsIgnoreCase(method)) {
> + request = new GETRequest();
> + } else {
> + throw new UnsupportedOperationException();
> + }
> + request.setProtocol(_protocol);
> + request.setMethod(method.toUpperCase());
> + return request;
> +
> + }
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,147 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.BufferedReader;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.io.InputStreamReader;
> +import java.io.OutputStream;
> +import java.net.HttpURLConnection;
> +import java.net.Socket;
> +import java.net.SocketException;
> +import java.util.ArrayList;
> +import java.util.Collections;
> +import java.util.Iterator;
> +import java.util.List;
> +import java.util.Map;
> +import java.util.Set;
> +import java.util.concurrent.Callable;
> +
> +import javax.persistence.EntityManager;
> +import javax.persistence.EntityNotFoundException;
> +
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
> +import org.apache.openjpa.kernel.StoreContext;
> +import org.apache.openjpa.lib.log.Log;
> +import org.apache.openjpa.lib.util.Localizer;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.FieldMetaData;
> +import org.apache.openjpa.persistence.JPAFacadeHelper;
> +import org.apache.openjpa.persistence.OpenJPAPersistence;
> +import org.apache.openjpa.util.ApplicationIds;
> +
> +/**
> + * Handles a request from a remote client.
> + * Reads the socket data.
> + * Populates the request.
> + * Determines the processor based on request method and action.
> + * Delegates to the processor.
> + * Processor generates the response.
> + * Writes the response to the stream.
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +public class RequestHandler implements Callable<Void> {
> + private static final int SPACE = ' ';
> + private final Socket _socket;
> + private final ServerContext _server;
> + private final Log _log;
> + private static final Localizer _loc = Localizer.forPackage(RequestHandler.class);
> +
> + public RequestHandler(Socket socket, ServerContext server) {
> + _socket = socket;
> + _server = server;
> + _log = _server.getLog();
> + }
> +
> + public Void call() throws Exception {
> + Request request = null;
> + Response response = null;
> + try {
> + request = readRequest(_socket.getInputStream());
> + response = request.process(_server, _socket.getOutputStream());
> + } catch (Exception e) {
> + response = new ErrorResponse(request, _server, e, HttpURLConnection.HTTP_INTERNAL_ERROR,
> + _socket.getOutputStream());
> + }
> + response.writeOut();
> + return null;
> + }
> +
> +
> +
> + /**
> + * Reads the given CR-LF delimited stream.
> + * The first line is scanned for the method (first space-delimited String) and protocol (the last
> + * space-delimited String). Accordingly a request is created and rest of the input stream content
> + * is parsed by the request itself.
> + *
> + * @param input
> + * @throws IOException
> + */
> + public Request readRequest(InputStream input) throws IOException {
> + if (_log.isTraceEnabled())
> + _log.trace("Reading request from the input stream ");
> +
> + BufferedReader reader = new BufferedReader(new InputStreamReader(input));
> + String status = reader.readLine();
> + if (_log.isInfoEnabled())
> + _log.info("Status Line [" + status + "]");
> + int spaceFirst = status.indexOf(SPACE);
> + if (spaceFirst == -1)
> + throw new IOException("HTTP Method could not be determined from [" + status + "]");
> + int spaceLast = status.lastIndexOf(SPACE);
> + if (spaceLast == -1)
> + throw new IOException("HTTP Protocol could not be determined from [" + status + "]");
> + String method = status.substring(0, spaceFirst);
> + String protocol = status.substring(spaceLast+1);
> + String path = status.substring(spaceFirst+1, spaceLast);
> + Request request = RequestFactory.getFactory(protocol).createRequest(method);
> + List<String> lines = new ArrayList<String>();
> + if (path.equals("/")) {
> + lines.add(path);
> + } else {
> + lines = readlines(reader);
> + lines.add(0, path);
> + }
> + if (lines.isEmpty()) {
> + throw new IOException("No CR-LF delimited lines could be read from " + input);
> + }
> + request.read(lines);
> + return request;
> + }
> +
> +
> + List<String> readlines(BufferedReader reader) throws IOException {
> + List<String> buffers = new ArrayList<String>();
> + String line;
> + while ((line = reader.readLine()) != null && line.length() > 0) {
> + buffers.add(line);
> + }
> + return buffers;
> + }
> +
> +
> + public String toString() {
> + return _socket.getInetAddress()+":"+_socket.getPort();
> + }
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,54 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.InputStream;
> +import java.io.OutputStream;
> +import java.io.RandomAccessFile;
> +
> +import javax.imageio.ImageIO;
> +
> +/**
> + * @author Pinaki Poddar
> + *
> + */
> +@SuppressWarnings("serial")
> +public class ResourceResponse extends AbstractResponse {
> + private final InputStream _in;
> + private final String _mimeType;
> + public ResourceResponse(Request request, ServerContext ctx, InputStream resource, String mimeType,
> + OutputStream out) throws Exception {
> + super(request, ctx, out);
> + _in = resource;
> + _mimeType = mimeType;
> + }
> +
> + public void writeOut() throws Exception {
> + print(_request.getProtocol()); println("200 OK");
> + printHeader("Connection", "close");
> + printHeader("Content-Type", _mimeType, "charset=UTF-8");
> + println();
> + for (int c = 0; (c = _in.read()) != -1;) {
> + print((char)c);
> + }
> + _in.close();
> + close();
> + }
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,31 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.Serializable;
> +
> +/**
> + * @author Pinaki Poddar
> + *
> + */
> +public interface Response extends Serializable {
> +// void setHeader(String key, String value);
> + void writeOut() throws Exception;
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java?rev=1028093&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java Wed Oct 27 20:42:44 2010
> @@ -0,0 +1,230 @@
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements. See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership. The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License. You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied. See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +
> +package org.apache.openjpa.persistence.jest;
> +
> +import java.io.IOException;
> +import java.net.ServerSocket;
> +import java.net.Socket;
> +import java.util.concurrent.ExecutorService;
> +import java.util.concurrent.Executors;
> +
> +import javax.persistence.EntityManagerFactory;
> +
> +import org.apache.openjpa.conf.OpenJPAConfiguration;
> +import org.apache.openjpa.lib.conf.Configurable;
> +import org.apache.openjpa.lib.conf.Configuration;
> +import org.apache.openjpa.lib.log.Log;
> +import org.apache.openjpa.lib.util.Localizer;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.MetaDataRepository;
> +import org.apache.openjpa.persistence.EntityManagerFactoryImpl;
> +
> +
> +/**
> + * A server running on an independent thread that allows a remote, language-neutral client to access OpenJPA runtime.
> + *
> + * @author Pinaki Poddar
> + *
> + */
> +public class Server implements ServerContext, Configurable, Runnable {
> + private ServerSocket _listenSocket;
> + protected ExecutorService _executors;
> + public final static int DEFAULT_PORT = 6789;
> + protected int _port = DEFAULT_PORT;
> + protected int _range = 1;
> + protected String _format = "xml";
> + protected Log _log;
> + protected Thread _thread;
> + private EntityManagerFactoryImpl _ctx;
> + private static Localizer _loc = Localizer.forPackage(Server.class);
> +
> + /**
> + * Sets the persistence unit context in which this server will serve requests.
> + * The context must be set before operation.
> + *
> + * @param emf an implementation of OpenJPA Persistence Unit. Must not be null.
> + */
> + public void setContext(EntityManagerFactoryImpl emf) {
> + if (emf == null)
> + throw new NullPointerException();
> + _ctx = emf;
> + }
> +
> + /**
> + * Gets the persistence unit context in which this server serves requests.
> + *
> + * @param emf an implementation of OpenJPA Persistence Unit.
> + */
> + public EntityManagerFactoryImpl getPersistenceUnit() {
> + return _ctx;
> + }
> +
> + public Log getLog() {
> + return _log;
> + }
> +
> + /**
> + * Start the server in a daemon thread.
> + * This method is idempotent.
> + *
> + * @return true if the server has started by this call or already running.
> + */
> + public synchronized boolean start() {
> + try {
> + if (_thread != null)
> + return true;
> + if (createServerSocket()) {
> + _thread = new Thread(this);
> + _thread.setDaemon(true);
> + _thread.start();
> + return true;
> + }
> + return false;
> + } catch (Exception ex) {
> + ex.printStackTrace();
> + return false;
> + }
> + }
> +
> + /**
> + * Stops the server.
> + */
> + public synchronized void stop() {
> + _thread.interrupt();
> + _thread = null;
> + _executors.shutdownNow();
> + }
> +
> + /**
> + * Sets the port in which the server will listen.
> + *
> + * @param port a positive integer.
> + */
> + public void setPort(int port) {
> + _port = port;
> + }
> +
> + /**
> + * Gets the current port.
> + *
> + * @return the port number. Defaults to default HTTP port.
> + */
> + public int getPort() {
> + return _port;
> + }
> +
> + /**
> + * Sets the range of ports the server will attempt at start.
> + *
> + * @param range a positive integer.
> + */
> + public void setRange(int range) {
> + if (range > 0)
> + _range = range;
> + }
> +
> + public void setFormat(String format) {
> + _format = format;
> + }
> +
> + public String getFormat() {
> + return _format;
> + }
> +
> + /**
> + * Sets the range of ports the server will attempt at start.
> + * @return a positive integer. Defaults to 1.
> + */
> + public int getRange() {
> + return _range;
> + }
> +
> + public void run() {
> + _log.info(_loc.get("server-starting", this));
> +
> + _executors = Executors.newCachedThreadPool();
> + while (!Thread.interrupted()) {
> + try {
> + Socket socket = _listenSocket.accept();
> + if (_log.isTraceEnabled())
> + _log.trace(_loc.get("server-request", socket));
> + RequestHandler request = new RequestHandler(socket, this);
> + _executors.submit(request);
> + } catch (IOException e) {
> + e.printStackTrace();
> + }
> + }
> + }
> +
> + private boolean createServerSocket() {
> + int p = _port;
> + int p2 = p + _range;
> + Exception error = null;
> + for (; _listenSocket == null && p < p2; ) {
> + try {
> + _listenSocket = new ServerSocket(p);
> + } catch (IOException ex) {
> + p++;
> + error = ex;
> + }
> + }
> + if (_listenSocket != null) {
> + if (p != _port) {
> + _port = p;
> + _log.warn(_loc.get("server-reconfigured", _port));
> + }
> + } else {
> + if (error != null) {
> + _log.warn(_loc.get("server-failed", this, _port, error));
> + }
> + }
> + return _listenSocket != null;
> + }
> +
> + // Configurable contract
> + public void setConfiguration(Configuration conf) {
> + _log = conf.getLog("Remote");
> + }
> +
> + public void startConfiguration() {
> + }
> +
> + public void endConfiguration() {
> + }
> +
> +
> + // Server side utilities
> +
> + /**
> + * Resolves the given alias to a persistent class meta data.
> + *
> + * @exception if no meta data available for the given alias
> + */
> + public ClassMetaData resolve(String alias) {
> + MetaDataRepository repos = _ctx.getConfiguration().getMetaDataRepositoryInstance();
> + return repos.getMetaData(alias, Thread.currentThread().getContextClassLoader(), true);
> + }
> +
> + public String toString() {
> + if (_listenSocket == null) return "JEST Server [not strated]";
> + return "JEST Server " + _listenSocket.getInetAddress()+":"+_listenSocket.getLocalPort();
> + }
> +
> +}
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> ------------------------------------------------------------------------------
> svn:eol-style = native
>
> Propchange: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> ------------------------------------------------------------------------------
> svn:mime-type = text/plain
>
>
>
Re: svn commit: r1028093 [1/2] - JEST...
Posted by Donald Woods <dw...@apache.org>.
Was thinking of a 2.1.0 release before the end of this year....
-Donald
On 10/27/10 6:32 PM, Jeremy Bauer wrote:
> Ditto on the concerns. Maybe this would be a good opportunity to discuss a
> 2.1 release date? That may help guide whether we decide to include large
> new features in 2.1 or hold off until 2.x.x and give them more time to brew.
>
> -Jeremy
>
> On Wed, Oct 27, 2010 at 5:11 PM, Kevin Sutter <kw...@gmail.com> wrote:
>
>> I have to admit that I was having the same questions/concerns that Donald
>> just raised. I thought we were still in the discussion and experimentation
>> phase of this REST work. Is it really ready for prime time?
>>
>> Kevin
>>
>> On Wed, Oct 27, 2010 at 4:58 PM, Donald Woods <dw...@apache.org> wrote:
>>
>>> Is this really ready to drop into trunk?
>>> And do we really want it in the base openjpa.jar?
>>>
>>>
>>> -Donald
>>>
>>>
>>> On 10/27/10 4:42 PM, ppoddar@apache.org wrote:
>>>> Author: ppoddar
>>>> Date: Wed Oct 27 20:42:44 2010
>>>> New Revision: 1028093
>>>>
>>>> URL: http://svn.apache.org/viewvc?rev=1028093&view=rev
>>>> Log:
>>>> OPENJPA-1851: First version of JEST (REST on OpenJPA)
>>>>
>>>> Added:
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ServerContext.java
>>> (with props)
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/XMLEncoder.java
>>> (with props)
>>>> Modified:
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
>>>>
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
>>>>
>>>> Modified:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java?rev=1028093&r1=1028092&r2=1028093&view=diff
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
>>> (original)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -45,10 +45,10 @@ import org.apache.openjpa.lib.util.Close
>>>> import org.apache.openjpa.lib.util.Localizer;
>>>> import org.apache.openjpa.persistence.criteria.CriteriaBuilderImpl;
>>>> import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
>>>> +import org.apache.openjpa.persistence.jest.Server;
>>>> import org.apache.openjpa.persistence.meta.MetamodelImpl;
>>>> import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
>>>> import org.apache.openjpa.persistence.query.QueryBuilderImpl;
>>>> -import org.apache.openjpa.util.UserException;
>>>>
>>>> /**
>>>> * Implementation of {@link EntityManagerFactory} that acts as a
>>>> @@ -70,7 +70,8 @@ public class EntityManagerFactoryImpl
>>>> private transient StoreCache _cache = null;
>>>> private transient QueryResultCache _queryCache = null;
>>>> private transient MetamodelImpl _metaModel;
>>>> -
>>>> + private transient Server _remoteAccess = null;
>>>> +
>>>> /**
>>>> * Default constructor provided for auto-instantiation.
>>>> */
>>>> @@ -93,10 +94,11 @@ public class EntityManagerFactoryImpl
>>>>
>>>> /**
>>>> * Delegate must be provided before use.
>>>> + * Configures for Remote Access, if appropriate.
>>>> */
>>>> public void setBrokerFactory(BrokerFactory factory) {
>>>> - _factory = new DelegatingBrokerFactory(factory,
>>>> - PersistenceExceptions.TRANSLATOR);
>>>> + _factory = new DelegatingBrokerFactory(factory,
>>> PersistenceExceptions.TRANSLATOR);
>>>> + configureRemoteAccess(getConfiguration());
>>>> }
>>>>
>>>> public OpenJPAConfiguration getConfiguration() {
>>>> @@ -271,6 +273,10 @@ public class EntityManagerFactoryImpl
>>>> if (log.isTraceEnabled()) {
>>>> log.trace(this + ".close() invoked.");
>>>> }
>>>> + if (_remoteAccess != null) {
>>>> + _remoteAccess.stop();
>>>> + _remoteAccess = null;
>>>> + }
>>>> _factory.close();
>>>> }
>>>>
>>>> @@ -396,4 +402,40 @@ public class EntityManagerFactoryImpl
>>>> }
>>>> }
>>>> }
>>>> +
>>>> + /**
>>>> + * Configures this unit for remote access.
>>>> + */
>>>> + protected void configureRemoteAccess(OpenJPAConfiguration conf) {
>>>> + Value value = conf.getValue("RemoteAccess");
>>>> + if (value == null) {
>>>> + return;
>>>> + }
>>>> + String props = value.getString();
>>>> + if (props == null)
>>>> + return;
>>>> + try {
>>>> + _remoteAccess = new Server();
>>>> + _remoteAccess.setContext(this);
>>>> + Configurations.configureInstance(_remoteAccess, conf,
>>> props);
>>>> + conf.removeValue(value);
>>>> + if (!_remoteAccess.start()) {
>>>> + _remoteAccess = null;
>>>> + }
>>>> + } catch (Exception ex) {
>>>> + Log log =
>>> _factory.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
>>>> + if (log != null) {
>>>> + log.error(_loc.get("remote-start-error"), ex);
>>>> + }
>>>> + }
>>>> + }
>>>> +
>>>> + /**
>>>> + * Affirms if this unit is accessible remotely.
>>>> + */
>>>> + public boolean allowsRemoteAccess() {
>>>> + return _remoteAccess != null;
>>>> + }
>>>> +
>>>> +
>>>> }
>>>>
>>>> Modified:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java?rev=1028093&r1=1028092&r2=1028093&view=diff
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
>>> (original)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -188,6 +188,7 @@ public class PersistenceProductDerivatio
>>>> conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(),
>>> PersistenceMetaDataFactory.class.getName());
>>>>
>>>> conf.addValue(new EntityManagerFactoryValue());
>>>> + conf.addString("RemoteAccess");
>>>>
>>>> conf.readLockLevel.setAlias("optimistic",
>>> String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
>>>> conf.readLockLevel.setAlias("optimistic-force-increment",
>> String
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,77 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.OutputStream;
>>>> +import java.io.PrintStream;
>>>> +
>>>> +/**
>>>> + * Abstract implementation of a stream-based response.
>>>> + * Every response is result of a {@linkplain Request} and operates
>>> within a {@linkplain ServerContext}.
>>>> + * This fact is enforced by the constructor argument of an abstract
>>> response.
>>>> + * <p>
>>>> + * Besides, this implementation provides common utility to write HTTP
>>> response or extract useful information
>>>> + * from the request.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public abstract class AbstractResponse extends PrintStream implements
>>> Response {
>>>> + protected final Request _request;
>>>> + protected final ServerContext _ctx;
>>>> +
>>>> + /**
>>>> + * Construct a response for the given request and server context.
>>>> + *
>>>> + * @param request the request for this response. Can be null if
>> the
>>> response is for server error.
>>>> + * @param ctx the processing context
>>>> + * @param out the output stream where the response is targeted.
>>>> + */
>>>> + protected AbstractResponse(Request request, ServerContext ctx,
>>> OutputStream out) {
>>>> + super(out);
>>>> + _request = request;
>>>> + _ctx = ctx;
>>>> + }
>>>> +
>>>> + /**
>>>> + * Write a HTTP header to the stream.
>>>> + * <br>
>>>> + * The format of HTTP header is <code>key: [value]* NEWLINE</code>
>>>> + *
>>>> + * @param key the key of the header
>>>> + * @param values one of more value of the header fields.
>>>> + */
>>>> + protected void printHeader(String key, String...values) {
>>>> + if (key == null)
>>>> + return;
>>>> + print(key);
>>>> + print(" :");
>>>> + if (values == null || values.length == 0)
>>>> + return;
>>>> + int n = values.length-1;
>>>> + for (int i = 0; i < n-1; i++) {
>>>> + print(values[i]);
>>>> + print(";");
>>>> + }
>>>> + println(values[n]);
>>>> + }
>>>> +
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,68 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.OutputStream;
>>>> +import java.net.HttpURLConnection;
>>>> +
>>>> +/**
>>>> + * A HTTP response for something gone wrong.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class ErrorResponse extends AbstractResponse {
>>>> + private final Exception _error;
>>>> + private final int _code;
>>>> + /**
>>>> + * Construct a response to describe a error.
>>>> + *
>>>> + * @param request a request that produced this response. VCan be
>>> null to denote that the request can not
>>>> + * be created.
>>>> + * @param ctx the processing context
>>>> + * @param ex the error
>>>> + * @param code HTTP error code
>>>> + * @param out the stream where the response is written
>>>> + *
>>>> + */
>>>> + public ErrorResponse(Request request, ServerContext ctx, Exception
>>> ex, int code, OutputStream out) {
>>>> + super(request, ctx, out);
>>>> + _error = ex;
>>>> + _code = code;
>>>> + }
>>>> +
>>>> + /**
>>>> + * Writes the response.
>>>> + * The response is always a HTTP response with the error stack
>>> trace.
>>>> + */
>>>> + public void writeOut() throws Exception {
>>>> + println("HTTP/1.1"); print(" " + _code); println("Error");
>>>> + printHeader("Connection", "close");
>>>> + printHeader("Content-Type", "text/html", "charset=UTF-8");
>>>> + println();
>>>> + println("<html><body><pre>");
>>>> + _error.printStackTrace(this);
>>>> + println("Response from JEST");
>>>> +
>>>> + println("</pre></body></html>");
>>>> + close();
>>>> + }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,147 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.InputStream;
>>>> +import java.io.OutputStream;
>>>> +import java.net.HttpURLConnection;
>>>> +import java.util.Iterator;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +
>>>> +import javax.persistence.EntityManager;
>>>> +import javax.persistence.EntityNotFoundException;
>>>> +import javax.persistence.Query;
>>>> +
>>>> +import org.apache.openjpa.kernel.OpenJPAStateManager;
>>>> +import org.apache.openjpa.kernel.StoreContext;
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.persistence.JPAFacadeHelper;
>>>> +import org.apache.openjpa.util.ApplicationIds;
>>>> +import org.apache.openjpa.util.ObjectNotFoundException;
>>>> +
>>>> +/**
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class GETRequest extends JESTRequest {
>>>> + public Response process(ServerContext server, OutputStream out)
>>> throws Exception {
>>>> + String action = getAction();
>>>> + try {
>>>> + if ("find".equals(action)) {
>>>> + return find(server, out);
>>>> + } else {
>>>> + return resource(server, out);
>>>> + }
>>>> + } catch (Exception e) {
>>>> + return new ErrorResponse(this, server, new
>>> RuntimeException("bad action " + action),
>>>> + HttpURLConnection.HTTP_BAD_REQUEST, out);
>>>> + }
>>>> + }
>>>> +
>>>> + Response find(ServerContext server, OutputStream out) throws
>>> Exception {
>>>> + EntityManager em =
>>> server.getPersistenceUnit().createEntityManager();
>>>> + Map<String, String> qualifiers = getQualifiers();
>>>> + Map<String, String> parameters = getParameters();
>>>> + if (parameters.size() < 2)
>>>> + throw new IllegalArgumentException("find must have at
>> least
>>> two parameters");
>>>> + Object[] pks = new Object[parameters.size()-1];
>>>> + Iterator<Map.Entry<String,String>> params =
>>> parameters.entrySet().iterator();
>>>> + String alias = null;
>>>> + for (int i = 0; i < parameters.size(); i++) {
>>>> + if (i == 0) {
>>>> + alias = params.next().getKey();
>>>> + } else {
>>>> + pks[i-1] = params.next().getKey();
>>>> + }
>>>> + }
>>>> + ClassMetaData meta = server.resolve(alias);
>>>> + Object oid = ApplicationIds.fromPKValues(pks, meta);
>>>> + Object pc = em.find(meta.getDescribedType(), oid);
>>>> + if (pc != null) {
>>>> + OpenJPAStateManager sm =
>>> ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(pc);
>>>> + return new JESTResponse(this, server, sm, out);
>>>> + } else {
>>>> + return new ErrorResponse(this, server, new
>>> EntityNotFoundException("not found!"),
>>>> + HttpURLConnection.HTTP_NOT_FOUND, out);
>>>> + }
>>>> + }
>>>> +
>>>> + Response query(ServerContext server, OutputStream out) throws
>>> Exception {
>>>> + EntityManager em =
>>> server.getPersistenceUnit().createEntityManager();
>>>> + Map<String, String> qualifiers = getQualifiers();
>>>> + boolean named = isBooleanQualifier("named");
>>>> + boolean single = isBooleanQualifier("single");
>>>> + Map<String, String> parameters = getParameters();
>>>> + if (parameters.size() < 1)
>>>> + throw new IllegalArgumentException("find must have at
>> least
>>> one parameter");
>>>> + Iterator<Map.Entry<String,String>> params =
>>> parameters.entrySet().iterator();
>>>> + Query query = null;
>>>> + int i = 0;
>>>> + for (Map.Entry<String, String> param : parameters.entrySet())
>> {
>>>> + if (i == 0) {
>>>> + query = named ? em.createQuery(param.getKey()) :
>>> em.createNamedQuery(param.getKey());
>>>> + } else {
>>>> + query.setParameter(param.getKey(), param.getValue());
>>>> + }
>>>> + }
>>>> + if (single) {
>>>> + Object result = query.getSingleResult();
>>>> + OpenJPAStateManager sm =
>>> ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(result);
>>>> + return new JESTResponse(this, server, sm, out);
>>>> + } else {
>>>> + List<Object> result = query.getResultList();
>>>> + return new ErrorResponse(this, server, new
>>> EntityNotFoundException("not found!"), 404, out);
>>>> + }
>>>> + }
>>>> +
>>>> + Response resource(ServerContext server, OutputStream out) throws
>>> Exception {
>>>> + String resource = getAction();
>>>> + if (resource.length() == 0)
>>>> + resource = "index.html";
>>>> + String mimeType = getMimeType(resource);
>>>> +// InputStream in =
>>>
>> Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
>>>> + InputStream in = getClass().getResourceAsStream(resource);
>>>> + if (in == null) {
>>>> + return new ErrorResponse(this, server, new
>>> ObjectNotFoundException(resource), 404, out);
>>>> + }
>>>> + if (server.getLog().isTraceEnabled())
>>>> + server.getLog().trace("Found resource " + resource);
>>>> + return mimeType.startsWith("image")
>>>> + ? new ImageResponse(this, server, in, mimeType, out)
>>>> + : new ResourceResponse(this, server, in, mimeType, out);
>>>> + }
>>>> +
>>>> + boolean isBooleanQualifier(String key) {
>>>> + String q = getQualifier(key);
>>>> + return hasQualifier(key) && (q == null || "true".equals(q));
>>>> + }
>>>> +
>>>> + String getMimeType(String resource) {
>>>> + int index = resource.lastIndexOf('.');
>>>> + String ext = (index != -1) ? resource.substring(index+1) : "";
>>>> + if (ext.equalsIgnoreCase("html") ||
>>> ext.equalsIgnoreCase(".html")) return "text/html";
>>>> + if (ext.equalsIgnoreCase(".png") ||
>> ext.equalsIgnoreCase(".ico")
>>> || ext.equalsIgnoreCase("jpeg"))
>>>> + return "image/"+ext;
>>>> + return "text/html";
>>>> + }
>>>> +
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,61 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.InputStream;
>>>> +import java.io.OutputStream;
>>>> +import java.io.RandomAccessFile;
>>>> +
>>>> +import javax.imageio.ImageIO;
>>>> +
>>>> +/**
>>>> + * Sends an image as response.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class ImageResponse extends AbstractResponse {
>>>> + private final InputStream _in;
>>>> + private final String _mimeType;
>>>> +
>>>> + public ImageResponse(Request request, ServerContext ctx,
>> InputStream
>>> resource, String mimeType,
>>>> + OutputStream out) throws Exception {
>>>> + super(request, ctx, out);
>>>> + _in = resource;
>>>> + _mimeType = mimeType;
>>>> + }
>>>> +
>>>> + public void writeOut() throws Exception {
>>>> + print(_request.getProtocol()); println("200 OK");
>>>> + printHeader("Connection", "close");
>>>> + printHeader("Content-Type", _mimeType);
>>>> + println();
>>>> + byte[] b = new byte[1024];
>>>> + int i = 0;
>>>> + for (int l = 0; (l = _in.read(b)) != -1;) {
>>>> + write(b, 0, l);
>>>> + i += l;
>>>> + }
>>>> + printHeader("Content-Length", ""+i);
>>>> + _in.close();
>>>> + close();
>>>> + }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,386 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.IOException;
>>>> +import java.util.Arrays;
>>>> +import java.util.Collections;
>>>> +import java.util.HashMap;
>>>> +import java.util.LinkedHashMap;
>>>> +import java.util.LinkedList;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +import java.util.NoSuchElementException;
>>>> +
>>>> +/**
>>>> + * A request carries requisite data for a JPA operation to be
>> performed.
>>>> + * The request is populated by parsing an input data stream.
>>>> + *
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public abstract class JESTRequest implements Request {
>>>> + private String _method;
>>>> + private String _protocol;
>>>> + private String _action;
>>>> + private String _body;
>>>> + private LinkedHashMap<String, String> _qualifiers = new
>>> LinkedHashMap<String, String>();
>>>> + private LinkedHashMap<String, String> _params = new
>>> LinkedHashMap<String, String>();
>>>> + private Map<String, List<String>> _headers = new HashMap<String,
>>> List<String>>();
>>>> + private ParseState _state;
>>>> + private StringBuffer buf = new StringBuffer();
>>>> + private LinkedList<Token> _stack = new LinkedList<Token>();
>>>> +
>>>> + public static final List<String> METHODS = Arrays.asList(new
>>> String[]{"GET","POST","PUT","DELETE"});
>>>> +
>>>> + /**
>>>> + * Parse States.
>>>> + */
>>>> + static enum ParseState {
>>>> + INIT, ACTION, QUALIFIER_KEY, QUALIFIER_VALUE, PARAM_KEY,
>>> PARAM_VALUE, END
>>>> + };
>>>> +
>>>> +
>>>> + public String getMethod() {
>>>> + return _method;
>>>> + }
>>>> +
>>>> + void setMethod(String method) {
>>>> + if (_method == null) {
>>>> + if (method != null &&
>>> METHODS.contains(method.toUpperCase())) {
>>>> + _method = method.toUpperCase();
>>>> + } else {
>>>> + throw new IllegalArgumentException("Unsupported method
>> "
>>> + method);
>>>> + }
>>>> + } else if (!_method.equalsIgnoreCase(method)) {
>>>> + throw new IllegalStateException("Method can not be changed
>>> to [" + method + "]. " +
>>>> + "Current method [" + _method + "]");
>>>> + }
>>>> + }
>>>> +
>>>> + public String getProtocol() {
>>>> + return _protocol == null ? "HTTP/1.1" : _protocol;
>>>> + }
>>>> +
>>>> + void setProtocol(String protocol) {
>>>> + if (_protocol == null) {
>>>> + if (protocol != null &&
>>> protocol.toUpperCase().startsWith("HTTP")) {
>>>> + _protocol = protocol.toUpperCase();
>>>> + } else {
>>>> + throw new IllegalArgumentException("Unsupported
>> protocol
>>> " + protocol);
>>>> + }
>>>> + } else if (!_protocol.equalsIgnoreCase(protocol)) {
>>>> + throw new IllegalStateException("Protocol can not be
>> changed
>>> to [" + protocol + "]. " +
>>>> + "Current protocol [" + _protocol + "]");
>>>> + }
>>>> + }
>>>> +
>>>> + /**
>>>> + * Sets an action. Once set, an action can not be modified.
>>>> + *
>>>> + * @param action
>>>> + */
>>>> + private void setAction(String action) {
>>>> + if (_action == null) {
>>>> + _action = action;
>>>> + } else if (!_action.equals(action)) {
>>>> + throw new IllegalStateException("Action can not be [" +
>>> action + "]. Already set to [" + _action + "]");
>>>> + }
>>>> + }
>>>> +
>>>> + public String getAction() {
>>>> + return _action == null ? "" : _action;
>>>> + }
>>>> +
>>>> + public String getBody() {
>>>> + return _body;
>>>> + }
>>>> +
>>>> + private void setQualifier(String key, String value) {
>>>> + _qualifiers.put(key, value);
>>>> + }
>>>> +
>>>> + public String getQualifier(String key) {
>>>> + return _qualifiers.get(key);
>>>> + }
>>>> +
>>>> + public Map<String, String> getQualifiers() {
>>>> + return Collections.unmodifiableMap(_qualifiers);
>>>> + }
>>>> +
>>>> + public boolean hasQualifier(String key) {
>>>> + return _qualifiers.containsKey(key);
>>>> + }
>>>> +
>>>> + private void setParameter(String key, String value) {
>>>> + _params.put(key, value);
>>>> + }
>>>> +
>>>> + public String getParameter(String key) {
>>>> + return _params.get(key);
>>>> + }
>>>> +
>>>> + public boolean hasParameter(String key) {
>>>> + return _params.containsKey(key);
>>>> + }
>>>> +
>>>> + public Map<String, String> getParameters() {
>>>> + return Collections.unmodifiableMap(_params);
>>>> + }
>>>> +
>>>> + public Map.Entry<String, String> getParameter(int n) {
>>>> + if (n >= _params.size())
>>>> + throw new NoSuchElementException("Index " + n + " size " +
>>> _params.size());
>>>> + int i = 0;
>>>> + for (Map.Entry<String, String> entry : _params.entrySet()) {
>>>> + if (i == n) {
>>>> + return entry;
>>>> + }
>>>> + i++;
>>>> + }
>>>> + return null;
>>>> + }
>>>> +
>>>> + public Map<String, List<String>> getHeaders() {
>>>> + return Collections.unmodifiableMap(_headers);
>>>> + }
>>>> +
>>>> + public List<String> getHeader(String key) {
>>>> + return _headers.get(key);
>>>> + }
>>>> +
>>>> +
>>>> + public void read(List<String> lines) throws IOException {
>>>> + parse(lines.get(0));
>>>> + int i = 1;
>>>> + for (; i < lines.size(); i++) {
>>>> + String line = lines.get(i);
>>>> + if (line.length() == 0) {
>>>> + break;
>>>> + } else {
>>>> + parseHeader(line);
>>>> + }
>>>> + }
>>>> + parseBody(lines.subList(i, lines.size()));
>>>> + }
>>>> +
>>>> + protected void parseHeader(String line) throws IOException {
>>>> + String key = null;
>>>> + StringBuilder token = new StringBuilder();
>>>> + int N = line.length();
>>>> + for (int i = 0; i < N; i++) {
>>>> + char c = line.charAt(i);
>>>> + if (c == ':' && key == null) {
>>>> + key = token.toString().trim();
>>>> + token.delete(0, token.length());
>>>> + } else {
>>>> + token.append(c);
>>>> + }
>>>> + }
>>>> + if (key != null) {
>>>> + _headers.put(key,
>>> Collections.singletonList(token.toString().trim()));
>>>> + }
>>>> + }
>>>> +
>>>> + protected void parseBody(List<String> lines) {
>>>> + if (lines == null || lines.isEmpty())
>>>> + return;
>>>> + for (String line : lines) {
>>>> + if (_body == null) {
>>>> + _body = line;
>>>> + } else {
>>>> + _body = _body + line;
>>>> + }
>>>> + }
>>>> + }
>>>> +
>>>> + /**
>>>> + * Parses JEST stream and populates a request.
>>>> + *
>>>> + */
>>>> + protected void parse(String s) {
>>>> + char[] chars = s.toCharArray();
>>>> + _state = ParseState.INIT;
>>>> + _stack.clear();
>>>> +
>>>> + for (int i = 0; i < chars.length; i++) {
>>>> + char ch = chars[i];
>>>> + switch (_state) {
>>>> + case INIT:
>>>> + if (ch == '/') {
>>>> + transit(ParseState.ACTION);
>>>> + } else if (!Character.isWhitespace(ch)) {
>>>> + parseError(ch, i, s, true, ' ');
>>>> + }
>>>> + break;
>>>> +
>>>> + case ACTION:
>>>> + if (ch == '/') {
>>>> + transit(ParseState.QUALIFIER_KEY);
>>>> + } else if (ch == '?') {
>>>> + transit(ParseState.PARAM_KEY);
>>>> + } else {
>>>> + buf.append(ch);
>>>> + }
>>>> + break;
>>>> +
>>>> + case QUALIFIER_KEY:
>>>> + if (Character.isJavaIdentifierPart(ch)) {
>>>> + buf.append(ch);
>>>> + } else if (ch == '=') {
>>>> + transit(ParseState.QUALIFIER_VALUE);
>>>> + } else if (ch == '/') {
>>>> + transit(ParseState.QUALIFIER_KEY);
>>>> + } else if (ch == '?') {
>>>> + transit(ParseState.PARAM_KEY);
>>>> + } else {
>>>> + parseError(ch, i, s, true, '/', '?', '=');
>>>> + }
>>>> + break;
>>>> +
>>>> + case QUALIFIER_VALUE:
>>>> + if (Character.isJavaIdentifierPart(ch)) {
>>>> + buf.append(ch);
>>>> + } else if (ch == '/') {
>>>> + transit(ParseState.QUALIFIER_KEY);
>>>> + } else if (ch == '?') {
>>>> + transit(ParseState.PARAM_KEY);
>>>> + } else {
>>>> + parseError(ch, i, s, true, '/', '?');
>>>> + }
>>>> + break;
>>>> +
>>>> + case PARAM_KEY:
>>>> + if (Character.isJavaIdentifierPart(ch)) {
>>>> + buf.append(ch);
>>>> + } else if (ch == '=') {
>>>> + if (isQueryKey())
>>>> + buf.append(ch);
>>>> + else
>>>> + transit(ParseState.PARAM_VALUE);
>>>> + } else if (ch == ';') {
>>>> + transit(ParseState.PARAM_KEY);
>>>> + } else if (isQueryKey() && isQueryChar(ch)) {
>>>> + buf.append(ch);
>>>> + } else {
>>>> + parseError(ch, i, s, true, ';', '=');
>>>> + }
>>>> + break;
>>>> +
>>>> + case PARAM_VALUE:
>>>> + if (Character.isJavaIdentifierPart(ch)) {
>>>> + buf.append(ch);
>>>> + } else if (ch == ';') {
>>>> + transit(ParseState.PARAM_KEY);
>>>> + } else {
>>>> + parseError(ch, i, s, true, ';');
>>>> + }
>>>> + break;
>>>> + default:
>>>> + throw new RuntimeException("ParseError: '" +
>> ch
>>> + "' at " + i + " in [" + s + "]. "
>>>> + + "Unknown state " + _state);
>>>> + }
>>>> + }
>>>> + if (buf.length() > 0) {
>>>> + transit(ParseState.END);
>>>> + }
>>>> + }
>>>> +
>>>> + /**
>>>> + * Affirms if parsing a query string.
>>>> + */
>>>> + private boolean isQueryKey() {
>>>> + return "query".equals(_action) && _stack.size() == 1;
>>>> + }
>>>> +
>>>> + /**
>>>> + * Affirms if the given character is valid in a query string
>>>> + */
>>>> + private boolean isQueryChar(char c) {
>>>> + return c == ' ' || c == '.' || c == ':' || c == '?' || c
>> ==
>>> '\'';
>>>> + }
>>>> +
>>>> + /**
>>>> + * Transitions to a new parse state.
>>>> + *
>>>> + * @param to target parse state
>>>> + */
>>>> + void transit(ParseState to) {
>>>> + String token = buf.toString();
>>>> + switch (_state) {
>>>> + case ACTION:
>>>> + setAction(token);
>>>> + break;
>>>> + case QUALIFIER_KEY:
>>>> + setQualifier(token, null);
>>>> + break;
>>>> + case QUALIFIER_VALUE:
>>>> + setQualifier(_stack.peekLast().getValue(), token);
>>>> + break;
>>>> + case PARAM_KEY:
>>>> + setParameter(token, null);
>>>> + break;
>>>> + case PARAM_VALUE:
>>>> + setParameter(_stack.peekLast().getValue(), token);
>>>> + break;
>>>> +
>>>> + }
>>>> + if (_state != ParseState.INIT && to != ParseState.END) {
>>>> + _stack.add(new Token(_state, token));
>>>> + }
>>>> + buf.delete(0, buf.length());
>>>> + _state = to;
>>>> + }
>>>> +
>>>> + protected void parseError(char ch, int pos, String line,
>> boolean
>>> java, char... expected) {
>>>> + throw new RuntimeException("ParseError: Encountered '" +
>> ch
>>> + "' at " + pos + " in [" + line + "] while "
>>>> + + "parsing " + _state + ". Expected " +
>>> Arrays.toString(expected) + (java ? " or Java identifer" : ""));
>>>> +
>>>> + }
>>>> +
>>>> + /**
>>>> + * Token in a JEST stream.
>>>> + *
>>>> + */
>>>> + static class Token {
>>>> + final ParseState _type;
>>>> + final String _value;
>>>> +
>>>> + public Token(ParseState type, String value) {
>>>> + _type = type;
>>>> + _value = value;
>>>> + }
>>>> +
>>>> + public ParseState getType() {
>>>> + return _type;
>>>> + }
>>>> +
>>>> + public String getValue() {
>>>> + return _value;
>>>> + }
>>>> +
>>>> + public String toString() {
>>>> + return _value + "[" + _type + "]";
>>>> + }
>>>> + }
>>>> +
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,71 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.IOException;
>>>> +import java.io.OutputStream;
>>>> +import java.io.PrintStream;
>>>> +import java.io.PrintWriter;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +import java.util.Set;
>>>> +
>>>> +import org.apache.openjpa.kernel.OpenJPAStateManager;
>>>> +import org.apache.openjpa.persistence.JPAFacadeHelper;
>>>> +import org.w3c.dom.Document;
>>>> +import org.w3c.dom.Element;
>>>> +
>>>> +/**
>>>> + * Response of a JEST Request.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class JESTResponse extends AbstractResponse {
>>>> + private final OpenJPAStateManager _sm;
>>>> +
>>>> + public JESTResponse(Request request, ServerContext ctx,
>>> OpenJPAStateManager sm, OutputStream out) throws Exception {
>>>> + super(request, ctx, out);
>>>> + _sm = sm;
>>>> + }
>>>> +
>>>> + public String getContentType() {
>>>> + return "text/html";
>>>> + }
>>>> +
>>>> + public void writeOut() throws Exception {
>>>> + print(_request.getProtocol()); println("200 OK");
>>>> + printHeader("Connection", "close");
>>>> + printHeader("Content-Type", "text/html", "charset=UTF-8");
>>>> + println();
>>>> + println("<html><body><pre>");
>>>> + XMLEncoder encoder = new
>>> XMLEncoder(_ctx.getPersistenceUnit().getMetamodel());
>>>> + Document doc = encoder.encode(_sm);
>>>> + encoder.writeDoc(doc, this);
>>>> + println("Response from JEST");
>>>> +
>>>> + println("</pre></body></html>");
>>>> + close();
>>>> + }
>>>> +
>>>> +}
>>>> +
>>>> +
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,316 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.BufferedReader;
>>>> +import java.io.CharArrayWriter;
>>>> +import java.io.IOException;
>>>> +import java.io.InputStream;
>>>> +import java.io.InputStreamReader;
>>>> +import java.io.Reader;
>>>> +import java.util.Arrays;
>>>> +import java.util.BitSet;
>>>> +import java.util.Collection;
>>>> +import java.util.HashSet;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +import java.util.Set;
>>>> +
>>>> +import javax.persistence.metamodel.Attribute;
>>>> +
>>>> +import org.apache.openjpa.kernel.OpenJPAStateManager;
>>>> +import org.apache.openjpa.kernel.StoreContext;
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.meta.FieldMetaData;
>>>> +import org.apache.openjpa.meta.JavaTypes;
>>>> +import org.apache.openjpa.meta.ValueMetaData;
>>>> +import org.apache.openjpa.persistence.meta.Members;
>>>> +import org.apache.openjpa.persistence.meta.MetamodelImpl;
>>>> +import org.w3c.dom.CDATASection;
>>>> +import org.w3c.dom.Document;
>>>> +import org.w3c.dom.Element;
>>>> +
>>>> +/**
>>>> + * Marshals a root instance and its persistent closure as JSON object.
>>>> + * The closure is resolved against the persistence context that
>> contains
>>> the root instance.
>>>> + * The JSON format introduces a $id and $ref to address reference that
>>> pure JSON does not.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class JSONEncoder {
>>>> + /**
>>>> + * The element/attribute tags declared in
>>> <code>jest-instance.xsd</code> XML schema.
>>>> + */
>>>> + public static final String ELEMENT_NULL_REF = "null";
>>>> + public static final String ELEMENT_INSTANCE = "instance";
>>>> + public static final String ELEMENT_REF = "ref";
>>>> +
>>>> +
>>>> + private MetamodelHelper _model;
>>>> +
>>>> + public JSONEncoder(MetamodelImpl model) {
>>>> + _model = new MetamodelHelper(model);
>>>> + }
>>>> +
>>>> + /**
>>>> + * Encodes the given managed instance into a new XML element as a
>>> child of the given parent node.
>>>> + *
>>>> + * @param sm a managed instance, can be null.
>>>> + * @param parent the parent node to which the new node be
>> attached.
>>>> + */
>>>> + public StringBuilder encode(final OpenJPAStateManager sm) {
>>>> + return encode(sm, new HashSet<OpenJPAStateManager>(), 0,
>> false);
>>>> + }
>>>> + StringBuilder indent(StringBuilder buf, int indent) {
>>>> + if (indent <= 0)
>>>> + return buf;
>>>> + char[] spaces = new char[indent*4];
>>>> + Arrays.fill(spaces, ' ');
>>>> + buf.insert(0, spaces);
>>>> + return buf;
>>>> + }
>>>> + StringBuilder end(StringBuilder buf, char ch, int indent) {
>>>> + char[] spaces = new char[indent*4];
>>>> + Arrays.fill(spaces, ' ');
>>>> + return buf.append("\r\n").append(spaces).append(ch);
>>>> + }
>>>> +
>>>> + /**
>>>> + * Encodes the closure of a persistent instance into a XML
>> element.
>>>> + *
>>>> + * @param sm the managed instance to be encoded. Can be null.
>>>> + * @param parent the parent XML element to which the new XML
>> element
>>> be added. Must not be null. Must be
>>>> + * owned by a document.
>>>> + * @param visited the persistent instances that had been encoded
>>> already. Must not be null or immutable.
>>>> + *
>>>> + * @return the new element. The element has been appended as a
>> child
>>> to the given parent in this method.
>>>> + */
>>>> + private StringBuilder encode(final OpenJPAStateManager sm, final
>>> Set<OpenJPAStateManager> visited,
>>>> + int indent, boolean indentPara) {
>>>> + if (visited == null) {
>>>> + throw new IllegalArgumentException("null closure for
>>> encoder");
>>>> + }
>>>> + StringBuilder root = indent(new StringBuilder("{"),
>> indentPara
>>> ? indent : 0);
>>>> + if (sm == null) {
>>>> + return root.append("null}");
>>>> + }
>>>> + boolean ref = !visited.add(sm);
>>>> + if (ref) {
>>>> + return indent(root.append(quoted("$ref")).append(":
>>> ").append(ior(sm)).append('}'),
>>>> + indentPara ? indent : 0);
>>>> + } else {
>>>> + indent(root.append(quoted("$id")).append(":
>>> ").append(ior(sm)), indentPara ? indent : 0);
>>>> + }
>>>> +
>>>> + StringBuilder child = new StringBuilder();
>>>> + BitSet loaded = sm.getLoaded();
>>>> + StoreContext ctx = (StoreContext)sm.getGenericContext();
>>>> + List<Attribute<?, ?>> attrs =
>>> _model.getAttributesInOrder(sm.getMetaData());
>>>> + for (int i = 0; i < attrs.size(); child = new StringBuilder(),
>>> i++) {
>>>> + FieldMetaData fmd = ((Members.Member<?, ?>)
>>> attrs.get(i)).fmd;
>>>> + if (!loaded.get(fmd.getIndex()))
>>>> + continue;
>>>> + Object value = sm.fetch(fmd.getIndex());
>>>> + child.append(quoted(fmd.getName())).append(": ");
>>>> + switch (fmd.getDeclaredTypeCode()) {
>>>> + case JavaTypes.BOOLEAN:
>>>> + case JavaTypes.BYTE:
>>>> + case JavaTypes.CHAR:
>>>> + case JavaTypes.DOUBLE:
>>>> + case JavaTypes.FLOAT:
>>>> + case JavaTypes.INT:
>>>> + case JavaTypes.LONG:
>>>> + case JavaTypes.SHORT:
>>>> +
>>>> + case JavaTypes.BOOLEAN_OBJ:
>>>> + case JavaTypes.BYTE_OBJ:
>>>> + case JavaTypes.CHAR_OBJ:
>>>> + case JavaTypes.DOUBLE_OBJ:
>>>> + case JavaTypes.FLOAT_OBJ:
>>>> + case JavaTypes.INT_OBJ:
>>>> + case JavaTypes.LONG_OBJ:
>>>> + case JavaTypes.SHORT_OBJ:
>>>> +
>>>> + case JavaTypes.BIGDECIMAL:
>>>> + case JavaTypes.BIGINTEGER:
>>>> + case JavaTypes.DATE:
>>>> + case JavaTypes.NUMBER:
>>>> + case JavaTypes.CALENDAR:
>>>> + case JavaTypes.LOCALE:
>>>> + case JavaTypes.STRING:
>>>> + case JavaTypes.ENUM:
>>>> + child.append(quoted(value));
>>>> + break;
>>>> +
>>>> + case JavaTypes.PC:
>>>> + if (value == null) {
>>>> + child.append("null");
>>>> + } else {
>>>> +
>> child.append(encode(ctx.getStateManager(value),
>>> visited, indent+1, false));
>>>> + }
>>>> + break;
>>>> +
>>>> + case JavaTypes.ARRAY:
>>>> + Object[] values = (Object[])value;
>>>> + value = Arrays.asList(values);
>>>> + // no break;
>>>> + case JavaTypes.COLLECTION:
>>>> + if (value == null) {
>>>> + child.append("null");
>>>> + break;
>>>> + }
>>>> + child.append("[");
>>>> + Collection<?> members = (Collection<?>)value;
>>>> + boolean basic = fmd.getElement().getTypeMetaData()
>>> == null;
>>>> + int k = 0;
>>>> + for (Object o : members) {
>>>> + child.append("\r\n");
>>>> + if (o == null) {
>>>> + child.append(indent(new
>>> StringBuilder("null"), indent+1));
>>>> + } else {
>>>> + if (basic) {
>>>> + child.append(indent(new
>>> StringBuilder(quoted(o)), indent+1));
>>>> + } else {
>>>> +
>>> child.append(encode(ctx.getStateManager(o), visited, indent+1, true));
>>>> + }
>>>> + }
>>>> + }
>>>> + end(child, ']', indent+1);
>>>> + break;
>>>> + case JavaTypes.MAP:
>>>> + if (value == null) {
>>>> + child.append("null");
>>>> + break;
>>>> + }
>>>> + child.append("[");
>>>> + Set<Map.Entry> entries = ((Map)value).entrySet();
>>>> + boolean basicKey =
>>> fmd.getElement().getTypeMetaData() == null;
>>>> + boolean basicValue =
>>> fmd.getValue().getTypeMetaData() == null;
>>>> + for (Map.Entry<?,?> e : entries) {
>>>> + if (e.getKey() == null) {
>>>> + child.append("null:");
>>>> + } else {
>>>> + if (basicKey) {
>>>> +
>>> child.append(quoted(e.getKey())).append(":");
>>>> + } else {
>>>> +
>>> child.append(encode(ctx.getStateManager(e.getKey()), visited, indent+1,
>>> true));
>>>> + }
>>>> + }
>>>> + if (e.getValue() == null) {
>>>> + child.append("null");
>>>> + } else {
>>>> + if (basicValue) {
>>>> + child.append(quoted(e.getValue()));
>>>> + } else {
>>>> +
>>> child.append(encode(ctx.getStateManager(e.getValue()), visited,
>> indent+1,
>>> false));
>>>> + }
>>>> + }
>>>> + }
>>>> + break;
>>>> +
>>>> + case JavaTypes.INPUT_STREAM:
>>>> + case JavaTypes.INPUT_READER:
>>>> + child = new StringBuilder(fmd.getName());
>>>> + if (value == null) {
>>>> + child.append("null");
>>>> + } else {
>>>> + child.append(streamToString(value));
>>>> + }
>>>> + break;
>>>> +
>>>> + case JavaTypes.PC_UNTYPED:
>>>> + case JavaTypes.OBJECT:
>>>> + case JavaTypes.OID:
>>>> + System.err.println("Not handled " + fmd.getName()
>> +
>>> " of type " + fmd.getDeclaredType());
>>>> + }
>>>> +
>>>> + if (child != null) {
>>>> + root.append("\r\n");
>>>> + root.append(indent(child, indent+1));
>>>> + if (loaded.length()-1 != i)
>>>> + root.append(",");
>>>> + }
>>>> + }
>>>> + return end(root, '}', indent);
>>>> + }
>>>> +
>>>> +
>>>> + String ior(OpenJPAStateManager sm) {
>>>> + return quoted(typeOf(sm)+"-"+sm.getObjectId().toString());
>>>> + }
>>>> +
>>>> + String typeOf(OpenJPAStateManager sm) {
>>>> + return sm.getMetaData().getDescribedType().getSimpleName();
>>>> + }
>>>> +
>>>> + String typeOf(Class<?> cls) {
>>>> + return cls.getSimpleName();
>>>> + }
>>>> +
>>>> + String typeOf(ClassMetaData meta) {
>>>> + return meta.getDescribedType().getSimpleName();
>>>> + }
>>>> +
>>>> + String typeOf(ValueMetaData vm) {
>>>> + if (vm.getTypeMetaData() == null)
>>>> + return typeOf(vm.getType());
>>>> + return typeOf(vm.getTypeMetaData());
>>>> + }
>>>> +
>>>> + String typeOf(FieldMetaData fmd) {
>>>> + return fmd.getType().getSimpleName();
>>>> + }
>>>> +
>>>> +
>>>> + /**
>>>> + * Convert the given stream (either an InutStream or a Reader) to
>> a
>>> String
>>>> + * to be included in CDATA section of a XML document.
>>>> + *
>>>> + * @param value the field value to be converted. Can not be null
>>>> + * @return
>>>> + */
>>>> + String streamToString(Object value) {
>>>> + Reader reader = null;
>>>> + if (value instanceof InputStream) {
>>>> + reader = new BufferedReader(new
>>> InputStreamReader((InputStream)value));
>>>> + } else if (value instanceof Reader) {
>>>> + reader = (Reader)value;
>>>> + } else {
>>>> + throw new RuntimeException();
>>>> + }
>>>> + CharArrayWriter writer = new CharArrayWriter();
>>>> + try {
>>>> + for (int c; (c = reader.read()) != -1;) {
>>>> + writer.write(c);
>>>> + }
>>>> + } catch (IOException ex) {
>>>> + throw new RuntimeException(ex);
>>>> + }
>>>> + return writer.toString();
>>>> + }
>>>> +
>>>> + String quoted(Object o) {
>>>> + if (o == null) return "null";
>>>> + if (o instanceof Number)
>>>> + return o.toString();
>>>> + return "\"" + o.toString() + "\"";
>>>> + }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,127 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.util.ArrayList;
>>>> +import java.util.Collections;
>>>> +import java.util.Comparator;
>>>> +import java.util.HashMap;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +
>>>> +import javax.persistence.metamodel.Attribute;
>>>> +import javax.persistence.metamodel.EntityType;
>>>> +import javax.persistence.metamodel.ManagedType;
>>>> +import javax.persistence.metamodel.Metamodel;
>>>> +import javax.persistence.metamodel.SingularAttribute;
>>>> +
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.meta.FieldMetaData;
>>>> +import org.apache.openjpa.persistence.meta.MetamodelImpl;
>>>> +
>>>> +/**
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class MetamodelHelper {
>>>> + private MetamodelImpl _model;
>>>> + private Map<ManagedType<?>, List<Attribute<?, ?>>> _attrs = new
>>> HashMap<ManagedType<?>, List<Attribute<?,?>>>();
>>>> +
>>>> + public MetamodelHelper(MetamodelImpl model) {
>>>> + _model = model;
>>>> + }
>>>> +
>>>> + public List<Attribute<?,?>> getAttributesInOrder(Class<?> cls) {
>>>> + return getAttributesInOrder(_model.managedType(cls));
>>>> + }
>>>> +
>>>> + public List<Attribute<?,?>> getAttributesInOrder(ClassMetaData
>> meta)
>>> {
>>>> + return getAttributesInOrder(meta.getDescribedType());
>>>> + }
>>>> +
>>>> + /**
>>>> + * Gets the attributes of the given type in defined order.
>>>> + * @param type
>>>> + * @return
>>>> + */
>>>> + public List<Attribute<?,?>> getAttributesInOrder(ManagedType<?>
>>> type) {
>>>> + List<Attribute<?,?>> attrs = _attrs.get(type);
>>>> + if (attrs != null)
>>>> + return attrs;
>>>> + List<Attribute<?,?>> list = new
>>> ArrayList<Attribute<?,?>>(type.getAttributes());
>>>> + Collections.sort(list, new AttributeComparator());
>>>> + _attrs.put(type, list);
>>>> + return list;
>>>> + }
>>>> +
>>>> + public static boolean isId(Attribute<?,?> a) {
>>>> + if (a instanceof SingularAttribute)
>>>> + return ((SingularAttribute<?,?>)a).isId();
>>>> + return false;
>>>> + }
>>>> +
>>>> + public static boolean isVersion(Attribute<?,?> a) {
>>>> + if (a instanceof SingularAttribute)
>>>> + return ((SingularAttribute<?,?>)a).isVersion();
>>>> + return false;
>>>> + }
>>>> +
>>>> + public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
>>>> + if (isId(attr))
>>>> + return 0;
>>>> + if (isVersion(attr))
>>>> + return 1;
>>>> +
>>>> + switch (attr.getPersistentAttributeType()) {
>>>> + case BASIC :
>>>> + case EMBEDDED:
>>>> + return 2;
>>>> + case ONE_TO_ONE:
>>>> + case MANY_TO_ONE:
>>>> + return 3;
>>>> + case ONE_TO_MANY:
>>>> + case MANY_TO_MANY:
>>>> + case ELEMENT_COLLECTION: return 4;
>>>> + default: return 5;
>>>> + }
>>>> + }
>>>> +
>>>> + /**
>>>> + * Compares attribute by their qualification.
>>>> + * Identity
>>>> + * Version
>>>> + * Basic
>>>> + * Singular association
>>>> + * Plural association
>>>> + *
>>>> + */
>>>> + public static class AttributeComparator implements
>>> Comparator<Attribute<?,?>> {
>>>> + @Override
>>>> + public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
>>>> + Integer t1 = getAttributeTypeCode(a1);
>>>> + Integer t2 = getAttributeTypeCode(a2);
>>>> + if (t1.equals(t2)) {
>>>> + return a1.getName().compareTo(a2.getName());
>>>> + } else {
>>>> + return t1.compareTo(t2);
>>>> + }
>>>> + }
>>>> + }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,138 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.IOException;
>>>> +import java.io.OutputStream;
>>>> +import java.io.Serializable;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +
>>>> +/**
>>>> + * A request from a remote client to a server to do something.
>>>> + * The request arrives as stream of bytes from a remote location
>>>> + * and if the server can interpret the protocol from the stream,
>>>> + * then make a concrete request object.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public interface Request extends Serializable {
>>>> + /**
>>>> + * Get the HTTP verb such as GET, POST
>>>> + *
>>>> + * @return uppercase string
>>>> + */
>>>> + String getMethod();
>>>> +
>>>> + /**
>>>> + * Get the first path segment as intended persistence action such
>> as
>>> <code>find</code> or <code>query</code>
>>>> + *
>>>> + * @return lowercase action name. Can be empty.
>>>> + */
>>>> + String getAction();
>>>> +
>>>> + /**
>>>> + * Get the protocol such as HTTP/1.1
>>>> + *
>>>> + * @return upper-case string
>>>> + */
>>>> + String getProtocol();
>>>> +
>>>> + /**
>>>> + * Get the body, if any.
>>>> + *
>>>> + * @return body of the request. null if no body.
>>>> + */
>>>> + String getBody();
>>>> +
>>>> + /**
>>>> + * Get the headers indexed by the keys.
>>>> + * Header values are list of Strings.
>>>> + *
>>>> + * @return empty map if there is no header
>>>> + */
>>>> + Map<String, List<String>> getHeaders();
>>>> +
>>>> + /**
>>>> + * Get the header values for the given key.
>>>> + * @param key a key
>>>> + * @return null if no header value for the given key
>>>> + */
>>>> + List<String> getHeader(String key);
>>>> +
>>>> + /**
>>>> + * Affirm if the the given qualifier is available in this request.
>>>> + *
>>>> + * @param key case-sensitive qualifier
>>>> + * @return true if the key is present.
>>>> + */
>>>> + boolean hasQualifier(String key);
>>>> +
>>>> + /**
>>>> + * Gets the value for the given qualifier key.
>>>> + *
>>>> + * @param key case-sensitive qualifier
>>>> + * @return value of the qualifier. null if the key is absent.
>>>> + */
>>>> + String getQualifier(String key);
>>>> +
>>>> + /**
>>>> + * Get all the qualifiers available in this request.
>>>> + *
>>>> + * @return key-value pairs of the qualifiers. Empty map if no
>>> qualifier is present.
>>>> + */
>>>> + Map<String,String> getQualifiers();
>>>> +
>>>> +
>>>> + /**
>>>> + * Affirm if the the given parameter is available in this request.
>>>> + *
>>>> + * @param key case-sensitive parameter
>>>> + * @return true if the key is present.
>>>> + */
>>>> + boolean hasParameter(String key);
>>>> +
>>>> +
>>>> + /**
>>>> + * Gets the value for the given parameter key.
>>>> + *
>>>> + * @param key case-sensitive parameter
>>>> + * @return value of the parameter. null if the key is absent.
>>>> + */
>>>> + String getParameter(String key);
>>>> +
>>>> + /**
>>>> + * Get all the parameters available in this request.
>>>> + *
>>>> + * @return key-value pairs of the parameters. Empty map if no
>>> parameter is present.
>>>> + */
>>>> + Map<String,String> getParameters();
>>>> +
>>>> + /**
>>>> + * Parse the request represented as a list of strings.
>>>> + *
>>>> + * @param lines each line of the request.
>>>> + * @throws IOException
>>>> + */
>>>> + void read(List<String> lines) throws IOException;
>>>> +
>>>> + Response process(ServerContext server, OutputStream stream) throws
>>> Exception;
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,63 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.util.HashMap;
>>>> +import java.util.Map;
>>>> +
>>>> +/**
>>>> + * A factory to create a specific type of request.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class RequestFactory {
>>>> + private final String _protocol;
>>>> + private static Map<String, RequestFactory> _registered = new
>>> HashMap<String, RequestFactory>();
>>>> + static {
>>>> + _registered.put("HTTP/1.0", new RequestFactory("HTTP/1.0"));
>>>> + _registered.put("HTTP/1.1", new RequestFactory("HTTP/1.1"));
>>>> + }
>>>> +
>>>> + public static void register(String protocol, RequestFactory
>> factory)
>>> {
>>>> + _registered.put(protocol, factory);
>>>> + }
>>>> +
>>>> + private RequestFactory(String proto) {
>>>> + _protocol = proto;
>>>> + }
>>>> +
>>>> + public static RequestFactory getFactory(String protocol) {
>>>> + return _registered.get(protocol);
>>>> + }
>>>> +
>>>> + Request createRequest(String method) {
>>>> + JESTRequest request = null;
>>>> + if ("GET".equalsIgnoreCase(method)) {
>>>> + request = new GETRequest();
>>>> + } else {
>>>> + throw new UnsupportedOperationException();
>>>> + }
>>>> + request.setProtocol(_protocol);
>>>> + request.setMethod(method.toUpperCase());
>>>> + return request;
>>>> +
>>>> + }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,147 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.BufferedReader;
>>>> +import java.io.IOException;
>>>> +import java.io.InputStream;
>>>> +import java.io.InputStreamReader;
>>>> +import java.io.OutputStream;
>>>> +import java.net.HttpURLConnection;
>>>> +import java.net.Socket;
>>>> +import java.net.SocketException;
>>>> +import java.util.ArrayList;
>>>> +import java.util.Collections;
>>>> +import java.util.Iterator;
>>>> +import java.util.List;
>>>> +import java.util.Map;
>>>> +import java.util.Set;
>>>> +import java.util.concurrent.Callable;
>>>> +
>>>> +import javax.persistence.EntityManager;
>>>> +import javax.persistence.EntityNotFoundException;
>>>> +
>>>> +import org.apache.openjpa.kernel.OpenJPAStateManager;
>>>> +import org.apache.openjpa.kernel.StoreContext;
>>>> +import org.apache.openjpa.lib.log.Log;
>>>> +import org.apache.openjpa.lib.util.Localizer;
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.meta.FieldMetaData;
>>>> +import org.apache.openjpa.persistence.JPAFacadeHelper;
>>>> +import org.apache.openjpa.persistence.OpenJPAPersistence;
>>>> +import org.apache.openjpa.util.ApplicationIds;
>>>> +
>>>> +/**
>>>> + * Handles a request from a remote client.
>>>> + * Reads the socket data.
>>>> + * Populates the request.
>>>> + * Determines the processor based on request method and action.
>>>> + * Delegates to the processor.
>>>> + * Processor generates the response.
>>>> + * Writes the response to the stream.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class RequestHandler implements Callable<Void> {
>>>> + private static final int SPACE = ' ';
>>>> + private final Socket _socket;
>>>> + private final ServerContext _server;
>>>> + private final Log _log;
>>>> + private static final Localizer _loc =
>>> Localizer.forPackage(RequestHandler.class);
>>>> +
>>>> + public RequestHandler(Socket socket, ServerContext server) {
>>>> + _socket = socket;
>>>> + _server = server;
>>>> + _log = _server.getLog();
>>>> + }
>>>> +
>>>> + public Void call() throws Exception {
>>>> + Request request = null;
>>>> + Response response = null;
>>>> + try {
>>>> + request = readRequest(_socket.getInputStream());
>>>> + response = request.process(_server,
>>> _socket.getOutputStream());
>>>> + } catch (Exception e) {
>>>> + response = new ErrorResponse(request, _server, e,
>>> HttpURLConnection.HTTP_INTERNAL_ERROR,
>>>> + _socket.getOutputStream());
>>>> + }
>>>> + response.writeOut();
>>>> + return null;
>>>> + }
>>>> +
>>>> +
>>>> +
>>>> + /**
>>>> + * Reads the given CR-LF delimited stream.
>>>> + * The first line is scanned for the method (first space-delimited
>>> String) and protocol (the last
>>>> + * space-delimited String). Accordingly a request is created and
>>> rest of the input stream content
>>>> + * is parsed by the request itself.
>>>> + *
>>>> + * @param input
>>>> + * @throws IOException
>>>> + */
>>>> + public Request readRequest(InputStream input) throws IOException {
>>>> + if (_log.isTraceEnabled())
>>>> + _log.trace("Reading request from the input stream ");
>>>> +
>>>> + BufferedReader reader = new BufferedReader(new
>>> InputStreamReader(input));
>>>> + String status = reader.readLine();
>>>> + if (_log.isInfoEnabled())
>>>> + _log.info("Status Line [" + status + "]");
>>>> + int spaceFirst = status.indexOf(SPACE);
>>>> + if (spaceFirst == -1)
>>>> + throw new IOException("HTTP Method could not be determined
>>> from [" + status + "]");
>>>> + int spaceLast = status.lastIndexOf(SPACE);
>>>> + if (spaceLast == -1)
>>>> + throw new IOException("HTTP Protocol could not be
>> determined
>>> from [" + status + "]");
>>>> + String method = status.substring(0, spaceFirst);
>>>> + String protocol = status.substring(spaceLast+1);
>>>> + String path = status.substring(spaceFirst+1, spaceLast);
>>>> + Request request =
>>> RequestFactory.getFactory(protocol).createRequest(method);
>>>> + List<String> lines = new ArrayList<String>();
>>>> + if (path.equals("/")) {
>>>> + lines.add(path);
>>>> + } else {
>>>> + lines = readlines(reader);
>>>> + lines.add(0, path);
>>>> + }
>>>> + if (lines.isEmpty()) {
>>>> + throw new IOException("No CR-LF delimited lines could be
>>> read from " + input);
>>>> + }
>>>> + request.read(lines);
>>>> + return request;
>>>> + }
>>>> +
>>>> +
>>>> + List<String> readlines(BufferedReader reader) throws IOException {
>>>> + List<String> buffers = new ArrayList<String>();
>>>> + String line;
>>>> + while ((line = reader.readLine()) != null && line.length() >
>> 0)
>>> {
>>>> + buffers.add(line);
>>>> + }
>>>> + return buffers;
>>>> + }
>>>> +
>>>> +
>>>> + public String toString() {
>>>> + return _socket.getInetAddress()+":"+_socket.getPort();
>>>> + }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,54 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.InputStream;
>>>> +import java.io.OutputStream;
>>>> +import java.io.RandomAccessFile;
>>>> +
>>>> +import javax.imageio.ImageIO;
>>>> +
>>>> +/**
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +@SuppressWarnings("serial")
>>>> +public class ResourceResponse extends AbstractResponse {
>>>> + private final InputStream _in;
>>>> + private final String _mimeType;
>>>> + public ResourceResponse(Request request, ServerContext ctx,
>>> InputStream resource, String mimeType,
>>>> + OutputStream out) throws Exception {
>>>> + super(request, ctx, out);
>>>> + _in = resource;
>>>> + _mimeType = mimeType;
>>>> + }
>>>> +
>>>> + public void writeOut() throws Exception {
>>>> + print(_request.getProtocol()); println("200 OK");
>>>> + printHeader("Connection", "close");
>>>> + printHeader("Content-Type", _mimeType, "charset=UTF-8");
>>>> + println();
>>>> + for (int c = 0; (c = _in.read()) != -1;) {
>>>> + print((char)c);
>>>> + }
>>>> + _in.close();
>>>> + close();
>>>> + }
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,31 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.Serializable;
>>>> +
>>>> +/**
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public interface Response extends Serializable {
>>>> +// void setHeader(String key, String value);
>>>> + void writeOut() throws Exception;
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>> Added:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>>> URL:
>>>
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java?rev=1028093&view=auto
>>>>
>>>
>> ==============================================================================
>>>> ---
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>> (added)
>>>> +++
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>> Wed Oct 27 20:42:44 2010
>>>> @@ -0,0 +1,230 @@
>>>> +/*
>>>> + * Licensed to the Apache Software Foundation (ASF) under one
>>>> + * or more contributor license agreements. See the NOTICE file
>>>> + * distributed with this work for additional information
>>>> + * regarding copyright ownership. The ASF licenses this file
>>>> + * to you under the Apache License, Version 2.0 (the
>>>> + * "License"); you may not use this file except in compliance
>>>> + * with the License. You may obtain a copy of the License at
>>>> + *
>>>> + * http://www.apache.org/licenses/LICENSE-2.0
>>>> + *
>>>> + * Unless required by applicable law or agreed to in writing,
>>>> + * software distributed under the License is distributed on an
>>>> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
>>>> + * KIND, either express or implied. See the License for the
>>>> + * specific language governing permissions and limitations
>>>> + * under the License.
>>>> + */
>>>> +
>>>> +package org.apache.openjpa.persistence.jest;
>>>> +
>>>> +import java.io.IOException;
>>>> +import java.net.ServerSocket;
>>>> +import java.net.Socket;
>>>> +import java.util.concurrent.ExecutorService;
>>>> +import java.util.concurrent.Executors;
>>>> +
>>>> +import javax.persistence.EntityManagerFactory;
>>>> +
>>>> +import org.apache.openjpa.conf.OpenJPAConfiguration;
>>>> +import org.apache.openjpa.lib.conf.Configurable;
>>>> +import org.apache.openjpa.lib.conf.Configuration;
>>>> +import org.apache.openjpa.lib.log.Log;
>>>> +import org.apache.openjpa.lib.util.Localizer;
>>>> +import org.apache.openjpa.meta.ClassMetaData;
>>>> +import org.apache.openjpa.meta.MetaDataRepository;
>>>> +import org.apache.openjpa.persistence.EntityManagerFactoryImpl;
>>>> +
>>>> +
>>>> +/**
>>>> + * A server running on an independent thread that allows a remote,
>>> language-neutral client to access OpenJPA runtime.
>>>> + *
>>>> + * @author Pinaki Poddar
>>>> + *
>>>> + */
>>>> +public class Server implements ServerContext, Configurable, Runnable {
>>>> + private ServerSocket _listenSocket;
>>>> + protected ExecutorService _executors;
>>>> + public final static int DEFAULT_PORT = 6789;
>>>> + protected int _port = DEFAULT_PORT;
>>>> + protected int _range = 1;
>>>> + protected String _format = "xml";
>>>> + protected Log _log;
>>>> + protected Thread _thread;
>>>> + private EntityManagerFactoryImpl _ctx;
>>>> + private static Localizer _loc =
>> Localizer.forPackage(Server.class);
>>>> +
>>>> + /**
>>>> + * Sets the persistence unit context in which this server will
>> serve
>>> requests.
>>>> + * The context must be set before operation.
>>>> + *
>>>> + * @param emf an implementation of OpenJPA Persistence Unit. Must
>>> not be null.
>>>> + */
>>>> + public void setContext(EntityManagerFactoryImpl emf) {
>>>> + if (emf == null)
>>>> + throw new NullPointerException();
>>>> + _ctx = emf;
>>>> + }
>>>> +
>>>> + /**
>>>> + * Gets the persistence unit context in which this server serves
>>> requests.
>>>> + *
>>>> + * @param emf an implementation of OpenJPA Persistence Unit.
>>>> + */
>>>> + public EntityManagerFactoryImpl getPersistenceUnit() {
>>>> + return _ctx;
>>>> + }
>>>> +
>>>> + public Log getLog() {
>>>> + return _log;
>>>> + }
>>>> +
>>>> + /**
>>>> + * Start the server in a daemon thread.
>>>> + * This method is idempotent.
>>>> + *
>>>> + * @return true if the server has started by this call or already
>>> running.
>>>> + */
>>>> + public synchronized boolean start() {
>>>> + try {
>>>> + if (_thread != null)
>>>> + return true;
>>>> + if (createServerSocket()) {
>>>> + _thread = new Thread(this);
>>>> + _thread.setDaemon(true);
>>>> + _thread.start();
>>>> + return true;
>>>> + }
>>>> + return false;
>>>> + } catch (Exception ex) {
>>>> + ex.printStackTrace();
>>>> + return false;
>>>> + }
>>>> + }
>>>> +
>>>> + /**
>>>> + * Stops the server.
>>>> + */
>>>> + public synchronized void stop() {
>>>> + _thread.interrupt();
>>>> + _thread = null;
>>>> + _executors.shutdownNow();
>>>> + }
>>>> +
>>>> + /**
>>>> + * Sets the port in which the server will listen.
>>>> + *
>>>> + * @param port a positive integer.
>>>> + */
>>>> + public void setPort(int port) {
>>>> + _port = port;
>>>> + }
>>>> +
>>>> + /**
>>>> + * Gets the current port.
>>>> + *
>>>> + * @return the port number. Defaults to default HTTP port.
>>>> + */
>>>> + public int getPort() {
>>>> + return _port;
>>>> + }
>>>> +
>>>> + /**
>>>> + * Sets the range of ports the server will attempt at start.
>>>> + *
>>>> + * @param range a positive integer.
>>>> + */
>>>> + public void setRange(int range) {
>>>> + if (range > 0)
>>>> + _range = range;
>>>> + }
>>>> +
>>>> + public void setFormat(String format) {
>>>> + _format = format;
>>>> + }
>>>> +
>>>> + public String getFormat() {
>>>> + return _format;
>>>> + }
>>>> +
>>>> + /**
>>>> + * Sets the range of ports the server will attempt at start.
>>>> + * @return a positive integer. Defaults to 1.
>>>> + */
>>>> + public int getRange() {
>>>> + return _range;
>>>> + }
>>>> +
>>>> + public void run() {
>>>> + _log.info(_loc.get("server-starting", this));
>>>> +
>>>> + _executors = Executors.newCachedThreadPool();
>>>> + while (!Thread.interrupted()) {
>>>> + try {
>>>> + Socket socket = _listenSocket.accept();
>>>> + if (_log.isTraceEnabled())
>>>> + _log.trace(_loc.get("server-request", socket));
>>>> + RequestHandler request = new RequestHandler(socket,
>>> this);
>>>> + _executors.submit(request);
>>>> + } catch (IOException e) {
>>>> + e.printStackTrace();
>>>> + }
>>>> + }
>>>> + }
>>>> +
>>>> + private boolean createServerSocket() {
>>>> + int p = _port;
>>>> + int p2 = p + _range;
>>>> + Exception error = null;
>>>> + for (; _listenSocket == null && p < p2; ) {
>>>> + try {
>>>> + _listenSocket = new ServerSocket(p);
>>>> + } catch (IOException ex) {
>>>> + p++;
>>>> + error = ex;
>>>> + }
>>>> + }
>>>> + if (_listenSocket != null) {
>>>> + if (p != _port) {
>>>> + _port = p;
>>>> + _log.warn(_loc.get("server-reconfigured", _port));
>>>> + }
>>>> + } else {
>>>> + if (error != null) {
>>>> + _log.warn(_loc.get("server-failed", this, _port,
>>> error));
>>>> + }
>>>> + }
>>>> + return _listenSocket != null;
>>>> + }
>>>> +
>>>> + // Configurable contract
>>>> + public void setConfiguration(Configuration conf) {
>>>> + _log = conf.getLog("Remote");
>>>> + }
>>>> +
>>>> + public void startConfiguration() {
>>>> + }
>>>> +
>>>> + public void endConfiguration() {
>>>> + }
>>>> +
>>>> +
>>>> + // Server side utilities
>>>> +
>>>> + /**
>>>> + * Resolves the given alias to a persistent class meta data.
>>>> + *
>>>> + * @exception if no meta data available for the given alias
>>>> + */
>>>> + public ClassMetaData resolve(String alias) {
>>>> + MetaDataRepository repos =
>>> _ctx.getConfiguration().getMetaDataRepositoryInstance();
>>>> + return repos.getMetaData(alias,
>>> Thread.currentThread().getContextClassLoader(), true);
>>>> + }
>>>> +
>>>> + public String toString() {
>>>> + if (_listenSocket == null) return "JEST Server [not strated]";
>>>> + return "JEST Server " +
>>> _listenSocket.getInetAddress()+":"+_listenSocket.getLocalPort();
>>>> + }
>>>> +
>>>> +}
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:eol-style = native
>>>>
>>>> Propchange:
>>>
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
>>>>
>>>
>> ------------------------------------------------------------------------------
>>>> svn:mime-type = text/plain
>>>>
>>>>
>>>>
>>>
>>
>
Re: svn commit: r1028093 [1/2] - JEST...
Posted by Jeremy Bauer <te...@gmail.com>.
Ditto on the concerns. Maybe this would be a good opportunity to discuss a
2.1 release date? That may help guide whether we decide to include large
new features in 2.1 or hold off until 2.x.x and give them more time to brew.
-Jeremy
On Wed, Oct 27, 2010 at 5:11 PM, Kevin Sutter <kw...@gmail.com> wrote:
> I have to admit that I was having the same questions/concerns that Donald
> just raised. I thought we were still in the discussion and experimentation
> phase of this REST work. Is it really ready for prime time?
>
> Kevin
>
> On Wed, Oct 27, 2010 at 4:58 PM, Donald Woods <dw...@apache.org> wrote:
>
> > Is this really ready to drop into trunk?
> > And do we really want it in the base openjpa.jar?
> >
> >
> > -Donald
> >
> >
> > On 10/27/10 4:42 PM, ppoddar@apache.org wrote:
> > > Author: ppoddar
> > > Date: Wed Oct 27 20:42:44 2010
> > > New Revision: 1028093
> > >
> > > URL: http://svn.apache.org/viewvc?rev=1028093&view=rev
> > > Log:
> > > OPENJPA-1851: First version of JEST (REST on OpenJPA)
> > >
> > > Added:
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ServerContext.java
> > (with props)
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/XMLEncoder.java
> > (with props)
> > > Modified:
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > >
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > >
> > > Modified:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > (original)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -45,10 +45,10 @@ import org.apache.openjpa.lib.util.Close
> > > import org.apache.openjpa.lib.util.Localizer;
> > > import org.apache.openjpa.persistence.criteria.CriteriaBuilderImpl;
> > > import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
> > > +import org.apache.openjpa.persistence.jest.Server;
> > > import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > > import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
> > > import org.apache.openjpa.persistence.query.QueryBuilderImpl;
> > > -import org.apache.openjpa.util.UserException;
> > >
> > > /**
> > > * Implementation of {@link EntityManagerFactory} that acts as a
> > > @@ -70,7 +70,8 @@ public class EntityManagerFactoryImpl
> > > private transient StoreCache _cache = null;
> > > private transient QueryResultCache _queryCache = null;
> > > private transient MetamodelImpl _metaModel;
> > > -
> > > + private transient Server _remoteAccess = null;
> > > +
> > > /**
> > > * Default constructor provided for auto-instantiation.
> > > */
> > > @@ -93,10 +94,11 @@ public class EntityManagerFactoryImpl
> > >
> > > /**
> > > * Delegate must be provided before use.
> > > + * Configures for Remote Access, if appropriate.
> > > */
> > > public void setBrokerFactory(BrokerFactory factory) {
> > > - _factory = new DelegatingBrokerFactory(factory,
> > > - PersistenceExceptions.TRANSLATOR);
> > > + _factory = new DelegatingBrokerFactory(factory,
> > PersistenceExceptions.TRANSLATOR);
> > > + configureRemoteAccess(getConfiguration());
> > > }
> > >
> > > public OpenJPAConfiguration getConfiguration() {
> > > @@ -271,6 +273,10 @@ public class EntityManagerFactoryImpl
> > > if (log.isTraceEnabled()) {
> > > log.trace(this + ".close() invoked.");
> > > }
> > > + if (_remoteAccess != null) {
> > > + _remoteAccess.stop();
> > > + _remoteAccess = null;
> > > + }
> > > _factory.close();
> > > }
> > >
> > > @@ -396,4 +402,40 @@ public class EntityManagerFactoryImpl
> > > }
> > > }
> > > }
> > > +
> > > + /**
> > > + * Configures this unit for remote access.
> > > + */
> > > + protected void configureRemoteAccess(OpenJPAConfiguration conf) {
> > > + Value value = conf.getValue("RemoteAccess");
> > > + if (value == null) {
> > > + return;
> > > + }
> > > + String props = value.getString();
> > > + if (props == null)
> > > + return;
> > > + try {
> > > + _remoteAccess = new Server();
> > > + _remoteAccess.setContext(this);
> > > + Configurations.configureInstance(_remoteAccess, conf,
> > props);
> > > + conf.removeValue(value);
> > > + if (!_remoteAccess.start()) {
> > > + _remoteAccess = null;
> > > + }
> > > + } catch (Exception ex) {
> > > + Log log =
> > _factory.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
> > > + if (log != null) {
> > > + log.error(_loc.get("remote-start-error"), ex);
> > > + }
> > > + }
> > > + }
> > > +
> > > + /**
> > > + * Affirms if this unit is accessible remotely.
> > > + */
> > > + public boolean allowsRemoteAccess() {
> > > + return _remoteAccess != null;
> > > + }
> > > +
> > > +
> > > }
> > >
> > > Modified:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > (original)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -188,6 +188,7 @@ public class PersistenceProductDerivatio
> > > conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(),
> > PersistenceMetaDataFactory.class.getName());
> > >
> > > conf.addValue(new EntityManagerFactoryValue());
> > > + conf.addString("RemoteAccess");
> > >
> > > conf.readLockLevel.setAlias("optimistic",
> > String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
> > > conf.readLockLevel.setAlias("optimistic-force-increment",
> String
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,77 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.OutputStream;
> > > +import java.io.PrintStream;
> > > +
> > > +/**
> > > + * Abstract implementation of a stream-based response.
> > > + * Every response is result of a {@linkplain Request} and operates
> > within a {@linkplain ServerContext}.
> > > + * This fact is enforced by the constructor argument of an abstract
> > response.
> > > + * <p>
> > > + * Besides, this implementation provides common utility to write HTTP
> > response or extract useful information
> > > + * from the request.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public abstract class AbstractResponse extends PrintStream implements
> > Response {
> > > + protected final Request _request;
> > > + protected final ServerContext _ctx;
> > > +
> > > + /**
> > > + * Construct a response for the given request and server context.
> > > + *
> > > + * @param request the request for this response. Can be null if
> the
> > response is for server error.
> > > + * @param ctx the processing context
> > > + * @param out the output stream where the response is targeted.
> > > + */
> > > + protected AbstractResponse(Request request, ServerContext ctx,
> > OutputStream out) {
> > > + super(out);
> > > + _request = request;
> > > + _ctx = ctx;
> > > + }
> > > +
> > > + /**
> > > + * Write a HTTP header to the stream.
> > > + * <br>
> > > + * The format of HTTP header is <code>key: [value]* NEWLINE</code>
> > > + *
> > > + * @param key the key of the header
> > > + * @param values one of more value of the header fields.
> > > + */
> > > + protected void printHeader(String key, String...values) {
> > > + if (key == null)
> > > + return;
> > > + print(key);
> > > + print(" :");
> > > + if (values == null || values.length == 0)
> > > + return;
> > > + int n = values.length-1;
> > > + for (int i = 0; i < n-1; i++) {
> > > + print(values[i]);
> > > + print(";");
> > > + }
> > > + println(values[n]);
> > > + }
> > > +
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,68 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.OutputStream;
> > > +import java.net.HttpURLConnection;
> > > +
> > > +/**
> > > + * A HTTP response for something gone wrong.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class ErrorResponse extends AbstractResponse {
> > > + private final Exception _error;
> > > + private final int _code;
> > > + /**
> > > + * Construct a response to describe a error.
> > > + *
> > > + * @param request a request that produced this response. VCan be
> > null to denote that the request can not
> > > + * be created.
> > > + * @param ctx the processing context
> > > + * @param ex the error
> > > + * @param code HTTP error code
> > > + * @param out the stream where the response is written
> > > + *
> > > + */
> > > + public ErrorResponse(Request request, ServerContext ctx, Exception
> > ex, int code, OutputStream out) {
> > > + super(request, ctx, out);
> > > + _error = ex;
> > > + _code = code;
> > > + }
> > > +
> > > + /**
> > > + * Writes the response.
> > > + * The response is always a HTTP response with the error stack
> > trace.
> > > + */
> > > + public void writeOut() throws Exception {
> > > + println("HTTP/1.1"); print(" " + _code); println("Error");
> > > + printHeader("Connection", "close");
> > > + printHeader("Content-Type", "text/html", "charset=UTF-8");
> > > + println();
> > > + println("<html><body><pre>");
> > > + _error.printStackTrace(this);
> > > + println("Response from JEST");
> > > +
> > > + println("</pre></body></html>");
> > > + close();
> > > + }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,147 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.InputStream;
> > > +import java.io.OutputStream;
> > > +import java.net.HttpURLConnection;
> > > +import java.util.Iterator;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +
> > > +import javax.persistence.EntityManager;
> > > +import javax.persistence.EntityNotFoundException;
> > > +import javax.persistence.Query;
> > > +
> > > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > > +import org.apache.openjpa.kernel.StoreContext;
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > > +import org.apache.openjpa.util.ApplicationIds;
> > > +import org.apache.openjpa.util.ObjectNotFoundException;
> > > +
> > > +/**
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class GETRequest extends JESTRequest {
> > > + public Response process(ServerContext server, OutputStream out)
> > throws Exception {
> > > + String action = getAction();
> > > + try {
> > > + if ("find".equals(action)) {
> > > + return find(server, out);
> > > + } else {
> > > + return resource(server, out);
> > > + }
> > > + } catch (Exception e) {
> > > + return new ErrorResponse(this, server, new
> > RuntimeException("bad action " + action),
> > > + HttpURLConnection.HTTP_BAD_REQUEST, out);
> > > + }
> > > + }
> > > +
> > > + Response find(ServerContext server, OutputStream out) throws
> > Exception {
> > > + EntityManager em =
> > server.getPersistenceUnit().createEntityManager();
> > > + Map<String, String> qualifiers = getQualifiers();
> > > + Map<String, String> parameters = getParameters();
> > > + if (parameters.size() < 2)
> > > + throw new IllegalArgumentException("find must have at
> least
> > two parameters");
> > > + Object[] pks = new Object[parameters.size()-1];
> > > + Iterator<Map.Entry<String,String>> params =
> > parameters.entrySet().iterator();
> > > + String alias = null;
> > > + for (int i = 0; i < parameters.size(); i++) {
> > > + if (i == 0) {
> > > + alias = params.next().getKey();
> > > + } else {
> > > + pks[i-1] = params.next().getKey();
> > > + }
> > > + }
> > > + ClassMetaData meta = server.resolve(alias);
> > > + Object oid = ApplicationIds.fromPKValues(pks, meta);
> > > + Object pc = em.find(meta.getDescribedType(), oid);
> > > + if (pc != null) {
> > > + OpenJPAStateManager sm =
> > ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(pc);
> > > + return new JESTResponse(this, server, sm, out);
> > > + } else {
> > > + return new ErrorResponse(this, server, new
> > EntityNotFoundException("not found!"),
> > > + HttpURLConnection.HTTP_NOT_FOUND, out);
> > > + }
> > > + }
> > > +
> > > + Response query(ServerContext server, OutputStream out) throws
> > Exception {
> > > + EntityManager em =
> > server.getPersistenceUnit().createEntityManager();
> > > + Map<String, String> qualifiers = getQualifiers();
> > > + boolean named = isBooleanQualifier("named");
> > > + boolean single = isBooleanQualifier("single");
> > > + Map<String, String> parameters = getParameters();
> > > + if (parameters.size() < 1)
> > > + throw new IllegalArgumentException("find must have at
> least
> > one parameter");
> > > + Iterator<Map.Entry<String,String>> params =
> > parameters.entrySet().iterator();
> > > + Query query = null;
> > > + int i = 0;
> > > + for (Map.Entry<String, String> param : parameters.entrySet())
> {
> > > + if (i == 0) {
> > > + query = named ? em.createQuery(param.getKey()) :
> > em.createNamedQuery(param.getKey());
> > > + } else {
> > > + query.setParameter(param.getKey(), param.getValue());
> > > + }
> > > + }
> > > + if (single) {
> > > + Object result = query.getSingleResult();
> > > + OpenJPAStateManager sm =
> > ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(result);
> > > + return new JESTResponse(this, server, sm, out);
> > > + } else {
> > > + List<Object> result = query.getResultList();
> > > + return new ErrorResponse(this, server, new
> > EntityNotFoundException("not found!"), 404, out);
> > > + }
> > > + }
> > > +
> > > + Response resource(ServerContext server, OutputStream out) throws
> > Exception {
> > > + String resource = getAction();
> > > + if (resource.length() == 0)
> > > + resource = "index.html";
> > > + String mimeType = getMimeType(resource);
> > > +// InputStream in =
> >
> Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
> > > + InputStream in = getClass().getResourceAsStream(resource);
> > > + if (in == null) {
> > > + return new ErrorResponse(this, server, new
> > ObjectNotFoundException(resource), 404, out);
> > > + }
> > > + if (server.getLog().isTraceEnabled())
> > > + server.getLog().trace("Found resource " + resource);
> > > + return mimeType.startsWith("image")
> > > + ? new ImageResponse(this, server, in, mimeType, out)
> > > + : new ResourceResponse(this, server, in, mimeType, out);
> > > + }
> > > +
> > > + boolean isBooleanQualifier(String key) {
> > > + String q = getQualifier(key);
> > > + return hasQualifier(key) && (q == null || "true".equals(q));
> > > + }
> > > +
> > > + String getMimeType(String resource) {
> > > + int index = resource.lastIndexOf('.');
> > > + String ext = (index != -1) ? resource.substring(index+1) : "";
> > > + if (ext.equalsIgnoreCase("html") ||
> > ext.equalsIgnoreCase(".html")) return "text/html";
> > > + if (ext.equalsIgnoreCase(".png") ||
> ext.equalsIgnoreCase(".ico")
> > || ext.equalsIgnoreCase("jpeg"))
> > > + return "image/"+ext;
> > > + return "text/html";
> > > + }
> > > +
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,61 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.InputStream;
> > > +import java.io.OutputStream;
> > > +import java.io.RandomAccessFile;
> > > +
> > > +import javax.imageio.ImageIO;
> > > +
> > > +/**
> > > + * Sends an image as response.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class ImageResponse extends AbstractResponse {
> > > + private final InputStream _in;
> > > + private final String _mimeType;
> > > +
> > > + public ImageResponse(Request request, ServerContext ctx,
> InputStream
> > resource, String mimeType,
> > > + OutputStream out) throws Exception {
> > > + super(request, ctx, out);
> > > + _in = resource;
> > > + _mimeType = mimeType;
> > > + }
> > > +
> > > + public void writeOut() throws Exception {
> > > + print(_request.getProtocol()); println("200 OK");
> > > + printHeader("Connection", "close");
> > > + printHeader("Content-Type", _mimeType);
> > > + println();
> > > + byte[] b = new byte[1024];
> > > + int i = 0;
> > > + for (int l = 0; (l = _in.read(b)) != -1;) {
> > > + write(b, 0, l);
> > > + i += l;
> > > + }
> > > + printHeader("Content-Length", ""+i);
> > > + _in.close();
> > > + close();
> > > + }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,386 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.IOException;
> > > +import java.util.Arrays;
> > > +import java.util.Collections;
> > > +import java.util.HashMap;
> > > +import java.util.LinkedHashMap;
> > > +import java.util.LinkedList;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +import java.util.NoSuchElementException;
> > > +
> > > +/**
> > > + * A request carries requisite data for a JPA operation to be
> performed.
> > > + * The request is populated by parsing an input data stream.
> > > + *
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public abstract class JESTRequest implements Request {
> > > + private String _method;
> > > + private String _protocol;
> > > + private String _action;
> > > + private String _body;
> > > + private LinkedHashMap<String, String> _qualifiers = new
> > LinkedHashMap<String, String>();
> > > + private LinkedHashMap<String, String> _params = new
> > LinkedHashMap<String, String>();
> > > + private Map<String, List<String>> _headers = new HashMap<String,
> > List<String>>();
> > > + private ParseState _state;
> > > + private StringBuffer buf = new StringBuffer();
> > > + private LinkedList<Token> _stack = new LinkedList<Token>();
> > > +
> > > + public static final List<String> METHODS = Arrays.asList(new
> > String[]{"GET","POST","PUT","DELETE"});
> > > +
> > > + /**
> > > + * Parse States.
> > > + */
> > > + static enum ParseState {
> > > + INIT, ACTION, QUALIFIER_KEY, QUALIFIER_VALUE, PARAM_KEY,
> > PARAM_VALUE, END
> > > + };
> > > +
> > > +
> > > + public String getMethod() {
> > > + return _method;
> > > + }
> > > +
> > > + void setMethod(String method) {
> > > + if (_method == null) {
> > > + if (method != null &&
> > METHODS.contains(method.toUpperCase())) {
> > > + _method = method.toUpperCase();
> > > + } else {
> > > + throw new IllegalArgumentException("Unsupported method
> "
> > + method);
> > > + }
> > > + } else if (!_method.equalsIgnoreCase(method)) {
> > > + throw new IllegalStateException("Method can not be changed
> > to [" + method + "]. " +
> > > + "Current method [" + _method + "]");
> > > + }
> > > + }
> > > +
> > > + public String getProtocol() {
> > > + return _protocol == null ? "HTTP/1.1" : _protocol;
> > > + }
> > > +
> > > + void setProtocol(String protocol) {
> > > + if (_protocol == null) {
> > > + if (protocol != null &&
> > protocol.toUpperCase().startsWith("HTTP")) {
> > > + _protocol = protocol.toUpperCase();
> > > + } else {
> > > + throw new IllegalArgumentException("Unsupported
> protocol
> > " + protocol);
> > > + }
> > > + } else if (!_protocol.equalsIgnoreCase(protocol)) {
> > > + throw new IllegalStateException("Protocol can not be
> changed
> > to [" + protocol + "]. " +
> > > + "Current protocol [" + _protocol + "]");
> > > + }
> > > + }
> > > +
> > > + /**
> > > + * Sets an action. Once set, an action can not be modified.
> > > + *
> > > + * @param action
> > > + */
> > > + private void setAction(String action) {
> > > + if (_action == null) {
> > > + _action = action;
> > > + } else if (!_action.equals(action)) {
> > > + throw new IllegalStateException("Action can not be [" +
> > action + "]. Already set to [" + _action + "]");
> > > + }
> > > + }
> > > +
> > > + public String getAction() {
> > > + return _action == null ? "" : _action;
> > > + }
> > > +
> > > + public String getBody() {
> > > + return _body;
> > > + }
> > > +
> > > + private void setQualifier(String key, String value) {
> > > + _qualifiers.put(key, value);
> > > + }
> > > +
> > > + public String getQualifier(String key) {
> > > + return _qualifiers.get(key);
> > > + }
> > > +
> > > + public Map<String, String> getQualifiers() {
> > > + return Collections.unmodifiableMap(_qualifiers);
> > > + }
> > > +
> > > + public boolean hasQualifier(String key) {
> > > + return _qualifiers.containsKey(key);
> > > + }
> > > +
> > > + private void setParameter(String key, String value) {
> > > + _params.put(key, value);
> > > + }
> > > +
> > > + public String getParameter(String key) {
> > > + return _params.get(key);
> > > + }
> > > +
> > > + public boolean hasParameter(String key) {
> > > + return _params.containsKey(key);
> > > + }
> > > +
> > > + public Map<String, String> getParameters() {
> > > + return Collections.unmodifiableMap(_params);
> > > + }
> > > +
> > > + public Map.Entry<String, String> getParameter(int n) {
> > > + if (n >= _params.size())
> > > + throw new NoSuchElementException("Index " + n + " size " +
> > _params.size());
> > > + int i = 0;
> > > + for (Map.Entry<String, String> entry : _params.entrySet()) {
> > > + if (i == n) {
> > > + return entry;
> > > + }
> > > + i++;
> > > + }
> > > + return null;
> > > + }
> > > +
> > > + public Map<String, List<String>> getHeaders() {
> > > + return Collections.unmodifiableMap(_headers);
> > > + }
> > > +
> > > + public List<String> getHeader(String key) {
> > > + return _headers.get(key);
> > > + }
> > > +
> > > +
> > > + public void read(List<String> lines) throws IOException {
> > > + parse(lines.get(0));
> > > + int i = 1;
> > > + for (; i < lines.size(); i++) {
> > > + String line = lines.get(i);
> > > + if (line.length() == 0) {
> > > + break;
> > > + } else {
> > > + parseHeader(line);
> > > + }
> > > + }
> > > + parseBody(lines.subList(i, lines.size()));
> > > + }
> > > +
> > > + protected void parseHeader(String line) throws IOException {
> > > + String key = null;
> > > + StringBuilder token = new StringBuilder();
> > > + int N = line.length();
> > > + for (int i = 0; i < N; i++) {
> > > + char c = line.charAt(i);
> > > + if (c == ':' && key == null) {
> > > + key = token.toString().trim();
> > > + token.delete(0, token.length());
> > > + } else {
> > > + token.append(c);
> > > + }
> > > + }
> > > + if (key != null) {
> > > + _headers.put(key,
> > Collections.singletonList(token.toString().trim()));
> > > + }
> > > + }
> > > +
> > > + protected void parseBody(List<String> lines) {
> > > + if (lines == null || lines.isEmpty())
> > > + return;
> > > + for (String line : lines) {
> > > + if (_body == null) {
> > > + _body = line;
> > > + } else {
> > > + _body = _body + line;
> > > + }
> > > + }
> > > + }
> > > +
> > > + /**
> > > + * Parses JEST stream and populates a request.
> > > + *
> > > + */
> > > + protected void parse(String s) {
> > > + char[] chars = s.toCharArray();
> > > + _state = ParseState.INIT;
> > > + _stack.clear();
> > > +
> > > + for (int i = 0; i < chars.length; i++) {
> > > + char ch = chars[i];
> > > + switch (_state) {
> > > + case INIT:
> > > + if (ch == '/') {
> > > + transit(ParseState.ACTION);
> > > + } else if (!Character.isWhitespace(ch)) {
> > > + parseError(ch, i, s, true, ' ');
> > > + }
> > > + break;
> > > +
> > > + case ACTION:
> > > + if (ch == '/') {
> > > + transit(ParseState.QUALIFIER_KEY);
> > > + } else if (ch == '?') {
> > > + transit(ParseState.PARAM_KEY);
> > > + } else {
> > > + buf.append(ch);
> > > + }
> > > + break;
> > > +
> > > + case QUALIFIER_KEY:
> > > + if (Character.isJavaIdentifierPart(ch)) {
> > > + buf.append(ch);
> > > + } else if (ch == '=') {
> > > + transit(ParseState.QUALIFIER_VALUE);
> > > + } else if (ch == '/') {
> > > + transit(ParseState.QUALIFIER_KEY);
> > > + } else if (ch == '?') {
> > > + transit(ParseState.PARAM_KEY);
> > > + } else {
> > > + parseError(ch, i, s, true, '/', '?', '=');
> > > + }
> > > + break;
> > > +
> > > + case QUALIFIER_VALUE:
> > > + if (Character.isJavaIdentifierPart(ch)) {
> > > + buf.append(ch);
> > > + } else if (ch == '/') {
> > > + transit(ParseState.QUALIFIER_KEY);
> > > + } else if (ch == '?') {
> > > + transit(ParseState.PARAM_KEY);
> > > + } else {
> > > + parseError(ch, i, s, true, '/', '?');
> > > + }
> > > + break;
> > > +
> > > + case PARAM_KEY:
> > > + if (Character.isJavaIdentifierPart(ch)) {
> > > + buf.append(ch);
> > > + } else if (ch == '=') {
> > > + if (isQueryKey())
> > > + buf.append(ch);
> > > + else
> > > + transit(ParseState.PARAM_VALUE);
> > > + } else if (ch == ';') {
> > > + transit(ParseState.PARAM_KEY);
> > > + } else if (isQueryKey() && isQueryChar(ch)) {
> > > + buf.append(ch);
> > > + } else {
> > > + parseError(ch, i, s, true, ';', '=');
> > > + }
> > > + break;
> > > +
> > > + case PARAM_VALUE:
> > > + if (Character.isJavaIdentifierPart(ch)) {
> > > + buf.append(ch);
> > > + } else if (ch == ';') {
> > > + transit(ParseState.PARAM_KEY);
> > > + } else {
> > > + parseError(ch, i, s, true, ';');
> > > + }
> > > + break;
> > > + default:
> > > + throw new RuntimeException("ParseError: '" +
> ch
> > + "' at " + i + " in [" + s + "]. "
> > > + + "Unknown state " + _state);
> > > + }
> > > + }
> > > + if (buf.length() > 0) {
> > > + transit(ParseState.END);
> > > + }
> > > + }
> > > +
> > > + /**
> > > + * Affirms if parsing a query string.
> > > + */
> > > + private boolean isQueryKey() {
> > > + return "query".equals(_action) && _stack.size() == 1;
> > > + }
> > > +
> > > + /**
> > > + * Affirms if the given character is valid in a query string
> > > + */
> > > + private boolean isQueryChar(char c) {
> > > + return c == ' ' || c == '.' || c == ':' || c == '?' || c
> ==
> > '\'';
> > > + }
> > > +
> > > + /**
> > > + * Transitions to a new parse state.
> > > + *
> > > + * @param to target parse state
> > > + */
> > > + void transit(ParseState to) {
> > > + String token = buf.toString();
> > > + switch (_state) {
> > > + case ACTION:
> > > + setAction(token);
> > > + break;
> > > + case QUALIFIER_KEY:
> > > + setQualifier(token, null);
> > > + break;
> > > + case QUALIFIER_VALUE:
> > > + setQualifier(_stack.peekLast().getValue(), token);
> > > + break;
> > > + case PARAM_KEY:
> > > + setParameter(token, null);
> > > + break;
> > > + case PARAM_VALUE:
> > > + setParameter(_stack.peekLast().getValue(), token);
> > > + break;
> > > +
> > > + }
> > > + if (_state != ParseState.INIT && to != ParseState.END) {
> > > + _stack.add(new Token(_state, token));
> > > + }
> > > + buf.delete(0, buf.length());
> > > + _state = to;
> > > + }
> > > +
> > > + protected void parseError(char ch, int pos, String line,
> boolean
> > java, char... expected) {
> > > + throw new RuntimeException("ParseError: Encountered '" +
> ch
> > + "' at " + pos + " in [" + line + "] while "
> > > + + "parsing " + _state + ". Expected " +
> > Arrays.toString(expected) + (java ? " or Java identifer" : ""));
> > > +
> > > + }
> > > +
> > > + /**
> > > + * Token in a JEST stream.
> > > + *
> > > + */
> > > + static class Token {
> > > + final ParseState _type;
> > > + final String _value;
> > > +
> > > + public Token(ParseState type, String value) {
> > > + _type = type;
> > > + _value = value;
> > > + }
> > > +
> > > + public ParseState getType() {
> > > + return _type;
> > > + }
> > > +
> > > + public String getValue() {
> > > + return _value;
> > > + }
> > > +
> > > + public String toString() {
> > > + return _value + "[" + _type + "]";
> > > + }
> > > + }
> > > +
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,71 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.IOException;
> > > +import java.io.OutputStream;
> > > +import java.io.PrintStream;
> > > +import java.io.PrintWriter;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +import java.util.Set;
> > > +
> > > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > > +import org.w3c.dom.Document;
> > > +import org.w3c.dom.Element;
> > > +
> > > +/**
> > > + * Response of a JEST Request.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class JESTResponse extends AbstractResponse {
> > > + private final OpenJPAStateManager _sm;
> > > +
> > > + public JESTResponse(Request request, ServerContext ctx,
> > OpenJPAStateManager sm, OutputStream out) throws Exception {
> > > + super(request, ctx, out);
> > > + _sm = sm;
> > > + }
> > > +
> > > + public String getContentType() {
> > > + return "text/html";
> > > + }
> > > +
> > > + public void writeOut() throws Exception {
> > > + print(_request.getProtocol()); println("200 OK");
> > > + printHeader("Connection", "close");
> > > + printHeader("Content-Type", "text/html", "charset=UTF-8");
> > > + println();
> > > + println("<html><body><pre>");
> > > + XMLEncoder encoder = new
> > XMLEncoder(_ctx.getPersistenceUnit().getMetamodel());
> > > + Document doc = encoder.encode(_sm);
> > > + encoder.writeDoc(doc, this);
> > > + println("Response from JEST");
> > > +
> > > + println("</pre></body></html>");
> > > + close();
> > > + }
> > > +
> > > +}
> > > +
> > > +
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,316 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.BufferedReader;
> > > +import java.io.CharArrayWriter;
> > > +import java.io.IOException;
> > > +import java.io.InputStream;
> > > +import java.io.InputStreamReader;
> > > +import java.io.Reader;
> > > +import java.util.Arrays;
> > > +import java.util.BitSet;
> > > +import java.util.Collection;
> > > +import java.util.HashSet;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +import java.util.Set;
> > > +
> > > +import javax.persistence.metamodel.Attribute;
> > > +
> > > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > > +import org.apache.openjpa.kernel.StoreContext;
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.meta.FieldMetaData;
> > > +import org.apache.openjpa.meta.JavaTypes;
> > > +import org.apache.openjpa.meta.ValueMetaData;
> > > +import org.apache.openjpa.persistence.meta.Members;
> > > +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > > +import org.w3c.dom.CDATASection;
> > > +import org.w3c.dom.Document;
> > > +import org.w3c.dom.Element;
> > > +
> > > +/**
> > > + * Marshals a root instance and its persistent closure as JSON object.
> > > + * The closure is resolved against the persistence context that
> contains
> > the root instance.
> > > + * The JSON format introduces a $id and $ref to address reference that
> > pure JSON does not.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class JSONEncoder {
> > > + /**
> > > + * The element/attribute tags declared in
> > <code>jest-instance.xsd</code> XML schema.
> > > + */
> > > + public static final String ELEMENT_NULL_REF = "null";
> > > + public static final String ELEMENT_INSTANCE = "instance";
> > > + public static final String ELEMENT_REF = "ref";
> > > +
> > > +
> > > + private MetamodelHelper _model;
> > > +
> > > + public JSONEncoder(MetamodelImpl model) {
> > > + _model = new MetamodelHelper(model);
> > > + }
> > > +
> > > + /**
> > > + * Encodes the given managed instance into a new XML element as a
> > child of the given parent node.
> > > + *
> > > + * @param sm a managed instance, can be null.
> > > + * @param parent the parent node to which the new node be
> attached.
> > > + */
> > > + public StringBuilder encode(final OpenJPAStateManager sm) {
> > > + return encode(sm, new HashSet<OpenJPAStateManager>(), 0,
> false);
> > > + }
> > > + StringBuilder indent(StringBuilder buf, int indent) {
> > > + if (indent <= 0)
> > > + return buf;
> > > + char[] spaces = new char[indent*4];
> > > + Arrays.fill(spaces, ' ');
> > > + buf.insert(0, spaces);
> > > + return buf;
> > > + }
> > > + StringBuilder end(StringBuilder buf, char ch, int indent) {
> > > + char[] spaces = new char[indent*4];
> > > + Arrays.fill(spaces, ' ');
> > > + return buf.append("\r\n").append(spaces).append(ch);
> > > + }
> > > +
> > > + /**
> > > + * Encodes the closure of a persistent instance into a XML
> element.
> > > + *
> > > + * @param sm the managed instance to be encoded. Can be null.
> > > + * @param parent the parent XML element to which the new XML
> element
> > be added. Must not be null. Must be
> > > + * owned by a document.
> > > + * @param visited the persistent instances that had been encoded
> > already. Must not be null or immutable.
> > > + *
> > > + * @return the new element. The element has been appended as a
> child
> > to the given parent in this method.
> > > + */
> > > + private StringBuilder encode(final OpenJPAStateManager sm, final
> > Set<OpenJPAStateManager> visited,
> > > + int indent, boolean indentPara) {
> > > + if (visited == null) {
> > > + throw new IllegalArgumentException("null closure for
> > encoder");
> > > + }
> > > + StringBuilder root = indent(new StringBuilder("{"),
> indentPara
> > ? indent : 0);
> > > + if (sm == null) {
> > > + return root.append("null}");
> > > + }
> > > + boolean ref = !visited.add(sm);
> > > + if (ref) {
> > > + return indent(root.append(quoted("$ref")).append(":
> > ").append(ior(sm)).append('}'),
> > > + indentPara ? indent : 0);
> > > + } else {
> > > + indent(root.append(quoted("$id")).append(":
> > ").append(ior(sm)), indentPara ? indent : 0);
> > > + }
> > > +
> > > + StringBuilder child = new StringBuilder();
> > > + BitSet loaded = sm.getLoaded();
> > > + StoreContext ctx = (StoreContext)sm.getGenericContext();
> > > + List<Attribute<?, ?>> attrs =
> > _model.getAttributesInOrder(sm.getMetaData());
> > > + for (int i = 0; i < attrs.size(); child = new StringBuilder(),
> > i++) {
> > > + FieldMetaData fmd = ((Members.Member<?, ?>)
> > attrs.get(i)).fmd;
> > > + if (!loaded.get(fmd.getIndex()))
> > > + continue;
> > > + Object value = sm.fetch(fmd.getIndex());
> > > + child.append(quoted(fmd.getName())).append(": ");
> > > + switch (fmd.getDeclaredTypeCode()) {
> > > + case JavaTypes.BOOLEAN:
> > > + case JavaTypes.BYTE:
> > > + case JavaTypes.CHAR:
> > > + case JavaTypes.DOUBLE:
> > > + case JavaTypes.FLOAT:
> > > + case JavaTypes.INT:
> > > + case JavaTypes.LONG:
> > > + case JavaTypes.SHORT:
> > > +
> > > + case JavaTypes.BOOLEAN_OBJ:
> > > + case JavaTypes.BYTE_OBJ:
> > > + case JavaTypes.CHAR_OBJ:
> > > + case JavaTypes.DOUBLE_OBJ:
> > > + case JavaTypes.FLOAT_OBJ:
> > > + case JavaTypes.INT_OBJ:
> > > + case JavaTypes.LONG_OBJ:
> > > + case JavaTypes.SHORT_OBJ:
> > > +
> > > + case JavaTypes.BIGDECIMAL:
> > > + case JavaTypes.BIGINTEGER:
> > > + case JavaTypes.DATE:
> > > + case JavaTypes.NUMBER:
> > > + case JavaTypes.CALENDAR:
> > > + case JavaTypes.LOCALE:
> > > + case JavaTypes.STRING:
> > > + case JavaTypes.ENUM:
> > > + child.append(quoted(value));
> > > + break;
> > > +
> > > + case JavaTypes.PC:
> > > + if (value == null) {
> > > + child.append("null");
> > > + } else {
> > > +
> child.append(encode(ctx.getStateManager(value),
> > visited, indent+1, false));
> > > + }
> > > + break;
> > > +
> > > + case JavaTypes.ARRAY:
> > > + Object[] values = (Object[])value;
> > > + value = Arrays.asList(values);
> > > + // no break;
> > > + case JavaTypes.COLLECTION:
> > > + if (value == null) {
> > > + child.append("null");
> > > + break;
> > > + }
> > > + child.append("[");
> > > + Collection<?> members = (Collection<?>)value;
> > > + boolean basic = fmd.getElement().getTypeMetaData()
> > == null;
> > > + int k = 0;
> > > + for (Object o : members) {
> > > + child.append("\r\n");
> > > + if (o == null) {
> > > + child.append(indent(new
> > StringBuilder("null"), indent+1));
> > > + } else {
> > > + if (basic) {
> > > + child.append(indent(new
> > StringBuilder(quoted(o)), indent+1));
> > > + } else {
> > > +
> > child.append(encode(ctx.getStateManager(o), visited, indent+1, true));
> > > + }
> > > + }
> > > + }
> > > + end(child, ']', indent+1);
> > > + break;
> > > + case JavaTypes.MAP:
> > > + if (value == null) {
> > > + child.append("null");
> > > + break;
> > > + }
> > > + child.append("[");
> > > + Set<Map.Entry> entries = ((Map)value).entrySet();
> > > + boolean basicKey =
> > fmd.getElement().getTypeMetaData() == null;
> > > + boolean basicValue =
> > fmd.getValue().getTypeMetaData() == null;
> > > + for (Map.Entry<?,?> e : entries) {
> > > + if (e.getKey() == null) {
> > > + child.append("null:");
> > > + } else {
> > > + if (basicKey) {
> > > +
> > child.append(quoted(e.getKey())).append(":");
> > > + } else {
> > > +
> > child.append(encode(ctx.getStateManager(e.getKey()), visited, indent+1,
> > true));
> > > + }
> > > + }
> > > + if (e.getValue() == null) {
> > > + child.append("null");
> > > + } else {
> > > + if (basicValue) {
> > > + child.append(quoted(e.getValue()));
> > > + } else {
> > > +
> > child.append(encode(ctx.getStateManager(e.getValue()), visited,
> indent+1,
> > false));
> > > + }
> > > + }
> > > + }
> > > + break;
> > > +
> > > + case JavaTypes.INPUT_STREAM:
> > > + case JavaTypes.INPUT_READER:
> > > + child = new StringBuilder(fmd.getName());
> > > + if (value == null) {
> > > + child.append("null");
> > > + } else {
> > > + child.append(streamToString(value));
> > > + }
> > > + break;
> > > +
> > > + case JavaTypes.PC_UNTYPED:
> > > + case JavaTypes.OBJECT:
> > > + case JavaTypes.OID:
> > > + System.err.println("Not handled " + fmd.getName()
> +
> > " of type " + fmd.getDeclaredType());
> > > + }
> > > +
> > > + if (child != null) {
> > > + root.append("\r\n");
> > > + root.append(indent(child, indent+1));
> > > + if (loaded.length()-1 != i)
> > > + root.append(",");
> > > + }
> > > + }
> > > + return end(root, '}', indent);
> > > + }
> > > +
> > > +
> > > + String ior(OpenJPAStateManager sm) {
> > > + return quoted(typeOf(sm)+"-"+sm.getObjectId().toString());
> > > + }
> > > +
> > > + String typeOf(OpenJPAStateManager sm) {
> > > + return sm.getMetaData().getDescribedType().getSimpleName();
> > > + }
> > > +
> > > + String typeOf(Class<?> cls) {
> > > + return cls.getSimpleName();
> > > + }
> > > +
> > > + String typeOf(ClassMetaData meta) {
> > > + return meta.getDescribedType().getSimpleName();
> > > + }
> > > +
> > > + String typeOf(ValueMetaData vm) {
> > > + if (vm.getTypeMetaData() == null)
> > > + return typeOf(vm.getType());
> > > + return typeOf(vm.getTypeMetaData());
> > > + }
> > > +
> > > + String typeOf(FieldMetaData fmd) {
> > > + return fmd.getType().getSimpleName();
> > > + }
> > > +
> > > +
> > > + /**
> > > + * Convert the given stream (either an InutStream or a Reader) to
> a
> > String
> > > + * to be included in CDATA section of a XML document.
> > > + *
> > > + * @param value the field value to be converted. Can not be null
> > > + * @return
> > > + */
> > > + String streamToString(Object value) {
> > > + Reader reader = null;
> > > + if (value instanceof InputStream) {
> > > + reader = new BufferedReader(new
> > InputStreamReader((InputStream)value));
> > > + } else if (value instanceof Reader) {
> > > + reader = (Reader)value;
> > > + } else {
> > > + throw new RuntimeException();
> > > + }
> > > + CharArrayWriter writer = new CharArrayWriter();
> > > + try {
> > > + for (int c; (c = reader.read()) != -1;) {
> > > + writer.write(c);
> > > + }
> > > + } catch (IOException ex) {
> > > + throw new RuntimeException(ex);
> > > + }
> > > + return writer.toString();
> > > + }
> > > +
> > > + String quoted(Object o) {
> > > + if (o == null) return "null";
> > > + if (o instanceof Number)
> > > + return o.toString();
> > > + return "\"" + o.toString() + "\"";
> > > + }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,127 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.util.ArrayList;
> > > +import java.util.Collections;
> > > +import java.util.Comparator;
> > > +import java.util.HashMap;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +
> > > +import javax.persistence.metamodel.Attribute;
> > > +import javax.persistence.metamodel.EntityType;
> > > +import javax.persistence.metamodel.ManagedType;
> > > +import javax.persistence.metamodel.Metamodel;
> > > +import javax.persistence.metamodel.SingularAttribute;
> > > +
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.meta.FieldMetaData;
> > > +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > > +
> > > +/**
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class MetamodelHelper {
> > > + private MetamodelImpl _model;
> > > + private Map<ManagedType<?>, List<Attribute<?, ?>>> _attrs = new
> > HashMap<ManagedType<?>, List<Attribute<?,?>>>();
> > > +
> > > + public MetamodelHelper(MetamodelImpl model) {
> > > + _model = model;
> > > + }
> > > +
> > > + public List<Attribute<?,?>> getAttributesInOrder(Class<?> cls) {
> > > + return getAttributesInOrder(_model.managedType(cls));
> > > + }
> > > +
> > > + public List<Attribute<?,?>> getAttributesInOrder(ClassMetaData
> meta)
> > {
> > > + return getAttributesInOrder(meta.getDescribedType());
> > > + }
> > > +
> > > + /**
> > > + * Gets the attributes of the given type in defined order.
> > > + * @param type
> > > + * @return
> > > + */
> > > + public List<Attribute<?,?>> getAttributesInOrder(ManagedType<?>
> > type) {
> > > + List<Attribute<?,?>> attrs = _attrs.get(type);
> > > + if (attrs != null)
> > > + return attrs;
> > > + List<Attribute<?,?>> list = new
> > ArrayList<Attribute<?,?>>(type.getAttributes());
> > > + Collections.sort(list, new AttributeComparator());
> > > + _attrs.put(type, list);
> > > + return list;
> > > + }
> > > +
> > > + public static boolean isId(Attribute<?,?> a) {
> > > + if (a instanceof SingularAttribute)
> > > + return ((SingularAttribute<?,?>)a).isId();
> > > + return false;
> > > + }
> > > +
> > > + public static boolean isVersion(Attribute<?,?> a) {
> > > + if (a instanceof SingularAttribute)
> > > + return ((SingularAttribute<?,?>)a).isVersion();
> > > + return false;
> > > + }
> > > +
> > > + public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
> > > + if (isId(attr))
> > > + return 0;
> > > + if (isVersion(attr))
> > > + return 1;
> > > +
> > > + switch (attr.getPersistentAttributeType()) {
> > > + case BASIC :
> > > + case EMBEDDED:
> > > + return 2;
> > > + case ONE_TO_ONE:
> > > + case MANY_TO_ONE:
> > > + return 3;
> > > + case ONE_TO_MANY:
> > > + case MANY_TO_MANY:
> > > + case ELEMENT_COLLECTION: return 4;
> > > + default: return 5;
> > > + }
> > > + }
> > > +
> > > + /**
> > > + * Compares attribute by their qualification.
> > > + * Identity
> > > + * Version
> > > + * Basic
> > > + * Singular association
> > > + * Plural association
> > > + *
> > > + */
> > > + public static class AttributeComparator implements
> > Comparator<Attribute<?,?>> {
> > > + @Override
> > > + public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
> > > + Integer t1 = getAttributeTypeCode(a1);
> > > + Integer t2 = getAttributeTypeCode(a2);
> > > + if (t1.equals(t2)) {
> > > + return a1.getName().compareTo(a2.getName());
> > > + } else {
> > > + return t1.compareTo(t2);
> > > + }
> > > + }
> > > + }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,138 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.IOException;
> > > +import java.io.OutputStream;
> > > +import java.io.Serializable;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +
> > > +/**
> > > + * A request from a remote client to a server to do something.
> > > + * The request arrives as stream of bytes from a remote location
> > > + * and if the server can interpret the protocol from the stream,
> > > + * then make a concrete request object.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public interface Request extends Serializable {
> > > + /**
> > > + * Get the HTTP verb such as GET, POST
> > > + *
> > > + * @return uppercase string
> > > + */
> > > + String getMethod();
> > > +
> > > + /**
> > > + * Get the first path segment as intended persistence action such
> as
> > <code>find</code> or <code>query</code>
> > > + *
> > > + * @return lowercase action name. Can be empty.
> > > + */
> > > + String getAction();
> > > +
> > > + /**
> > > + * Get the protocol such as HTTP/1.1
> > > + *
> > > + * @return upper-case string
> > > + */
> > > + String getProtocol();
> > > +
> > > + /**
> > > + * Get the body, if any.
> > > + *
> > > + * @return body of the request. null if no body.
> > > + */
> > > + String getBody();
> > > +
> > > + /**
> > > + * Get the headers indexed by the keys.
> > > + * Header values are list of Strings.
> > > + *
> > > + * @return empty map if there is no header
> > > + */
> > > + Map<String, List<String>> getHeaders();
> > > +
> > > + /**
> > > + * Get the header values for the given key.
> > > + * @param key a key
> > > + * @return null if no header value for the given key
> > > + */
> > > + List<String> getHeader(String key);
> > > +
> > > + /**
> > > + * Affirm if the the given qualifier is available in this request.
> > > + *
> > > + * @param key case-sensitive qualifier
> > > + * @return true if the key is present.
> > > + */
> > > + boolean hasQualifier(String key);
> > > +
> > > + /**
> > > + * Gets the value for the given qualifier key.
> > > + *
> > > + * @param key case-sensitive qualifier
> > > + * @return value of the qualifier. null if the key is absent.
> > > + */
> > > + String getQualifier(String key);
> > > +
> > > + /**
> > > + * Get all the qualifiers available in this request.
> > > + *
> > > + * @return key-value pairs of the qualifiers. Empty map if no
> > qualifier is present.
> > > + */
> > > + Map<String,String> getQualifiers();
> > > +
> > > +
> > > + /**
> > > + * Affirm if the the given parameter is available in this request.
> > > + *
> > > + * @param key case-sensitive parameter
> > > + * @return true if the key is present.
> > > + */
> > > + boolean hasParameter(String key);
> > > +
> > > +
> > > + /**
> > > + * Gets the value for the given parameter key.
> > > + *
> > > + * @param key case-sensitive parameter
> > > + * @return value of the parameter. null if the key is absent.
> > > + */
> > > + String getParameter(String key);
> > > +
> > > + /**
> > > + * Get all the parameters available in this request.
> > > + *
> > > + * @return key-value pairs of the parameters. Empty map if no
> > parameter is present.
> > > + */
> > > + Map<String,String> getParameters();
> > > +
> > > + /**
> > > + * Parse the request represented as a list of strings.
> > > + *
> > > + * @param lines each line of the request.
> > > + * @throws IOException
> > > + */
> > > + void read(List<String> lines) throws IOException;
> > > +
> > > + Response process(ServerContext server, OutputStream stream) throws
> > Exception;
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,63 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.util.HashMap;
> > > +import java.util.Map;
> > > +
> > > +/**
> > > + * A factory to create a specific type of request.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class RequestFactory {
> > > + private final String _protocol;
> > > + private static Map<String, RequestFactory> _registered = new
> > HashMap<String, RequestFactory>();
> > > + static {
> > > + _registered.put("HTTP/1.0", new RequestFactory("HTTP/1.0"));
> > > + _registered.put("HTTP/1.1", new RequestFactory("HTTP/1.1"));
> > > + }
> > > +
> > > + public static void register(String protocol, RequestFactory
> factory)
> > {
> > > + _registered.put(protocol, factory);
> > > + }
> > > +
> > > + private RequestFactory(String proto) {
> > > + _protocol = proto;
> > > + }
> > > +
> > > + public static RequestFactory getFactory(String protocol) {
> > > + return _registered.get(protocol);
> > > + }
> > > +
> > > + Request createRequest(String method) {
> > > + JESTRequest request = null;
> > > + if ("GET".equalsIgnoreCase(method)) {
> > > + request = new GETRequest();
> > > + } else {
> > > + throw new UnsupportedOperationException();
> > > + }
> > > + request.setProtocol(_protocol);
> > > + request.setMethod(method.toUpperCase());
> > > + return request;
> > > +
> > > + }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,147 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.BufferedReader;
> > > +import java.io.IOException;
> > > +import java.io.InputStream;
> > > +import java.io.InputStreamReader;
> > > +import java.io.OutputStream;
> > > +import java.net.HttpURLConnection;
> > > +import java.net.Socket;
> > > +import java.net.SocketException;
> > > +import java.util.ArrayList;
> > > +import java.util.Collections;
> > > +import java.util.Iterator;
> > > +import java.util.List;
> > > +import java.util.Map;
> > > +import java.util.Set;
> > > +import java.util.concurrent.Callable;
> > > +
> > > +import javax.persistence.EntityManager;
> > > +import javax.persistence.EntityNotFoundException;
> > > +
> > > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > > +import org.apache.openjpa.kernel.StoreContext;
> > > +import org.apache.openjpa.lib.log.Log;
> > > +import org.apache.openjpa.lib.util.Localizer;
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.meta.FieldMetaData;
> > > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > > +import org.apache.openjpa.persistence.OpenJPAPersistence;
> > > +import org.apache.openjpa.util.ApplicationIds;
> > > +
> > > +/**
> > > + * Handles a request from a remote client.
> > > + * Reads the socket data.
> > > + * Populates the request.
> > > + * Determines the processor based on request method and action.
> > > + * Delegates to the processor.
> > > + * Processor generates the response.
> > > + * Writes the response to the stream.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class RequestHandler implements Callable<Void> {
> > > + private static final int SPACE = ' ';
> > > + private final Socket _socket;
> > > + private final ServerContext _server;
> > > + private final Log _log;
> > > + private static final Localizer _loc =
> > Localizer.forPackage(RequestHandler.class);
> > > +
> > > + public RequestHandler(Socket socket, ServerContext server) {
> > > + _socket = socket;
> > > + _server = server;
> > > + _log = _server.getLog();
> > > + }
> > > +
> > > + public Void call() throws Exception {
> > > + Request request = null;
> > > + Response response = null;
> > > + try {
> > > + request = readRequest(_socket.getInputStream());
> > > + response = request.process(_server,
> > _socket.getOutputStream());
> > > + } catch (Exception e) {
> > > + response = new ErrorResponse(request, _server, e,
> > HttpURLConnection.HTTP_INTERNAL_ERROR,
> > > + _socket.getOutputStream());
> > > + }
> > > + response.writeOut();
> > > + return null;
> > > + }
> > > +
> > > +
> > > +
> > > + /**
> > > + * Reads the given CR-LF delimited stream.
> > > + * The first line is scanned for the method (first space-delimited
> > String) and protocol (the last
> > > + * space-delimited String). Accordingly a request is created and
> > rest of the input stream content
> > > + * is parsed by the request itself.
> > > + *
> > > + * @param input
> > > + * @throws IOException
> > > + */
> > > + public Request readRequest(InputStream input) throws IOException {
> > > + if (_log.isTraceEnabled())
> > > + _log.trace("Reading request from the input stream ");
> > > +
> > > + BufferedReader reader = new BufferedReader(new
> > InputStreamReader(input));
> > > + String status = reader.readLine();
> > > + if (_log.isInfoEnabled())
> > > + _log.info("Status Line [" + status + "]");
> > > + int spaceFirst = status.indexOf(SPACE);
> > > + if (spaceFirst == -1)
> > > + throw new IOException("HTTP Method could not be determined
> > from [" + status + "]");
> > > + int spaceLast = status.lastIndexOf(SPACE);
> > > + if (spaceLast == -1)
> > > + throw new IOException("HTTP Protocol could not be
> determined
> > from [" + status + "]");
> > > + String method = status.substring(0, spaceFirst);
> > > + String protocol = status.substring(spaceLast+1);
> > > + String path = status.substring(spaceFirst+1, spaceLast);
> > > + Request request =
> > RequestFactory.getFactory(protocol).createRequest(method);
> > > + List<String> lines = new ArrayList<String>();
> > > + if (path.equals("/")) {
> > > + lines.add(path);
> > > + } else {
> > > + lines = readlines(reader);
> > > + lines.add(0, path);
> > > + }
> > > + if (lines.isEmpty()) {
> > > + throw new IOException("No CR-LF delimited lines could be
> > read from " + input);
> > > + }
> > > + request.read(lines);
> > > + return request;
> > > + }
> > > +
> > > +
> > > + List<String> readlines(BufferedReader reader) throws IOException {
> > > + List<String> buffers = new ArrayList<String>();
> > > + String line;
> > > + while ((line = reader.readLine()) != null && line.length() >
> 0)
> > {
> > > + buffers.add(line);
> > > + }
> > > + return buffers;
> > > + }
> > > +
> > > +
> > > + public String toString() {
> > > + return _socket.getInetAddress()+":"+_socket.getPort();
> > > + }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,54 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.InputStream;
> > > +import java.io.OutputStream;
> > > +import java.io.RandomAccessFile;
> > > +
> > > +import javax.imageio.ImageIO;
> > > +
> > > +/**
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +@SuppressWarnings("serial")
> > > +public class ResourceResponse extends AbstractResponse {
> > > + private final InputStream _in;
> > > + private final String _mimeType;
> > > + public ResourceResponse(Request request, ServerContext ctx,
> > InputStream resource, String mimeType,
> > > + OutputStream out) throws Exception {
> > > + super(request, ctx, out);
> > > + _in = resource;
> > > + _mimeType = mimeType;
> > > + }
> > > +
> > > + public void writeOut() throws Exception {
> > > + print(_request.getProtocol()); println("200 OK");
> > > + printHeader("Connection", "close");
> > > + printHeader("Content-Type", _mimeType, "charset=UTF-8");
> > > + println();
> > > + for (int c = 0; (c = _in.read()) != -1;) {
> > > + print((char)c);
> > > + }
> > > + _in.close();
> > > + close();
> > > + }
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,31 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.Serializable;
> > > +
> > > +/**
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public interface Response extends Serializable {
> > > +// void setHeader(String key, String value);
> > > + void writeOut() throws Exception;
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > > URL:
> >
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java?rev=1028093&view=auto
> > >
> >
> ==============================================================================
> > > ---
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > (added)
> > > +++
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > Wed Oct 27 20:42:44 2010
> > > @@ -0,0 +1,230 @@
> > > +/*
> > > + * Licensed to the Apache Software Foundation (ASF) under one
> > > + * or more contributor license agreements. See the NOTICE file
> > > + * distributed with this work for additional information
> > > + * regarding copyright ownership. The ASF licenses this file
> > > + * to you under the Apache License, Version 2.0 (the
> > > + * "License"); you may not use this file except in compliance
> > > + * with the License. You may obtain a copy of the License at
> > > + *
> > > + * http://www.apache.org/licenses/LICENSE-2.0
> > > + *
> > > + * Unless required by applicable law or agreed to in writing,
> > > + * software distributed under the License is distributed on an
> > > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > > + * KIND, either express or implied. See the License for the
> > > + * specific language governing permissions and limitations
> > > + * under the License.
> > > + */
> > > +
> > > +package org.apache.openjpa.persistence.jest;
> > > +
> > > +import java.io.IOException;
> > > +import java.net.ServerSocket;
> > > +import java.net.Socket;
> > > +import java.util.concurrent.ExecutorService;
> > > +import java.util.concurrent.Executors;
> > > +
> > > +import javax.persistence.EntityManagerFactory;
> > > +
> > > +import org.apache.openjpa.conf.OpenJPAConfiguration;
> > > +import org.apache.openjpa.lib.conf.Configurable;
> > > +import org.apache.openjpa.lib.conf.Configuration;
> > > +import org.apache.openjpa.lib.log.Log;
> > > +import org.apache.openjpa.lib.util.Localizer;
> > > +import org.apache.openjpa.meta.ClassMetaData;
> > > +import org.apache.openjpa.meta.MetaDataRepository;
> > > +import org.apache.openjpa.persistence.EntityManagerFactoryImpl;
> > > +
> > > +
> > > +/**
> > > + * A server running on an independent thread that allows a remote,
> > language-neutral client to access OpenJPA runtime.
> > > + *
> > > + * @author Pinaki Poddar
> > > + *
> > > + */
> > > +public class Server implements ServerContext, Configurable, Runnable {
> > > + private ServerSocket _listenSocket;
> > > + protected ExecutorService _executors;
> > > + public final static int DEFAULT_PORT = 6789;
> > > + protected int _port = DEFAULT_PORT;
> > > + protected int _range = 1;
> > > + protected String _format = "xml";
> > > + protected Log _log;
> > > + protected Thread _thread;
> > > + private EntityManagerFactoryImpl _ctx;
> > > + private static Localizer _loc =
> Localizer.forPackage(Server.class);
> > > +
> > > + /**
> > > + * Sets the persistence unit context in which this server will
> serve
> > requests.
> > > + * The context must be set before operation.
> > > + *
> > > + * @param emf an implementation of OpenJPA Persistence Unit. Must
> > not be null.
> > > + */
> > > + public void setContext(EntityManagerFactoryImpl emf) {
> > > + if (emf == null)
> > > + throw new NullPointerException();
> > > + _ctx = emf;
> > > + }
> > > +
> > > + /**
> > > + * Gets the persistence unit context in which this server serves
> > requests.
> > > + *
> > > + * @param emf an implementation of OpenJPA Persistence Unit.
> > > + */
> > > + public EntityManagerFactoryImpl getPersistenceUnit() {
> > > + return _ctx;
> > > + }
> > > +
> > > + public Log getLog() {
> > > + return _log;
> > > + }
> > > +
> > > + /**
> > > + * Start the server in a daemon thread.
> > > + * This method is idempotent.
> > > + *
> > > + * @return true if the server has started by this call or already
> > running.
> > > + */
> > > + public synchronized boolean start() {
> > > + try {
> > > + if (_thread != null)
> > > + return true;
> > > + if (createServerSocket()) {
> > > + _thread = new Thread(this);
> > > + _thread.setDaemon(true);
> > > + _thread.start();
> > > + return true;
> > > + }
> > > + return false;
> > > + } catch (Exception ex) {
> > > + ex.printStackTrace();
> > > + return false;
> > > + }
> > > + }
> > > +
> > > + /**
> > > + * Stops the server.
> > > + */
> > > + public synchronized void stop() {
> > > + _thread.interrupt();
> > > + _thread = null;
> > > + _executors.shutdownNow();
> > > + }
> > > +
> > > + /**
> > > + * Sets the port in which the server will listen.
> > > + *
> > > + * @param port a positive integer.
> > > + */
> > > + public void setPort(int port) {
> > > + _port = port;
> > > + }
> > > +
> > > + /**
> > > + * Gets the current port.
> > > + *
> > > + * @return the port number. Defaults to default HTTP port.
> > > + */
> > > + public int getPort() {
> > > + return _port;
> > > + }
> > > +
> > > + /**
> > > + * Sets the range of ports the server will attempt at start.
> > > + *
> > > + * @param range a positive integer.
> > > + */
> > > + public void setRange(int range) {
> > > + if (range > 0)
> > > + _range = range;
> > > + }
> > > +
> > > + public void setFormat(String format) {
> > > + _format = format;
> > > + }
> > > +
> > > + public String getFormat() {
> > > + return _format;
> > > + }
> > > +
> > > + /**
> > > + * Sets the range of ports the server will attempt at start.
> > > + * @return a positive integer. Defaults to 1.
> > > + */
> > > + public int getRange() {
> > > + return _range;
> > > + }
> > > +
> > > + public void run() {
> > > + _log.info(_loc.get("server-starting", this));
> > > +
> > > + _executors = Executors.newCachedThreadPool();
> > > + while (!Thread.interrupted()) {
> > > + try {
> > > + Socket socket = _listenSocket.accept();
> > > + if (_log.isTraceEnabled())
> > > + _log.trace(_loc.get("server-request", socket));
> > > + RequestHandler request = new RequestHandler(socket,
> > this);
> > > + _executors.submit(request);
> > > + } catch (IOException e) {
> > > + e.printStackTrace();
> > > + }
> > > + }
> > > + }
> > > +
> > > + private boolean createServerSocket() {
> > > + int p = _port;
> > > + int p2 = p + _range;
> > > + Exception error = null;
> > > + for (; _listenSocket == null && p < p2; ) {
> > > + try {
> > > + _listenSocket = new ServerSocket(p);
> > > + } catch (IOException ex) {
> > > + p++;
> > > + error = ex;
> > > + }
> > > + }
> > > + if (_listenSocket != null) {
> > > + if (p != _port) {
> > > + _port = p;
> > > + _log.warn(_loc.get("server-reconfigured", _port));
> > > + }
> > > + } else {
> > > + if (error != null) {
> > > + _log.warn(_loc.get("server-failed", this, _port,
> > error));
> > > + }
> > > + }
> > > + return _listenSocket != null;
> > > + }
> > > +
> > > + // Configurable contract
> > > + public void setConfiguration(Configuration conf) {
> > > + _log = conf.getLog("Remote");
> > > + }
> > > +
> > > + public void startConfiguration() {
> > > + }
> > > +
> > > + public void endConfiguration() {
> > > + }
> > > +
> > > +
> > > + // Server side utilities
> > > +
> > > + /**
> > > + * Resolves the given alias to a persistent class meta data.
> > > + *
> > > + * @exception if no meta data available for the given alias
> > > + */
> > > + public ClassMetaData resolve(String alias) {
> > > + MetaDataRepository repos =
> > _ctx.getConfiguration().getMetaDataRepositoryInstance();
> > > + return repos.getMetaData(alias,
> > Thread.currentThread().getContextClassLoader(), true);
> > > + }
> > > +
> > > + public String toString() {
> > > + if (_listenSocket == null) return "JEST Server [not strated]";
> > > + return "JEST Server " +
> > _listenSocket.getInetAddress()+":"+_listenSocket.getLocalPort();
> > > + }
> > > +
> > > +}
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:eol-style = native
> > >
> > > Propchange:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > >
> >
> ------------------------------------------------------------------------------
> > > svn:mime-type = text/plain
> > >
> > >
> > >
> >
>
Re: svn commit: r1028093 [1/2] - JEST...
Posted by Kevin Sutter <kw...@gmail.com>.
I have to admit that I was having the same questions/concerns that Donald
just raised. I thought we were still in the discussion and experimentation
phase of this REST work. Is it really ready for prime time?
Kevin
On Wed, Oct 27, 2010 at 4:58 PM, Donald Woods <dw...@apache.org> wrote:
> Is this really ready to drop into trunk?
> And do we really want it in the base openjpa.jar?
>
>
> -Donald
>
>
> On 10/27/10 4:42 PM, ppoddar@apache.org wrote:
> > Author: ppoddar
> > Date: Wed Oct 27 20:42:44 2010
> > New Revision: 1028093
> >
> > URL: http://svn.apache.org/viewvc?rev=1028093&view=rev
> > Log:
> > OPENJPA-1851: First version of JEST (REST on OpenJPA)
> >
> > Added:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ServerContext.java
> (with props)
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/XMLEncoder.java
> (with props)
> > Modified:
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> >
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> >
> > Modified:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> (original)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerFactoryImpl.java
> Wed Oct 27 20:42:44 2010
> > @@ -45,10 +45,10 @@ import org.apache.openjpa.lib.util.Close
> > import org.apache.openjpa.lib.util.Localizer;
> > import org.apache.openjpa.persistence.criteria.CriteriaBuilderImpl;
> > import org.apache.openjpa.persistence.criteria.OpenJPACriteriaBuilder;
> > +import org.apache.openjpa.persistence.jest.Server;
> > import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > import org.apache.openjpa.persistence.query.OpenJPAQueryBuilder;
> > import org.apache.openjpa.persistence.query.QueryBuilderImpl;
> > -import org.apache.openjpa.util.UserException;
> >
> > /**
> > * Implementation of {@link EntityManagerFactory} that acts as a
> > @@ -70,7 +70,8 @@ public class EntityManagerFactoryImpl
> > private transient StoreCache _cache = null;
> > private transient QueryResultCache _queryCache = null;
> > private transient MetamodelImpl _metaModel;
> > -
> > + private transient Server _remoteAccess = null;
> > +
> > /**
> > * Default constructor provided for auto-instantiation.
> > */
> > @@ -93,10 +94,11 @@ public class EntityManagerFactoryImpl
> >
> > /**
> > * Delegate must be provided before use.
> > + * Configures for Remote Access, if appropriate.
> > */
> > public void setBrokerFactory(BrokerFactory factory) {
> > - _factory = new DelegatingBrokerFactory(factory,
> > - PersistenceExceptions.TRANSLATOR);
> > + _factory = new DelegatingBrokerFactory(factory,
> PersistenceExceptions.TRANSLATOR);
> > + configureRemoteAccess(getConfiguration());
> > }
> >
> > public OpenJPAConfiguration getConfiguration() {
> > @@ -271,6 +273,10 @@ public class EntityManagerFactoryImpl
> > if (log.isTraceEnabled()) {
> > log.trace(this + ".close() invoked.");
> > }
> > + if (_remoteAccess != null) {
> > + _remoteAccess.stop();
> > + _remoteAccess = null;
> > + }
> > _factory.close();
> > }
> >
> > @@ -396,4 +402,40 @@ public class EntityManagerFactoryImpl
> > }
> > }
> > }
> > +
> > + /**
> > + * Configures this unit for remote access.
> > + */
> > + protected void configureRemoteAccess(OpenJPAConfiguration conf) {
> > + Value value = conf.getValue("RemoteAccess");
> > + if (value == null) {
> > + return;
> > + }
> > + String props = value.getString();
> > + if (props == null)
> > + return;
> > + try {
> > + _remoteAccess = new Server();
> > + _remoteAccess.setContext(this);
> > + Configurations.configureInstance(_remoteAccess, conf,
> props);
> > + conf.removeValue(value);
> > + if (!_remoteAccess.start()) {
> > + _remoteAccess = null;
> > + }
> > + } catch (Exception ex) {
> > + Log log =
> _factory.getConfiguration().getLog(OpenJPAConfiguration.LOG_RUNTIME);
> > + if (log != null) {
> > + log.error(_loc.get("remote-start-error"), ex);
> > + }
> > + }
> > + }
> > +
> > + /**
> > + * Affirms if this unit is accessible remotely.
> > + */
> > + public boolean allowsRemoteAccess() {
> > + return _remoteAccess != null;
> > + }
> > +
> > +
> > }
> >
> > Modified:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java?rev=1028093&r1=1028092&r2=1028093&view=diff
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> (original)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/PersistenceProductDerivation.java
> Wed Oct 27 20:42:44 2010
> > @@ -188,6 +188,7 @@ public class PersistenceProductDerivatio
> > conf.metaFactoryPlugin.setAlias(SPEC_JPA.getName(),
> PersistenceMetaDataFactory.class.getName());
> >
> > conf.addValue(new EntityManagerFactoryValue());
> > + conf.addString("RemoteAccess");
> >
> > conf.readLockLevel.setAlias("optimistic",
> String.valueOf(MixedLockLevels.LOCK_OPTIMISTIC));
> > conf.readLockLevel.setAlias("optimistic-force-increment", String
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,77 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.OutputStream;
> > +import java.io.PrintStream;
> > +
> > +/**
> > + * Abstract implementation of a stream-based response.
> > + * Every response is result of a {@linkplain Request} and operates
> within a {@linkplain ServerContext}.
> > + * This fact is enforced by the constructor argument of an abstract
> response.
> > + * <p>
> > + * Besides, this implementation provides common utility to write HTTP
> response or extract useful information
> > + * from the request.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public abstract class AbstractResponse extends PrintStream implements
> Response {
> > + protected final Request _request;
> > + protected final ServerContext _ctx;
> > +
> > + /**
> > + * Construct a response for the given request and server context.
> > + *
> > + * @param request the request for this response. Can be null if the
> response is for server error.
> > + * @param ctx the processing context
> > + * @param out the output stream where the response is targeted.
> > + */
> > + protected AbstractResponse(Request request, ServerContext ctx,
> OutputStream out) {
> > + super(out);
> > + _request = request;
> > + _ctx = ctx;
> > + }
> > +
> > + /**
> > + * Write a HTTP header to the stream.
> > + * <br>
> > + * The format of HTTP header is <code>key: [value]* NEWLINE</code>
> > + *
> > + * @param key the key of the header
> > + * @param values one of more value of the header fields.
> > + */
> > + protected void printHeader(String key, String...values) {
> > + if (key == null)
> > + return;
> > + print(key);
> > + print(" :");
> > + if (values == null || values.length == 0)
> > + return;
> > + int n = values.length-1;
> > + for (int i = 0; i < n-1; i++) {
> > + print(values[i]);
> > + print(";");
> > + }
> > + println(values[n]);
> > + }
> > +
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/AbstractResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,68 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.OutputStream;
> > +import java.net.HttpURLConnection;
> > +
> > +/**
> > + * A HTTP response for something gone wrong.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class ErrorResponse extends AbstractResponse {
> > + private final Exception _error;
> > + private final int _code;
> > + /**
> > + * Construct a response to describe a error.
> > + *
> > + * @param request a request that produced this response. VCan be
> null to denote that the request can not
> > + * be created.
> > + * @param ctx the processing context
> > + * @param ex the error
> > + * @param code HTTP error code
> > + * @param out the stream where the response is written
> > + *
> > + */
> > + public ErrorResponse(Request request, ServerContext ctx, Exception
> ex, int code, OutputStream out) {
> > + super(request, ctx, out);
> > + _error = ex;
> > + _code = code;
> > + }
> > +
> > + /**
> > + * Writes the response.
> > + * The response is always a HTTP response with the error stack
> trace.
> > + */
> > + public void writeOut() throws Exception {
> > + println("HTTP/1.1"); print(" " + _code); println("Error");
> > + printHeader("Connection", "close");
> > + printHeader("Content-Type", "text/html", "charset=UTF-8");
> > + println();
> > + println("<html><body><pre>");
> > + _error.printStackTrace(this);
> > + println("Response from JEST");
> > +
> > + println("</pre></body></html>");
> > + close();
> > + }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ErrorResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,147 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.InputStream;
> > +import java.io.OutputStream;
> > +import java.net.HttpURLConnection;
> > +import java.util.Iterator;
> > +import java.util.List;
> > +import java.util.Map;
> > +
> > +import javax.persistence.EntityManager;
> > +import javax.persistence.EntityNotFoundException;
> > +import javax.persistence.Query;
> > +
> > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > +import org.apache.openjpa.kernel.StoreContext;
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > +import org.apache.openjpa.util.ApplicationIds;
> > +import org.apache.openjpa.util.ObjectNotFoundException;
> > +
> > +/**
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class GETRequest extends JESTRequest {
> > + public Response process(ServerContext server, OutputStream out)
> throws Exception {
> > + String action = getAction();
> > + try {
> > + if ("find".equals(action)) {
> > + return find(server, out);
> > + } else {
> > + return resource(server, out);
> > + }
> > + } catch (Exception e) {
> > + return new ErrorResponse(this, server, new
> RuntimeException("bad action " + action),
> > + HttpURLConnection.HTTP_BAD_REQUEST, out);
> > + }
> > + }
> > +
> > + Response find(ServerContext server, OutputStream out) throws
> Exception {
> > + EntityManager em =
> server.getPersistenceUnit().createEntityManager();
> > + Map<String, String> qualifiers = getQualifiers();
> > + Map<String, String> parameters = getParameters();
> > + if (parameters.size() < 2)
> > + throw new IllegalArgumentException("find must have at least
> two parameters");
> > + Object[] pks = new Object[parameters.size()-1];
> > + Iterator<Map.Entry<String,String>> params =
> parameters.entrySet().iterator();
> > + String alias = null;
> > + for (int i = 0; i < parameters.size(); i++) {
> > + if (i == 0) {
> > + alias = params.next().getKey();
> > + } else {
> > + pks[i-1] = params.next().getKey();
> > + }
> > + }
> > + ClassMetaData meta = server.resolve(alias);
> > + Object oid = ApplicationIds.fromPKValues(pks, meta);
> > + Object pc = em.find(meta.getDescribedType(), oid);
> > + if (pc != null) {
> > + OpenJPAStateManager sm =
> ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(pc);
> > + return new JESTResponse(this, server, sm, out);
> > + } else {
> > + return new ErrorResponse(this, server, new
> EntityNotFoundException("not found!"),
> > + HttpURLConnection.HTTP_NOT_FOUND, out);
> > + }
> > + }
> > +
> > + Response query(ServerContext server, OutputStream out) throws
> Exception {
> > + EntityManager em =
> server.getPersistenceUnit().createEntityManager();
> > + Map<String, String> qualifiers = getQualifiers();
> > + boolean named = isBooleanQualifier("named");
> > + boolean single = isBooleanQualifier("single");
> > + Map<String, String> parameters = getParameters();
> > + if (parameters.size() < 1)
> > + throw new IllegalArgumentException("find must have at least
> one parameter");
> > + Iterator<Map.Entry<String,String>> params =
> parameters.entrySet().iterator();
> > + Query query = null;
> > + int i = 0;
> > + for (Map.Entry<String, String> param : parameters.entrySet()) {
> > + if (i == 0) {
> > + query = named ? em.createQuery(param.getKey()) :
> em.createNamedQuery(param.getKey());
> > + } else {
> > + query.setParameter(param.getKey(), param.getValue());
> > + }
> > + }
> > + if (single) {
> > + Object result = query.getSingleResult();
> > + OpenJPAStateManager sm =
> ((StoreContext)JPAFacadeHelper.toBroker(em)).getStateManager(result);
> > + return new JESTResponse(this, server, sm, out);
> > + } else {
> > + List<Object> result = query.getResultList();
> > + return new ErrorResponse(this, server, new
> EntityNotFoundException("not found!"), 404, out);
> > + }
> > + }
> > +
> > + Response resource(ServerContext server, OutputStream out) throws
> Exception {
> > + String resource = getAction();
> > + if (resource.length() == 0)
> > + resource = "index.html";
> > + String mimeType = getMimeType(resource);
> > +// InputStream in =
> Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
> > + InputStream in = getClass().getResourceAsStream(resource);
> > + if (in == null) {
> > + return new ErrorResponse(this, server, new
> ObjectNotFoundException(resource), 404, out);
> > + }
> > + if (server.getLog().isTraceEnabled())
> > + server.getLog().trace("Found resource " + resource);
> > + return mimeType.startsWith("image")
> > + ? new ImageResponse(this, server, in, mimeType, out)
> > + : new ResourceResponse(this, server, in, mimeType, out);
> > + }
> > +
> > + boolean isBooleanQualifier(String key) {
> > + String q = getQualifier(key);
> > + return hasQualifier(key) && (q == null || "true".equals(q));
> > + }
> > +
> > + String getMimeType(String resource) {
> > + int index = resource.lastIndexOf('.');
> > + String ext = (index != -1) ? resource.substring(index+1) : "";
> > + if (ext.equalsIgnoreCase("html") ||
> ext.equalsIgnoreCase(".html")) return "text/html";
> > + if (ext.equalsIgnoreCase(".png") || ext.equalsIgnoreCase(".ico")
> || ext.equalsIgnoreCase("jpeg"))
> > + return "image/"+ext;
> > + return "text/html";
> > + }
> > +
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/GETRequest.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,61 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.InputStream;
> > +import java.io.OutputStream;
> > +import java.io.RandomAccessFile;
> > +
> > +import javax.imageio.ImageIO;
> > +
> > +/**
> > + * Sends an image as response.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class ImageResponse extends AbstractResponse {
> > + private final InputStream _in;
> > + private final String _mimeType;
> > +
> > + public ImageResponse(Request request, ServerContext ctx, InputStream
> resource, String mimeType,
> > + OutputStream out) throws Exception {
> > + super(request, ctx, out);
> > + _in = resource;
> > + _mimeType = mimeType;
> > + }
> > +
> > + public void writeOut() throws Exception {
> > + print(_request.getProtocol()); println("200 OK");
> > + printHeader("Connection", "close");
> > + printHeader("Content-Type", _mimeType);
> > + println();
> > + byte[] b = new byte[1024];
> > + int i = 0;
> > + for (int l = 0; (l = _in.read(b)) != -1;) {
> > + write(b, 0, l);
> > + i += l;
> > + }
> > + printHeader("Content-Length", ""+i);
> > + _in.close();
> > + close();
> > + }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ImageResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,386 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.IOException;
> > +import java.util.Arrays;
> > +import java.util.Collections;
> > +import java.util.HashMap;
> > +import java.util.LinkedHashMap;
> > +import java.util.LinkedList;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.NoSuchElementException;
> > +
> > +/**
> > + * A request carries requisite data for a JPA operation to be performed.
> > + * The request is populated by parsing an input data stream.
> > + *
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public abstract class JESTRequest implements Request {
> > + private String _method;
> > + private String _protocol;
> > + private String _action;
> > + private String _body;
> > + private LinkedHashMap<String, String> _qualifiers = new
> LinkedHashMap<String, String>();
> > + private LinkedHashMap<String, String> _params = new
> LinkedHashMap<String, String>();
> > + private Map<String, List<String>> _headers = new HashMap<String,
> List<String>>();
> > + private ParseState _state;
> > + private StringBuffer buf = new StringBuffer();
> > + private LinkedList<Token> _stack = new LinkedList<Token>();
> > +
> > + public static final List<String> METHODS = Arrays.asList(new
> String[]{"GET","POST","PUT","DELETE"});
> > +
> > + /**
> > + * Parse States.
> > + */
> > + static enum ParseState {
> > + INIT, ACTION, QUALIFIER_KEY, QUALIFIER_VALUE, PARAM_KEY,
> PARAM_VALUE, END
> > + };
> > +
> > +
> > + public String getMethod() {
> > + return _method;
> > + }
> > +
> > + void setMethod(String method) {
> > + if (_method == null) {
> > + if (method != null &&
> METHODS.contains(method.toUpperCase())) {
> > + _method = method.toUpperCase();
> > + } else {
> > + throw new IllegalArgumentException("Unsupported method "
> + method);
> > + }
> > + } else if (!_method.equalsIgnoreCase(method)) {
> > + throw new IllegalStateException("Method can not be changed
> to [" + method + "]. " +
> > + "Current method [" + _method + "]");
> > + }
> > + }
> > +
> > + public String getProtocol() {
> > + return _protocol == null ? "HTTP/1.1" : _protocol;
> > + }
> > +
> > + void setProtocol(String protocol) {
> > + if (_protocol == null) {
> > + if (protocol != null &&
> protocol.toUpperCase().startsWith("HTTP")) {
> > + _protocol = protocol.toUpperCase();
> > + } else {
> > + throw new IllegalArgumentException("Unsupported protocol
> " + protocol);
> > + }
> > + } else if (!_protocol.equalsIgnoreCase(protocol)) {
> > + throw new IllegalStateException("Protocol can not be changed
> to [" + protocol + "]. " +
> > + "Current protocol [" + _protocol + "]");
> > + }
> > + }
> > +
> > + /**
> > + * Sets an action. Once set, an action can not be modified.
> > + *
> > + * @param action
> > + */
> > + private void setAction(String action) {
> > + if (_action == null) {
> > + _action = action;
> > + } else if (!_action.equals(action)) {
> > + throw new IllegalStateException("Action can not be [" +
> action + "]. Already set to [" + _action + "]");
> > + }
> > + }
> > +
> > + public String getAction() {
> > + return _action == null ? "" : _action;
> > + }
> > +
> > + public String getBody() {
> > + return _body;
> > + }
> > +
> > + private void setQualifier(String key, String value) {
> > + _qualifiers.put(key, value);
> > + }
> > +
> > + public String getQualifier(String key) {
> > + return _qualifiers.get(key);
> > + }
> > +
> > + public Map<String, String> getQualifiers() {
> > + return Collections.unmodifiableMap(_qualifiers);
> > + }
> > +
> > + public boolean hasQualifier(String key) {
> > + return _qualifiers.containsKey(key);
> > + }
> > +
> > + private void setParameter(String key, String value) {
> > + _params.put(key, value);
> > + }
> > +
> > + public String getParameter(String key) {
> > + return _params.get(key);
> > + }
> > +
> > + public boolean hasParameter(String key) {
> > + return _params.containsKey(key);
> > + }
> > +
> > + public Map<String, String> getParameters() {
> > + return Collections.unmodifiableMap(_params);
> > + }
> > +
> > + public Map.Entry<String, String> getParameter(int n) {
> > + if (n >= _params.size())
> > + throw new NoSuchElementException("Index " + n + " size " +
> _params.size());
> > + int i = 0;
> > + for (Map.Entry<String, String> entry : _params.entrySet()) {
> > + if (i == n) {
> > + return entry;
> > + }
> > + i++;
> > + }
> > + return null;
> > + }
> > +
> > + public Map<String, List<String>> getHeaders() {
> > + return Collections.unmodifiableMap(_headers);
> > + }
> > +
> > + public List<String> getHeader(String key) {
> > + return _headers.get(key);
> > + }
> > +
> > +
> > + public void read(List<String> lines) throws IOException {
> > + parse(lines.get(0));
> > + int i = 1;
> > + for (; i < lines.size(); i++) {
> > + String line = lines.get(i);
> > + if (line.length() == 0) {
> > + break;
> > + } else {
> > + parseHeader(line);
> > + }
> > + }
> > + parseBody(lines.subList(i, lines.size()));
> > + }
> > +
> > + protected void parseHeader(String line) throws IOException {
> > + String key = null;
> > + StringBuilder token = new StringBuilder();
> > + int N = line.length();
> > + for (int i = 0; i < N; i++) {
> > + char c = line.charAt(i);
> > + if (c == ':' && key == null) {
> > + key = token.toString().trim();
> > + token.delete(0, token.length());
> > + } else {
> > + token.append(c);
> > + }
> > + }
> > + if (key != null) {
> > + _headers.put(key,
> Collections.singletonList(token.toString().trim()));
> > + }
> > + }
> > +
> > + protected void parseBody(List<String> lines) {
> > + if (lines == null || lines.isEmpty())
> > + return;
> > + for (String line : lines) {
> > + if (_body == null) {
> > + _body = line;
> > + } else {
> > + _body = _body + line;
> > + }
> > + }
> > + }
> > +
> > + /**
> > + * Parses JEST stream and populates a request.
> > + *
> > + */
> > + protected void parse(String s) {
> > + char[] chars = s.toCharArray();
> > + _state = ParseState.INIT;
> > + _stack.clear();
> > +
> > + for (int i = 0; i < chars.length; i++) {
> > + char ch = chars[i];
> > + switch (_state) {
> > + case INIT:
> > + if (ch == '/') {
> > + transit(ParseState.ACTION);
> > + } else if (!Character.isWhitespace(ch)) {
> > + parseError(ch, i, s, true, ' ');
> > + }
> > + break;
> > +
> > + case ACTION:
> > + if (ch == '/') {
> > + transit(ParseState.QUALIFIER_KEY);
> > + } else if (ch == '?') {
> > + transit(ParseState.PARAM_KEY);
> > + } else {
> > + buf.append(ch);
> > + }
> > + break;
> > +
> > + case QUALIFIER_KEY:
> > + if (Character.isJavaIdentifierPart(ch)) {
> > + buf.append(ch);
> > + } else if (ch == '=') {
> > + transit(ParseState.QUALIFIER_VALUE);
> > + } else if (ch == '/') {
> > + transit(ParseState.QUALIFIER_KEY);
> > + } else if (ch == '?') {
> > + transit(ParseState.PARAM_KEY);
> > + } else {
> > + parseError(ch, i, s, true, '/', '?', '=');
> > + }
> > + break;
> > +
> > + case QUALIFIER_VALUE:
> > + if (Character.isJavaIdentifierPart(ch)) {
> > + buf.append(ch);
> > + } else if (ch == '/') {
> > + transit(ParseState.QUALIFIER_KEY);
> > + } else if (ch == '?') {
> > + transit(ParseState.PARAM_KEY);
> > + } else {
> > + parseError(ch, i, s, true, '/', '?');
> > + }
> > + break;
> > +
> > + case PARAM_KEY:
> > + if (Character.isJavaIdentifierPart(ch)) {
> > + buf.append(ch);
> > + } else if (ch == '=') {
> > + if (isQueryKey())
> > + buf.append(ch);
> > + else
> > + transit(ParseState.PARAM_VALUE);
> > + } else if (ch == ';') {
> > + transit(ParseState.PARAM_KEY);
> > + } else if (isQueryKey() && isQueryChar(ch)) {
> > + buf.append(ch);
> > + } else {
> > + parseError(ch, i, s, true, ';', '=');
> > + }
> > + break;
> > +
> > + case PARAM_VALUE:
> > + if (Character.isJavaIdentifierPart(ch)) {
> > + buf.append(ch);
> > + } else if (ch == ';') {
> > + transit(ParseState.PARAM_KEY);
> > + } else {
> > + parseError(ch, i, s, true, ';');
> > + }
> > + break;
> > + default:
> > + throw new RuntimeException("ParseError: '" + ch
> + "' at " + i + " in [" + s + "]. "
> > + + "Unknown state " + _state);
> > + }
> > + }
> > + if (buf.length() > 0) {
> > + transit(ParseState.END);
> > + }
> > + }
> > +
> > + /**
> > + * Affirms if parsing a query string.
> > + */
> > + private boolean isQueryKey() {
> > + return "query".equals(_action) && _stack.size() == 1;
> > + }
> > +
> > + /**
> > + * Affirms if the given character is valid in a query string
> > + */
> > + private boolean isQueryChar(char c) {
> > + return c == ' ' || c == '.' || c == ':' || c == '?' || c ==
> '\'';
> > + }
> > +
> > + /**
> > + * Transitions to a new parse state.
> > + *
> > + * @param to target parse state
> > + */
> > + void transit(ParseState to) {
> > + String token = buf.toString();
> > + switch (_state) {
> > + case ACTION:
> > + setAction(token);
> > + break;
> > + case QUALIFIER_KEY:
> > + setQualifier(token, null);
> > + break;
> > + case QUALIFIER_VALUE:
> > + setQualifier(_stack.peekLast().getValue(), token);
> > + break;
> > + case PARAM_KEY:
> > + setParameter(token, null);
> > + break;
> > + case PARAM_VALUE:
> > + setParameter(_stack.peekLast().getValue(), token);
> > + break;
> > +
> > + }
> > + if (_state != ParseState.INIT && to != ParseState.END) {
> > + _stack.add(new Token(_state, token));
> > + }
> > + buf.delete(0, buf.length());
> > + _state = to;
> > + }
> > +
> > + protected void parseError(char ch, int pos, String line, boolean
> java, char... expected) {
> > + throw new RuntimeException("ParseError: Encountered '" + ch
> + "' at " + pos + " in [" + line + "] while "
> > + + "parsing " + _state + ". Expected " +
> Arrays.toString(expected) + (java ? " or Java identifer" : ""));
> > +
> > + }
> > +
> > + /**
> > + * Token in a JEST stream.
> > + *
> > + */
> > + static class Token {
> > + final ParseState _type;
> > + final String _value;
> > +
> > + public Token(ParseState type, String value) {
> > + _type = type;
> > + _value = value;
> > + }
> > +
> > + public ParseState getType() {
> > + return _type;
> > + }
> > +
> > + public String getValue() {
> > + return _value;
> > + }
> > +
> > + public String toString() {
> > + return _value + "[" + _type + "]";
> > + }
> > + }
> > +
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTRequest.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,71 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.IOException;
> > +import java.io.OutputStream;
> > +import java.io.PrintStream;
> > +import java.io.PrintWriter;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.Set;
> > +
> > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > +import org.w3c.dom.Document;
> > +import org.w3c.dom.Element;
> > +
> > +/**
> > + * Response of a JEST Request.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class JESTResponse extends AbstractResponse {
> > + private final OpenJPAStateManager _sm;
> > +
> > + public JESTResponse(Request request, ServerContext ctx,
> OpenJPAStateManager sm, OutputStream out) throws Exception {
> > + super(request, ctx, out);
> > + _sm = sm;
> > + }
> > +
> > + public String getContentType() {
> > + return "text/html";
> > + }
> > +
> > + public void writeOut() throws Exception {
> > + print(_request.getProtocol()); println("200 OK");
> > + printHeader("Connection", "close");
> > + printHeader("Content-Type", "text/html", "charset=UTF-8");
> > + println();
> > + println("<html><body><pre>");
> > + XMLEncoder encoder = new
> XMLEncoder(_ctx.getPersistenceUnit().getMetamodel());
> > + Document doc = encoder.encode(_sm);
> > + encoder.writeDoc(doc, this);
> > + println("Response from JEST");
> > +
> > + println("</pre></body></html>");
> > + close();
> > + }
> > +
> > +}
> > +
> > +
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JESTResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,316 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.BufferedReader;
> > +import java.io.CharArrayWriter;
> > +import java.io.IOException;
> > +import java.io.InputStream;
> > +import java.io.InputStreamReader;
> > +import java.io.Reader;
> > +import java.util.Arrays;
> > +import java.util.BitSet;
> > +import java.util.Collection;
> > +import java.util.HashSet;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.Set;
> > +
> > +import javax.persistence.metamodel.Attribute;
> > +
> > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > +import org.apache.openjpa.kernel.StoreContext;
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.meta.FieldMetaData;
> > +import org.apache.openjpa.meta.JavaTypes;
> > +import org.apache.openjpa.meta.ValueMetaData;
> > +import org.apache.openjpa.persistence.meta.Members;
> > +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > +import org.w3c.dom.CDATASection;
> > +import org.w3c.dom.Document;
> > +import org.w3c.dom.Element;
> > +
> > +/**
> > + * Marshals a root instance and its persistent closure as JSON object.
> > + * The closure is resolved against the persistence context that contains
> the root instance.
> > + * The JSON format introduces a $id and $ref to address reference that
> pure JSON does not.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class JSONEncoder {
> > + /**
> > + * The element/attribute tags declared in
> <code>jest-instance.xsd</code> XML schema.
> > + */
> > + public static final String ELEMENT_NULL_REF = "null";
> > + public static final String ELEMENT_INSTANCE = "instance";
> > + public static final String ELEMENT_REF = "ref";
> > +
> > +
> > + private MetamodelHelper _model;
> > +
> > + public JSONEncoder(MetamodelImpl model) {
> > + _model = new MetamodelHelper(model);
> > + }
> > +
> > + /**
> > + * Encodes the given managed instance into a new XML element as a
> child of the given parent node.
> > + *
> > + * @param sm a managed instance, can be null.
> > + * @param parent the parent node to which the new node be attached.
> > + */
> > + public StringBuilder encode(final OpenJPAStateManager sm) {
> > + return encode(sm, new HashSet<OpenJPAStateManager>(), 0, false);
> > + }
> > + StringBuilder indent(StringBuilder buf, int indent) {
> > + if (indent <= 0)
> > + return buf;
> > + char[] spaces = new char[indent*4];
> > + Arrays.fill(spaces, ' ');
> > + buf.insert(0, spaces);
> > + return buf;
> > + }
> > + StringBuilder end(StringBuilder buf, char ch, int indent) {
> > + char[] spaces = new char[indent*4];
> > + Arrays.fill(spaces, ' ');
> > + return buf.append("\r\n").append(spaces).append(ch);
> > + }
> > +
> > + /**
> > + * Encodes the closure of a persistent instance into a XML element.
> > + *
> > + * @param sm the managed instance to be encoded. Can be null.
> > + * @param parent the parent XML element to which the new XML element
> be added. Must not be null. Must be
> > + * owned by a document.
> > + * @param visited the persistent instances that had been encoded
> already. Must not be null or immutable.
> > + *
> > + * @return the new element. The element has been appended as a child
> to the given parent in this method.
> > + */
> > + private StringBuilder encode(final OpenJPAStateManager sm, final
> Set<OpenJPAStateManager> visited,
> > + int indent, boolean indentPara) {
> > + if (visited == null) {
> > + throw new IllegalArgumentException("null closure for
> encoder");
> > + }
> > + StringBuilder root = indent(new StringBuilder("{"), indentPara
> ? indent : 0);
> > + if (sm == null) {
> > + return root.append("null}");
> > + }
> > + boolean ref = !visited.add(sm);
> > + if (ref) {
> > + return indent(root.append(quoted("$ref")).append(":
> ").append(ior(sm)).append('}'),
> > + indentPara ? indent : 0);
> > + } else {
> > + indent(root.append(quoted("$id")).append(":
> ").append(ior(sm)), indentPara ? indent : 0);
> > + }
> > +
> > + StringBuilder child = new StringBuilder();
> > + BitSet loaded = sm.getLoaded();
> > + StoreContext ctx = (StoreContext)sm.getGenericContext();
> > + List<Attribute<?, ?>> attrs =
> _model.getAttributesInOrder(sm.getMetaData());
> > + for (int i = 0; i < attrs.size(); child = new StringBuilder(),
> i++) {
> > + FieldMetaData fmd = ((Members.Member<?, ?>)
> attrs.get(i)).fmd;
> > + if (!loaded.get(fmd.getIndex()))
> > + continue;
> > + Object value = sm.fetch(fmd.getIndex());
> > + child.append(quoted(fmd.getName())).append(": ");
> > + switch (fmd.getDeclaredTypeCode()) {
> > + case JavaTypes.BOOLEAN:
> > + case JavaTypes.BYTE:
> > + case JavaTypes.CHAR:
> > + case JavaTypes.DOUBLE:
> > + case JavaTypes.FLOAT:
> > + case JavaTypes.INT:
> > + case JavaTypes.LONG:
> > + case JavaTypes.SHORT:
> > +
> > + case JavaTypes.BOOLEAN_OBJ:
> > + case JavaTypes.BYTE_OBJ:
> > + case JavaTypes.CHAR_OBJ:
> > + case JavaTypes.DOUBLE_OBJ:
> > + case JavaTypes.FLOAT_OBJ:
> > + case JavaTypes.INT_OBJ:
> > + case JavaTypes.LONG_OBJ:
> > + case JavaTypes.SHORT_OBJ:
> > +
> > + case JavaTypes.BIGDECIMAL:
> > + case JavaTypes.BIGINTEGER:
> > + case JavaTypes.DATE:
> > + case JavaTypes.NUMBER:
> > + case JavaTypes.CALENDAR:
> > + case JavaTypes.LOCALE:
> > + case JavaTypes.STRING:
> > + case JavaTypes.ENUM:
> > + child.append(quoted(value));
> > + break;
> > +
> > + case JavaTypes.PC:
> > + if (value == null) {
> > + child.append("null");
> > + } else {
> > + child.append(encode(ctx.getStateManager(value),
> visited, indent+1, false));
> > + }
> > + break;
> > +
> > + case JavaTypes.ARRAY:
> > + Object[] values = (Object[])value;
> > + value = Arrays.asList(values);
> > + // no break;
> > + case JavaTypes.COLLECTION:
> > + if (value == null) {
> > + child.append("null");
> > + break;
> > + }
> > + child.append("[");
> > + Collection<?> members = (Collection<?>)value;
> > + boolean basic = fmd.getElement().getTypeMetaData()
> == null;
> > + int k = 0;
> > + for (Object o : members) {
> > + child.append("\r\n");
> > + if (o == null) {
> > + child.append(indent(new
> StringBuilder("null"), indent+1));
> > + } else {
> > + if (basic) {
> > + child.append(indent(new
> StringBuilder(quoted(o)), indent+1));
> > + } else {
> > +
> child.append(encode(ctx.getStateManager(o), visited, indent+1, true));
> > + }
> > + }
> > + }
> > + end(child, ']', indent+1);
> > + break;
> > + case JavaTypes.MAP:
> > + if (value == null) {
> > + child.append("null");
> > + break;
> > + }
> > + child.append("[");
> > + Set<Map.Entry> entries = ((Map)value).entrySet();
> > + boolean basicKey =
> fmd.getElement().getTypeMetaData() == null;
> > + boolean basicValue =
> fmd.getValue().getTypeMetaData() == null;
> > + for (Map.Entry<?,?> e : entries) {
> > + if (e.getKey() == null) {
> > + child.append("null:");
> > + } else {
> > + if (basicKey) {
> > +
> child.append(quoted(e.getKey())).append(":");
> > + } else {
> > +
> child.append(encode(ctx.getStateManager(e.getKey()), visited, indent+1,
> true));
> > + }
> > + }
> > + if (e.getValue() == null) {
> > + child.append("null");
> > + } else {
> > + if (basicValue) {
> > + child.append(quoted(e.getValue()));
> > + } else {
> > +
> child.append(encode(ctx.getStateManager(e.getValue()), visited, indent+1,
> false));
> > + }
> > + }
> > + }
> > + break;
> > +
> > + case JavaTypes.INPUT_STREAM:
> > + case JavaTypes.INPUT_READER:
> > + child = new StringBuilder(fmd.getName());
> > + if (value == null) {
> > + child.append("null");
> > + } else {
> > + child.append(streamToString(value));
> > + }
> > + break;
> > +
> > + case JavaTypes.PC_UNTYPED:
> > + case JavaTypes.OBJECT:
> > + case JavaTypes.OID:
> > + System.err.println("Not handled " + fmd.getName() +
> " of type " + fmd.getDeclaredType());
> > + }
> > +
> > + if (child != null) {
> > + root.append("\r\n");
> > + root.append(indent(child, indent+1));
> > + if (loaded.length()-1 != i)
> > + root.append(",");
> > + }
> > + }
> > + return end(root, '}', indent);
> > + }
> > +
> > +
> > + String ior(OpenJPAStateManager sm) {
> > + return quoted(typeOf(sm)+"-"+sm.getObjectId().toString());
> > + }
> > +
> > + String typeOf(OpenJPAStateManager sm) {
> > + return sm.getMetaData().getDescribedType().getSimpleName();
> > + }
> > +
> > + String typeOf(Class<?> cls) {
> > + return cls.getSimpleName();
> > + }
> > +
> > + String typeOf(ClassMetaData meta) {
> > + return meta.getDescribedType().getSimpleName();
> > + }
> > +
> > + String typeOf(ValueMetaData vm) {
> > + if (vm.getTypeMetaData() == null)
> > + return typeOf(vm.getType());
> > + return typeOf(vm.getTypeMetaData());
> > + }
> > +
> > + String typeOf(FieldMetaData fmd) {
> > + return fmd.getType().getSimpleName();
> > + }
> > +
> > +
> > + /**
> > + * Convert the given stream (either an InutStream or a Reader) to a
> String
> > + * to be included in CDATA section of a XML document.
> > + *
> > + * @param value the field value to be converted. Can not be null
> > + * @return
> > + */
> > + String streamToString(Object value) {
> > + Reader reader = null;
> > + if (value instanceof InputStream) {
> > + reader = new BufferedReader(new
> InputStreamReader((InputStream)value));
> > + } else if (value instanceof Reader) {
> > + reader = (Reader)value;
> > + } else {
> > + throw new RuntimeException();
> > + }
> > + CharArrayWriter writer = new CharArrayWriter();
> > + try {
> > + for (int c; (c = reader.read()) != -1;) {
> > + writer.write(c);
> > + }
> > + } catch (IOException ex) {
> > + throw new RuntimeException(ex);
> > + }
> > + return writer.toString();
> > + }
> > +
> > + String quoted(Object o) {
> > + if (o == null) return "null";
> > + if (o instanceof Number)
> > + return o.toString();
> > + return "\"" + o.toString() + "\"";
> > + }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/JSONEncoder.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,127 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.util.ArrayList;
> > +import java.util.Collections;
> > +import java.util.Comparator;
> > +import java.util.HashMap;
> > +import java.util.List;
> > +import java.util.Map;
> > +
> > +import javax.persistence.metamodel.Attribute;
> > +import javax.persistence.metamodel.EntityType;
> > +import javax.persistence.metamodel.ManagedType;
> > +import javax.persistence.metamodel.Metamodel;
> > +import javax.persistence.metamodel.SingularAttribute;
> > +
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.meta.FieldMetaData;
> > +import org.apache.openjpa.persistence.meta.MetamodelImpl;
> > +
> > +/**
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class MetamodelHelper {
> > + private MetamodelImpl _model;
> > + private Map<ManagedType<?>, List<Attribute<?, ?>>> _attrs = new
> HashMap<ManagedType<?>, List<Attribute<?,?>>>();
> > +
> > + public MetamodelHelper(MetamodelImpl model) {
> > + _model = model;
> > + }
> > +
> > + public List<Attribute<?,?>> getAttributesInOrder(Class<?> cls) {
> > + return getAttributesInOrder(_model.managedType(cls));
> > + }
> > +
> > + public List<Attribute<?,?>> getAttributesInOrder(ClassMetaData meta)
> {
> > + return getAttributesInOrder(meta.getDescribedType());
> > + }
> > +
> > + /**
> > + * Gets the attributes of the given type in defined order.
> > + * @param type
> > + * @return
> > + */
> > + public List<Attribute<?,?>> getAttributesInOrder(ManagedType<?>
> type) {
> > + List<Attribute<?,?>> attrs = _attrs.get(type);
> > + if (attrs != null)
> > + return attrs;
> > + List<Attribute<?,?>> list = new
> ArrayList<Attribute<?,?>>(type.getAttributes());
> > + Collections.sort(list, new AttributeComparator());
> > + _attrs.put(type, list);
> > + return list;
> > + }
> > +
> > + public static boolean isId(Attribute<?,?> a) {
> > + if (a instanceof SingularAttribute)
> > + return ((SingularAttribute<?,?>)a).isId();
> > + return false;
> > + }
> > +
> > + public static boolean isVersion(Attribute<?,?> a) {
> > + if (a instanceof SingularAttribute)
> > + return ((SingularAttribute<?,?>)a).isVersion();
> > + return false;
> > + }
> > +
> > + public static Integer getAttributeTypeCode(Attribute<?,?> attr) {
> > + if (isId(attr))
> > + return 0;
> > + if (isVersion(attr))
> > + return 1;
> > +
> > + switch (attr.getPersistentAttributeType()) {
> > + case BASIC :
> > + case EMBEDDED:
> > + return 2;
> > + case ONE_TO_ONE:
> > + case MANY_TO_ONE:
> > + return 3;
> > + case ONE_TO_MANY:
> > + case MANY_TO_MANY:
> > + case ELEMENT_COLLECTION: return 4;
> > + default: return 5;
> > + }
> > + }
> > +
> > + /**
> > + * Compares attribute by their qualification.
> > + * Identity
> > + * Version
> > + * Basic
> > + * Singular association
> > + * Plural association
> > + *
> > + */
> > + public static class AttributeComparator implements
> Comparator<Attribute<?,?>> {
> > + @Override
> > + public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
> > + Integer t1 = getAttributeTypeCode(a1);
> > + Integer t2 = getAttributeTypeCode(a2);
> > + if (t1.equals(t2)) {
> > + return a1.getName().compareTo(a2.getName());
> > + } else {
> > + return t1.compareTo(t2);
> > + }
> > + }
> > + }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,138 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.IOException;
> > +import java.io.OutputStream;
> > +import java.io.Serializable;
> > +import java.util.List;
> > +import java.util.Map;
> > +
> > +/**
> > + * A request from a remote client to a server to do something.
> > + * The request arrives as stream of bytes from a remote location
> > + * and if the server can interpret the protocol from the stream,
> > + * then make a concrete request object.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public interface Request extends Serializable {
> > + /**
> > + * Get the HTTP verb such as GET, POST
> > + *
> > + * @return uppercase string
> > + */
> > + String getMethod();
> > +
> > + /**
> > + * Get the first path segment as intended persistence action such as
> <code>find</code> or <code>query</code>
> > + *
> > + * @return lowercase action name. Can be empty.
> > + */
> > + String getAction();
> > +
> > + /**
> > + * Get the protocol such as HTTP/1.1
> > + *
> > + * @return upper-case string
> > + */
> > + String getProtocol();
> > +
> > + /**
> > + * Get the body, if any.
> > + *
> > + * @return body of the request. null if no body.
> > + */
> > + String getBody();
> > +
> > + /**
> > + * Get the headers indexed by the keys.
> > + * Header values are list of Strings.
> > + *
> > + * @return empty map if there is no header
> > + */
> > + Map<String, List<String>> getHeaders();
> > +
> > + /**
> > + * Get the header values for the given key.
> > + * @param key a key
> > + * @return null if no header value for the given key
> > + */
> > + List<String> getHeader(String key);
> > +
> > + /**
> > + * Affirm if the the given qualifier is available in this request.
> > + *
> > + * @param key case-sensitive qualifier
> > + * @return true if the key is present.
> > + */
> > + boolean hasQualifier(String key);
> > +
> > + /**
> > + * Gets the value for the given qualifier key.
> > + *
> > + * @param key case-sensitive qualifier
> > + * @return value of the qualifier. null if the key is absent.
> > + */
> > + String getQualifier(String key);
> > +
> > + /**
> > + * Get all the qualifiers available in this request.
> > + *
> > + * @return key-value pairs of the qualifiers. Empty map if no
> qualifier is present.
> > + */
> > + Map<String,String> getQualifiers();
> > +
> > +
> > + /**
> > + * Affirm if the the given parameter is available in this request.
> > + *
> > + * @param key case-sensitive parameter
> > + * @return true if the key is present.
> > + */
> > + boolean hasParameter(String key);
> > +
> > +
> > + /**
> > + * Gets the value for the given parameter key.
> > + *
> > + * @param key case-sensitive parameter
> > + * @return value of the parameter. null if the key is absent.
> > + */
> > + String getParameter(String key);
> > +
> > + /**
> > + * Get all the parameters available in this request.
> > + *
> > + * @return key-value pairs of the parameters. Empty map if no
> parameter is present.
> > + */
> > + Map<String,String> getParameters();
> > +
> > + /**
> > + * Parse the request represented as a list of strings.
> > + *
> > + * @param lines each line of the request.
> > + * @throws IOException
> > + */
> > + void read(List<String> lines) throws IOException;
> > +
> > + Response process(ServerContext server, OutputStream stream) throws
> Exception;
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Request.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,63 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.util.HashMap;
> > +import java.util.Map;
> > +
> > +/**
> > + * A factory to create a specific type of request.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class RequestFactory {
> > + private final String _protocol;
> > + private static Map<String, RequestFactory> _registered = new
> HashMap<String, RequestFactory>();
> > + static {
> > + _registered.put("HTTP/1.0", new RequestFactory("HTTP/1.0"));
> > + _registered.put("HTTP/1.1", new RequestFactory("HTTP/1.1"));
> > + }
> > +
> > + public static void register(String protocol, RequestFactory factory)
> {
> > + _registered.put(protocol, factory);
> > + }
> > +
> > + private RequestFactory(String proto) {
> > + _protocol = proto;
> > + }
> > +
> > + public static RequestFactory getFactory(String protocol) {
> > + return _registered.get(protocol);
> > + }
> > +
> > + Request createRequest(String method) {
> > + JESTRequest request = null;
> > + if ("GET".equalsIgnoreCase(method)) {
> > + request = new GETRequest();
> > + } else {
> > + throw new UnsupportedOperationException();
> > + }
> > + request.setProtocol(_protocol);
> > + request.setMethod(method.toUpperCase());
> > + return request;
> > +
> > + }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestFactory.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,147 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.BufferedReader;
> > +import java.io.IOException;
> > +import java.io.InputStream;
> > +import java.io.InputStreamReader;
> > +import java.io.OutputStream;
> > +import java.net.HttpURLConnection;
> > +import java.net.Socket;
> > +import java.net.SocketException;
> > +import java.util.ArrayList;
> > +import java.util.Collections;
> > +import java.util.Iterator;
> > +import java.util.List;
> > +import java.util.Map;
> > +import java.util.Set;
> > +import java.util.concurrent.Callable;
> > +
> > +import javax.persistence.EntityManager;
> > +import javax.persistence.EntityNotFoundException;
> > +
> > +import org.apache.openjpa.kernel.OpenJPAStateManager;
> > +import org.apache.openjpa.kernel.StoreContext;
> > +import org.apache.openjpa.lib.log.Log;
> > +import org.apache.openjpa.lib.util.Localizer;
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.meta.FieldMetaData;
> > +import org.apache.openjpa.persistence.JPAFacadeHelper;
> > +import org.apache.openjpa.persistence.OpenJPAPersistence;
> > +import org.apache.openjpa.util.ApplicationIds;
> > +
> > +/**
> > + * Handles a request from a remote client.
> > + * Reads the socket data.
> > + * Populates the request.
> > + * Determines the processor based on request method and action.
> > + * Delegates to the processor.
> > + * Processor generates the response.
> > + * Writes the response to the stream.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class RequestHandler implements Callable<Void> {
> > + private static final int SPACE = ' ';
> > + private final Socket _socket;
> > + private final ServerContext _server;
> > + private final Log _log;
> > + private static final Localizer _loc =
> Localizer.forPackage(RequestHandler.class);
> > +
> > + public RequestHandler(Socket socket, ServerContext server) {
> > + _socket = socket;
> > + _server = server;
> > + _log = _server.getLog();
> > + }
> > +
> > + public Void call() throws Exception {
> > + Request request = null;
> > + Response response = null;
> > + try {
> > + request = readRequest(_socket.getInputStream());
> > + response = request.process(_server,
> _socket.getOutputStream());
> > + } catch (Exception e) {
> > + response = new ErrorResponse(request, _server, e,
> HttpURLConnection.HTTP_INTERNAL_ERROR,
> > + _socket.getOutputStream());
> > + }
> > + response.writeOut();
> > + return null;
> > + }
> > +
> > +
> > +
> > + /**
> > + * Reads the given CR-LF delimited stream.
> > + * The first line is scanned for the method (first space-delimited
> String) and protocol (the last
> > + * space-delimited String). Accordingly a request is created and
> rest of the input stream content
> > + * is parsed by the request itself.
> > + *
> > + * @param input
> > + * @throws IOException
> > + */
> > + public Request readRequest(InputStream input) throws IOException {
> > + if (_log.isTraceEnabled())
> > + _log.trace("Reading request from the input stream ");
> > +
> > + BufferedReader reader = new BufferedReader(new
> InputStreamReader(input));
> > + String status = reader.readLine();
> > + if (_log.isInfoEnabled())
> > + _log.info("Status Line [" + status + "]");
> > + int spaceFirst = status.indexOf(SPACE);
> > + if (spaceFirst == -1)
> > + throw new IOException("HTTP Method could not be determined
> from [" + status + "]");
> > + int spaceLast = status.lastIndexOf(SPACE);
> > + if (spaceLast == -1)
> > + throw new IOException("HTTP Protocol could not be determined
> from [" + status + "]");
> > + String method = status.substring(0, spaceFirst);
> > + String protocol = status.substring(spaceLast+1);
> > + String path = status.substring(spaceFirst+1, spaceLast);
> > + Request request =
> RequestFactory.getFactory(protocol).createRequest(method);
> > + List<String> lines = new ArrayList<String>();
> > + if (path.equals("/")) {
> > + lines.add(path);
> > + } else {
> > + lines = readlines(reader);
> > + lines.add(0, path);
> > + }
> > + if (lines.isEmpty()) {
> > + throw new IOException("No CR-LF delimited lines could be
> read from " + input);
> > + }
> > + request.read(lines);
> > + return request;
> > + }
> > +
> > +
> > + List<String> readlines(BufferedReader reader) throws IOException {
> > + List<String> buffers = new ArrayList<String>();
> > + String line;
> > + while ((line = reader.readLine()) != null && line.length() > 0)
> {
> > + buffers.add(line);
> > + }
> > + return buffers;
> > + }
> > +
> > +
> > + public String toString() {
> > + return _socket.getInetAddress()+":"+_socket.getPort();
> > + }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/RequestHandler.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,54 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.InputStream;
> > +import java.io.OutputStream;
> > +import java.io.RandomAccessFile;
> > +
> > +import javax.imageio.ImageIO;
> > +
> > +/**
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +@SuppressWarnings("serial")
> > +public class ResourceResponse extends AbstractResponse {
> > + private final InputStream _in;
> > + private final String _mimeType;
> > + public ResourceResponse(Request request, ServerContext ctx,
> InputStream resource, String mimeType,
> > + OutputStream out) throws Exception {
> > + super(request, ctx, out);
> > + _in = resource;
> > + _mimeType = mimeType;
> > + }
> > +
> > + public void writeOut() throws Exception {
> > + print(_request.getProtocol()); println("200 OK");
> > + printHeader("Connection", "close");
> > + printHeader("Content-Type", _mimeType, "charset=UTF-8");
> > + println();
> > + for (int c = 0; (c = _in.read()) != -1;) {
> > + print((char)c);
> > + }
> > + _in.close();
> > + close();
> > + }
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/ResourceResponse.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,31 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.Serializable;
> > +
> > +/**
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public interface Response extends Serializable {
> > +// void setHeader(String key, String value);
> > + void writeOut() throws Exception;
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Response.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> > Added:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> > URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java?rev=1028093&view=auto
> >
> ==============================================================================
> > ---
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> (added)
> > +++
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> Wed Oct 27 20:42:44 2010
> > @@ -0,0 +1,230 @@
> > +/*
> > + * Licensed to the Apache Software Foundation (ASF) under one
> > + * or more contributor license agreements. See the NOTICE file
> > + * distributed with this work for additional information
> > + * regarding copyright ownership. The ASF licenses this file
> > + * to you under the Apache License, Version 2.0 (the
> > + * "License"); you may not use this file except in compliance
> > + * with the License. You may obtain a copy of the License at
> > + *
> > + * http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing,
> > + * software distributed under the License is distributed on an
> > + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> > + * KIND, either express or implied. See the License for the
> > + * specific language governing permissions and limitations
> > + * under the License.
> > + */
> > +
> > +package org.apache.openjpa.persistence.jest;
> > +
> > +import java.io.IOException;
> > +import java.net.ServerSocket;
> > +import java.net.Socket;
> > +import java.util.concurrent.ExecutorService;
> > +import java.util.concurrent.Executors;
> > +
> > +import javax.persistence.EntityManagerFactory;
> > +
> > +import org.apache.openjpa.conf.OpenJPAConfiguration;
> > +import org.apache.openjpa.lib.conf.Configurable;
> > +import org.apache.openjpa.lib.conf.Configuration;
> > +import org.apache.openjpa.lib.log.Log;
> > +import org.apache.openjpa.lib.util.Localizer;
> > +import org.apache.openjpa.meta.ClassMetaData;
> > +import org.apache.openjpa.meta.MetaDataRepository;
> > +import org.apache.openjpa.persistence.EntityManagerFactoryImpl;
> > +
> > +
> > +/**
> > + * A server running on an independent thread that allows a remote,
> language-neutral client to access OpenJPA runtime.
> > + *
> > + * @author Pinaki Poddar
> > + *
> > + */
> > +public class Server implements ServerContext, Configurable, Runnable {
> > + private ServerSocket _listenSocket;
> > + protected ExecutorService _executors;
> > + public final static int DEFAULT_PORT = 6789;
> > + protected int _port = DEFAULT_PORT;
> > + protected int _range = 1;
> > + protected String _format = "xml";
> > + protected Log _log;
> > + protected Thread _thread;
> > + private EntityManagerFactoryImpl _ctx;
> > + private static Localizer _loc = Localizer.forPackage(Server.class);
> > +
> > + /**
> > + * Sets the persistence unit context in which this server will serve
> requests.
> > + * The context must be set before operation.
> > + *
> > + * @param emf an implementation of OpenJPA Persistence Unit. Must
> not be null.
> > + */
> > + public void setContext(EntityManagerFactoryImpl emf) {
> > + if (emf == null)
> > + throw new NullPointerException();
> > + _ctx = emf;
> > + }
> > +
> > + /**
> > + * Gets the persistence unit context in which this server serves
> requests.
> > + *
> > + * @param emf an implementation of OpenJPA Persistence Unit.
> > + */
> > + public EntityManagerFactoryImpl getPersistenceUnit() {
> > + return _ctx;
> > + }
> > +
> > + public Log getLog() {
> > + return _log;
> > + }
> > +
> > + /**
> > + * Start the server in a daemon thread.
> > + * This method is idempotent.
> > + *
> > + * @return true if the server has started by this call or already
> running.
> > + */
> > + public synchronized boolean start() {
> > + try {
> > + if (_thread != null)
> > + return true;
> > + if (createServerSocket()) {
> > + _thread = new Thread(this);
> > + _thread.setDaemon(true);
> > + _thread.start();
> > + return true;
> > + }
> > + return false;
> > + } catch (Exception ex) {
> > + ex.printStackTrace();
> > + return false;
> > + }
> > + }
> > +
> > + /**
> > + * Stops the server.
> > + */
> > + public synchronized void stop() {
> > + _thread.interrupt();
> > + _thread = null;
> > + _executors.shutdownNow();
> > + }
> > +
> > + /**
> > + * Sets the port in which the server will listen.
> > + *
> > + * @param port a positive integer.
> > + */
> > + public void setPort(int port) {
> > + _port = port;
> > + }
> > +
> > + /**
> > + * Gets the current port.
> > + *
> > + * @return the port number. Defaults to default HTTP port.
> > + */
> > + public int getPort() {
> > + return _port;
> > + }
> > +
> > + /**
> > + * Sets the range of ports the server will attempt at start.
> > + *
> > + * @param range a positive integer.
> > + */
> > + public void setRange(int range) {
> > + if (range > 0)
> > + _range = range;
> > + }
> > +
> > + public void setFormat(String format) {
> > + _format = format;
> > + }
> > +
> > + public String getFormat() {
> > + return _format;
> > + }
> > +
> > + /**
> > + * Sets the range of ports the server will attempt at start.
> > + * @return a positive integer. Defaults to 1.
> > + */
> > + public int getRange() {
> > + return _range;
> > + }
> > +
> > + public void run() {
> > + _log.info(_loc.get("server-starting", this));
> > +
> > + _executors = Executors.newCachedThreadPool();
> > + while (!Thread.interrupted()) {
> > + try {
> > + Socket socket = _listenSocket.accept();
> > + if (_log.isTraceEnabled())
> > + _log.trace(_loc.get("server-request", socket));
> > + RequestHandler request = new RequestHandler(socket,
> this);
> > + _executors.submit(request);
> > + } catch (IOException e) {
> > + e.printStackTrace();
> > + }
> > + }
> > + }
> > +
> > + private boolean createServerSocket() {
> > + int p = _port;
> > + int p2 = p + _range;
> > + Exception error = null;
> > + for (; _listenSocket == null && p < p2; ) {
> > + try {
> > + _listenSocket = new ServerSocket(p);
> > + } catch (IOException ex) {
> > + p++;
> > + error = ex;
> > + }
> > + }
> > + if (_listenSocket != null) {
> > + if (p != _port) {
> > + _port = p;
> > + _log.warn(_loc.get("server-reconfigured", _port));
> > + }
> > + } else {
> > + if (error != null) {
> > + _log.warn(_loc.get("server-failed", this, _port,
> error));
> > + }
> > + }
> > + return _listenSocket != null;
> > + }
> > +
> > + // Configurable contract
> > + public void setConfiguration(Configuration conf) {
> > + _log = conf.getLog("Remote");
> > + }
> > +
> > + public void startConfiguration() {
> > + }
> > +
> > + public void endConfiguration() {
> > + }
> > +
> > +
> > + // Server side utilities
> > +
> > + /**
> > + * Resolves the given alias to a persistent class meta data.
> > + *
> > + * @exception if no meta data available for the given alias
> > + */
> > + public ClassMetaData resolve(String alias) {
> > + MetaDataRepository repos =
> _ctx.getConfiguration().getMetaDataRepositoryInstance();
> > + return repos.getMetaData(alias,
> Thread.currentThread().getContextClassLoader(), true);
> > + }
> > +
> > + public String toString() {
> > + if (_listenSocket == null) return "JEST Server [not strated]";
> > + return "JEST Server " +
> _listenSocket.getInetAddress()+":"+_listenSocket.getLocalPort();
> > + }
> > +
> > +}
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> >
> ------------------------------------------------------------------------------
> > svn:eol-style = native
> >
> > Propchange:
> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/jest/Server.java
> >
> ------------------------------------------------------------------------------
> > svn:mime-type = text/plain
> >
> >
> >
>