All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openjpa.persistence.jest.JESTContext Maven / Gradle / Ivy

The newest version!
/*
 * 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_NOT_FOUND;

import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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}.
 * 
* 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 _cf = new PrototypeFactory<>(); public static final Localizer _loc = Localizer.forPackage(JESTContext.class); private static final String ONE_YEAR_FROM_NOW; public static final char QUERY_SEPARATOR = '?'; public static final String CONTEXT_ROOT = "/"; public static final String JEST_TEMPLATE = "jest.html"; /** * 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. */ @Override public String getPersistenceUnitName() { return _unit; } /** * Gets the persistence context. The persistence context is lazily constructed because all commands * may not need it. */ @Override public OpenJPAEntityManager getPersistenceContext() { if (_em == null) { _em = _emf.createEntityManager(); } return _em; } /** * Gets the request. */ @Override public HttpServletRequest getRequest() { return _request; } @Override public String getRequestURI() { StringBuffer buf = _request.getRequestURL(); String query = _request.getQueryString(); if (!isEmpty(query)) { buf.append(QUERY_SEPARATOR).append(query); } return buf.toString(); } /** * Gets the response. */ @Override public HttpServletResponse getResponse() { return _response; } /** * Executes the request. *
* 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. *
* 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. *
* For example, let the original request URL be:
* http://host:port/demo/jest/find?type=Actor&Robert *
* The response to this request is a HTML page that contained a hyperlink to jest.css stylesheet * in its <head> section.
* <link ref="jest.css" .....> *
* The browser will resolve the hyperlink by sending back another request as
* http://host:port/demo/jest/find/jest.css *
* * @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. '/' character. * Otherwise, if the path starts with context root, then returns the substring before the * next '/' character or end of the string, whichever is earlier. * If the path does not start with context root, returns * the substring before the first '/' 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); } @Override 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); if (mimeType == null) { mimeType = "application/text"; } _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); } } @Override 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 */ boolean isContextRoot(String path) { return (path == null || CONTEXT_ROOT.equals(path)); } boolean isEmpty(String s) { return s == null || s.trim().length() == 0; } /** * 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(JEST_TEMPLATE); CharArrayWriter out = new CharArrayWriter(); new TokenReplacedStream().replace(in, out, tokens); _rootResource = out.toString(); } _response.getOutputStream().write(_rootResource.getBytes()); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy