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

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

Author: ppoddar
Date: Thu Dec  9 21:50:47 2010
New Revision: 1044138

URL: http://svn.apache.org/viewvc?rev=1044138&view=rev
Log:
OPENJPA-1851: Add JEST to OpenJPA trunk

Added:
    openjpa/trunk/openjpa-jest/
    openjpa/trunk/openjpa-jest/pom.xml   (with props)
    openjpa/trunk/openjpa-jest/src/
    openjpa/trunk/openjpa-jest/src/main/
    openjpa/trunk/openjpa-jest/src/main/java/
    openjpa/trunk/openjpa-jest/src/main/java/org/
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/AbstractCommand.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Closure.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Constants.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/DomainCommand.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/ExceptionFormatter.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/FindCommand.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/IOR.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTCommand.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTContext.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTServlet.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JPAServletContext.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSON.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObject.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObjectFormatter.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/MetamodelHelper.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/ObjectFormatter.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/ProcessingException.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/PropertiesCommand.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/PropertiesFormatter.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/PrototypeFactory.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/QueryCommand.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/TokenReplacedStream.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/XMLFormatter.java   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/
    openjpa/trunk/openjpa-jest/src/main/resources/org/
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/help/
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/help/entity-name.html   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/help/fetch-plan.html   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/help/query.html   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/help/response-format.html   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/arrow_right.jpg   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/domain.jpg   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/find.jpg   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/help.jpg   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/home.jpg   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/jest.jpg   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/monitor.jpg   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/properties.jpg   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/query2.png   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/images/underconstruction.jpg   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/jest-instance.xsd   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/jest.css   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/jest.html   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/jest.js   (with props)
    openjpa/trunk/openjpa-jest/src/main/resources/org/apache/openjpa/persistence/jest/localizer.properties   (with props)

Added: openjpa/trunk/openjpa-jest/pom.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/pom.xml?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/pom.xml (added)
+++ openjpa/trunk/openjpa-jest/pom.xml Thu Dec  9 21:50:47 2010
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.   
+-->
+<!-- 
+    Please keep the project tag on one line to avoid confusing 
+    the release plugin.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.openjpa</groupId>
+        <artifactId>openjpa-parent</artifactId>
+        <version>2.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.openjpa</groupId>
+    <artifactId>openjpa-jest</artifactId>
+    <packaging>jar</packaging>
+    <name>OpenJPA JEST</name>
+    
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.openjpa</groupId>
+            <artifactId>openjpa-kernel</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-jpa_2.0_spec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.openjpa</groupId>
+            <artifactId>openjpa-persistence</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.4</version>
+        </dependency>
+    </dependencies>
+</project>

Propchange: openjpa/trunk/openjpa-jest/pom.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/AbstractCommand.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/AbstractCommand.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/AbstractCommand.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/AbstractCommand.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,342 @@
+/*
+ * 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 static java.net.HttpURLConnection.HTTP_BAD_REQUEST;
+import static org.apache.openjpa.persistence.jest.Constants.QUALIFIER_FORMAT;
+import static org.apache.openjpa.persistence.jest.Constants.QUALIFIER_PLAN;
+import static org.apache.openjpa.persistence.jest.Constants._loc;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.openjpa.enhance.PersistenceCapable;
+import org.apache.openjpa.kernel.BrokerImpl;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.persistence.FetchPlan;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.OpenJPAQuery;
+
+/**
+ * The abstract base class for all commands available to JEST.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+abstract class AbstractCommand implements JESTCommand {
+    public static final char EQUAL = '=';
+    public static final String PATH_SEPARATOR = "/";
+    public static final Collection<String> EMPTY_LIST = Collections.emptySet();
+    protected ObjectFormatter<?> _formatter;
+    protected static PrototypeFactory<Format,ObjectFormatter<?>> _ff = 
+        new PrototypeFactory<Format,ObjectFormatter<?>>();
+
+    private Map<String, String> _qualifiers = new HashMap<String, String>();
+    private Map<String, String> _args = new HashMap<String, String>();
+    private Map<String, String> _margs = new HashMap<String, String>();
+    protected final JPAServletContext ctx;
+    
+    static {
+        _ff.register(Format.xml,  XMLFormatter.class);
+        _ff.register(Format.json, JSONObjectFormatter.class);
+    }
+    protected AbstractCommand(JPAServletContext ctx) {
+        this.ctx = ctx;
+    }
+    
+    public String getMandatoryArgument(String key) {
+        return get(key, _margs);
+    }
+    
+    public String getArgument(String key) {
+        return get(key, _args);
+    }
+    
+    public boolean hasArgument(String key) {
+        return has(key, _args);
+    }
+    
+    public Map<String, String> getArguments() {
+        return _args;
+    }
+    
+    public String getQualifier(String key) {
+        return get(key, _qualifiers);
+    }
+    
+    public boolean hasQualifier(String key) {
+        return has(key, _qualifiers);
+    }
+    
+    protected boolean isBooleanQualifier(String key) {
+        if (hasQualifier(key)) {
+            Object value = getQualifier(key);
+            return value == null || "true".equalsIgnoreCase(value.toString());
+        }
+        return false;
+    }
+    
+    public Map<String, String> getQualifiers() {
+        return _qualifiers;
+    }
+    
+    /**
+     * Parses HTTP Request for the qualifier and argument of a command.
+     * <br>
+     * Each servlet path segment, except the first (which is the command name itself), is a qualifier.
+     * Each qualifier can be either a key or a key-value pair separated by a = sign.
+     * <br>
+     * Each request parameter key-value pair is an argument. A concrete command may specify mandatory
+     * arguments (e.g. <code>type</code> must be mandatory argument for <code>find</code> command,
+     * or <code>q</code> for <code>query</code>. The mandatory arguments, if any, are <em>not</em> captured
+     * in the argument list. 
+     * <br>
+     * The qualifiers and arguments are immutable after parse.
+     */
+    public void parse() throws ProcessingException {
+        HttpServletRequest request = ctx.getRequest();
+        String path = request.getPathInfo();
+        if (path != null) {
+            path = path.substring(1);
+            String[] segments = path.split(PATH_SEPARATOR);
+            for (int i = 1; i < segments.length; i++) {
+                String segment = segments[i];
+                int idx = segment.indexOf(EQUAL);
+                if (idx == -1) {
+                    _qualifiers.put(segment, null);
+                } else {
+                    _qualifiers.put(segment.substring(0, idx), segment.substring(idx+1));
+                }
+            }
+        }
+        _qualifiers = Collections.unmodifiableMap(_qualifiers);
+        
+        Enumeration<?> names = request.getParameterNames();
+        Collection<String> mandatoryArgs = getMandatoryArguments();
+        
+        while (names.hasMoreElements()) {
+            String key = names.nextElement().toString();
+            if (key.startsWith("dojo.")) continue;
+            put(key, request.getParameter(key), mandatoryArgs.contains(key) ? _margs : _args);
+        }
+        _args = Collections.unmodifiableMap(_args);
+        _margs = Collections.unmodifiableMap(_margs);
+        
+        validate();
+    }
+    
+    /**
+     * Gets the mandatory arguments.
+     * 
+     * @return empty list by default.
+     */
+    protected Collection<String> getMandatoryArguments() {
+        return EMPTY_LIST;
+    }
+    
+    /**
+     * Gets the minimum number of arguments excluding the mandatory arguments.
+     * 
+     * @return zero by default.
+     */
+    protected int getMinimumArguments() {
+        return 0;
+    }
+    
+    /**
+     * Gets the maximum number of arguments excluding the mandatory arguments.
+     * 
+     * @return Integer.MAX_VALUE by default.
+     */
+    protected int getMaximumArguments() {
+        return Integer.MAX_VALUE;
+    }
+    
+    protected Format getDefaultFormat() {
+        return Format.xml;
+    }
+    
+    /**
+     * Gets the valid qualifiers.
+     * 
+     * @return empty list by default.
+     */
+    protected Collection<String> getValidQualifiers() {
+        return EMPTY_LIST;
+    }
+    
+    /**
+     * Called post-parse to validate this command has requisite qualifiers and arguments.
+     */
+    protected void validate() {
+        HttpServletRequest request = ctx.getRequest();
+        Collection<String> validQualifiers = getValidQualifiers();
+        for (String key : _qualifiers.keySet()) {
+            if (!validQualifiers.contains(key)) {
+                throw new ProcessingException(ctx,_loc.get("parse-invalid-qualifier", this, key, validQualifiers),
+                    HTTP_BAD_REQUEST);
+            }
+        }
+        Collection<String> mandatoryArgs = getMandatoryArguments();
+        for (String key : mandatoryArgs) {
+            if (request.getParameter(key) == null) {
+                throw new ProcessingException(ctx, _loc.get("parse-missing-mandatory-argument", this, key,  
+                    request.getParameterMap().keySet()), HTTP_BAD_REQUEST);
+            }
+        }
+        if (_args.size() < getMinimumArguments()) {
+            throw new ProcessingException(ctx, _loc.get("parse-less-argument", this, _args.keySet(),  
+                getMinimumArguments()), HTTP_BAD_REQUEST);
+        }
+        if (_args.size() > getMaximumArguments()) {
+            throw new ProcessingException(ctx, _loc.get("parse-less-argument", this, _args.keySet(),  
+                getMinimumArguments()), HTTP_BAD_REQUEST);
+        }
+    }
+    
+    private String get(String key, Map<String,String> map) {
+        return map.get(key);
+    }
+    
+    private String put(String key, String value, Map<String,String> map) {
+        return map.put(key, value);
+    }
+    
+    private boolean has(String key, Map<String,String> map) {
+        return map.containsKey(key);
+    }
+    
+    public ObjectFormatter<?> getObjectFormatter() {
+        if (_formatter == null) {
+            String rformat = getQualifier(QUALIFIER_FORMAT);
+            Format format = null;
+            if (rformat == null) {
+                format = getDefaultFormat();
+            } else {
+                try {
+                    format = Format.valueOf(rformat);
+                } catch (Exception e) {
+                    throw new ProcessingException(ctx, _loc.get("format-not-supported", new Object[]{format, 
+                        ctx.getRequest().getPathInfo(), _ff.getRegisteredKeys()}), HTTP_BAD_REQUEST);
+                }
+            }
+            _formatter = _ff.newInstance(format);
+            if (_formatter == null) {
+                throw new ProcessingException(ctx, _loc.get("format-not-supported", new Object[]{format, 
+                    ctx.getRequest().getPathInfo(), _ff.getRegisteredKeys()}), HTTP_BAD_REQUEST);
+            }
+        }
+        return _formatter;
+    }
+
+    protected OpenJPAStateManager toStateManager(Object obj) {
+        if (obj instanceof OpenJPAStateManager)
+            return (OpenJPAStateManager)obj;
+        if (obj instanceof PersistenceCapable) {
+            return (OpenJPAStateManager)((PersistenceCapable)obj).pcGetStateManager();
+        }
+        return null;
+    }
+    
+    protected List<OpenJPAStateManager> toStateManager(Collection<?> objects) {
+        List<OpenJPAStateManager> sms = new ArrayList<OpenJPAStateManager>();
+        for (Object o : objects) {
+            OpenJPAStateManager sm = toStateManager(o);
+            if (sm != null) sms.add(sm);
+        }
+        return sms;
+    }
+    
+    protected void pushFetchPlan(Object target) {
+        if (!hasQualifier(QUALIFIER_PLAN))
+            return;
+        OpenJPAEntityManager em = ctx.getPersistenceContext();
+        FetchPlan plan = em.pushFetchPlan();
+        BrokerImpl broker = (BrokerImpl)JPAFacadeHelper.toBroker(em);
+        if (target instanceof OpenJPAEntityManager) {
+            broker.setCacheFinderQuery(false);
+        } else if (target instanceof OpenJPAQuery) {
+            broker.setCachePreparedQuery(false);
+        }
+        
+        String[] plans = getQualifier(QUALIFIER_PLAN).split(",");
+        for (String p : plans) {
+            p = p.trim();
+            if (p.charAt(0) == '-') {
+                plan.removeFetchGroup(p.substring(1));
+            } else {
+                plan.addFetchGroup(p);
+            }
+        }
+    }
+    
+    protected void popFetchPlan(boolean finder) {
+        if (!hasQualifier(QUALIFIER_PLAN))
+            return;
+        OpenJPAEntityManager em = ctx.getPersistenceContext();
+        BrokerImpl broker = (BrokerImpl)JPAFacadeHelper.toBroker(em);
+        if (finder) {
+            broker.setCacheFinderQuery(false);
+        } else {
+            broker.setCachePreparedQuery(false);
+        }
+    }
+
+    protected void debug(HttpServletRequest request, HttpServletResponse response, JPAServletContext ctx) 
+        throws IOException {
+        response.setContentType(Constants.MIME_TYPE_PLAIN);
+        PrintWriter writer = response.getWriter();
+        
+        writer.println("URI             = [" + request.getRequestURI() + "]");
+        writer.println("URL             = [" + request.getRequestURL() + "]");
+        writer.println("Servlet Path    = [" + request.getServletPath() + "]"); // this is one we need
+        writer.println("Context Path    = [" + request.getContextPath() + "]");
+        writer.println("Translated Path = [" + request.getPathTranslated() + "]");// not decoded
+        writer.println("Path Info       = [" + request.getPathInfo() + "]");// decoded
+        String query = request.getQueryString();
+        if (query != null) {
+            query = URLDecoder.decode(request.getQueryString(),"UTF-8");
+        }
+        writer.println("Query           = [" + query + "]"); // and this one
+        int i = 0;
+        for (Map.Entry<String, String> e : _qualifiers.entrySet()) {
+            writer.println("Qualifier [" + i + "] = [" + e.getKey() + ": " + e.getValue() + "]");
+            i++;
+        }
+        i = 0;
+        for (Map.Entry<String, String> e : _args.entrySet()) {
+            writer.println("Parameter [" + i + "] = [" + e.getKey() + ": " + e.getValue() + "]");
+            i++;
+        }
+    }
+    
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Closure.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Closure.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Closure.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Closure.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,107 @@
+/*
+ * 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.BitSet;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.openjpa.enhance.PersistenceCapable;
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.meta.FieldMetaData;
+import org.apache.openjpa.meta.JavaTypes;
+
+/**
+ * Computes closure of a collection of managed objects.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class Closure implements Iterable<OpenJPAStateManager> {
+    private Set<OpenJPAStateManager> _visited = new LinkedHashSet<OpenJPAStateManager>();
+    
+    public Closure(OpenJPAStateManager root) {
+        this(Collections.singleton(root));
+    }
+    
+    public Closure(Collection<OpenJPAStateManager> roots) {
+        for (OpenJPAStateManager sm : roots) {
+            visit(sm);
+        }
+    }
+    
+    private void visit(OpenJPAStateManager sm) {
+        if (sm == null)
+            return;
+        boolean isVisited = !_visited.add(sm);
+        if (isVisited) return;
+        BitSet loaded = sm.getLoaded();
+        FieldMetaData[] fmds = sm.getMetaData().getFields();
+        for (FieldMetaData fmd : fmds) {
+            int idx = fmd.getIndex();
+            if (!loaded.get(idx))
+                continue;
+            if (fmd.getElement().getTypeMetaData() == null && fmd.getValue().getTypeMetaData() == null)
+                continue;
+            switch (fmd.getDeclaredTypeCode()) {
+                case JavaTypes.PC:
+                    visit(toStateManager(sm.fetch(idx)));
+                    break;
+                case JavaTypes.ARRAY:
+                    Object[] values = (Object[])sm.fetch(idx);
+                    for (Object o : values)
+                        visit(toStateManager(o));
+                    break;
+                case JavaTypes.COLLECTION:
+                    Collection<?> members = (Collection<?>)sm.fetch(idx);
+                    for (Object o : members)
+                        visit(toStateManager(o));
+                    break;
+                case JavaTypes.MAP:
+                    Map<?,?> map = (Map<?,?>)sm.fetch(idx);
+                    for (Map.Entry<?,?> entry : map.entrySet()) {
+                        visit(toStateManager(entry.getKey()));
+                        visit(toStateManager(entry.getValue()));
+                    }
+                    break;
+                default:
+            }
+        }
+    }
+    
+    OpenJPAStateManager toStateManager(Object o) {
+        if (o instanceof PersistenceCapable) {
+            return (OpenJPAStateManager)((PersistenceCapable)o).pcGetStateManager();
+        }
+        return null;
+    }
+
+    public Iterator<OpenJPAStateManager> iterator() {
+        return _visited.iterator();
+    }
+    
+    String ior(OpenJPAStateManager sm) {
+        return sm.getMetaData().getDescribedType().getSimpleName()+'-'+sm.getObjectId();
+    }
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Constants.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Constants.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Constants.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/Constants.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,141 @@
+/*
+ * 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 org.apache.openjpa.lib.util.Localizer;
+
+/**
+ * Static String constants
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public interface Constants {
+    /**
+     * Command Qualifiers
+     */
+    public static final String QUALIFIER_FORMAT      = "format";
+    public static final String QUALIFIER_PLAN        = "plan";
+    public static final String QUALIFIER_MAXRESULT   = "max";
+    public static final String QUALIFIER_FIRSTRESULT = "first";
+    public static final String QUALIFIER_NAMED       = "named";
+    public static final String QUALIFIER_SINGLE      = "single";
+    
+    /**
+     * Command Arguments
+     */
+    public static final String ARG_QUERY    = "q";
+    public static final String ARG_TYPE     = "type";
+
+    
+    /**
+     * Mime Types
+     */
+    public static final String MIME_TYPE_PLAIN = "text/plain";
+    public static final String MIME_TYPE_JS    = "text/javascript";
+    public static final String MIME_TYPE_CSS   = "text/css";
+    public static final String MIME_TYPE_XML   = "text/xml";
+    public static final String MIME_TYPE_JSON  = "application/json";
+     
+    public static final String CONTEXT_ROOT  = "/";
+    public static final String JEST_TEMPLATE  = "jest.html";
+    
+    /**
+     * Servlet initialization parameters
+     */
+    public static final String INIT_PARA_UNIT       = "persistence.unit";
+    public static final String INIT_PARA_STANDALONE = "standalone";
+   
+    /**
+     * Dojo Toolkit URL and Themes
+     */
+    public static final String DOJO_BASE_URL = "http://ajax.googleapis.com/ajax/libs/dojo/1.5";
+    public static final String DOJO_THEME    = "claro";
+    
+    
+      
+    /**
+     * Root element of XML instances. Must match the name defined in <A href="jest-instance.xsd>jest-instance.xsd</A>.
+     */
+    public static final String ROOT_ELEMENT_INSTANCE   = "instances";
+    public static final String ATTR_ID      = "id";
+    public static final String ATTR_REL     = "rel";
+    public static final String ATTR_SRC     = "src";
+    public static final String ATTR_TYPE    = "type";
+    public static final String ATTR_NAME    = "name";
+    public static final String ATTR_VERSION = "version";
+    public static final String ATTR_CLASS   = "class";
+    public static final String ATTR_HREF    = "href";
+    public static final String ATTR_STYLE   = "style";
+    public static final String ATTR_NULL           = "null";
+    public static final String ATTR_MEMBER_TYPE    = "member-type";
+    public static final String ATTR_KEY_TYPE       = "key-type";
+    public static final String ATTR_VALUE_TYPE     = "value-type";
+    
+    /**
+     * Elements and attributes in properties XML.
+     */
+    public static final String ROOT_ELEMENT_PROPERTIES = "properties";
+    public static final String ELEMENT_PROPERTY     = "property";
+    public static final String ATTR_PROPERTY_KEY     = "name";
+    public static final String ATTR_PROPERTY_VALUE     = "value";
+    
+    
+    public static final String ROOT_ELEMENT_ERROR      = "error";
+    public static final String ELEMENT_ERROR_HEADER    = "error-header";
+    public static final String ELEMENT_ERROR_MESSAGE   = "error-message";
+    public static final String ELEMENT_ERROR_TRACE     = "stacktrace";
+    
+    /**
+     * Root element of XML meta-model. Must match the name defined in <A href="jest-model.xsd>jest-model.xsd</A>.
+     */
+    public static final String ROOT_ELEMENT_MODEL      = "metamodel";
+    
+    public static final String ELEMENT_INSTANCE      = "instance";
+    public static final String ELEMENT_URI           = "uri";
+    public static final String ELEMENT_DESCRIPTION   = "description";
+    public static final String ELEMENT_REF           = "ref";
+    public static final String ELEMENT_NULL_REF      = "null";
+    public static final String ELEMENT_MEMBER        = "member";
+    public static final String ELEMENT_ENTRY         = "entry";
+    public static final String ELEMENT_ENTRY_KEY     = "key";
+    public static final String ELEMENT_ENTRY_VALUE   = "value";
+
+    
+    
+    /**
+     * JEST resources
+     */
+//    public static final String JEST_STYLESHEET         = "jest.css";
+//    public static final String JEST_SCRIPT_INSTANCES   = "instances.js";
+//    public static final String JEST_SCRIPT_DOMAIN      = "model.js";
+    public static final String JEST_INSTANCE_XSD       = "jest-instance.xsd";
+    
+    static final String JAXP_SCHEMA_SOURCE      = "http://java.sun.com/xml/jaxp/properties/schemaSource";
+    static final String JAXP_SCHEMA_LANGUAGE    = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+    
+    /**
+     * Common instances
+     */
+    public static final Localizer _loc = Localizer.forPackage(JESTContext.class);
+    public static final String NULL_VALUE          = "null";
+
+
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/DomainCommand.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/DomainCommand.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/DomainCommand.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/DomainCommand.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,60 @@
+/*
+ * 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.Collection;
+import java.util.List;
+
+import static org.apache.openjpa.persistence.jest.Constants.ARG_TYPE;
+import static org.apache.openjpa.persistence.jest.Constants._loc;
+
+/**
+ * Marshals a JPA meta-model in the configured format to the response output stream.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+class DomainCommand extends AbstractCommand {
+    private static final List<String> _validQualifiers = Arrays.asList("format");
+    
+    public DomainCommand(JPAServletContext ctx) {
+        super(ctx);
+    }
+    
+    protected Collection<String> getValidQualifiers() {
+        return _validQualifiers;
+    }
+    
+    protected int getMaximumArguments() {
+        return 0;
+    }    
+    
+    public String getAction() {
+        return "domain";
+    }
+    
+    public void process() throws ProcessingException, IOException {
+        getObjectFormatter().writeOut(ctx.getPersistenceContext().getMetamodel(), 
+            _loc.get("domain-title").toString(), _loc.get("domain-desc").toString(), ctx.getRequestURI(), 
+            ctx.getResponse().getOutputStream());
+    }
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/ExceptionFormatter.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/ExceptionFormatter.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/ExceptionFormatter.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/ExceptionFormatter.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,62 @@
+/*
+ * 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.PrintWriter;
+import java.io.StringWriter;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+
+/**
+ * Formats error stack trace.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+class ExceptionFormatter extends XMLFormatter {
+    /**
+     * Creates a XML Document with given header and stack trace of the given error.
+     * @param header
+     * @param e
+     */
+    public Document createXML(String header, Throwable e) {
+        Element root = newDocument(Constants.ROOT_ELEMENT_ERROR);
+        Document doc = root.getOwnerDocument();
+        Element errorHeader  = doc.createElement(Constants.ELEMENT_ERROR_HEADER);
+        Element errorMessage = doc.createElement(Constants.ELEMENT_ERROR_MESSAGE);
+        Element stackTrace   = doc.createElement(Constants.ELEMENT_ERROR_TRACE);
+
+        errorHeader.setTextContent(header);
+        errorMessage.appendChild(doc.createCDATASection(e.getMessage()));
+        
+        StringWriter buf = new StringWriter();
+        e.printStackTrace(new PrintWriter(buf, true));
+        stackTrace.appendChild(doc.createCDATASection(buf.toString()));
+        
+        root.appendChild(errorHeader);
+        root.appendChild(errorMessage);
+        root.appendChild(stackTrace);
+        
+        return doc;
+    }
+
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/FindCommand.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/FindCommand.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/FindCommand.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/FindCommand.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,100 @@
+/*
+ * 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 static org.apache.openjpa.persistence.jest.Constants.*;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.EntityManager;
+
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+import org.apache.openjpa.util.ApplicationIds;
+
+
+/**
+ * @author Pinaki Poddar
+ *
+ */
+class FindCommand extends AbstractCommand {
+    private static final List<String> _mandatoryArgs   = Arrays.asList(ARG_TYPE);
+    private static final List<String> _validQualifiers = Arrays.asList("format", "plan");
+    
+    public FindCommand(JPAServletContext ctx) {
+        super(ctx);
+    }
+    
+    @Override
+    protected Collection<String> getMandatoryArguments() {
+        return _mandatoryArgs;
+    }
+    
+    @Override
+    protected int getMinimumArguments() {
+        return 1;
+    }
+    
+    protected Collection<String> getValidQualifiers() {
+        return _validQualifiers;
+    }
+
+    @Override
+    public void process() throws ProcessingException {
+        OpenJPAEntityManager em = ctx.getPersistenceContext();
+        String type = getMandatoryArgument(ARG_TYPE);
+        ClassMetaData meta = ctx.resolve(type);
+        Map<String,String> parameters = getArguments();
+        Object[] pks = new Object[parameters.size()];
+        Iterator<Map.Entry<String,String>> params = parameters.entrySet().iterator();
+        for (int i = 0; i < parameters.size(); i++) {
+            pks[i] = params.next().getKey();
+        }
+        Object oid = ApplicationIds.fromPKValues(pks, meta);
+        pushFetchPlan(em);
+        try {
+            Object pc = em.find(meta.getDescribedType(), oid); 
+            if (pc != null) {
+                OpenJPAStateManager sm = toStateManager(pc);
+                ObjectFormatter<?> formatter = getObjectFormatter();
+                ctx.getResponse().setContentType(formatter.getMimeType());
+                try {
+                    formatter.writeOut(Collections.singleton(sm), em.getMetamodel(), 
+                        _loc.get("find-title").toString(), _loc.get("find-desc").toString(), ctx.getRequestURI(), 
+                        ctx.getResponse().getOutputStream());
+                } catch (IOException e) {
+                    throw new ProcessingException(ctx, e);
+                }
+            } else {
+                throw new ProcessingException(ctx, _loc.get("entity-not-found", type, Arrays.toString(pks)), 
+                    HttpURLConnection.HTTP_NOT_FOUND);
+            }
+        } finally {
+            popFetchPlan(true);
+        }
+    }
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/IOR.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/IOR.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/IOR.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/IOR.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,45 @@
+/*
+ * 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 static org.apache.openjpa.persistence.jest.Constants.NULL_VALUE;
+
+import org.apache.openjpa.kernel.OpenJPAStateManager;
+
+/**
+ * String reference of a managed object.
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+public class IOR {
+    public static final char DASH = '-';
+    /**
+     * Stringified representation of a managed instance identity.
+     * The simple Java type name and the persistent identity separated by a {@link Constants#DASH dash}.
+     *  
+     * @param sm a managed instance.
+     * @return
+     */
+    public static String toString(OpenJPAStateManager sm) {
+        if (sm == null) return NULL_VALUE;
+        return sm.getMetaData().getDescribedType().getSimpleName() + DASH + sm.getObjectId();
+    }
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTCommand.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTCommand.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTCommand.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTCommand.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,92 @@
+/*
+ * 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.Map;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+
+
+/**
+ * Interface for JEST commands. A JEST command denotes a JPA operation such as <code>find</code>,
+ * <code>query</code> or <code>domain</code>. Besides signifying a JPA operation, a command may have
+ * zero or more qualifiers and arguments. 
+ * <br>
+ * A qualifier qualifies the action to be performed. For example, a <code>query</code> command may be qualified
+ * to return a single instance as its result, or limit its result to first 20 instances etc.
+ * <br>
+ * An argument is an argument to the target JPA method. For example, <code>find</code> command has
+ * arguments for the type of the instance and the primary key. A <code>query</code> command has the
+ * query string as its argument.
+ * <p>
+ * A concrete command instance is an outcome of parsing a {@link HttpServletRequest request}. 
+ * The {@link HttpServletRequest#getPathInfo() path} segments are parsed for qualifiers. 
+ * The {@link HttpServletRequest#getQueryString() query string} is parsed for the arguments. 
+ * <p>
+ * A JEST command often attaches special semantics to a standard URI syntax. For example, all JEST
+ * URI enforces that the first segment of a servlet path denotes the command moniker e.g. the URI<br>
+ * <code>http://www.jpa.com/jest/find/plan=myPlan?type=Person&1234</code><br>
+ * with context root <code>http://www.jpa.com/jest</code> has the servlet path <code>/find/plan=myPlan</code>
+ * and query string <code>type=Person&1234</code>.
+ * <br>
+ * The first path segment <code>find</code> will determine that the command is to <em>find</em> a 
+ * persistent entity of type <code>Person</code> and primary key <code>1234</code> using a fetch plan
+ * named <code>myPlan</code>.
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+public interface JESTCommand {
+    /**
+     * Supported format monikers.
+     */
+    public static enum Format {xml, json};
+    
+    /**
+     * Parse the given request to populate qualifiers and parameters of this command.
+     * A command can interpret and consume certain path segments or parameters of the
+     * original request. During {@link #process(ServletRequest, ServletResponse, JPAServletContext) processing}
+     * phase, the parameters and qualifiers are accessed from the parsed command itself rather than
+     * from the 
+     */
+    public void parse() throws ProcessingException;
+    
+    /**
+     * Accessors for this command's arguments and qualifiers. 
+     * @return
+     * @exception IllegalStateException if accessed prior to parsing.
+     */
+    public Map<String,String> getArguments();
+    public String getArgument(String key);
+    public boolean hasArgument(String key);
+    public Map<String,String> getQualifiers();
+    public String getQualifier(String key);
+    public boolean hasQualifier(String key);
+    
+    /**
+     * Process the given request and write the output on to the given response in the given context.
+     * @throws ProcessingException 
+     * 
+     */
+    public void process() throws ProcessingException, IOException;
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTContext.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTContext.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTContext.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTContext.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,330 @@
+/*
+ * 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 static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR;
+import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
+import static org.apache.openjpa.persistence.jest.Constants.CONTEXT_ROOT;
+
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.openjpa.conf.OpenJPAConfiguration;
+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.OpenJPAEntityManager;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
+
+/**
+ * An operational context combines a {@link OpenJPAEntityManager persistence context} and a HTTP execution
+ * context expressed as a {@link HttpServletRequest request} and {@link HttpServletResponse response}. 
+ * <br>
+ * This context {@link #getAction(String) parses} the HTTP request URL to identity the command and then 
+ * {@link #execute() executes} it.
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+public class JESTContext implements JPAServletContext {
+    private final String _unit;
+    private final OpenJPAEntityManagerFactory _emf;
+    private OpenJPAEntityManager _em;
+    private final HttpServletRequest   _request;
+    private final HttpServletResponse  _response;
+    protected MetaDataRepository       _repos;
+    private String _rootResource;
+    protected Log _log;
+    protected static PrototypeFactory<String,JESTCommand> _cf = new PrototypeFactory<String,JESTCommand>();
+    public static final Localizer _loc = Localizer.forPackage(JESTContext.class);
+    private static final String ONE_YEAR_FROM_NOW; 
+    public static final char QUERY_SEPARATOR = '?'; 
+    
+    /**
+     * Registers known commands in a {@link PrototypeFactory registry}.
+     * 
+     */
+    static {
+        _cf.register("find",  FindCommand.class);
+        _cf.register("query", QueryCommand.class);
+        _cf.register("domain", DomainCommand.class);
+        _cf.register("properties", PropertiesCommand.class);
+        
+        Calendar now = Calendar.getInstance();
+        now.add(Calendar.YEAR, 1);
+        ONE_YEAR_FROM_NOW = new Date(now.getTimeInMillis()).toString();
+    }
+    
+    public JESTContext(String unit, OpenJPAEntityManagerFactory emf, HttpServletRequest request, 
+        HttpServletResponse response) {
+        _unit = unit;
+        _emf = emf;
+        _request = request;
+        _response = response;
+        OpenJPAConfiguration conf = _emf.getConfiguration();
+        _log = conf.getLog("JEST");
+        _repos = conf.getMetaDataRepositoryInstance();
+    }
+        
+    /**
+     * Gets the name of the persistence unit.
+     */
+    public String getPersistenceUnitName() {
+        return _unit;
+    }
+    
+    /**
+     * Gets the persistence context. The persistence context is lazily constructed because all commands
+     * may not need it.  
+     */
+    public OpenJPAEntityManager getPersistenceContext() {
+        if (_em == null) {
+            _em = _emf.createEntityManager();
+        }
+        return _em;
+    }
+    
+    /**
+     * Gets the request.
+     */
+    public HttpServletRequest getRequest() {
+        return _request;
+    }
+    
+    /**
+     * 
+     */
+    public URI getRequestURI() {
+        StringBuffer buf = _request.getRequestURL();
+        String query = _request.getQueryString();
+        if (query != null) {
+            buf.append(QUERY_SEPARATOR).append(query);
+        }
+        try {
+            return new URI(buf.toString());
+        } catch (URISyntaxException e) {
+            throw new ProcessingException(this, _loc.get("bad-uri", _request.getRequestURL()), HTTP_INTERNAL_ERROR);
+        }
+        
+    }
+    
+    /**
+     * Gets the response.
+     */
+    public HttpServletResponse getResponse() {
+        return _response;
+    }
+    
+    /**
+     * Executes the request.
+     * <br>
+     * Execution starts with parsing the {@link HttpServletRequest#getPathInfo() request path}. 
+     * The {@linkplain #getAction(String) first path segment} is interpreted as action key, and
+     * if a action with the given key is registered then the control is delegated to the command.
+     * The command parses the entire {@link HttpServletRequest request} for requisite qualifiers and
+     * arguments and if the parse is successful then the command is 
+     * {@linkplain JESTCommand#process() executed} in this context.
+     * <br>
+     * If path is null, or no  command is registered for the action or the command can not parse
+     * the request, then a last ditch attempt is made to {@linkplain #findResource(String) find} a resource. 
+     * This fallback lookup is important because the response can contain hyperlinks to stylesheets or
+     * scripts. The browser will resolve such hyperlinks relative to the original response. 
+     * <br>
+     * For example, let the original request URL be:<br>
+     * <code>http://host:port/demo/jest/find?type=Actor&Robert</code>
+     * <br>
+     * The response to this request is a HTML page that contained a hyperlink to <code>jest.css</code> stylesheet
+     * in its &lt;head&gt; section.<br>
+     * <code>&lt;link ref="jest.css" .....></code> 
+     * <br> 
+     * The browser will resolve the hyperlink by sending back another request as<br>
+     * <code>http://host:port/demo/jest/find/jest.css</code>
+     * <br>
+     *   
+     * @throws Exception
+     */
+    public void execute() throws Exception {
+        String path = _request.getPathInfo();
+        if (isContextRoot(path)) {
+            getRootResource();
+            return;
+        }
+        String action = getAction(path);
+        JESTCommand command = _cf.newInstance(action, this);
+        if (command == null) {
+            findResource(path.substring(1));
+            return;
+        }
+        try {
+            command.parse();
+            command.process();
+        } catch (ProcessingException e1) {
+            throw e1;
+        } catch (Exception e2) {
+            try {
+                findResource(path.substring(action.length()+1));
+            } catch (ProcessingException e3) {
+                throw e2;
+            }
+        }
+    }
+    
+    /**
+     * Gets the action from the given path.
+     * 
+     * @param path a string
+     * @return if null, returns context root i.e. <code>'/'</code> character. 
+     * Otherwise, if the path starts with context root, then returns the substring before the 
+     * next <code>'/'</code> character or end of the string, whichever is earlier. 
+     * If the path does not start with context root, returns 
+     * the substring before the first <code>'/'</code> character or end of the string, whichever is earlier. 
+     */
+    public static String getAction(String path) {
+        if (path == null)
+            return CONTEXT_ROOT;
+        if (path.startsWith(CONTEXT_ROOT))
+            path = path.substring(1); 
+        int idx = path.indexOf(CONTEXT_ROOT); 
+        return idx == -1 ? path : path.substring(0, idx);
+    }
+    
+    
+    public ClassMetaData resolve(String alias) {
+        ClassLoader loader = Thread.currentThread().getContextClassLoader();
+        return _repos.getMetaData(alias, loader, true);
+    }
+    
+    /**
+     * A resource is always looked up with respect to this class. 
+     * 
+     * @param rsrc
+     * @throws ProcessingException
+     */
+    void findResource(String rsrc) throws ProcessingException {
+        _response.setHeader("Cache-Control", "public");
+        _response.setHeader("Expires", ONE_YEAR_FROM_NOW);
+        InputStream in = getClass().getResourceAsStream(rsrc);
+        if (in == null) { // try again as a relative path
+            if (rsrc.startsWith(CONTEXT_ROOT)) {
+                in = getClass().getResourceAsStream(rsrc.substring(1));
+                if (in == null) {
+                    throw new ProcessingException(this, _loc.get("resource-not-found", rsrc), HTTP_NOT_FOUND);
+                }
+            }
+        }
+        try {
+            String mimeType = _request.getSession().getServletContext().getMimeType(rsrc);
+            _response.setContentType(mimeType);
+            OutputStream out = _response.getOutputStream();
+            if (mimeType.startsWith("image/")) {
+                byte[] b = new byte[1024];
+                int i = 0;
+                for (int l = 0; (l = in.read(b)) != -1;) {
+                    out.write(b, 0, l);
+                    i += l;
+                }
+                _response.setContentLength(i);
+            } else {
+                for (int c = 0; (c = in.read()) != -1;) {
+                    out.write((char)c);
+                }
+            }
+        } catch (IOException e) {
+            throw new ProcessingException(this, e, _loc.get("resource-not-found", rsrc), HTTP_NOT_FOUND);
+        }
+    }
+    
+
+    
+    private void log(String s) {
+        log((short)-1, s);
+    }
+    
+    public void log(short level, String message) {
+        switch (level) {
+            case Log.INFO:  _log.info(message); break;
+            case Log.ERROR: _log.fatal(message); break;
+            case Log.FATAL: _log.fatal(message); break;
+            case Log.TRACE: _log.trace(message); break;
+            case Log.WARN:  _log.warn(message); break;
+            default: _request.getSession().getServletContext().log(message); 
+            
+            break;
+        }
+    }
+    
+    /**
+     * Is this path a context root?
+     * @param path
+     * @return
+     */
+    boolean isContextRoot(String path) {
+        return (path == null || CONTEXT_ROOT.equals(path));
+    }
+    
+    /**
+     * Root resource is a HTML template with deployment specific tokens such as name of the persistence unit
+     * or base url. On first request for this resource, the tokens in the templated HTML file gets replaced  
+     * by the actual deployment specific value into a string. This string (which is an entire HTML file)
+     * is then written to the response.
+     * 
+     * @see TokenReplacedStream
+     * @throws IOException
+     */
+    private void getRootResource() throws IOException {
+        _response.setHeader("Cache-Control", "public");
+        _response.setHeader("Expires", ONE_YEAR_FROM_NOW);
+        if (_rootResource == null) {
+            String[] tokens = {
+                "${persistence.unit}", getPersistenceUnitName(),
+                "${jest.uri}",         _request.getRequestURL().toString(),
+                "${webapp.name}",     _request.getContextPath().startsWith(CONTEXT_ROOT) 
+                                    ? _request.getContextPath().substring(1)
+                                    : _request.getContextPath(),
+                "${servlet.name}",     _request.getServletPath().startsWith(CONTEXT_ROOT) 
+                                    ? _request.getServletPath().substring(1)
+                                    : _request.getServletPath(),
+                "${server.name}",     _request.getServerName(),
+                "${server.port}",     ""+_request.getServerPort(),
+                
+                "${dojo.base}",     Constants.DOJO_BASE_URL,
+                "${dojo.theme}",    Constants.DOJO_THEME,
+                
+            };
+            InputStream in = getClass().getResourceAsStream(Constants.JEST_TEMPLATE);
+            CharArrayWriter out = new CharArrayWriter();
+            new TokenReplacedStream().replace(in, out, tokens);
+            _rootResource = out.toString();
+        }
+        _response.getOutputStream().write(_rootResource.getBytes());
+    }
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTServlet.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTServlet.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTServlet.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JESTServlet.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,161 @@
+/*
+ * 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 static org.apache.openjpa.persistence.jest.Constants.INIT_PARA_UNIT;
+import static org.apache.openjpa.persistence.jest.Constants.INIT_PARA_STANDALONE;
+import static org.apache.openjpa.persistence.jest.Constants._loc;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.persistence.Persistence;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.openjpa.kernel.AbstractBrokerFactory;
+import org.apache.openjpa.kernel.BrokerFactory;
+import org.apache.openjpa.persistence.JPAFacadeHelper;
+import org.apache.openjpa.persistence.OpenJPAEntityManagerFactory;
+import org.apache.openjpa.persistence.OpenJPAPersistence;
+
+/**
+ * A specialized HTTP servlet to interpret HTTP requests as Java Persistent API commands
+ * on a running persistence unit. The persistence unit is identified by the name of the
+ * unit and is supplied to this servlet during its initialization. The component using
+ * the persistent unit and this servlet must be within the same module scope.
+ * <p>
+ * The syntax of the request URL is described in 
+ * <a href="https://cwiki.apache.org/openjpa/jest-syntax.html">OpenJPA web site</a>.
+ * <p>
+ * The response to a resource request is represented in various format, namely  
+ * XML, JSON or a JavaScript that will dynamically render in the browser. The format
+ * can be controlled via the initialization parameter <code>response.format</code> in 
+ * <code>&lt;init-param&gt;</code> clause or per request basis via <code>format=xml|dojo|json</code> 
+ * encoded in the path expression of the Request URI. 
+ * <p>
+ * Servlet initialization parameter
+ * <table cellspacing="20px">
+ * <tr><th>Parameter</th><th>Value</th></tr>
+ * <tr><td>persistence.unit</td><td>Name of the persistence unit. Mandatory</td></tr>
+ * <tr><td>response.format</td><td>Default format used for representation. Defaults to <code>xml</code>.</td></tr>
+ * </table>
+ * <br>
+ * @author Pinaki Poddar
+ *
+ */
+@SuppressWarnings("serial")
+public class JESTServlet extends HttpServlet  {
+    private String _unit;
+    private boolean _debug;
+    private OpenJPAEntityManagerFactory _emf;
+    
+    @Override
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+        _debug = "true".equalsIgnoreCase(config.getInitParameter("debug"));
+        _unit = config.getInitParameter(INIT_PARA_UNIT);
+        if (_unit == null) {
+            throw new ServletException(_loc.get("no-persistence-unit-param").toString());
+        }
+        boolean standalone = "true".equalsIgnoreCase(config.getInitParameter(INIT_PARA_STANDALONE));
+        if (standalone) {
+            createPersistenceUnit();
+        }
+        if (findPersistenceUnit()) {
+            config.getServletContext().log(_loc.get("servlet-init", _unit).toString());
+        } else {
+            config.getServletContext().log(_loc.get("servlet-not-init", _unit).toString());
+        }
+    }
+    
+    /**
+     * Peeks into the servlet path of the request to create appropriate {@link JESTCommand JEST command}.
+     * Passes the request on to the command which is responsible for generating a response. 
+     */
+    @Override
+    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+        debug(request);
+        if (findPersistenceUnit()) {
+            JESTContext ctx = new JESTContext(_unit, _emf, request, response);
+            try {
+                ctx.execute();
+            } catch (Exception e) {
+                handleError(ctx, e);
+            }
+        } else {
+            throw new ServletException(_loc.get("no-persistence-unit", _unit).toString());
+        } 
+    }
+    
+    protected void createPersistenceUnit() throws ServletException {
+        try {
+            Map<String, Object> map = new HashMap<String, Object>();
+            map.put("openjpa.EntityManagerFactoryPool", true);
+            _emf = OpenJPAPersistence.cast(Persistence.createEntityManagerFactory(_unit, map));
+        } catch (Exception e) {
+            throw new ServletException(_loc.get("no-persistence-unit").toString(), e);
+        } 
+    }
+    protected boolean findPersistenceUnit() {
+        if (_emf == null) {
+            BrokerFactory bf = AbstractBrokerFactory.getPooledFactoryForKey(_unit);
+            if (bf != null) {
+                _emf = (OpenJPAEntityManagerFactory)bf.getUserObject(JPAFacadeHelper.EMF_KEY);
+            }
+        }
+        return _emf != null;
+    }
+    
+    protected void handleError(JPAServletContext ctx, Throwable t) throws IOException {
+        if (t instanceof ProcessingException) {
+            ((ProcessingException)t).printStackTrace();
+        } else {
+            new ProcessingException(ctx, t).printStackTrace();
+        }
+    }
+
+    @Override
+    public void destroy() {
+        _emf = null;
+        _unit = null;;
+    }
+    
+    private void debug(HttpServletRequest r) {
+        if (!_debug) return;
+//        log("-----------------------------------------------------------");
+        log(r.getRemoteUser() + "@" + r.getRemoteHost() + ":" + r.getRemotePort() + "[" + r.getPathInfo() + "]");
+//        log("Request URL    = [" + request.getRequestURL() + "]");
+//        log("Request URI    = [" + request.getRequestURI() + "]");
+//        log("Servlet Path   = [" + request.getServletPath() + "]");
+//        log("Context Path   = [" + request.getContextPath() + "]");
+//        log("Path Info      = [" + request.getPathInfo() + "]");
+//        log("Path Translated = [" + request.getPathTranslated() + "]");
+    }
+    
+    public void log(String s) {
+        System.err.println(s);
+        super.log(s);
+    }
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JPAServletContext.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JPAServletContext.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JPAServletContext.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JPAServletContext.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,82 @@
+/*
+ * 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.net.URI;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.meta.ClassMetaData;
+import org.apache.openjpa.persistence.OpenJPAEntityManager;
+
+/**
+ * An operating context provides a {@link EntityManage persistence context} and utility functions within
+ * which all JEST commands execute.
+ *  
+ * @author Pinaki Poddar
+ *
+ */
+public interface JPAServletContext {
+    /**
+     * Get the persistence context of the operational context.
+     */
+    public OpenJPAEntityManager getPersistenceContext();
+    
+    /**
+     * Get the persistence unit name.
+     */
+    public String getPersistenceUnitName();
+    
+    /**
+     * Get the HTTP Request.
+     */
+    public HttpServletRequest getRequest();
+    
+    /**
+     * Get the HTTP Response.
+     */
+    public HttpServletResponse getResponse();
+    
+    /**
+     * Get the requested URI. 
+     * @return
+     */
+    public URI getRequestURI();
+    
+    /**
+     * Resolve the given alias to meta-data of the persistent type.
+     * @param alias a moniker for the Java type. It can be fully qualified type name or entity name
+     * or simple name of the actual persistent Java class.
+     * 
+     * @return meta-data for the given name. 
+     * @exception raises runtime exception if the given name can not be identified to a persistent
+     * Java type.
+     */
+    public ClassMetaData resolve(String alias);
+    
+    /**
+     * Logging message.
+     * @param level OpenJPA defined {@link Log#INFO log levels}. Invalid levels will print the message on console.
+     * @param message a printable message.
+     */
+    public void log(short level, String message);
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSON.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSON.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSON.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSON.java Thu Dec  9 21:50:47 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;
+
+/**
+ * A generic interface for a JSON encoded instance.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public interface JSON {
+    /**
+     * Render into a string buffer.
+     * 
+     * @param level level at which this instance is being rendered
+     * @return a mutable buffer
+     */
+    public StringBuilder asString(int level);
+    
+    public static final char FIELD_SEPARATOR  = ',';
+    public static final char MEMBER_SEPARATOR = ',';
+    public static final char VALUE_SEPARATOR  = ':';
+    public static final char IOR_SEPARTOR     = '-';
+    public static final char QUOTE            = '"';
+    public static final char SPACE            = ' ';
+    public static final char OBJECT_START     = '{';
+    public static final char OBJECT_END       = '}';
+    public static final char ARRAY_START      = '[';
+    public static final char ARRAY_END        = ']';
+    
+    public static final String NEWLINE        = "\r\n";
+    public static final String NULL_LITERAL   = "null";
+    public static final String REF_MARKER     = "$ref";
+    public static final String ID_MARKER      = "$id";
+    public static final String ARRAY_EMPTY    = "[]";
+    
+}

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

