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 <head> section.<br>
+ * <code><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><init-param></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