org.nabucco.alfresco.enhScriptEnv.share.util.VersionInfoContributor.java Source code

Java tutorial

Introduction

Here is the source code for org.nabucco.alfresco.enhScriptEnv.share.util.VersionInfoContributor.java

Source

/*
 * Copyright 2015 PRODYNA AG
 *
 * Licensed under the Eclipse Public License (EPL), Version 1.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the License at
 *
 * https://www.eclipse.org/legal/epl-v10.html
 *
 * 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.nabucco.alfresco.enhScriptEnv.share.util;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.alfresco.util.PropertyCheck;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.WrapFactory;
import org.nabucco.alfresco.enhScriptEnv.common.script.EnhancedScriptProcessor;
import org.nabucco.alfresco.enhScriptEnv.common.script.ReferenceScript;
import org.nabucco.alfresco.enhScriptEnv.common.script.ScopeContributor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.surf.FrameworkBean;
import org.springframework.extensions.surf.LinkBuilder;
import org.springframework.extensions.surf.RequestContext;
import org.springframework.extensions.surf.WebFrameworkServiceRegistry;
import org.springframework.extensions.surf.exception.ConnectorServiceException;
import org.springframework.extensions.surf.support.AbstractRequestContext;
import org.springframework.extensions.surf.support.ThreadLocalRequestContext;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.connector.Connector;
import org.springframework.extensions.webscripts.connector.ConnectorService;
import org.springframework.extensions.webscripts.connector.Response;

/**
 * @author Axel Faust, <a href="http://www.prodyna.com">PRODYNA AG</a>
 */