Added: openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObject.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObject.java?rev=1044138&view=auto
==============================================================================
--- openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObject.java (added)
+++ openjpa/trunk/openjpa-jest/src/main/java/org/apache/openjpa/persistence/jest/JSONObject.java Thu Dec  9 21:50:47 2010
@@ -0,0 +1,225 @@
+/*
+ * 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.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A JSON instance for persistence.
+ * <br>
+ * Persistent instances have a persistent identity that extends beyond the process lifetime unlike other common
+ * identity such as {@linkplain System#identityHashCode(Object) identity hash code} for a Java instance in a JVM.
+ * <br>
+ * A JSONObject instance must need such a persistent identity.  
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class JSONObject implements JSON {
+    private final String _type;
+    private final String _id;
+    private final boolean _ref;
+    private final Map<String, Object> _values;
+    
+    public JSONObject(String type, Object id, boolean ref) {
+        _type = type;
+        _id   = id.toString();
+        _ref  = ref;
+        _values = new LinkedHashMap<String, Object>();
+    }
+    
+    public void set(String key, Object value) {
+        _values.put(key, value);
+    }
+    
+    public void write(PrintWriter writer) {
+        writer.println(toString());
+    }
+    public String toString() {
+        return asString(0).toString();
+    }
+    
+    public StringBuilder asString(int indent) {
+        StringBuilder buf = new StringBuilder().append(OBJECT_START);
+        buf.append(encodeField(_ref ? REF_MARKER : ID_MARKER, ior(), 0));
+        if (_ref) {
+            return buf.append(OBJECT_END);
+        }
+        StringBuilder tab = newIndent(indent+1);
+        for (Map.Entry<String, Object> e : _values.entrySet()) {
+            buf.append(FIELD_SEPARATOR).append(NEWLINE);
+            buf.append(tab).append(encodeField(e.getKey(), e.getValue(), indent+1));
+        }
+        buf.append(NEWLINE)
+           .append(newIndent(indent))
+           .append(OBJECT_END);
+        return buf;
+    }
+    
+    /**
+     * Encoding a JSON field is a quoted field name, followed by a :, followed by a value (which itself can be JSON)
+     * @param field
+     * @param value
+     * @param indent
+     * @return
+     */
+    private static StringBuilder encodeField(String field, Object value, int indent) {
+        return new StringBuilder()
+              .append(quoteFieldName(field))
+              .append(VALUE_SEPARATOR)
+              .append(quoteFieldValue(value, indent));
+    }
+    
+    private static StringBuilder newIndent(int indent) {
+        char[] tabs = new char[indent*4];
+        Arrays.fill(tabs, SPACE);
+        return new StringBuilder().append(tabs);
+    }
+    
+    
+    StringBuilder ior() {
+        return new StringBuilder(_type).append('-').append(_id);
+    }
+    
+    private static StringBuilder quoteFieldName(String s) {
+        return new StringBuilder().append(QUOTE).append(s).append(QUOTE);
+    }
+    
+    /**
+     * Creates a StringBuilder for the given value.
+     * If the value is null, outputs <code>null</code> without quote
+     * If the value is Number, outputs the value without quote
+     * If the value is JSON, outputs the string rendition of value
+     * Otherwise quoted value
+     * @param o
+     * @return
+     */
+    private static StringBuilder quoteFieldValue(Object o, int indent) {
+        if (o == null) return new StringBuilder(NULL_LITERAL);
+        if (o instanceof Number) return new StringBuilder(o.toString());
+        if (o instanceof JSON) return ((JSON)o).asString(indent);
+        return quoted(o.toString());
+    }
+    
+    private static StringBuilder quoted(Object o) {
+        if (o == null) return new StringBuilder(NULL_LITERAL);
+        return new StringBuilder().append(QUOTE).append(o.toString()).append(QUOTE);
+    }
+    
+    public static class Array implements JSON {
+        private List<Object> _members = new ArrayList<Object>();
+        
+        public void add(Object o) {
+            _members.add(o);
+        }
+        public String toString() {
+            return asString(0).toString();
+        }
+        
+        public StringBuilder asString(int indent) {
+            StringBuilder buf = new StringBuilder().append(ARRAY_START);
+            StringBuilder tab = JSONObject.newIndent(indent+1);
+            for (Object o : _members) {
+                if (buf.length() > 1) buf.append(MEMBER_SEPARATOR);
+                buf.append(NEWLINE);
+                if (o instanceof JSON)
+                    buf.append(tab).append(((JSON)o).asString(indent+1));
+                else 
+                    buf.append(tab).append(o);
+            }
+            buf.append(NEWLINE)
+               .append(JSONObject.newIndent(indent))
+               .append(ARRAY_END);
+           
+            return buf;
+        }
+    }
+    
+    public static class KVMap implements JSON {
+        private Map<Object,Object> _entries = new LinkedHashMap<Object,Object>();
+        
+        public void put(Object k, Object v) {
+            _entries.put(k,v);
+        }
+        
+        public String toString() {
+            return asString(0).toString();
+        }
+        
+        public StringBuilder asString(int indent) {
+            StringBuilder buf = new StringBuilder().append(ARRAY_START);
+            StringBuilder tab = JSONObject.newIndent(indent+1);
+            for (Map.Entry<Object, Object> e : _entries.entrySet()) {
+                if (buf.length()>1) buf.append(MEMBER_SEPARATOR);
+                buf.append(NEWLINE);
+                Object key = e.getKey();
+                if (key instanceof JSON)
+                    buf.append(tab).append(((JSON)key).asString(indent+1));
+                else 
+                    buf.append(tab).append(key);
+                buf.append(VALUE_SEPARATOR);
+                Object value = e.getValue();
+                if (value instanceof JSON)
+                    buf.append(((JSON)value).asString(indent+2));
+                else 
+                    buf.append(value);
+                
+            }
+            buf.append(NEWLINE)
+               .append(JSONObject.newIndent(indent))
+               .append(ARRAY_END);
+            return buf;
+        }
+    }
+    
+    public static void main(String[] args) throws Exception {
+        JSONObject o = new JSONObject("Person", 1234, false);
+        JSONObject r = new JSONObject("Person", 1234, true);
+        JSONObject f = new JSONObject("Person", 2345, false);
+        Array  a = new Array();
+        a.add(f);
+        a.add(3456);
+        a.add(null);
+        a.add(r);
+        a.add(null);
+        KVMap map = new KVMap();
+        map.put("k1", r);
+        map.put("k2", f);
+        map.put("k3", null);
+        map.put("k4", 3456);
+        map.put(null, 6789);
+
+        f.set("name", "Mary");
+        f.set("age", 30);
+        f.set("friend", r);
+        o.set("name", "John");
+        o.set("age", 20);
+        o.set("friend", f);
+        o.set("friends", a);
+        o.set("map", map);
+        
+        System.err.println(o);
+    }
+}

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