Java tutorial
/** * personium.io * Copyright 2014 FUJITSU LIMITED * * Licensed 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 com.fujitsu.dc.engine; import java.io.Closeable; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.URL; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import org.apache.http.HttpStatus; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.Function; import org.mozilla.javascript.NativeObject; import org.mozilla.javascript.Script; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.WrappedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fujitsu.dc.client.utils.DcLoggerFactory; import com.fujitsu.dc.engine.adapter.DcEngineDao; import com.fujitsu.dc.engine.adapter.DcRequestBodyStream; import com.fujitsu.dc.engine.adapter.Require; import com.fujitsu.dc.engine.extension.support.AbstractExtensionScriptableObject; import com.fujitsu.dc.engine.extension.support.ExtensionJarLoader; import com.fujitsu.dc.engine.extension.support.ExtensionLogger; import com.fujitsu.dc.engine.extension.support.IExtensionLogger; import com.fujitsu.dc.engine.extension.support.JavaClassRevealFilter; import com.fujitsu.dc.engine.jsgi.DcResponse; import com.fujitsu.dc.engine.jsgi.JSGIRequest; import com.fujitsu.dc.engine.source.ISourceManager; import com.fujitsu.dc.engine.utils.DcEngineConfig; import com.fujitsu.dc.engine.utils.DcEngineLoggerFactory; /** * DC-Engine?. */ public class DcEngineContext implements Closeable { /** . */ private static Logger log = LoggerFactory.getLogger(DcEngineContext.class); private static final String DC_SCOPE = "dc"; private static final String DC_EXTENSION_SCOPE = "extension"; private static Map<String, Script> engineLibCache = new ConcurrentHashMap<String, Script>(); /** Cell??. */ private String currentCellName; /** Box??. */ private String currentBoxName; /** URI. */ private String currentSchemeUri; /** Rhino?Context. */ private org.mozilla.javascript.Context cx; /** Rhino?ContextFactory. */ private DcJsContextFactory factory; /** Rhino?Scope. */ private Scriptable scope; /** URL. */ private String baseUrl; /** ?. */ private ISourceManager sourceManager; static { ContextFactory.initGlobal(new DcJsContextFactory()); } /** * . * @throws DcEngineException DcEngine */ public DcEngineContext() throws DcEngineException { // Rhino??? this.factory = new DcJsContextFactory(); this.cx = factory.enterContext(); this.scope = cx.initStandardObjects(); } /** * Extension JavaScript??. * ??? Extension??? * @throws DcEngineException */ /** * @throws DcEngineException */ private void prepareExtensionClass() throws DcEngineException { // Extension jar? ExtensionJarLoader extLoader = null; try { extLoader = ExtensionJarLoader.getInstance(this.cx.getApplicationClassLoader(), new JavaClassRevealFilter()); factory.initApplicationClassLoader(extLoader.getClassLoader()); } catch (IOException e) { throw new DcEngineException("Server Error", DcEngineException.STATUSCODE_SERVER_ERROR, e); } catch (DcEngineException e) { throw e; } // Javascript?????? Java? // ? NativeObject dcScope = (NativeObject) this.scope.get(DC_SCOPE, this.scope); NativeObject declaringClass = (NativeObject) dcScope.get(DC_EXTENSION_SCOPE, dcScope); for (Class<? extends Scriptable> clazz : extLoader.getPrototypeClassSet()) { try { if (AbstractExtensionScriptableObject.class.isAssignableFrom(clazz)) { // AbstractExtensionScriptableObject????? @SuppressWarnings("unchecked") Class<? extends AbstractExtensionScriptableObject> extensionClazz = (Class<? extends AbstractExtensionScriptableObject>) clazz; // Extension???? // ?????????????????????) try { Method setLoggerMethod = extensionClazz.getMethod("setLogger", new Class[] { Class.class, IExtensionLogger.class }); setLoggerMethod.setAccessible(true); setLoggerMethod.invoke(null, new Object[] { extensionClazz, new ExtensionLogger(extensionClazz) }); } catch (Exception e) { log.info("setLogger method cannot be called.", e); } } // ############################################################################3 // ????????????? extension?????? // ??? extension????? UserScript??????????????????????? // ???????Script??? // ############################################################################3 ScriptableObject.defineClass(declaringClass, clazz); } catch (RuntimeException e) { log.warn(String.format("Warn: Extension class(%s) could not be revealed to javascript.: %s", clazz.getCanonicalName(), e.getMessage())); } catch (Exception e) { log.warn(String.format("Warn: Extension class(%s) could not be revealed to javascript.: %s", clazz.getCanonicalName(), e.getMessage())); } } } /** * ?. * @param value the ISourceManager */ public final void setSourceManager(final ISourceManager value) { this.sourceManager = value; } /** * ?. ??????????????setter????? * @param url URL * @param cell Cell?? * @param scheme URI * @param box Box?? * @param service ?? */ public final void loadGlobalObject(final String url, final String cell, final String scheme, final String box, final String service) { this.baseUrl = url; this.currentCellName = cell; this.currentBoxName = box; this.currentSchemeUri = scheme; } /** * JSGI. * @param source ? * @param req Request * @param res Response * @param is * @param serviceSubject * @return Response * @throws DcEngineException DcEngine */ public final Response runJsgi(final String source, final HttpServletRequest req, final HttpServletResponse res, final InputStream is, final String serviceSubject) throws DcEngineException { // JSGI // DAO? DcEngineDao dc = createDao(req, serviceSubject); // DAOJavaScript? javaToJs(dc, "dcjvm"); // RequireJavaScript? javaToJs(createRequireObject(), "dcrequire"); // dc-dao.js ?? try { loadJs("dc-dao"); } catch (IOException e1) { log.info("runJsgi error (DAO load io error) ", e1); throw new DcEngineException("Server Error", DcEngineException.STATUSCODE_SERVER_ERROR, e1); } // dc-lib.js ?? try { loadJs("dc-lib"); } catch (IOException e1) { log.info("runJsgi error (dc-lib load io error) ", e1); throw new DcEngineException("Server Error", DcEngineException.STATUSCODE_SERVER_ERROR, e1); } // jsgi-lib.js?? try { loadJs("jsgi-lib"); } catch (IOException e1) { log.info("runJsgi error (jsgi-lib load io error) ", e1); throw new DcEngineException("Server Error", DcEngineException.STATUSCODE_SERVER_ERROR, e1); } // dc?????Extension?? prepareExtensionClass(); // RequestJavaScript? JSGIRequest dcReq = new JSGIRequest(req, new DcRequestBodyStream(is)); // JSGI // (eval)? try { Object ret; log.info("eval user script : script size = " + source.length()); ret = evalUserScript(source, dcReq); log.info("[" + DcEngineConfig.getVersion() + "] " + "<<< Request Ended "); DcResponse dcRes = DcResponse.parseJsgiResponse(ret); return dcRes.build(); } catch (Error e) { // ??INFO? log.info("UserScript TimeOut", e); throw new DcEngineException("Script TimeOut", HttpStatus.SC_SERVICE_UNAVAILABLE); } catch (Exception e) { if (e instanceof WrappedException) { e = (Exception) ((WrappedException) e).getWrappedException(); } // ???INFO? log.info("User Script Evalucation Error : " + e.getMessage(), e); throw new DcEngineException("Server Error : " + e.getMessage(), DcEngineException.STATUSCODE_SERVER_ERROR, e); } } /** * UserScript. * @param source * @throws IOException IO * @throws DcEngineException DcEngineException */ private Object evalUserScript(final String source, JSGIRequest dcReq) throws DcEngineException { cx.evaluateString(scope, "fn_jsgi = " + source, null, 1, null); Object fObj = scope.get("fn_jsgi", scope); Object result = null; if (!(fObj instanceof Function)) { log.warn("fn_jsgi not found"); throw new DcEngineException("Server Error", DcEngineException.STATUSCODE_SERVER_ERROR); } Object[] functionArgs = { dcReq.getRequestObject() }; Function f = (Function) fObj; result = f.call(cx, scope, scope, functionArgs); return result; } /** * DAO?. * @param req Request * @param serviceSubject * @return DAO */ private DcEngineDao createDao(final HttpServletRequest req, final String serviceSubject) { DcEngineLoggerFactory engLogFactory = new DcEngineLoggerFactory(); DcLoggerFactory.setDefaultFactory(engLogFactory); DcEngineDao dccx = new DcEngineDao(baseUrl, currentCellName, currentSchemeUri, currentBoxName); dccx.setServiceSubject(serviceSubject); dccx.setBoxSchema(req.getHeader("X-Dc-Box-Schema")); String auth = req.getHeader(HttpHeaders.AUTHORIZATION); String version = req.getHeader(DcEngineDao.DC_VERSION); if (version != null && !(version.equals(""))) { dccx.setDcVersion(version); } log.debug("auth : --------------------------------------------------------------------------"); log.debug(auth); if (auth != null && auth.length() > "Bearer".length()) { dccx.setClientToken(auth.substring("Bearer".length()).trim()); } return dccx; } /** * Require?. * @param localPath ? * @return ???Require */ private Require createRequireObject() { Require requireComp = new Require(this); requireComp.setSourceManager(this.sourceManager); log.debug("RequireObject created"); return requireComp; } /** * JavaScript????. * @param name JavaScript?? * @throws IOException IO */ private Object loadJs(final String name) throws IOException { URL path = getClass().getResource("/js-lib/" + name + ".js"); Script jsBuildObject = null; if (engineLibCache.containsKey(path.toString())) { jsBuildObject = engineLibCache.get(path.toString()); } else { FileInputStream fis = new FileInputStream(path.getFile()); InputStreamReader isr = new InputStreamReader(fis, "UTF-8"); jsBuildObject = cx.compileReader(isr, path.getPath(), 1, null); engineLibCache.put(path.toString(), jsBuildObject); } if (jsBuildObject == null) { return null; } Object ret = jsBuildObject.exec(cx, scope); log.debug("Load JavaScript from Local Resource : " + path); return ret; } /** * Java?JavaScript??. * @param obj Java * @param propertyName JavaScript??? */ private void javaToJs(final Object obj, final String propertyName) { log.debug("JavaObject to JavaScriptProperty " + propertyName); Object jObj = org.mozilla.javascript.Context.javaToJS(obj, scope); ScriptableObject.putProperty(scope, propertyName, jObj); } /** * JavaScript????. * @param source JavaScript? * @param path JavaScript?? * @return */ public Object requireJs(final String source, final String path) { Object ret = cx.evaluateString(scope, source, path, 1, null); log.debug("Load JavaScript from Require Resource : " + path); return ret; } @Override public void close() throws IOException { DcJsContext.exit(); } }