public class VersionInfoContributor implements ScopeContributor, InitializingBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(VersionInfoContributor.class);

    protected static final String EDITION_TEAM = "team";
    protected static final String EDITION_COMMUNITY = "community";
    protected static final String EDITION_ENTERPRISE = "enterprise";
    protected static final String EDITION_UNKNOWN = "unknown";

    protected static final String KEY_DESCRIPTOR = "DESCRIPTOR";

    protected static final String KEY_EDITION = "EDITION";
    protected static final String KEY_FULL_VERSION = "FULL_VERSION";
    protected static final String KEY_VERSION = "VERSION";
    protected static final String KEY_SCHEMA = "SCHEMA";
    protected static final String KEY_IS_COMMUNITY = "IS_COMMUNITY";

    protected static final WrapFactory DEFAULT_WRAP_FACTORY = new WrapFactory();

    /**
     * Default interval between version information update checks (24 hours by default, since version upgrades while Share is running are
     * rather infrequent)
     */
    protected static final long DEFAULT_UPDATE_CHECK_INTERVAL = 24 * 60 * 60 * 1000;

    protected static final String CORE_VERSION_PATTERN = "^(\\d+(\\.\\d+)+)";

    protected ConnectorService connectorService;

    protected WebFrameworkServiceRegistry serviceRegistry;

    protected FrameworkBean frameworkUtil;

    protected EnhancedScriptProcessor<? extends ReferenceScript> scriptProcessor;

    protected final Map<String, String> cachedInformation = new HashMap<String, String>();

    protected long lastUpdateCheckTimestamp = 0l;

    protected long nextUpdateInterval = DEFAULT_UPDATE_CHECK_INTERVAL;

    /**
     * {@inheritDoc}
     */
    @Override
    public void afterPropertiesSet() {
        PropertyCheck.mandatory(this, "connectorService", this.connectorService);
        PropertyCheck.mandatory(this, "scriptProcessor", this.scriptProcessor);

        PropertyCheck.mandatory(this, "serviceRegistry", this.serviceRegistry);
        PropertyCheck.mandatory(this, "frameworkUtil", this.frameworkUtil);

        this.scriptProcessor.registerScopeContributor(this);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void contributeToScope(final Object scope, final boolean trustworthyScript, final boolean mutableScope) {
        if (scope instanceof Scriptable) {
            final Context context = Context.enter();
            try {
                // simple cache check and update / initialization
                final long now = System.currentTimeMillis();
                if (now - this.lastUpdateCheckTimestamp > this.nextUpdateInterval) {
                    synchronized (this) {
                        if (now - this.lastUpdateCheckTimestamp > this.nextUpdateInterval) {
                            this.retrieveReposioryVersionInfo();
                        }
                    }
                }

                if (!this.cachedInformation.isEmpty()) {
                    final NativeObject descriptorObj = new NativeObject();

                    ScriptableObject.defineConstProperty(descriptorObj, KEY_EDITION);
                    ScriptableObject.defineConstProperty(descriptorObj, KEY_FULL_VERSION);
                    ScriptableObject.defineConstProperty(descriptorObj, KEY_VERSION);
                    ScriptableObject.defineConstProperty(descriptorObj, KEY_SCHEMA);
                    ScriptableObject.defineConstProperty(descriptorObj, KEY_IS_COMMUNITY);

                    ScriptableObject.putConstProperty(descriptorObj, KEY_EDITION, DEFAULT_WRAP_FACTORY.wrap(context,
                            (Scriptable) scope, this.cachedInformation.get(KEY_EDITION), String.class));
                    ScriptableObject.putConstProperty(descriptorObj, KEY_FULL_VERSION,
                            DEFAULT_WRAP_FACTORY.wrap(context, (Scriptable) scope,
                                    this.cachedInformation.get(KEY_FULL_VERSION), String.class));
                    ScriptableObject.putConstProperty(descriptorObj, KEY_VERSION, DEFAULT_WRAP_FACTORY.wrap(context,
                            (Scriptable) scope, this.cachedInformation.get(KEY_VERSION), String.class));
                    ScriptableObject.putConstProperty(descriptorObj, KEY_SCHEMA,
                            DEFAULT_WRAP_FACTORY.wrap(context, (Scriptable) scope,
                                    this.cachedInformation.containsKey(KEY_SCHEMA)
                                            ? this.cachedInformation.get(KEY_SCHEMA)
                                            : Integer.valueOf(-1),
                                    Integer.class));

                    // assume community if flag not set
                    ScriptableObject.putConstProperty(descriptorObj, KEY_IS_COMMUNITY,
                            DEFAULT_WRAP_FACTORY.wrap(context, (Scriptable) scope,
                                    this.cachedInformation.containsKey(KEY_IS_COMMUNITY)
                                            ? Boolean.valueOf(this.cachedInformation.get(KEY_IS_COMMUNITY))
                                            : Boolean.TRUE,
                                    Boolean.class));

                    descriptorObj.sealObject();

                    ScriptableObject.defineConstProperty((Scriptable) scope, KEY_DESCRIPTOR);
                    ScriptableObject.putConstProperty((Scriptable) scope, KEY_DESCRIPTOR, descriptorObj);
                }
            } finally {
                Context.exit();
            }
        }
    }

    /**
     * @param connectorService
     *            the connectorService to set
     */
    public final void setConnectorService(final ConnectorService connectorService) {
        this.connectorService = connectorService;
    }

    /**
     * @param scriptProcessor
     *            the scriptProcessor to set
     */
    public final void setScriptProcessor(final EnhancedScriptProcessor<? extends ReferenceScript> scriptProcessor) {
        this.scriptProcessor = scriptProcessor;
    }

    /**
     * @param serviceRegistry
     *            the serviceRegistry to set
     */
    public final void setServiceRegistry(final WebFrameworkServiceRegistry serviceRegistry) {
        this.serviceRegistry = serviceRegistry;
    }

    /**
     * @param frameworkUtil
     *            the frameworkUtil to set
     */
    public final void setFrameworkUtil(final FrameworkBean frameworkUtil) {
        this.frameworkUtil = frameworkUtil;
    }

    protected void retrieveReposioryVersionInfo() {
        try {
            final Connector connector = this.connectorService.getConnector("alfresco");

            // workaround NPE-bugs in RequestCachingConnector when calling "call" outside of active request
            final boolean fakedRequestContext;
            RequestContext requestContext = ThreadLocalRequestContext.getRequestContext();
            if (requestContext == null) {
                fakedRequestContext = true;
                requestContext = new AbstractRequestContext(this.serviceRegistry, this.frameworkUtil) {

                    private static final long serialVersionUID = 1L;

                    @Override
                    public LinkBuilder getLinkBuilder() {
                        return null;
                    }

                    // Only relevant in Alfresco 5 - thus no @Override
                    public boolean isExtensibilitySuppressed() {
                        return false;
                    }
                };
            } else {
                fakedRequestContext = false;
            }

            try {

                final Response response = connector.call("/api/server");
                if (response.getStatus().getCode() == Status.STATUS_OK) {
                    try {
                        final JSONObject json = new JSONObject(new JSONTokener(response.getResponse()));

                        final JSONObject data = json.getJSONObject("data");
                        if (data != null) {
                            final String edition = data.getString("edition");
                            final String schema = data.getString("schema");
                            final String version = data.getString("version");

                            final String coreVersion;
                            final Matcher versionMatcher = Pattern.compile(CORE_VERSION_PATTERN).matcher(version);
                            if (versionMatcher.find()) {
                                coreVersion = versionMatcher.group(1);
                            } else {
                                coreVersion = null;
                            }

                            this.cachedInformation.put(KEY_EDITION, edition);
                            this.cachedInformation.put(KEY_FULL_VERSION, version);
                            this.cachedInformation.put(KEY_VERSION, coreVersion);
                            this.cachedInformation.put(KEY_SCHEMA, schema);

                            final Boolean isCommunity;
                            if (EDITION_ENTERPRISE.equalsIgnoreCase(edition)
                                    || EDITION_TEAM.equalsIgnoreCase(edition)) {
                                isCommunity = Boolean.FALSE;
                            } else if (EDITION_UNKNOWN.equalsIgnoreCase(edition)
                                    || EDITION_COMMUNITY.equalsIgnoreCase(edition) || edition == null
                                    || edition.trim().length() == 0) {
                                isCommunity = Boolean.TRUE;
                            } else {
                                LOGGER.warn("Unknown / unexpected edition {} - assuming 'community mode'", edition);
                                isCommunity = Boolean.TRUE;
                            }
                            this.cachedInformation.put(KEY_IS_COMMUNITY, isCommunity.toString());

                            this.lastUpdateCheckTimestamp = System.currentTimeMillis();
                            this.nextUpdateInterval = DEFAULT_UPDATE_CHECK_INTERVAL;
                        } else {
                            this.lastUpdateCheckTimestamp = System.currentTimeMillis();
                            // check again in a minute
                            this.nextUpdateInterval = 1000 * 60;
                            LOGGER.warn("Unexpected response content from /api/server");
                        }
                    } catch (final JSONException jsonEx) {
                        this.lastUpdateCheckTimestamp = System.currentTimeMillis();
                        // check again in a minute
                        this.nextUpdateInterval = 1000 * 60;
                        LOGGER.error("Error parsing version information from Repository", jsonEx);
                    }
                } else {
                    this.lastUpdateCheckTimestamp = System.currentTimeMillis();
                    // check again in a minute
                    this.nextUpdateInterval = 1000 * 60;
                    LOGGER.warn("Failed to load version information from Repository: " + response.getStatus());
                }
            } finally {
                if (fakedRequestContext && requestContext != null) {
                    requestContext.release();
                }
            }
        } catch (final ConnectorServiceException conEx) {
            this.lastUpdateCheckTimestamp = System.currentTimeMillis();
            // check again in a minute
            this.nextUpdateInterval = 1000 * 60;

            LOGGER.error("Error retrieving connector for version information query to Repository", conEx);
        }
    }
}