io.personium.engine.PersoniumEngineContext.java Source code

Java tutorial

Introduction

Here is the source code for io.personium.engine.PersoniumEngineContext.java

Source

/**
 * Personium
 * Copyright 2014 - 2018 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 io.personium.engine;

import io.personium.client.utils.PersoniumLoggerFactory;
import io.personium.common.utils.PersoniumCoreUtils;
import io.personium.engine.adapter.PersoniumEngineDao;
import io.personium.engine.adapter.PersoniumRequestBodyStream;
import io.personium.engine.adapter.Require;
import io.personium.engine.extension.support.AbstractExtensionScriptableObject;
import io.personium.engine.extension.support.ExtensionJarLoader;
import io.personium.engine.extension.support.ExtensionLogger;
import io.personium.engine.extension.support.IExtensionLogger;
import io.personium.engine.extension.support.JavaClassRevealFilter;
import io.personium.engine.jsgi.JSGIRequest;
import io.personium.engine.jsgi.PersoniumResponse;
import io.personium.engine.source.ISourceManager;
import io.personium.engine.utils.PersoniumEngineConfig;
import io.personium.engine.utils.PersoniumEngineLoggerFactory;

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;

/**
 * Personium-Engine?.
 */
public class PersoniumEngineContext implements Closeable {
    /** . */
    private static Logger log = LoggerFactory.getLogger(PersoniumEngineContext.class);

    private static final String PERSONIUM_SCOPE = "_p";
    private static final String 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 PersoniumJsContextFactory factory;
    /** Rhino?Scope. */
    private Scriptable scope;

    /** URL. */
    private String baseUrl;

    /** ?. */
    private ISourceManager sourceManager;

    static {
        ContextFactory.initGlobal(new PersoniumJsContextFactory());
    }

    /**
     * .
     * @throws PersoniumEngineException PersoniumEngine
     */
    public PersoniumEngineContext() throws PersoniumEngineException {
        // Rhino???
        this.factory = new PersoniumJsContextFactory();
        this.cx = factory.enterContext();

        this.scope = cx.initStandardObjects();

    }

