OPENJPA-1851: Add JEST to OpenJPA trunk
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1044138 13f79535-47bb-0310-9956-ffa450edef68
|
@ -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>
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
||||
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 = "[]";
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.openjpa.persistence.jest;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.CharArrayWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.kernel.StoreContext;
|
||||
import org.apache.openjpa.meta.FieldMetaData;
|
||||
import org.apache.openjpa.meta.JavaTypes;
|
||||
import org.apache.openjpa.persistence.meta.Members;
|
||||
import static org.apache.openjpa.persistence.jest.Constants.MIME_TYPE_JSON;
|
||||
|
||||
/**
|
||||
* Marshals a root instance and its persistent closure as JSON object.
|
||||
* The closure is resolved against the persistence context that contains the root instance.
|
||||
* The JSON format introduces a $id and $ref to address reference that pure JSON does not.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class JSONObjectFormatter implements ObjectFormatter<JSON> {
|
||||
|
||||
public String getMimeType() {
|
||||
return MIME_TYPE_JSON;
|
||||
}
|
||||
|
||||
public void encode(Object obj, JPAServletContext ctx) {
|
||||
if (obj instanceof OpenJPAStateManager) {
|
||||
try {
|
||||
JSON result = encodeManagedInstance((OpenJPAStateManager)obj,
|
||||
ctx.getPersistenceContext().getMetamodel());
|
||||
PrintWriter writer = ctx.getResponse().getWriter();
|
||||
writer.println(result.toString());
|
||||
} catch (Exception e) {
|
||||
throw new ProcessingException(ctx, e);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException(this + " does not know how to encode " + obj);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public JSON writeOut(Collection<OpenJPAStateManager> sms, Metamodel model, String title, String desc,
|
||||
URI uri, OutputStream out) throws IOException {
|
||||
JSON json = encode(sms,model);
|
||||
out.write(json.toString().getBytes());
|
||||
return json;
|
||||
}
|
||||
|
||||
public JSON encode(Collection<OpenJPAStateManager> sms, Metamodel model) {
|
||||
return encodeManagedInstances(sms, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given managed instance into a new XML element as a child of the given parent node.
|
||||
*
|
||||
* @param sm a managed instance, can be null.
|
||||
* @param parent the parent node to which the new node be attached.
|
||||
*/
|
||||
private JSON encodeManagedInstance(final OpenJPAStateManager sm, Metamodel model) {
|
||||
return encodeManagedInstance(sm, new HashSet<OpenJPAStateManager>(), 0, false, model);
|
||||
}
|
||||
|
||||
private JSON encodeManagedInstances(final Collection<OpenJPAStateManager> sms, Metamodel model) {
|
||||
JSONObject.Array result = new JSONObject.Array();
|
||||
for (OpenJPAStateManager sm : sms) {
|
||||
result.add(encodeManagedInstance(sm, new HashSet<OpenJPAStateManager>(), 0, false, model));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes the closure of a persistent instance into a XML element.
|
||||
*
|
||||
* @param sm the managed instance to be encoded. Can be null.
|
||||
* @param parent the parent XML element to which the new XML element be added. Must not be null. Must be
|
||||
* owned by a document.
|
||||
* @param visited the persistent instances that had been encoded already. Must not be null or immutable.
|
||||
*
|
||||
* @return the new element. The element has been appended as a child to the given parent in this method.
|
||||
*/
|
||||
private JSONObject encodeManagedInstance(final OpenJPAStateManager sm, final Set<OpenJPAStateManager> visited,
|
||||
int indent, boolean indentPara, Metamodel model) {
|
||||
if (visited == null) {
|
||||
throw new IllegalArgumentException("null closure for encoder");
|
||||
}
|
||||
if (sm == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean ref = !visited.add(sm);
|
||||
JSONObject root = new JSONObject(typeOf(sm), sm.getObjectId(), ref);;
|
||||
if (ref) {
|
||||
return root;
|
||||
}
|
||||
|
||||
BitSet loaded = sm.getLoaded();
|
||||
StoreContext ctx = (StoreContext)sm.getGenericContext();
|
||||
List<Attribute<?, ?>> attrs = MetamodelHelper.getAttributesInOrder(sm.getMetaData(), model);
|
||||
|
||||
for (int i = 0; i < attrs.size(); i++) {
|
||||
FieldMetaData fmd = ((Members.Member<?, ?>) attrs.get(i)).fmd;
|
||||
if (!loaded.get(fmd.getIndex()))
|
||||
continue;
|
||||
Object value = sm.fetch(fmd.getIndex());
|
||||
switch (fmd.getDeclaredTypeCode()) {
|
||||
case JavaTypes.BOOLEAN:
|
||||
case JavaTypes.BYTE:
|
||||
case JavaTypes.CHAR:
|
||||
case JavaTypes.DOUBLE:
|
||||
case JavaTypes.FLOAT:
|
||||
case JavaTypes.INT:
|
||||
case JavaTypes.LONG:
|
||||
case JavaTypes.SHORT:
|
||||
|
||||
case JavaTypes.BOOLEAN_OBJ:
|
||||
case JavaTypes.BYTE_OBJ:
|
||||
case JavaTypes.CHAR_OBJ:
|
||||
case JavaTypes.DOUBLE_OBJ:
|
||||
case JavaTypes.FLOAT_OBJ:
|
||||
case JavaTypes.INT_OBJ:
|
||||
case JavaTypes.LONG_OBJ:
|
||||
case JavaTypes.SHORT_OBJ:
|
||||
|
||||
case JavaTypes.BIGDECIMAL:
|
||||
case JavaTypes.BIGINTEGER:
|
||||
case JavaTypes.DATE:
|
||||
case JavaTypes.NUMBER:
|
||||
case JavaTypes.CALENDAR:
|
||||
case JavaTypes.LOCALE:
|
||||
case JavaTypes.STRING:
|
||||
case JavaTypes.ENUM:
|
||||
root.set(fmd.getName(),value);
|
||||
break;
|
||||
|
||||
case JavaTypes.PC:
|
||||
if (value == null) {
|
||||
root.set(fmd.getName(), null);
|
||||
} else {
|
||||
root.set(fmd.getName(),encodeManagedInstance(ctx.getStateManager(value), visited,
|
||||
indent+1, false, model));
|
||||
}
|
||||
break;
|
||||
|
||||
case JavaTypes.ARRAY:
|
||||
Object[] values = (Object[])value;
|
||||
value = Arrays.asList(values);
|
||||
// no break;
|
||||
case JavaTypes.COLLECTION:
|
||||
if (value == null) {
|
||||
root.set(fmd.getName(), null);
|
||||
break;
|
||||
}
|
||||
Collection<?> members = (Collection<?>)value;
|
||||
JSONObject.Array array = new JSONObject.Array();
|
||||
root.set(fmd.getName(), array);
|
||||
if (members.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
boolean basic = fmd.getElement().getTypeMetaData() == null;
|
||||
for (Object o : members) {
|
||||
if (o == null) {
|
||||
array.add(null);
|
||||
} else {
|
||||
if (basic) {
|
||||
array.add(o);
|
||||
} else {
|
||||
array.add(encodeManagedInstance(ctx.getStateManager(o), visited, indent+1, true,
|
||||
model));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JavaTypes.MAP:
|
||||
if (value == null) {
|
||||
root.set(fmd.getName(), null);
|
||||
break;
|
||||
}
|
||||
Set<Map.Entry> entries = ((Map)value).entrySet();
|
||||
JSONObject.KVMap map = new JSONObject.KVMap();
|
||||
root.set(fmd.getName(), map);
|
||||
if (entries.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
boolean basicKey = fmd.getElement().getTypeMetaData() == null;
|
||||
boolean basicValue = fmd.getValue().getTypeMetaData() == null;
|
||||
for (Map.Entry<?,?> e : entries) {
|
||||
Object k = e.getKey();
|
||||
Object v = e.getValue();
|
||||
if (!basicKey) {
|
||||
k = encodeManagedInstance(ctx.getStateManager(k), visited, indent+1, true, model);
|
||||
}
|
||||
if (!basicValue) {
|
||||
v = encodeManagedInstance(ctx.getStateManager(e.getValue()), visited,
|
||||
indent+1, false, model);
|
||||
}
|
||||
map.put(k,v);
|
||||
}
|
||||
break;
|
||||
|
||||
case JavaTypes.INPUT_STREAM:
|
||||
case JavaTypes.INPUT_READER:
|
||||
root.set(fmd.getName(), streamToString(value));
|
||||
break;
|
||||
|
||||
case JavaTypes.PC_UNTYPED:
|
||||
case JavaTypes.OBJECT:
|
||||
case JavaTypes.OID:
|
||||
root.set(fmd.getName(), "***UNSUPPORTED***");
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
String typeOf(OpenJPAStateManager sm) {
|
||||
return sm.getMetaData().getDescribedType().getSimpleName();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert the given stream (either an InutStream or a Reader) to a String
|
||||
* to be included in CDATA section of a XML document.
|
||||
*
|
||||
* @param value the field value to be converted. Can not be null
|
||||
* @return
|
||||
*/
|
||||
String streamToString(Object value) {
|
||||
Reader reader = null;
|
||||
if (value instanceof InputStream) {
|
||||
reader = new BufferedReader(new InputStreamReader((InputStream)value));
|
||||
} else if (value instanceof Reader) {
|
||||
reader = (Reader)value;
|
||||
} else {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
CharArrayWriter writer = new CharArrayWriter();
|
||||
try {
|
||||
for (int c; (c = reader.read()) != -1;) {
|
||||
writer.write(c);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSON encode(Metamodel model) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSON writeOut(Metamodel model, String title, String desc, URI uri, OutputStream out) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.openjpa.persistence.jest;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.ManagedType;
|
||||
import javax.persistence.metamodel.MapAttribute;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
import javax.persistence.metamodel.PluralAttribute;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.meta.JavaTypes;
|
||||
import org.apache.openjpa.persistence.meta.Members;
|
||||
import static org.apache.openjpa.persistence.jest.Constants.*;
|
||||
|
||||
/**
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class MetamodelHelper {
|
||||
public static final char DASH = '-';
|
||||
public static final char UNDERSCORE = '_';
|
||||
|
||||
/**
|
||||
* Attribute Category makes a finer distinction over PersistentAttributeType declared in
|
||||
* {@link Attribute.PersistentAttributeType} such as id, version, lob or enum.
|
||||
* <br>
|
||||
* <b>Important</b>: The name of the enumerated elements is important because
|
||||
* a) some of these names are same as in Attribute.PersistentAttributeType enumeration
|
||||
* b) names are used by XML serialization with underscores replaced by dash and decapitalized
|
||||
*
|
||||
*/
|
||||
public static enum AttributeCategory {
|
||||
ID, VERSION, BASIC, ENUM, EMBEDDED, LOB,
|
||||
ONE_TO_ONE, MANY_TO_ONE, ONE_TO_MANY, ELEMENT_COLLECTION, MANY_TO_MANY
|
||||
}
|
||||
|
||||
public static List<Attribute<?,?>> getAttributesInOrder(Class<?> cls, Metamodel model) {
|
||||
return getAttributesInOrder(model.managedType(cls));
|
||||
}
|
||||
|
||||
public static List<Attribute<?,?>> getAttributesInOrder(ClassMetaData meta, Metamodel model) {
|
||||
return getAttributesInOrder(meta.getDescribedType(), model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the attributes of the given type in defined order.
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
public static List<Attribute<?,?>> getAttributesInOrder(ManagedType<?> type) {
|
||||
List<Attribute<?,?>> list = new ArrayList<Attribute<?,?>>(type.getAttributes());
|
||||
Collections.sort(list, new AttributeComparator());
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean isId(Attribute<?,?> a) {
|
||||
if (a instanceof SingularAttribute)
|
||||
return ((SingularAttribute<?,?>)a).isId();
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isVersion(Attribute<?,?> a) {
|
||||
if (a instanceof SingularAttribute)
|
||||
return ((SingularAttribute<?,?>)a).isVersion();
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isEnum(Attribute<?,?> a) {
|
||||
if (a instanceof Members.Member) {
|
||||
int type = ((Members.Member<?,?>)a).fmd.getDeclaredTypeCode();
|
||||
return type == JavaTypes.ENUM;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isLob(Attribute<?,?> a) {
|
||||
if (a instanceof Members.Member) {
|
||||
int type = ((Members.Member<?,?>)a).fmd.getDeclaredTypeCode();
|
||||
return type == JavaTypes.INPUT_READER || type == JavaTypes.INPUT_STREAM;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a ordinal value of enumerated persistent attribute category.
|
||||
*
|
||||
* @param attr
|
||||
* @return
|
||||
*/
|
||||
public static AttributeCategory getAttributeCategory(Attribute<?,?> attr) {
|
||||
if (isId(attr))
|
||||
return AttributeCategory.ID;
|
||||
if (isVersion(attr))
|
||||
return AttributeCategory.VERSION;
|
||||
if (isLob(attr))
|
||||
return AttributeCategory.LOB;
|
||||
if (isEnum(attr))
|
||||
return AttributeCategory.ENUM;
|
||||
switch (attr.getPersistentAttributeType()) {
|
||||
case BASIC :
|
||||
return AttributeCategory.BASIC;
|
||||
case EMBEDDED:
|
||||
return AttributeCategory.EMBEDDED;
|
||||
case ONE_TO_ONE:
|
||||
return AttributeCategory.ONE_TO_ONE;
|
||||
case MANY_TO_ONE:
|
||||
return AttributeCategory.MANY_TO_ONE;
|
||||
case ONE_TO_MANY:
|
||||
case ELEMENT_COLLECTION:
|
||||
return AttributeCategory.ONE_TO_MANY;
|
||||
case MANY_TO_MANY:
|
||||
return AttributeCategory.MANY_TO_MANY;
|
||||
}
|
||||
throw new RuntimeException(attr.toString());
|
||||
}
|
||||
|
||||
public static String getTagByAttributeType(Attribute<?, ?> attr) {
|
||||
return getAttributeCategory(attr).name().replace(UNDERSCORE, DASH).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets name of the attribute type. For collection and map type attribute, the name is
|
||||
* appended with generic type argument names.
|
||||
* @param attr
|
||||
* @return
|
||||
*/
|
||||
public static String getAttributeTypeName(Attribute<?, ?> attr) {
|
||||
StringBuilder name = new StringBuilder(attr.getJavaType().getSimpleName());
|
||||
switch (attr.getPersistentAttributeType()) {
|
||||
case ONE_TO_MANY:
|
||||
case ELEMENT_COLLECTION:
|
||||
name.append("<")
|
||||
.append(((PluralAttribute<?,?,?>)attr).getBindableJavaType().getSimpleName())
|
||||
.append(">");
|
||||
break;
|
||||
case MANY_TO_MANY:
|
||||
name.append("<")
|
||||
.append(((MapAttribute<?,?,?>)attr).getKeyJavaType().getSimpleName())
|
||||
.append(',')
|
||||
.append(((MapAttribute<?,?,?>)attr).getBindableJavaType().getSimpleName())
|
||||
.append(">");
|
||||
break;
|
||||
default:
|
||||
}
|
||||
return name.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares attribute by their category and within the same category by name.
|
||||
*
|
||||
*/
|
||||
public static class AttributeComparator implements Comparator<Attribute<?,?>> {
|
||||
public int compare(Attribute<?, ?> a1, Attribute<?, ?> a2) {
|
||||
AttributeCategory t1 = getAttributeCategory(a1);
|
||||
AttributeCategory t2 = getAttributeCategory(a2);
|
||||
if (t1.equals(t2)) {
|
||||
return a1.getName().compareTo(a2.getName());
|
||||
} else {
|
||||
return t1.compareTo(t2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
|
||||
/**
|
||||
* A parameterized interface defines the protocol for converting {@link OpenJPAStateManager managed} persistence
|
||||
* instances or a persistent {@link Metamodel domain model} into a form suitable for transport to a language-neutral
|
||||
* client such as an web browser.
|
||||
* <p>
|
||||
* The interface prefers that the resultant resource as a <em>complete</em> representation i.e. all the references
|
||||
* contained in the resource can be resolved within the same resource itself. As the intended recipient of this
|
||||
* resource is a remote client, an <em>incomplete</em> resource will require the client to request further for
|
||||
* any (unresolved) reference resulting in a <em>chatty</em> protocol.
|
||||
* <p>
|
||||
* This interface also defines methods for writing the representation into an output stream e.g.
|
||||
* {@link HttpServletResponse#getOutputStream() response output stream} of a HTTP Servlet.
|
||||
* <p>
|
||||
* Implementation Note: Each concrete formatter type is registered with {@linkplain PrototypeFactory factory}
|
||||
* that requires the implementation to have a no-argument constructor.
|
||||
*
|
||||
* @param <T> the type of encoded output
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public interface ObjectFormatter<T> {
|
||||
public static final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd, yyyy");
|
||||
|
||||
/**
|
||||
* Gets the mime type produced by this formatter.
|
||||
*/
|
||||
public String getMimeType();
|
||||
|
||||
/**
|
||||
* Encode the {@link Closure persistent closure} of the given collection of managed instances as a
|
||||
* resource e.g a XML or HTML document or an interactive document with JavaScript or a JSON array.
|
||||
* Exact nature of the output type is the generic parameter of this interface.
|
||||
*
|
||||
* @param objs a collection of managed instances
|
||||
* @param model domain model
|
||||
*
|
||||
* @return an encoded object e.g. a XML or HTML Document or a JSON object.
|
||||
*/
|
||||
public T encode(Collection<OpenJPAStateManager> objs, Metamodel model);
|
||||
|
||||
/**
|
||||
* Encode the given domain model in to a object.
|
||||
*
|
||||
* @param model a meta-model of managed types
|
||||
*
|
||||
* @return an encoded object e.g. a XML or HTML Document or a JSON object.
|
||||
*/
|
||||
public T encode(Metamodel model);
|
||||
|
||||
/**
|
||||
* Encodes the {@link Closure persistent closure} of the given collection of objects, then write it into
|
||||
* the given output stream.
|
||||
*
|
||||
* @param objs the collection of objects to be formatted.
|
||||
* @param model a meta-model of managed types, provided for easier introspection if necessary
|
||||
* @param title TODO
|
||||
* @param desc TODO
|
||||
* @param uri TODO
|
||||
* @param writer a text-oriented output stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public T writeOut(Collection<OpenJPAStateManager> objs, Metamodel model,
|
||||
String title, String desc, URI uri, OutputStream out) throws IOException;
|
||||
|
||||
/**
|
||||
* Encodes the given domain model, then write it into the given output stream.
|
||||
*
|
||||
* @param model a meta-model of managed types
|
||||
* @param writer a text-oriented output stream
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public T writeOut(Metamodel model, String title, String desc, URI uri, OutputStream out) throws IOException;
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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 org.apache.openjpa.persistence.jest.Constants.MIME_TYPE_XML;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLDecoder;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.openjpa.lib.util.Localizer.Message;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
* Specialized RuntimException thrown by JEST commands.
|
||||
* The exception can be serialized to the output stream of a HTTP Servlet response as a HTML page.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ProcessingException extends RuntimeException {
|
||||
private final JPAServletContext ctx;
|
||||
private final int _errorCode;
|
||||
|
||||
public ProcessingException(JPAServletContext ctx, Throwable error) {
|
||||
this(ctx, error, HTTP_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
public ProcessingException(JPAServletContext ctx, Throwable error, int errorCode) {
|
||||
super(error);
|
||||
this.ctx = ctx;
|
||||
this._errorCode = errorCode;
|
||||
}
|
||||
|
||||
public ProcessingException(JPAServletContext ctx, Message message, int errorCode) {
|
||||
super(message.toString());
|
||||
this.ctx = ctx;
|
||||
this._errorCode = errorCode;
|
||||
}
|
||||
|
||||
public ProcessingException(JPAServletContext ctx, Throwable error, Message message) {
|
||||
this(ctx, error, message, HTTP_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
public ProcessingException(JPAServletContext ctx, Throwable error, Message message, int errorCode) {
|
||||
super(message.toString(), error);
|
||||
this.ctx = ctx;
|
||||
this._errorCode = errorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the stack trace in a HTML format on the given response output stream.
|
||||
*
|
||||
* @param response
|
||||
* @throws IOException
|
||||
*/
|
||||
public void printStackTrace() {
|
||||
HttpServletResponse response = ctx.getResponse();
|
||||
response.setContentType(MIME_TYPE_XML);
|
||||
response.setStatus(_errorCode);
|
||||
|
||||
String uri = ctx.getRequestURI().toString();
|
||||
try {
|
||||
uri = URLDecoder.decode(uri, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
}
|
||||
Throwable t = this.getCause() == null ? this : getCause();
|
||||
ExceptionFormatter formatter = new ExceptionFormatter();
|
||||
Document xml = formatter.createXML("Request URI: " + uri, t);
|
||||
try {
|
||||
formatter.write(xml, response.getOutputStream());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Request URI: " + uri, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.openjpa.persistence.jest;
|
||||
|
||||
import static org.apache.openjpa.persistence.jest.Constants.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
|
||||
/**
|
||||
* Represents configuration properties in HTML.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class PropertiesCommand extends AbstractCommand {
|
||||
private static final char DOT = '.';
|
||||
|
||||
public PropertiesCommand(JPAServletContext ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
protected int getMaximumArguments() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() throws ProcessingException, IOException {
|
||||
HttpServletResponse response = ctx.getResponse();
|
||||
response.setContentType(MIME_TYPE_XML);
|
||||
|
||||
Map<String,Object> properties = ctx.getPersistenceContext().getProperties();
|
||||
removeBadEntries(properties);
|
||||
PropertiesFormatter formatter = new PropertiesFormatter();
|
||||
String caption = _loc.get("properties-caption", ctx.getPersistenceUnitName()).toString();
|
||||
Document xml = formatter.createXML(caption, "", "", properties);
|
||||
formatter.write(xml, response.getOutputStream());
|
||||
response.setStatus(HttpURLConnection.HTTP_OK);
|
||||
}
|
||||
|
||||
private void removeBadEntries(Map<String,Object> map) {
|
||||
Iterator<String> keys = map.keySet().iterator();
|
||||
for (; keys.hasNext();) {
|
||||
if (keys.next().indexOf(DOT) == -1) keys.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Format getDefaultFormat() {
|
||||
return Format.xml;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Formats a key-value pair in a HTML Document.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
class PropertiesFormatter extends XMLFormatter {
|
||||
public Document createXML(String title, String tkey, String tvalue, Map<String,Object> properties) {
|
||||
Element root = newDocument(Constants.ROOT_ELEMENT_PROPERTIES);
|
||||
for (Map.Entry<String,Object> entry : properties.entrySet()) {
|
||||
Element property = root.getOwnerDocument().createElement("property");
|
||||
Object value = entry.getValue();
|
||||
String v = value == null
|
||||
? Constants.NULL_VALUE
|
||||
: value.getClass().isArray() ? Arrays.toString((Object[])value) : value.toString();
|
||||
property.setAttribute(Constants.ATTR_PROPERTY_KEY, entry.getKey());
|
||||
property.setAttribute(Constants.ATTR_PROPERTY_VALUE, v);
|
||||
root.appendChild(property);
|
||||
}
|
||||
return root.getOwnerDocument();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* 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.lang.reflect.Constructor;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.openjpa.kernel.Filters;
|
||||
|
||||
/**
|
||||
* A factory for a specific type of objects registered by a key.
|
||||
* The client registers a type indexed by name.
|
||||
* The client can get a new instance of the registered type.
|
||||
* The requested registered type <em>not</em> necessarily have to have a no-arg
|
||||
* constructor. The constructor arguments can be passed during
|
||||
* {@link #newInstance(Class, Object...) new instance} request. Based on the
|
||||
* arguments, a matching constructor, if any, is located and invoked.
|
||||
*
|
||||
* <K> type of key for this registry
|
||||
* <T> base type of the objects to construct
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class PrototypeFactory<K,T> {
|
||||
private Map<K, Class<? extends T>> _registry = new TreeMap<K, Class<? extends T>>();
|
||||
|
||||
/**
|
||||
* Register the given class with the given key.
|
||||
*
|
||||
* @param key a non-null key.
|
||||
* @param prototype a type.
|
||||
*/
|
||||
public void register(K key, Class<? extends T> prototype) {
|
||||
_registry.put(key, prototype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the type {@linkplain #register(Object, Class) registered} before
|
||||
* with the given key, if any.
|
||||
* The given arguments are used to identify a constructor of the registered type and
|
||||
* passed to the constructor of the registered type.
|
||||
*
|
||||
* @param key a key to identify a registered type.
|
||||
* @param args arguments to pass to the constructor of the type.
|
||||
*
|
||||
* @return null if no type has been registered against the given key.
|
||||
*/
|
||||
public T newInstance(K key, Object... args) {
|
||||
return _registry.containsKey(key) ? newInstance(_registry.get(key), args) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the keys registered in this factory.
|
||||
*
|
||||
* @return immutable set of registered keys.
|
||||
*/
|
||||
public Set<K> getRegisteredKeys() {
|
||||
return Collections.unmodifiableSet(_registry.keySet());
|
||||
}
|
||||
|
||||
private T newInstance(Class<? extends T> type, Object... args) {
|
||||
try {
|
||||
return findConstructor(type, getConstructorParameterTypes(args)).newInstance(args);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
Class<?>[] getConstructorParameterTypes(Object... args) {
|
||||
if (args == null || args.length == 0) {
|
||||
return new Class<?>[0];
|
||||
}
|
||||
Class<?>[] types = new Class<?>[args.length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
types[i] = args[i] == null ? Object.class : args[i].getClass();
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a constructor of the given class with given argument types.
|
||||
*/
|
||||
Constructor<? extends T> findConstructor(Class<? extends T> cls, Class<?>[] types) {
|
||||
try {
|
||||
return cls.getConstructor(types);
|
||||
} catch (Exception e) {
|
||||
Constructor<?>[] constructors = cls.getConstructors();
|
||||
for (Constructor<?> cons : constructors) {
|
||||
Class<?>[] paramTypes = cons.getParameterTypes();
|
||||
boolean match = false;
|
||||
if (paramTypes.length == types.length) {
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
match = paramTypes[i].isAssignableFrom(Filters.wrap(types[i]));
|
||||
if (!match)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
return (Constructor<? extends T>)cons;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new RuntimeException();//_loc.get("fill-ctor-none", cls, Arrays.toString(types)).getMessage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import static org.apache.openjpa.persistence.jest.Constants.*;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.apache.openjpa.persistence.ArgumentException;
|
||||
import org.apache.openjpa.persistence.OpenJPAEntityManager;
|
||||
|
||||
import static java.net.HttpURLConnection.HTTP_BAD_REQUEST;;
|
||||
|
||||
/**
|
||||
* Executes query.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
class QueryCommand extends AbstractCommand {
|
||||
private static final List<String> _mandatoryArgs = Arrays.asList(ARG_QUERY);
|
||||
private static final List<String> _validQualifiers = Arrays.asList(
|
||||
QUALIFIER_FORMAT, QUALIFIER_PLAN, QUALIFIER_NAMED, QUALIFIER_SINGLE,
|
||||
QUALIFIER_FIRSTRESULT, QUALIFIER_MAXRESULT);
|
||||
|
||||
public QueryCommand(JPAServletContext ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<String> getMandatoryArguments() {
|
||||
return _mandatoryArgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getMinimumArguments() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected Collection<String> getValidQualifiers() {
|
||||
return _validQualifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() throws ProcessingException {
|
||||
String spec = getMandatoryArgument(ARG_QUERY);
|
||||
OpenJPAEntityManager em = ctx.getPersistenceContext();
|
||||
try {
|
||||
Query query = isBooleanQualifier(QUALIFIER_NAMED) ? em.createNamedQuery(spec) : em.createQuery(spec);
|
||||
if (hasQualifier(QUALIFIER_FIRSTRESULT))
|
||||
query.setFirstResult(Integer.parseInt(getQualifier(QUALIFIER_FIRSTRESULT)));
|
||||
if (hasQualifier(QUALIFIER_MAXRESULT))
|
||||
query.setMaxResults(Integer.parseInt(getQualifier(QUALIFIER_MAXRESULT)));
|
||||
pushFetchPlan(query);
|
||||
|
||||
Map<String, String> args = getArguments();
|
||||
for (Map.Entry<String, String> entry : args.entrySet()) {
|
||||
query.setParameter(entry.getKey(), entry.getValue());
|
||||
}
|
||||
getObjectFormatter()
|
||||
.writeOut(toStateManager(isBooleanQualifier(QUALIFIER_SINGLE)
|
||||
? Collections.singleton(query.getSingleResult()) : query.getResultList()),
|
||||
em.getMetamodel(),
|
||||
_loc.get("query-title").toString(), _loc.get("query-desc").toString(), ctx.getRequestURI(),
|
||||
ctx.getResponse().getOutputStream());
|
||||
} catch (ArgumentException e1) {
|
||||
throw new ProcessingException(ctx, e1, _loc.get("query-execution-error", spec), HTTP_BAD_REQUEST);
|
||||
} catch (Exception e) {
|
||||
throw new ProcessingException(ctx, e, _loc.get("query-execution-error", spec));
|
||||
} finally {
|
||||
popFetchPlan(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.apache.openjpa.persistence.jest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Reads from an input stream and writes to an output stream after replacing matched tokens
|
||||
* by their counterpart.
|
||||
*
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class TokenReplacedStream {
|
||||
/**
|
||||
* Read the given input stream and replaces the tokens as it reads. The replaced stream is written to the
|
||||
* given output stream.
|
||||
*
|
||||
* @param in a non-null input stream
|
||||
* @param out a character oriented writer
|
||||
* @param replacements an even number of Strings. Any occurrence of the even-indexed i-th String in the
|
||||
* input stream will be replaced by the (i+1)-th String in the output writer.
|
||||
*/
|
||||
public void replace(InputStream in, Writer out, String... prs) throws IOException {
|
||||
if (prs.length%2 != 0)
|
||||
throw new IllegalArgumentException("Even number of pattern/string pairs: " + Arrays.toString(prs)
|
||||
+ ". Must be even number of arguments.");
|
||||
Pattern[] patterns = new Pattern[prs.length/2];
|
||||
for (int i = 0; i < prs.length; i += 2) {
|
||||
patterns[i/2] = new Pattern(prs[i], prs[i+1]);
|
||||
}
|
||||
|
||||
StringBuilder tmp = new StringBuilder();
|
||||
for (int c = 0; (c = in.read()) != -1;) {
|
||||
int cursor = match((char)c, patterns);
|
||||
if (cursor < 0) { // no pattern recognized at all
|
||||
if (tmp.length() > 0) { // append partial match then discard partial memory
|
||||
for (int j = 0; j < tmp.length(); j++) {
|
||||
out.write(tmp.charAt(j));
|
||||
}
|
||||
tmp.delete(0, tmp.length());
|
||||
}
|
||||
out.write((char)c); // directly output
|
||||
} else {
|
||||
Pattern p = matched(patterns); // has any pattern matched completely
|
||||
if (p != null) { // a pattern matched completely
|
||||
char[] replace = p.replace().toCharArray();
|
||||
for (int j = 0; j < replace.length; j++) {
|
||||
out.write(replace[j]);
|
||||
}
|
||||
reset(patterns);
|
||||
tmp.delete(0, tmp.length());
|
||||
} else {
|
||||
tmp.append((char)c); // remember partial match
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the given character to all patterns and return the index of highest match.
|
||||
* @param c a character to match
|
||||
* @param patterns an array of patterns
|
||||
* @return -1 if character matched no pattern
|
||||
*/
|
||||
int match(char c, Pattern...patterns) {
|
||||
if (patterns == null)
|
||||
return -1;
|
||||
int result = -1;
|
||||
for (Pattern p : patterns) {
|
||||
result = Math.max(result, p.match(c));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the pattern if any in matched state
|
||||
* @param patterns
|
||||
* @return
|
||||
*/
|
||||
Pattern matched(Pattern...patterns) {
|
||||
if (patterns == null)
|
||||
return null;
|
||||
for (Pattern p : patterns) {
|
||||
if (p.isMatched()) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets all the patterns.
|
||||
* @param patterns
|
||||
*/
|
||||
void reset(Pattern...patterns) {
|
||||
if (patterns == null)
|
||||
return;
|
||||
for (Pattern p : patterns) {
|
||||
p.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Pattern {
|
||||
private final char[] chars;
|
||||
private final String _replace;
|
||||
private int _cursor;
|
||||
|
||||
/**
|
||||
* Construct a pattern and its replacement.
|
||||
*/
|
||||
public Pattern(String s, String replace) {
|
||||
if (s == null || s.length() == 0)
|
||||
throw new IllegalArgumentException("Pattern [" + s + "] can not be empty or null ");
|
||||
if (replace == null)
|
||||
throw new IllegalArgumentException("Replacement [" + replace + "] is null for pattern [" + s + "]");
|
||||
chars = s.toCharArray();
|
||||
_cursor = -1;
|
||||
_replace = replace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the given character with the current cursor and advance the matching length.
|
||||
* @param c
|
||||
* @return the matching length. -1 denotes the pattern did not match the character.
|
||||
*/
|
||||
public int match(char c) {
|
||||
if (c != chars[++_cursor]) {
|
||||
reset();
|
||||
}
|
||||
return _cursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the cursor. Subsequent matching will begin at start.
|
||||
*/
|
||||
public void reset() {
|
||||
_cursor = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this pattern matched fully?
|
||||
* A pattern is fully matched when the matching length is equal to the length of the pattern string.
|
||||
*/
|
||||
public boolean isMatched() {
|
||||
return _cursor == chars.length-1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string to be replaced.
|
||||
*/
|
||||
public String replace() {
|
||||
return _replace;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new String(chars) + ":" + _cursor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,516 @@
|
|||
/*
|
||||
* 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.BufferedReader;
|
||||
import java.io.CharArrayWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.metamodel.Attribute;
|
||||
import javax.persistence.metamodel.ManagedType;
|
||||
import javax.persistence.metamodel.MapAttribute;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
import javax.persistence.metamodel.PluralAttribute;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import javax.xml.validation.Validator;
|
||||
|
||||
import org.apache.openjpa.kernel.OpenJPAStateManager;
|
||||
import org.apache.openjpa.kernel.StoreContext;
|
||||
import org.apache.openjpa.meta.ClassMetaData;
|
||||
import org.apache.openjpa.meta.FieldMetaData;
|
||||
import org.apache.openjpa.meta.JavaTypes;
|
||||
import org.apache.openjpa.meta.ValueMetaData;
|
||||
import org.apache.openjpa.persistence.meta.Members;
|
||||
import org.apache.openjpa.util.InternalException;
|
||||
import org.w3c.dom.CDATASection;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* Marshals a root instance and its persistent closure as an XML element.
|
||||
* The closure is resolved against the persistence context that contains the root instance.
|
||||
* The XML document adheres to the <code>jest-instance.xsd</code> schema.
|
||||
*
|
||||
* @author Pinaki Poddar
|
||||
*
|
||||
*/
|
||||
public class XMLFormatter implements ObjectFormatter<Document> {
|
||||
|
||||
public static final Schema _xsd;
|
||||
private static final DocumentBuilder _builder;
|
||||
private static final Transformer _transformer;
|
||||
private static final String EMPTY_TEXT = " ";
|
||||
|
||||
static {
|
||||
try {
|
||||
_builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
_transformer = TransformerFactory.newInstance().newTransformer();
|
||||
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
InputStream xsd = XMLFormatter.class.getResourceAsStream(JEST_INSTANCE_XSD);
|
||||
_xsd = factory.newSchema(new StreamSource(xsd));
|
||||
|
||||
_transformer.setOutputProperty(OutputKeys.METHOD, "xml");
|
||||
_transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
|
||||
_transformer.setOutputProperty(OutputKeys.INDENT, "yes");
|
||||
_transformer.setOutputProperty(OutputKeys.STANDALONE, "no");
|
||||
_transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
|
||||
_transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getMimeType() {
|
||||
return MIME_TYPE_XML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the closure of given collection of managed instance into a new XML document
|
||||
* according to JEST Instance XML Schema.
|
||||
*
|
||||
* @param sm a collection of managed instances.
|
||||
* @param parent the parent node to which the new node be attached.
|
||||
*/
|
||||
public Document encode(final Collection<OpenJPAStateManager> sms, Metamodel model) {
|
||||
Element root = newDocument(ROOT_ELEMENT_INSTANCE);
|
||||
Closure closure = new Closure(sms);
|
||||
for (OpenJPAStateManager sm : closure) {
|
||||
encodeManagedInstance(sm, root, false, model);
|
||||
}
|
||||
return root.getOwnerDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given meta-model into a new XML document according to JEST Domain XML Schema.
|
||||
*
|
||||
* @param model a persistent domain model. Must not be null.
|
||||
*/
|
||||
public Document encode(Metamodel model) {
|
||||
Element root = newDocument(ROOT_ELEMENT_MODEL);
|
||||
for (ManagedType<?> t : model.getManagedTypes()) {
|
||||
encodeManagedType(t, root);
|
||||
}
|
||||
return root.getOwnerDocument();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new document with the given tag as the root element.
|
||||
*
|
||||
* @param rootTag the tag of the root element
|
||||
*
|
||||
* @return the document element of a new document
|
||||
*/
|
||||
public Element newDocument(String rootTag) {
|
||||
Document doc = _builder.newDocument();
|
||||
Element root = doc.createElement(rootTag);
|
||||
doc.appendChild(root);
|
||||
String[] nvpairs = new String[] {
|
||||
"xmlns:xsi", XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI,
|
||||
// "xsi:noNamespaceSchemaLocation", JEST_INSTANCE_XSD,
|
||||
ATTR_VERSION, "1.0",
|
||||
};
|
||||
for (int i = 0; i < nvpairs.length; i += 2) {
|
||||
root.setAttribute(nvpairs[i], nvpairs[i+1]);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Document writeOut(Collection<OpenJPAStateManager> objs, Metamodel model, String title, String desc,
|
||||
URI uri, OutputStream out) throws IOException {
|
||||
Document doc = encode(objs, model);
|
||||
decorate(doc, title, desc, uri);
|
||||
write(doc, out);
|
||||
return doc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Document writeOut(Metamodel model, String title, String desc, URI uri, OutputStream out)
|
||||
throws IOException {
|
||||
Document doc = encode(model);
|
||||
decorate(doc, title, desc, uri);
|
||||
write(doc, out);
|
||||
return doc;
|
||||
}
|
||||
|
||||
Document decorate(Document doc, String title, String desc, URI uri) {
|
||||
Element root = doc.getDocumentElement();
|
||||
Element instance = (Element)root.getElementsByTagName(ELEMENT_INSTANCE).item(0);
|
||||
Element uriElement = doc.createElement(ELEMENT_URI);
|
||||
uriElement.setTextContent(uri == null ? NULL_VALUE : uri.toString());
|
||||
Element descElement = doc.createElement(ELEMENT_DESCRIPTION);
|
||||
descElement.setTextContent(desc == null ? NULL_VALUE : desc);
|
||||
root.insertBefore(uriElement, instance);
|
||||
root.insertBefore(descElement, instance);
|
||||
return doc;
|
||||
}
|
||||
|
||||
public void write(Document doc, OutputStream out) throws IOException {
|
||||
try {
|
||||
_transformer.transform(new DOMSource(doc), new StreamResult(out));
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void write(Document doc, Writer writer) throws IOException {
|
||||
try {
|
||||
_transformer.transform(new DOMSource(doc), new StreamResult(writer));
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the closure of a persistent instance into a XML element.
|
||||
*
|
||||
* @param sm the managed instance to be encoded. Can be null.
|
||||
* @param parent the parent XML element to which the new XML element be added. Must not be null. Must be
|
||||
* owned by a document.
|
||||
* @param visited the persistent instances that had been encoded already. Must not be null or immutable.
|
||||
*
|
||||
* @return the new element. The element has been appended as a child to the given parent in this method.
|
||||
*/
|
||||
private Element encodeManagedInstance(final OpenJPAStateManager sm, final Element parent,
|
||||
boolean isRef, Metamodel model) {
|
||||
if (parent == null)
|
||||
throw new InternalException(_loc.get("format-xml-null-parent"));
|
||||
Document doc = parent.getOwnerDocument();
|
||||
if (doc == null)
|
||||
throw new InternalException(_loc.get("format-xml-null-doc"));
|
||||
|
||||
if (sm == null || isRef) {
|
||||
return encodeRef(parent, sm);
|
||||
}
|
||||
Element root = doc.createElement(ELEMENT_INSTANCE);
|
||||
parent.appendChild(root);
|
||||
root.setAttribute(ATTR_ID, ior(sm));
|
||||
Element child = null;
|
||||
BitSet loaded = sm.getLoaded();
|
||||
StoreContext ctx = (StoreContext)sm.getGenericContext();
|
||||
List<Attribute<?, ?>> attrs = MetamodelHelper.getAttributesInOrder(sm.getMetaData(), model);
|
||||
for (int i = 0; i < attrs.size(); child = null, i++) {
|
||||
Members.Member<?, ?> attr = (Members.Member<?, ?>) attrs.get(i);
|
||||
FieldMetaData fmd = attr.fmd;
|
||||
if (!loaded.get(fmd.getIndex()))
|
||||
continue;
|
||||
String tag = MetamodelHelper.getTagByAttributeType(attr);
|
||||
Object value = sm.fetch(fmd.getIndex());
|
||||
switch (fmd.getDeclaredTypeCode()) {
|
||||
case JavaTypes.BOOLEAN:
|
||||
case JavaTypes.BYTE:
|
||||
case JavaTypes.CHAR:
|
||||
case JavaTypes.DOUBLE:
|
||||
case JavaTypes.FLOAT:
|
||||
case JavaTypes.INT:
|
||||
case JavaTypes.LONG:
|
||||
case JavaTypes.SHORT:
|
||||
|
||||
case JavaTypes.BOOLEAN_OBJ:
|
||||
case JavaTypes.BYTE_OBJ:
|
||||
case JavaTypes.CHAR_OBJ:
|
||||
case JavaTypes.DOUBLE_OBJ:
|
||||
case JavaTypes.FLOAT_OBJ:
|
||||
case JavaTypes.INT_OBJ:
|
||||
case JavaTypes.LONG_OBJ:
|
||||
case JavaTypes.SHORT_OBJ:
|
||||
|
||||
case JavaTypes.BIGDECIMAL:
|
||||
case JavaTypes.BIGINTEGER:
|
||||
case JavaTypes.DATE:
|
||||
case JavaTypes.NUMBER:
|
||||
case JavaTypes.CALENDAR:
|
||||
case JavaTypes.LOCALE:
|
||||
case JavaTypes.STRING:
|
||||
case JavaTypes.ENUM:
|
||||
child = doc.createElement(tag);
|
||||
child.setAttribute(ATTR_NAME, fmd.getName());
|
||||
if (value == null) {
|
||||
encodeNull(child);
|
||||
} else {
|
||||
encodeBasic(child, value, fmd.getDeclaredType());
|
||||
}
|
||||
break;
|
||||
|
||||
case JavaTypes.OID:
|
||||
child = doc.createElement(ELEMENT_REF);
|
||||
child.setAttribute(ATTR_NAME, fmd.getName());
|
||||
if (value == null) {
|
||||
encodeNull(child);
|
||||
} else {
|
||||
encodeBasic(child, value, fmd.getDeclaredType());
|
||||
}
|
||||
break;
|
||||
|
||||
case JavaTypes.PC:
|
||||
child = doc.createElement(tag);
|
||||
child.setAttribute(ATTR_NAME, fmd.getName());
|
||||
child.setAttribute(ATTR_TYPE, typeOf(fmd));
|
||||
OpenJPAStateManager other = ctx.getStateManager(value);
|
||||
encodeManagedInstance(other, child, true, model);
|
||||
break;
|
||||
|
||||
case JavaTypes.ARRAY:
|
||||
Object[] values = (Object[])value;
|
||||
value = Arrays.asList(values);
|
||||
// no break;
|
||||
case JavaTypes.COLLECTION:
|
||||
child = doc.createElement(tag);
|
||||
child.setAttribute(ATTR_NAME, fmd.getName());
|
||||
child.setAttribute(ATTR_TYPE, typeOf(fmd));
|
||||
child.setAttribute(ATTR_MEMBER_TYPE, typeOf(fmd.getElement().getDeclaredType()));
|
||||
if (value == null) {
|
||||
encodeNull(child);
|
||||
break;
|
||||
}
|
||||
Collection<?> members = (Collection<?>)value;
|
||||
boolean basic = fmd.getElement().getTypeMetaData() == null;
|
||||
for (Object o : members) {
|
||||
Element member = doc.createElement(ELEMENT_MEMBER);
|
||||
child.appendChild(member);
|
||||
if (o == null) {
|
||||
encodeNull(member);
|
||||
} else {
|
||||
if (basic) {
|
||||
encodeBasic(member, o, o.getClass());
|
||||
} else {
|
||||
encodeManagedInstance(ctx.getStateManager(o), member, true, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JavaTypes.MAP:
|
||||
child = doc.createElement(tag);
|
||||
child.setAttribute(ATTR_NAME, fmd.getName());
|
||||
child.setAttribute(ATTR_TYPE, typeOf(fmd));
|
||||
child.setAttribute(ATTR_KEY_TYPE, typeOf(fmd.getElement().getDeclaredType()));
|
||||
child.setAttribute(ATTR_VALUE_TYPE, typeOf(fmd.getValue().getDeclaredType()));
|
||||
if (value == null) {
|
||||
encodeNull(child);
|
||||
break;
|
||||
}
|
||||
Set<Map.Entry> entries = ((Map)value).entrySet();
|
||||
boolean basicKey = fmd.getElement().getTypeMetaData() == null;
|
||||
boolean basicValue = fmd.getValue().getTypeMetaData() == null;
|
||||
for (Map.Entry<?,?> e : entries) {
|
||||
Element entry = doc.createElement(ELEMENT_ENTRY);
|
||||
Element entryKey = doc.createElement(ELEMENT_ENTRY_KEY);
|
||||
Element entryValue = doc.createElement(ELEMENT_ENTRY_VALUE);
|
||||
entry.appendChild(entryKey);
|
||||
entry.appendChild(entryValue);
|
||||
child.appendChild(entry);
|
||||
if (e.getKey() == null) {
|
||||
encodeNull(entryKey);
|
||||
} else {
|
||||
if (basicKey) {
|
||||
encodeBasic(entryKey, e.getKey(), e.getKey().getClass());
|
||||
} else {
|
||||
encodeManagedInstance(ctx.getStateManager(e.getKey()), entryKey, true, model);
|
||||
}
|
||||
}
|
||||
if (e.getValue() == null) {
|
||||
encodeNull(entryValue);
|
||||
} else {
|
||||
if (basicValue) {
|
||||
encodeBasic(entryValue, e.getValue(), e.getValue().getClass());
|
||||
} else {
|
||||
encodeManagedInstance(ctx.getStateManager(e.getValue()), entryValue, true, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case JavaTypes.INPUT_STREAM:
|
||||
case JavaTypes.INPUT_READER:
|
||||
child = doc.createElement(tag);
|
||||
child.setAttribute(ATTR_NAME, fmd.getName());
|
||||
child.setAttribute(ATTR_TYPE, typeOf(fmd));
|
||||
if (value == null) {
|
||||
encodeNull(child);
|
||||
} else {
|
||||
CDATASection data = doc.createCDATASection(streamToString(value));
|
||||
child.appendChild(data);
|
||||
}
|
||||
break;
|
||||
|
||||
case JavaTypes.PC_UNTYPED:
|
||||
case JavaTypes.OBJECT:
|
||||
System.err.println("Not handled " + fmd.getName() + " of type " + fmd.getDeclaredType());
|
||||
}
|
||||
|
||||
if (child != null) {
|
||||
root.appendChild(child);
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given value element as null. The <code>null</code> attribute is set to true.
|
||||
*
|
||||
* @param element the XML element to be set
|
||||
*/
|
||||
private void encodeNull(Element element) {
|
||||
element.setAttribute(ATTR_NULL, "true");
|
||||
}
|
||||
|
||||
private Element encodeRef(Element parent, OpenJPAStateManager sm) {
|
||||
Element ref = parent.getOwnerDocument().createElement(sm == null ? ELEMENT_NULL_REF : ELEMENT_REF);
|
||||
if (sm != null)
|
||||
ref.setAttribute(ATTR_ID, ior(sm));
|
||||
// IMPORTANT: for xml transformer not to omit the closing tag, otherwise dojo is confused
|
||||
ref.setTextContent(EMPTY_TEXT);
|
||||
parent.appendChild(ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the given value element. The <code>type</code> is set to the given runtime type.
|
||||
* String form of the given object is set as the text content.
|
||||
*
|
||||
* @param element the XML element to be set
|
||||
* @param obj value of the element. Never null.
|
||||
*/
|
||||
private void encodeBasic(Element element, Object obj, Class<?> runtimeType) {
|
||||
element.setAttribute(ATTR_TYPE, typeOf(runtimeType));
|
||||
if (obj instanceof Date)
|
||||
element.setTextContent(dateFormat.format(obj));
|
||||
else
|
||||
element.setTextContent(obj == null ? NULL_VALUE : obj.toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Convert the given stream (either an InutStream or a Reader) to a String
|
||||
* to be included in CDATA section of a XML document.
|
||||
*
|
||||
* @param value the field value to be converted. Can not be null
|
||||
* @return
|
||||
*/
|
||||
private String streamToString(Object value) {
|
||||
Reader reader = null;
|
||||
if (value instanceof InputStream) {
|
||||
reader = new BufferedReader(new InputStreamReader((InputStream)value));
|
||||
} else if (value instanceof Reader) {
|
||||
reader = (Reader)value;
|
||||
} else {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
CharArrayWriter writer = new CharArrayWriter();
|
||||
try {
|
||||
for (int c; (c = reader.read()) != -1;) {
|
||||
writer.write(c);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
|
||||
private void encodeManagedType(ManagedType<?> type, Element parent) {
|
||||
Document doc = parent.getOwnerDocument();
|
||||
Element root = doc.createElement(type.getPersistenceType().toString().toLowerCase());
|
||||
parent.appendChild(root);
|
||||
root.setAttribute(ATTR_NAME, type.getJavaType().getSimpleName());
|
||||
List<Attribute<?,?>> attributes = MetamodelHelper.getAttributesInOrder(type);
|
||||
for (Attribute<?,?> a : attributes) {
|
||||
String tag = MetamodelHelper.getTagByAttributeType(a);
|
||||
|
||||
Element child = doc.createElement(tag);
|
||||
root.appendChild(child);
|
||||
child.setAttribute(ATTR_TYPE, typeOf(a.getJavaType()));
|
||||
if (a instanceof PluralAttribute) {
|
||||
if (a instanceof MapAttribute) {
|
||||
child.setAttribute(ATTR_KEY_TYPE, typeOf(((MapAttribute)a).getKeyJavaType()));
|
||||
child.setAttribute(ATTR_VALUE_TYPE, typeOf(((MapAttribute)a).getBindableJavaType()));
|
||||
} else {
|
||||
child.setAttribute(ATTR_MEMBER_TYPE, typeOf(((PluralAttribute)a).getBindableJavaType()));
|
||||
}
|
||||
}
|
||||
child.setTextContent(a.getName());
|
||||
}
|
||||
}
|
||||
|
||||
void validate(Document doc) throws Exception {
|
||||
Validator validator = _xsd.newValidator();
|
||||
validator.validate(new DOMSource(doc));
|
||||
}
|
||||
|
||||
String ior(OpenJPAStateManager sm) {
|
||||
return typeOf(sm) + "-" + sm.getObjectId();
|
||||
}
|
||||
|
||||
String typeOf(OpenJPAStateManager sm) {
|
||||
return sm.getMetaData().getDescribedType().getSimpleName();
|
||||
}
|
||||
|
||||
String typeOf(Class<?> cls) {
|
||||
return cls.getSimpleName();
|
||||
}
|
||||
|
||||
String typeOf(ClassMetaData meta) {
|
||||
return meta.getDescribedType().getSimpleName();
|
||||
}
|
||||
|
||||
String typeOf(ValueMetaData vm) {
|
||||
if (vm.getTypeMetaData() == null)
|
||||
return typeOf(vm.getType());
|
||||
return typeOf(vm.getTypeMetaData());
|
||||
}
|
||||
|
||||
String typeOf(FieldMetaData fmd) {
|
||||
return fmd.getType().getSimpleName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<link href="help.css" type="text/css" rel="stylesheet">
|
||||
<body>
|
||||
<h3>Entity Name</h3>
|
||||
|
||||
Specify simple name of a persistent entity.
|
||||
JEST will resolve the name to the fully-qualified name of the entity.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<link href="help.css" type="text/css" rel="stylesheet">
|
||||
<body>
|
||||
<h3>Dynamic Fetch Plan</h3>
|
||||
|
||||
Specify one or more named fetch plan. Separate multiple names with comma, e.g.
|
||||
<p align="center">
|
||||
myPlanA, mYPlanB
|
||||
</p>
|
||||
There are two pre-defined plans named: <b>default</b> and <b>all</b>. The <b>default</b> plan that fetches
|
||||
all properties of basic type (i.e. <tt>String, int, Date</tt> etc.) and uni-cardinality relations, are
|
||||
active by default. As the plans are additive, to exclude the default plan, you can specify
|
||||
<p align="center">
|
||||
myPlanA, -default
|
||||
</p>
|
||||
|
||||
|
||||
Fetch Plan determines which properties will be fetched when an entity instance is accessed from the data store.
|
||||
A plan can traverse relationship path into other entities at arbitrary depth. By default, when an entity
|
||||
is fetched, all properties of basic type (i.e. <tt>String, int, Date</tt> etc.) and uni-cardinality relations are
|
||||
fetched.
|
||||
<br>
|
||||
JPA specification allows <tt>@Fetch.LAZY</tt> and <tt>@Fetch.EAGER</tt> annotation on persistent properties to control
|
||||
fetch behavior. But only <em>statically</em> i.e. at class definition.
|
||||
<p>
|
||||
OpenJPA, on the other hand, provides a far richer syntax and semantics to its user to define the
|
||||
properties to be fetched through its dynamic Fetch Plan facility. And, more importantly, these
|
||||
fetch plans can be modified <em>dynamically</em> per use case basis.
|
||||
<p>
|
||||
To learn more aboout Fetch Plan, Refer <a href="http://openjpa.apache.org/documentation.html">OpenJPA documentation</a>.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<link href="help.css" type="text/css" rel="stylesheet">
|
||||
<body>
|
||||
<h3>JPQL Query</h3>
|
||||
|
||||
Specify a JPQL query or name of a pre-defined NamedQuery.
|
||||
<br>
|
||||
Both queries can accept query bind parameters. The type of the parameters are guessed by JEST
|
||||
from the string you specify in this web form.
|
||||
<p>
|
||||
If using named query, check the Named Query Box.
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,28 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<html>
|
||||
<link href="help.css" type="text/css" rel="stylesheet">
|
||||
<body>
|
||||
<h3>Response Format</h3>
|
||||
|
||||
Select the response format as XML or JSON. By default, JEST responses are in XML format.
|
||||
|
||||
</body>
|
||||
</html>
|
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 999 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 784 B |
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 70 KiB |
|
@ -0,0 +1,166 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- 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. -->
|
||||
<!-- ========================================================================= -->
|
||||
<!-- Schema for serialized persistence instance. -->
|
||||
<!-- ========================================================================= -->
|
||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
attributeFormDefault="unqualified" elementFormDefault="qualified"
|
||||
version="1.0">
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Describes closure of managed persistence instance.
|
||||
Each instance is described by all its loaded persistent attribute.
|
||||
The related instances are resolved within the document root.
|
||||
Document root represents zero or more instances.
|
||||
|
||||
The file must be named "jest-instance.xsd".
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<!-- The root element of the document contains zero or more instances -->
|
||||
<xsd:element name="instances">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="uri" minOccurs="1" maxOccurs="1" type="xsd:anyURI"/>
|
||||
<xsd:element name="description" minOccurs="0" maxOccurs="1" type="xsd:string"/>
|
||||
<xsd:element name="instance" minOccurs="0" maxOccurs="unbounded" type="instance-type" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="version" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<!-- The root element for a single instance. Children of this element are persistent attribute -->
|
||||
<!-- Persistent Attributes occur in order. The order is determined by the attribute category. -->
|
||||
<!-- Attribute category is determined by the enumerated PersistentAttributeType defined in -->
|
||||
<!-- javax.persistence.metamodel and then further refined by id, version, lob and enum. -->
|
||||
<!-- See org.apache.openjpa.persistence.jest.MetamodelHelper for further details. -->
|
||||
<xsd:complexType name="instance-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="id" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="version" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="basic" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="enum" type="basic-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="embedded" type="instance-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="lob" type="lob-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="one-to-one" type="singular-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="many-to-one" type="singular-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="element-collection" type="collection-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="one-to-many" type="collection-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
<xsd:element name="many-to-many" type="map-attr-type" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id" type="xsd:ID" use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- A reference to another instance within the same(?) document -->
|
||||
<xsd:complexType name="ref-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="id" type="xsd:IDREF" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- A null reference -->
|
||||
<xsd:complexType name="ref-null">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Basic Attribute has a name and its runtime type -->
|
||||
<!-- non-null value appears as text content. -->
|
||||
<!-- null value appears as attribute with empty text . -->
|
||||
<xsd:complexType name="basic-attr-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="type" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="null" type="xsd:boolean" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Large Binary Objects (LOB) represented as hex array -->
|
||||
<xsd:complexType name="lob-attr-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:hexBinary">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="type" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="null" type="xsd:boolean" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Singular attribute is a reference to another instance or a null reference. -->
|
||||
<xsd:complexType name="singular-attr-type">
|
||||
<xsd:choice>
|
||||
<xsd:element name="null" type="ref-null" />
|
||||
<xsd:element name="ref" type="ref-type" />
|
||||
</xsd:choice>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="type" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Collection attributes list their members with their runtime type -->
|
||||
<!-- Members can be basic or other managed instance -->
|
||||
<xsd:complexType name="collection-attr-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="member" type="member-type" minOccurs="0"
|
||||
maxOccurs="unbounded" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="type" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="member-type" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Map attributes list their entries with runtime type of key and value -->
|
||||
<!-- Both key and value can be independently basic or other managed instance -->
|
||||
<xsd:complexType name="map-attr-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="entry" type="entry-type" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="type" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="key-type" type="xsd:string" use="required" />
|
||||
<xsd:attribute name="value-type" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Value of a member of basic type. -->
|
||||
<xsd:complexType name="basic-value-type">
|
||||
<xsd:simpleContent>
|
||||
<xsd:extension base="xsd:string">
|
||||
<xsd:attribute name="null" type="xsd:boolean" />
|
||||
</xsd:extension>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Value of a member of a collection/map -->
|
||||
<xsd:complexType name="member-type">
|
||||
<xsd:choice>
|
||||
<xsd:element name="basic" type="basic-value-type" />
|
||||
<xsd:element name="null" type="ref-null" />
|
||||
<xsd:element name="ref" type="ref-type" />
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
|
||||
<!-- Denotes entry of a map element -->
|
||||
<xsd:complexType name="entry-type">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="key" type="member-type" minOccurs="1" maxOccurs="1" />
|
||||
<xsd:element name="value" type="member-type" minOccurs="1" maxOccurs="1" />
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/** -----------------------------------------------------------------------
|
||||
* Basic layout consists of a left menu section (class='menu')and two
|
||||
* vertical sections for command specification (class='highlight') and
|
||||
* JEST response (class='canvas') respectively.
|
||||
* --------------------------------------------------------------------- */
|
||||
|
||||
/** -----------------------------------------------------------------------
|
||||
* div section containing the command menu.
|
||||
* Lodged on the top-left corner
|
||||
* -------------------------------------------------------------------- */
|
||||
.menu {
|
||||
position:absolute;
|
||||
left:0em;
|
||||
top:80px;
|
||||
width:10em;
|
||||
}
|
||||
/** -----------------------------------------------------------------------
|
||||
* Highlighted blocks appear relative to the menu. The same top position
|
||||
* of the menu and shifted to left of the menu by the menu's width+padding.
|
||||
* --------------------------------------------------------------------- */
|
||||
.highlight {
|
||||
position:relative;
|
||||
left:10em;
|
||||
top:0em;
|
||||
// background-color:#F9F9F9;
|
||||
width:60em;
|
||||
|
||||
// border:1px solid black;
|
||||
// padding:0em 1em 1em 1em; // top, right, bottom, left
|
||||
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size:2em;
|
||||
font-weight:bold;
|
||||
font-family: "Palatino Linotype, Times Roman"
|
||||
}
|
||||
|
||||
/** -----------------------------------------------------------------------
|
||||
* Canvas section appears relative to the menu and the highlighted section.
|
||||
* The top position is relative to the highlighted section and left position
|
||||
* is relative to the menu section.
|
||||
* --------------------------------------------------------------------- */
|
||||
.canvas {
|
||||
position:relative;
|
||||
left:10em;
|
||||
top:1em;
|
||||
width:60em;
|
||||
padding:0em 1em 0em 1em; // top, right, bottom, left
|
||||
}
|
||||
|
||||
.help {
|
||||
float:right;
|
||||
text-align:right;
|
||||
margin-right:0.1em;
|
||||
margin-top: -0.1em;
|
||||
}
|
||||
/** -----------------------------------------------------------------------
|
||||
* Visible/Invisible divisions
|
||||
* --------------------------------------------------------------------- */
|
||||
.open {
|
||||
display:block;
|
||||
}
|
||||
.close {
|
||||
display:none;
|
||||
}
|
||||
|
||||
/** -----------------------------------------------------------------------
|
||||
* Hyperlinks
|
||||
* --------------------------------------------------------------------- */
|
||||
.url {
|
||||
color:blue;
|
||||
font-size:1.1em;
|
||||
font-family:"Courier New", Arial;
|
||||
border:0px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.url-invalid {
|
||||
color:red;
|
||||
font-size:0.9em;
|
||||
font-family:"Courier New", Arial;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** -----------------------------------------------------------------------
|
||||
* XML Tag
|
||||
* --------------------------------------------------------------------- */
|
||||
pre, code {
|
||||
background-color: white;
|
||||
}
|
||||
.tag {
|
||||
color:green;
|
||||
font-family:"Courier New";
|
||||
font-weight:bold;
|
||||
border:0px;
|
||||
}
|
||||
/** -----------------------------------------------------------------------
|
||||
* Data Table used for Tabular HTML
|
||||
* --------------------------------------------------------------------- */
|
||||
table.data td th {
|
||||
width : 70%;
|
||||
border-collapse:collapse;
|
||||
border:2px solid black;
|
||||
margin: 10px 10px 10px 10px;
|
||||
}
|
||||
tr {
|
||||
vertical-align:top;
|
||||
}
|
||||
th {
|
||||
background-color:#444444;
|
||||
color:white;
|
||||
text-align:left;
|
||||
}
|
||||
caption {
|
||||
background-color:#000000;
|
||||
color:white;
|
||||
font-size:1.2em;
|
||||
font-weight:bold;
|
||||
padding:5px 5px 5px 5px;
|
||||
}
|
||||
/** -----------------------------------------------------------------------
|
||||
* Alternate Table Row
|
||||
* --------------------------------------------------------------------- */
|
||||
tr.even td {
|
||||
background-color: #FFFFFF; color: black;
|
||||
padding:2px 20px;
|
||||
border:2px solid black;
|
||||
vertical-align:top;
|
||||
}
|
||||
tr.odd td {
|
||||
background-color: #EEEEEE; color: black;
|
||||
padding:2px 20px;
|
||||
border:2px solid black;
|
||||
vertical-align:top;
|
||||
}
|
||||
td.mandatory {
|
||||
font-weight:bold;
|
||||
color:red;
|
||||
}
|
||||
|
||||
td input {
|
||||
width : 99%;
|
||||
}
|
||||
td select {
|
||||
width : 99%;
|
||||
}
|
||||
|
||||
/** -----------------------------------------------------------------------
|
||||
* Paragraph with smaller line breaks
|
||||
* --------------------------------------------------------------------- */
|
||||
p.small {
|
||||
line-height:60%;
|
||||
}
|
||||
|
||||
/** -----------------------------------------------------------------------
|
||||
* Error Page
|
||||
* --------------------------------------------------------------------- */
|
||||
.error-header {
|
||||
color:red;
|
||||
font-size:2em;
|
||||
font-weight:bold;
|
||||
}
|
||||
.error-message {
|
||||
color:red;
|
||||
font-size:1.2em;
|
||||
}
|
||||
|
||||
/*
|
||||
* JPA styles
|
||||
*/
|
||||
.id {
|
||||
color:red;
|
||||
font-weight:bold;
|
||||
}
|
||||
.enum {
|
||||
color:magenta;
|
||||
font-weight:bold;
|
||||
}
|
||||
.basic {
|
||||
color:green;
|
||||
font-weight:bold;
|
||||
}
|
||||
.one-to-one {
|
||||
color:lightblue;
|
||||
font-weight:bold;
|
||||
}
|
||||
.one-to-many {
|
||||
color:darkblue;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.ref {
|
||||
color : blue;
|
||||
}
|
||||
.null {
|
||||
color : red;
|
||||
}
|
||||
|
||||
.delimiter {
|
||||
color:lightgray;
|
||||
font-weight:bold;
|
||||
}
|
||||
.attr-name {
|
||||
color:gray;
|
||||
}
|
||||
.attr-value {
|
||||
color:green;
|
||||
}
|
||||
.node-value {
|
||||
font-weight:bold;
|
||||
}
|
||||
.entity {
|
||||
font-weight:bold;
|
||||
}
|
||||
.metamodel {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.instances {
|
||||
font-weight:bold;
|
||||
}
|
||||
.instance {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,329 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<!--
|
||||
The single web page for JEST is a templated HTML. This page consists of three major divisions
|
||||
a) a section holds the menu items for available commands
|
||||
b) a section for the command window where user enters command specifications
|
||||
c) a section displays the server response of the command execution
|
||||
|
||||
This templated file contains replaceable tokens in ${token} form. JEST servlet replaces the tokens with
|
||||
actual deployment values on initialization.
|
||||
|
||||
The menu navigation and command window are managed by jest.js script.
|
||||
The rendition of server response is managed by dojo script.
|
||||
|
||||
All hyperlinks in this page are relative to the base URI of this page to prevent
|
||||
any cross-domain scripting.
|
||||
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- Base URI is replaced with actual deployment data on initialization -->
|
||||
<!-- ==================================================================================== -->
|
||||
<base href="${jest.uri}">
|
||||
<!-- base href="http://localhost:8080/demo/jest/" -->
|
||||
<title>JEST: REST on OpenJPA</title>
|
||||
|
||||
<link type="text/css" rel="stylesheet" href="${dojo.base}/dijit/themes/${dojo.theme}/${dojo.theme}.css">
|
||||
<!-- link type="text/css" rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dijit/themes/claro/claro.css" -->
|
||||
<link type="text/css" rel="stylesheet" href="jest.css">
|
||||
<script type="text/javascript" language="javascript" djConfig="parseOnLoad: true, isDebug: true"
|
||||
src="${dojo.base}/dojo/dojo.xd.js">
|
||||
</script>
|
||||
<!-- script type="text/javascript" djConfig="parseOnLoad:true;isDebug=true" src="http://ajax.googleapis.com/ajax/libs/dojo/1.5/dojo/dojo.xd.js"></script -->
|
||||
<script type="text/javascript" language="javascript" src="jest.js">
|
||||
</script>
|
||||
|
||||
<meta content="REST,OpenJPA,JPA,JEST">
|
||||
</head>
|
||||
|
||||
|
||||
<body class=" ${dojo.theme} ">
|
||||
<!-- body class=" claro " -->
|
||||
<a name="top"></a>
|
||||
<img src="images/jest.jpg" width="63px" height="69px" alt="JEST logo" align="middle">
|
||||
<span class="logo">JEST: REST on OpenJPA</span>
|
||||
<hr>
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- Section for menu based commands. -->
|
||||
<!-- -->
|
||||
<!-- Each menu opens a command window by making a menu section visible -->
|
||||
<!-- ==================================================================================== -->
|
||||
<div id="menu" class="menu">
|
||||
<table cellspacing="1em">
|
||||
<tr><td><img alt="Home" src="images/home.jpg" width="100" height="100"></td></tr>
|
||||
<tr><td style="text-align:center"><a href="javascript:openMenu('home');">Info</a></td></tr>
|
||||
<tr><td><img alt="Domain" src="images/domain.jpg" width="100" height="100"></td></tr>
|
||||
<tr><td style="text-align:center"><a href="javascript:openMenu('domain');">browse domain</a></td></tr>
|
||||
<tr><td><img alt="Find" src="images/find.jpg" width="100" height="100"></td></tr>
|
||||
<tr><td style="text-align:center"><a href="javascript:openMenu('find');">find instances</a></td></tr>
|
||||
<tr><td><img alt="Query" src="images/query2.png" width="100" height="100"></td></tr>
|
||||
<tr><td style="text-align:center"><a href="javascript:openMenu('query');">query objects</a></td></tr>
|
||||
<tr><td><img alt="View" src="images/properties.jpg" width="100" height="100"></td></tr>
|
||||
<tr><td style="text-align: center"><a href="javascript:openMenu('properties');">view properties</a></td></tr>
|
||||
<tr><td><img alt="Deploy" src="images/monitor.jpg" width="100" height="100"></td></tr>
|
||||
<tr><td style="text-align:center"><a href="javascript:openMenu('deploy');">deploy</a></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- Section for each command specification -->
|
||||
<!-- -->
|
||||
<!-- Contains a set of div sub-sections each for a single command. A command window -->
|
||||
<!-- consists of a brief description of the command and set of input elements where user -->
|
||||
<!-- can enter command qualifiers and arguments. -->
|
||||
<!-- The URI for the command is updated as user entry changes. If the user clicks on the -->
|
||||
<!-- URI hyperlink, then a request with the URI is sent to the server. -->
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- Introduces basic information about JEST -->
|
||||
<!-- ==================================================================================== -->
|
||||
<div id="home" class="highlight" dojoType="dijit.TitlePane"
|
||||
title="<a href='http://openjpa.apache.org/jest-usage.html' target='_blank'>JEST</a>
|
||||
<b>facilitates RESTful interaction with OpenJPA</b>">
|
||||
|
||||
|
||||
<UL>
|
||||
<LI>Deployed as a HTTP servlet in <b>any</b> web or enterprise application module using
|
||||
OpenJPA as its JPA persistence provider.
|
||||
</LI>
|
||||
|
||||
<LI>Completely <b>metadata-driven</b> and hence <b>generic</b> as opposed to specific to any
|
||||
application domain.
|
||||
</LI>
|
||||
|
||||
<LI>Introduces a <b>URI syntax</b>. Interprets the HTTP request as a JPA resource based on
|
||||
that syntax. The response is in <tt>XML</tt> or <tt>JSON</tt> format. </LI>
|
||||
|
||||
<LI>Representational state for persistent instances is <b>coherent</b>.
|
||||
The response always contains the <em>closure</em> of the persistent instances.
|
||||
</LI>
|
||||
|
||||
<LI>Connects to the <b>persistent unit</b> of an application in the same module. Or
|
||||
instantiates its own persistence unit.
|
||||
</LI>
|
||||
</UL>
|
||||
</div>
|
||||
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- Describes deployment details -->
|
||||
<!-- ==================================================================================== -->
|
||||
<div id="deploy" class="highlight" style="display:none;" dojoType="dijit.TitlePane"
|
||||
title='<a href="http://openjpa.apache.org/jest-usage.html" target="_blank">Deploy JEST</a>
|
||||
<b>as a servlet in a web application.</b>'>
|
||||
|
||||
|
||||
Following <code>WEB-INF/web.xml</code> descriptor will enable JEST to
|
||||
operate on a persistence unit named <code>${persistence.unit}</code>. Of course, JEST
|
||||
servlet must be in the same module scope of the application that is using using
|
||||
<code>${persistence.unit}</code> as its persistence unit.
|
||||
<br>
|
||||
<pre>
|
||||
<code class="tag"><servlet></code>
|
||||
<code class="tag"><servlet-name></code>${servlet.name}<code class="tag"></servlet-name></code>
|
||||
<code class="tag"><servlet-class></code><span style="color:blue">org.apache.openjpa.persistence.jest.JESTServlet</span><code class="tag"></servlet-class></code>
|
||||
<code class="tag"><init-param></code>
|
||||
<code class="tag"><param-name></code><span style="color:red;">persistence.unit</span><code class="tag"></param-name></code>
|
||||
<code class="tag"><param-value></code><span style="color:red">${persistence.unit}</span><code class="tag"></param-value></code>
|
||||
<code class="tag"></init-param></code>
|
||||
<code class="tag"></servlet></code>
|
||||
<code class="tag"><servlet-mapping></code>
|
||||
<code class="tag"><servlet-name></code>${servlet.name}<code class="tag"></servlet-name></code>
|
||||
<code class="tag"><url-pattern></code><span style="color:red">/${servlet.name}/*</span><code class="tag"></url-pattern></code>
|
||||
<code class="tag"></servlet-mapping></code>
|
||||
|
||||
</pre>
|
||||
When an web application module named <code>${webapp.name}</code> containing the above JEST servlet
|
||||
in a servlet container runs on <code>${server.name}</code> at port ${server.port}, then the JEST servlet
|
||||
can be accessed at <br>
|
||||
<code class="url">http://${server.name}:${server.port}/${webapp.name}/${servlet.name}/</code>
|
||||
<br>
|
||||
(<span style="color:red">do not miss the the trailing forward-slash / character</span>)
|
||||
<p>
|
||||
</div>
|
||||
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- Command window for find -->
|
||||
<!-- -->
|
||||
<!-- The div element contains a table and some of the table columns hold the input -->
|
||||
<!-- elements. The div, table and input elements are all identified with following -->
|
||||
<!-- naming convention: -->
|
||||
<!-- the main div section identifier is the moniker of the command itself i.e. 'find' -->
|
||||
<!-- the table identifier is 'find.command' -->
|
||||
<!-- the input element identifiers are 'find.{qualifier or argument name}' e.g. -->
|
||||
<!-- 'find.type' or 'find.format'. The qualifier or argument name must be the same as -->
|
||||
<!-- specified in jest.js JavaScript. -->
|
||||
<!-- -->
|
||||
<!-- The naming convention is crucial because the JavaScript jest.js identifies these -->
|
||||
<!-- elements by this implicit naming conventions. -->
|
||||
<!-- -->
|
||||
<!-- The user entries result in a relative URI displayed in this section itself. -->
|
||||
<!-- The event handlers on the input elements update the URI on change. -->
|
||||
<!-- The user can click the hyperlink to send the request to the server. -->
|
||||
<!-- ==================================================================================== -->
|
||||
<div id="find" class="highlight" style="display:none;" dojoType="dijit.TitlePane"
|
||||
title='<a href="http://openjpa.apache.org/jest-syntax.html" target="_blank">Find</a>
|
||||
<b>persistent objects by primary identifier.</b>'>
|
||||
|
||||
<!-- a form for user to fill in the find specifications. Script uses these entries to -->
|
||||
<!-- build up a request URI for JEST server -->
|
||||
<table id="find.command">
|
||||
<tr>
|
||||
<th width="30%" class="mandatory" title="Name of a persistent entity to find.">Entity Name
|
||||
<span class="help">
|
||||
<img src="images/help.jpg" width="8px" height="8px" onclick="showHelp('Entity', 'help/entity-name.html');">
|
||||
</span>
|
||||
</th>
|
||||
<th width="30%" class="mandatory" title="Primary Key of the entity.">Identifier</th>
|
||||
<th width="30%" title="Fetch Plan(s) to use. Separate each plan with comma.">Fetch Plan
|
||||
<span class="help">
|
||||
<img src="images/help.jpg" width="8px" height="8px"
|
||||
onclick="showHelp('Fetch Plan', 'help/fetch-plan.html');">
|
||||
</span>
|
||||
</th>
|
||||
<th width="10%" title="Format of the response.">Format
|
||||
<span class="help">
|
||||
<img src="images/help.jpg" width="8px" height="8px"
|
||||
onclick="showHelp('Response Format', 'help/response-format.html');"></img>
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input id="find.type" onblur="javascript:toURI('find');"></td>
|
||||
<td><input id="find.pk" onblur="javascript:toURI('find');"></td>
|
||||
<td><input id="find.plan" onblur="javascript:toURI('find');" ></td>
|
||||
<td>
|
||||
<select id="find.format" onchange="javascript:toURI('find');" >
|
||||
<option value=""></option>
|
||||
<option value="xml">XML</option>
|
||||
<option value="json">JSON</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
<!-- Script will fill in the URI in this element based on user entries from the above -->
|
||||
<!-- The element is made to look like a hyperlink but actually a span to omit the -->
|
||||
<!-- associated complexity of preventing the browser's default handling of hyperlink -->
|
||||
<!-- click events. -->
|
||||
<b>URI: </b><span id="find.uri" class="url-invalid">filling the form will update
|
||||
this hyperlink.</span>
|
||||
<button id="find.execute" style="display:none;">
|
||||
<img src="images/arrow_right.jpg" width="12px" height="12px"></button>
|
||||
</div>
|
||||
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- Command window for Query -->
|
||||
<!-- -->
|
||||
<!-- Similar to window for 'find' command. The added complexity is variable number of -->
|
||||
<!-- query binding parameters that the user may enter for a query. -->
|
||||
<!-- Each query binding parameter is accepted via two inputs on a dynamically created row -->
|
||||
<!-- The dynamic row is identified as 'query.vararg.{n}' and the pair of input elements -->
|
||||
<!-- 'query.vararg.{n}.key' and 'query.vararg.{n}.value' respectively. {n} is a monotonic -->
|
||||
<!-- integer. If a dynamically created row is removed by the user, the index {n} does not -->
|
||||
<!-- decrement. -->
|
||||
<!-- ==================================================================================== -->
|
||||
<div id="query" class="highlight" style="display:none;" dojoType="dijit.TitlePane"
|
||||
title='<a href="http://openjpa.apache.org/jest-syntax.html" target="_blank">Query</a>
|
||||
<b>with JPQL or named query and binding parameters.</b>'>
|
||||
|
||||
<!-- a form for user to fill in the find specifications. Script uses these entries to -->
|
||||
<!-- build up a request URI for JEST server. This form is more complicated than that -->
|
||||
<!-- of find, because a query can accept variable number of binding parameters -->
|
||||
<table id="query.command">
|
||||
<tr>
|
||||
<th width="20%" class="mandatory" title="JPQL query or a named query">Query
|
||||
<span class="help"><img src="images/help.jpg" width="8px" height="8px"
|
||||
onclick="showHelp('Query', 'help/query.html');"></img></span></th>
|
||||
<th width="10%"style="display: hidden"></th>
|
||||
<th width="10%"style="display: hidden"></th>
|
||||
<th width="10%"style="display: hidden"></th>
|
||||
<th width="5%" title="Is it a named query?">Single</th>
|
||||
<th width="5%" title="Returns single result?">Named</th>
|
||||
<th width="30%" title="Fetch Plan(s) to use. Separate each plan with comma.">Fetch Plan
|
||||
<span class="help"><img src="images/help.jpg" width="8px" height="8px"
|
||||
onclick="showHelp('Fetch Plan', 'help/fetch-plan.html');"></img></span></th>
|
||||
<th width="10%" title="Format of the response.">Format
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4"><input id="query.q" onblur="javascript:toURI('query');"></td>
|
||||
<td><input id="query.single" type="checkbox" value="true" onchange="javascript:toURI('query');"></td>
|
||||
<td><input id="query.named" type="checkbox" value="true" onchange="javascript:toURI('query');"></td>
|
||||
<td><input id="query.plan" onblur="javascript:toURI('query');" ></td>
|
||||
<td>
|
||||
<select id="query.format" onchange="javascript:toURI('query');" >
|
||||
<option value=""></option>
|
||||
<option value="xml">XML</option>
|
||||
<option value="json">JSON</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="query.vararg.0">
|
||||
<td>
|
||||
<button onclick="javascript:addVarArgRow('query.vararg',0,'Bind Query Parameter');">Bind Parameter</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
<!-- Script will fill in the URI in this element based on user entries from the above -->
|
||||
<!-- The element is made to look like a hyperlink but actually a span to omit the -->
|
||||
<!-- associated complexity of preventing the browser's default handling of hyperlink -->
|
||||
<!-- click events. -->
|
||||
<b>URI: </b><span id="query.uri" class="url-invalid">filling the form will update
|
||||
this hyperlink.</span> <button id="query.execute" style="display:none;">
|
||||
<img src="images/arrow_right.jpg" width="12px" height="12px"></button>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- Command window for browsing persistent domain model -->
|
||||
<!-- ==================================================================================== -->
|
||||
<div id="domain" class="highlight" style="display:none;" dojoType="dijit.TitlePane"
|
||||
title='<a href="http://openjpa.apache.org/jest-syntax.html" target="_blank">Browse</a>
|
||||
<b>persistent domain model.</b>'>
|
||||
|
||||
This command accepts no qualifier or arguments. <br>
|
||||
|
||||
<b>JEST URI: </b><span id="domain.uri" class="url"
|
||||
onclick="javascript:render('domain','canvas', 'domain', 'xml');">domain</span>
|
||||
</div>
|
||||
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- Command window for viewing configuration of the persistence unit. -->
|
||||
<!-- ==================================================================================== -->
|
||||
<div id="properties" class="highlight" style="display:none;" dojoType="dijit.TitlePane"
|
||||
title='<a href="http://openjpa.apache.org/jest-syntax.html" target="_blank">View</a>
|
||||
<b>configuration properties of the persistence unit.</b>'>
|
||||
|
||||
This command accepts no qualifier or arguments <br>
|
||||
|
||||
<b>URI: </b><span id="properties.uri" class="url"
|
||||
onclick="javascript:render('properties','canvas', 'properties', 'xml');">properties</span>
|
||||
</div>
|
||||
|
||||
<!-- ==================================================================================== -->
|
||||
<!-- This empty section will be filled in by server response -->
|
||||
<!-- ==================================================================================== -->
|
||||
<div id="canvas" class="canvas">
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -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.
|
||||
|
||||
no-persistence-unit-param: Missing <b>persistence.unit</b> parameter. JEST Servlet must be \
|
||||
configured with a parameter named <b>persistence.unit<b> in <init-param> clause \
|
||||
of <servlet> declaration in <code>WEB-INF/web.xml</code> descriptor.
|
||||
servlet-init: JEST Servlet is initialized for "{0}" persistence unit.
|
||||
servlet-not-init: JEST Servlet can not find "{0}" persistence unit during servlet initialization. \
|
||||
JEST Servlet will try to locate the unit when a request is to be served.
|
||||
|
||||
no-persistence-unit: JEST can not locate the component using persistence unit <b>{0}</b>. This can happen \
|
||||
for several reasons: \
|
||||
<OL>the component is not initialized. </OL>\
|
||||
<OL>the component and JEST servlet do not belong to the same deployment module</OL>\
|
||||
<OL>the component did not configure the persistence unit for pooling. To enable pooling, \
|
||||
create the persistence unit with configuration property <code>openjpa.EntityManagerFactoryPool=true</code>.<br> \
|
||||
The property must be passed to <code>Persistence.createEntityManagerFactory(String unit, Map props)</code> \
|
||||
with the second <code>Map</code> argument and <em>not</em> via <code>META-INF/persistence.xml</code></OL>.
|
||||
|
||||
|
||||
resource-not-found: Can not locate resource {0}. <br>This can happen for wrong URI syntax. See \
|
||||
<A href="http://openjpa.apache.org/jest-syntax.html" target="_blank">JEST URI Help page</A> for correct syntax.
|
||||
|
||||
query-execution-error: Error executing query "{0}". See stacktrace for details.
|
||||
parse-invalid-qualifier: {0} command does not recognize "{1}" as a qualifier. Valid qualifiers are {2}.
|
||||
parse-missing-mandatory-argument: {0} command must have "{1}" argument. Available arguments are {2}.
|
||||
parse-less-argument: {0} command must have at least {2} argument. Available arguments are {1}.
|
||||
# ----------------------------------------------------------------------
|
||||
# Format related error
|
||||
# ----------------------------------------------------------------------
|
||||
format-xml-null-parent: A null XML parent element encountered during serialization
|
||||
format-xml-null-doc: Given parent element is not part of XML document
|
||||
format-xml-null-closure: Set of visited instances can not be null for serialization
|
||||
format-not-supported: format {0} in command {1} is not registered. Available formats are {2}.
|
||||
|
||||
properties-caption: Configuration of {0} Persistence Unit
|
||||
entity-not-found: Resource of type {0} with identifier {1} is not found.
|
||||
bad-uri: Can not reconstruct URI from original URL {0}
|
||||
|
||||
|
||||
find-title: JEST find
|
||||
find-desc: JEST find command may return more than one result. Why?
|
||||
query-title: JEST query
|
||||
query-desc: JEST query command may return more than the directly selected result. Why?
|
||||
domain-command: JEST domain
|
||||
domain-desc: JEST domain command prints the persistent domain model
|