    /**
     * Extension JavaScript??.
     * ??? Extension???
     * @throws PersoniumEngineException 
     */
    private void prepareExtensionClass() throws PersoniumEngineException {
        // Extension jar?
        ExtensionJarLoader extLoader = null;
        try {
            extLoader = ExtensionJarLoader.getInstance(this.cx.getApplicationClassLoader(),
                    new JavaClassRevealFilter());
            factory.initApplicationClassLoader(extLoader.getClassLoader());
        } catch (IOException e) {
            throw new PersoniumEngineException("Server Error", PersoniumEngineException.STATUSCODE_SERVER_ERROR, e);
        } catch (PersoniumEngineException e) {
            throw e;
        }

        // Javascript?????? Java?
        // ?
        NativeObject pScope = (NativeObject) this.scope.get(PERSONIUM_SCOPE, this.scope);
        NativeObject declaringClass = (NativeObject) pScope.get(EXTENSION_SCOPE, pScope);

        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 schema URI
     * @param box Box??
     * @param service ??
     */
    public final void loadGlobalObject(final String url, final String cell, final String schema, final String box,
            final String service) {
        this.baseUrl = url;
        this.currentCellName = cell;
        this.currentBoxName = box;
        this.currentSchemeUri = schema;
    }

    /**
     * JSGI.
     * @param source ?
     * @param req Request
     * @param res Response
     * @param is 
     * @param serviceSubject 
     * @return Response
     * @throws PersoniumEngineException PersoniumEngine
     */
    public final Response runJsgi(final String source, final HttpServletRequest req, final HttpServletResponse res,
            final InputStream is, final String serviceSubject) throws PersoniumEngineException {
        // JSGI
        // DAO?
        PersoniumEngineDao ed = createDao(req, serviceSubject);

        // DAOJavaScript?
        javaToJs(ed, "pjvm");

        // RequireJavaScript?
        javaToJs(createRequireObject(), "_require");

        // personium-dao.js ??
        try {
            loadJs("personium-dao");
        } catch (IOException e1) {
            log.info("runJsgi error (DAO load io error) ", e1);
            throw new PersoniumEngineException("Server Error", PersoniumEngineException.STATUSCODE_SERVER_ERROR,
                    e1);
        }

        // personium-lib.js ??
        try {
            loadJs("personium-lib");
        } catch (IOException e1) {
            log.info("runJsgi error (personium-lib load io error) ", e1);
            throw new PersoniumEngineException("Server Error", PersoniumEngineException.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 PersoniumEngineException("Server Error", PersoniumEngineException.STATUSCODE_SERVER_ERROR,
                    e1);
        }

        // p?????Extension??
        prepareExtensionClass();

        // RequestJavaScript?
        JSGIRequest jsReq = new JSGIRequest(req, new PersoniumRequestBodyStream(is));

        // JSGI
        // (eval)?
        try {
            Object ret;
            log.info("eval user script : script size = " + source.length());
            ret = evalUserScript(source, jsReq);
            log.info("[" + PersoniumEngineConfig.getVersion() + "] " + "<<< Request Ended ");

            PersoniumResponse pRes = PersoniumResponse.parseJsgiResponse(ret);

            return pRes.build();
        } catch (Error e) {
            // ??INFO?
            log.info("UserScript TimeOut", e);
            throw new PersoniumEngineException("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 PersoniumEngineException("Server Error : " + e.getMessage(),
                    PersoniumEngineException.STATUSCODE_SERVER_ERROR, e);
        }
    }

    /**
     * UserScript.
     * @param source 
     * @throws IOException IO
     * @throws PersoniumEngineException
     */
    private Object evalUserScript(final String source, JSGIRequest jsReq) throws PersoniumEngineException {
        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 PersoniumEngineException("Server Error", PersoniumEngineException.STATUSCODE_SERVER_ERROR);
        }

        Object[] functionArgs = { jsReq.getRequestObject() };
        Function f = (Function) fObj;
        result = f.call(cx, scope, scope, functionArgs);
        return result;
    }

    /**
     * DAO?.
     * @param req Request
     * @param serviceSubject 
     * @return DAO
     */
    private PersoniumEngineDao createDao(final HttpServletRequest req, final String serviceSubject) {
        PersoniumEngineLoggerFactory engLogFactory = new PersoniumEngineLoggerFactory();
        PersoniumLoggerFactory.setDefaultFactory(engLogFactory);

        PersoniumEngineDao pcx = new PersoniumEngineDao(baseUrl, currentCellName, currentSchemeUri, currentBoxName);
        pcx.setServiceSubject(serviceSubject);
        pcx.setBoxSchema(req.getHeader("X-Personium-Box-Schema"));
        String auth = req.getHeader(HttpHeaders.AUTHORIZATION);
        String version = req.getHeader(PersoniumEngineDao.PERSONIUM_VERSION);
        if (version != null && !(version.equals(""))) {
            pcx.setPersoniumVersion(version);
        }
        log.debug("auth : --------------------------------------------------------------------------");
        log.debug(auth);
        if (auth != null && auth.length() > "Bearer".length()) {
            pcx.setClientToken(auth.substring("Bearer".length()).trim());
        }
        // set defaultHeaders
        String requestKey = req.getHeader(PersoniumCoreUtils.HttpHeaders.X_PERSONIUM_REQUESTKEY);
        if (requestKey != null) {
            pcx.setDefaultHeader(PersoniumCoreUtils.HttpHeaders.X_PERSONIUM_REQUESTKEY, requestKey);
        }
        String eventId = req.getHeader(PersoniumCoreUtils.HttpHeaders.X_PERSONIUM_EVENTID);
        if (eventId != null) {
            pcx.setDefaultHeader(PersoniumCoreUtils.HttpHeaders.X_PERSONIUM_EVENTID, eventId);
        }
        String ruleChain = req.getHeader(PersoniumCoreUtils.HttpHeaders.X_PERSONIUM_RULECHAIN);
        if (ruleChain != null) {
            pcx.setDefaultHeader(PersoniumCoreUtils.HttpHeaders.X_PERSONIUM_RULECHAIN, ruleChain);
        }
        String via = req.getHeader(PersoniumCoreUtils.HttpHeaders.X_PERSONIUM_VIA);
        if (via != null) {
            pcx.setDefaultHeader(PersoniumCoreUtils.HttpHeaders.X_PERSONIUM_VIA, via);
        }

        return pcx;
    }

    /**
     * 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 {
        PersoniumJsContext.exit();
    }
}