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

org.cyclades.engine.nyxlet.templates.stroma.STROMANyxlet Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2012, THE BOARD OF TRUSTEES OF THE LELAND STANFORD JUNIOR UNIVERSITY
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *    Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *    Neither the name of the STANFORD UNIVERSITY nor the names of its contributors
 *    may be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/
package org.cyclades.engine.nyxlet.templates.stroma;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import org.cyclades.engine.ResponseCodeEnum;
import org.cyclades.engine.NyxletSession;
import org.cyclades.engine.api.Nyxlet;
import org.cyclades.engine.exception.CycladesException;
import org.cyclades.engine.logging.LogWriter;
import org.cyclades.engine.logging.LogWriterInterface;
import org.cyclades.engine.logging.LoggingEnum;
import org.cyclades.engine.logging.LoggingDelegate;
import org.cyclades.engine.nyxlet.templates.stroma.actionhandler.ActionHandler;
import org.cyclades.engine.nyxlet.templates.stroma.actionhandler.GetServiceMetaHandler;
import org.cyclades.engine.nyxlet.templates.stroma.actionhandler.ListActionsHandler;
import org.cyclades.engine.stroma.STROMARequestParameterAggregate;
import org.cyclades.engine.stroma.STROMAResponseWriter;
import org.cyclades.engine.util.MapHelper;
import org.cyclades.engine.util.XProperties;
import org.cyclades.io.ResourceRequestUtils;
import org.cyclades.engine.validator.ValidationFaultElement;

public abstract class STROMANyxlet extends Nyxlet {

    public STROMANyxlet () throws Exception {
        super();
    }

    @Override
    public byte[] process (NyxletSession nyxletSession) throws CycladesException {
        try {
            nyxletSession.setResponseContentType(nyxletSession.getDataContentType());
            this.processInternal(nyxletSession);
            return null;
        } catch (Exception ex) {
            // Something went terribly wrong...this is the final last resort...
            logError(ex.getMessage() + " " + ex.toString());
            return this.handleError(ResponseCodeEnum.GENERAL_ERROR.getCode(), ex);
        }
    }

    protected void processInternal (NyxletSession nyxletSession) throws CycladesException {
        final String eLabel = "STROMANyxlet.processInternal: ";
        String action = null;
        try {
            if (!this.isActive()) throw new CycladesException("This service is inactive", ResponseCodeEnum.SERVICE_INACTIVE.getCode());
            Map> RRDParameterMap = null;
            if (nyxletSession.isRRDRequest()) {
                RRDParameterMap = getRRDURIParameterMap(nyxletSession);
            }
            // First...detect if this is a request that ignores STROMA input, with an action handler specified as a query parameter or a RRD URI part.
            // The query parameter has precedence.
            ActionHandler handler = getActionHandler(nyxletSession.getActionString());
            if (handler == null && RRDParameterMap != null) {
                if (RRDParameterMap.containsKey(NyxletSession.ACTION_PARAMETER)) handler = getActionHandler(RRDParameterMap.get(NyxletSession.ACTION_PARAMETER).get(0));
            }
            // Second...load the parameters based on this detection
            Map> mergedBaseParameters;
            if (handler != null && handler.ignoreSTROMAParameters()) {
                mergedBaseParameters = baseParametersNonSTROMA(nyxletSession, RRDParameterMap);
            } else {
                mergedBaseParameters = baseParameters(nyxletSession, RRDParameterMap);
            }
            // Third...move forward and process now that all configuration scenarios have been covered
            action = nyxletSession.getActionString();
            if (action == null) {
                throw new Exception("No action has been specified.");
            }
            handler = getActionHandler(action);
            if (handler == null) throw new Exception("Unknown action specified: " + action);
            if (handler.getFieldValidators().size() > 0) processFieldValidators(handler.getFieldValidators().validate(nyxletSession, mergedBaseParameters));
            handler.handle(nyxletSession, mergedBaseParameters, new STROMAResponseWriter(getName(), nyxletSession, this));
        } catch (Exception e) {
            logError(eLabel + e);
            try {
                // Try and handle the error gracefully
                nyxletSession.raiseOrchestrationFault(eLabel + e);
                new STROMAResponseWriter(this.getName(), nyxletSession, this).writeErrorResponse((e instanceof CycladesException) ? ((CycladesException)e).getCode() : ResponseCodeEnum.GENERAL_ERROR.getCode(),
                        eLabel + e);
            } catch (Exception ex) {
                // Last resort....
                nyxletSession.setResponseContentType("text/html");
                StringBuffer sb = new StringBuffer("STROMANyxlet");
                sb.append("FAILURE: " + e);
                sb.append("");
                try {
                    nyxletSession.getOutputStream().write(sb.toString().getBytes());
                } catch (Exception exx) {
                    logError(eLabel + exx);
                }
            }
        }
    } // end of process(...)

    private ActionHandler getActionHandler (String actionHandlerString) {
        if (actionHandlerString == null) return null;
        Object handler = this.actionHandlers.get(actionHandlerString);
        return ((handler == null) ? null : (ActionHandler) handler);
    }

    @SuppressWarnings("unchecked")
    private Map> baseParameters (NyxletSession nyxletSession, Map> additionalMap) throws Exception {
        final String eLabel = "STROMANyxlet.baseParameters: ";
        try {
            Map> mergedParameters;
            if (additionalMap != null) {
                mergedParameters = STROMARequestParameterAggregate.getParameters(nyxletSession,
                        MapHelper.mergeMaps(nyxletSession.getParameterMap(), additionalMap, null),
                        null);
            } else {
                mergedParameters = STROMARequestParameterAggregate.getParameters(nyxletSession, null);
            }
            setReservedBaseParameters(nyxletSession, mergedParameters);
            return mergedParameters;
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        }
    }

    @SuppressWarnings("unchecked")
    private Map> baseParametersNonSTROMA (NyxletSession nyxletSession, Map> additionalMap) throws Exception {
        final String eLabel = "STROMANyxlet.baseParametersNonSTROMA: ";
        try {
            Map> mergedParameters;
            mergedParameters = MapHelper.immutableParameterMap(MapHelper.mergeMaps(nyxletSession.getParameterMap(), additionalMap, null));
            setReservedBaseParameters(nyxletSession, mergedParameters);
            return mergedParameters;
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        }
    }

    private void setReservedBaseParameters (NyxletSession nyxletSession, Map> mergedParameters) throws Exception {
        final String eLabel = "STROMANyxlet.setReservedBaseParameters: ";
        try {
            if (mergedParameters.containsKey(NyxletSession.ACTION_PARAMETER)) nyxletSession.setActionObject(mergedParameters.get(NyxletSession.ACTION_PARAMETER).get(0));
            if (mergedParameters.containsKey(NyxletSession.TRANSACTION_DATA_PARAMETER)) nyxletSession.setTransactionDataObject(mergedParameters.get(NyxletSession.TRANSACTION_DATA_PARAMETER).get(0));
            if (mergedParameters.containsKey(NyxletSession.LOG_LEVEL_PARAMETER)) nyxletSession.setUserLoggingLevel(Enum.valueOf(LoggingEnum.class, mergedParameters.get(NyxletSession.LOG_LEVEL_PARAMETER).get(0).toUpperCase()));
            if (mergedParameters.containsKey(NyxletSession.DURATION_PARAMETER)) {
                nyxletSession.setDurationRequested((mergedParameters.get(NyxletSession.DURATION_PARAMETER).get(0).isEmpty()) ? true : mergedParameters.get(NyxletSession.DURATION_PARAMETER).get(0).equalsIgnoreCase("true"));
            }
            if (mergedParameters.containsKey(NyxletSession.SERVICE_AGENT_PARAMETER)) {
                nyxletSession.setServiceAgentRequested((mergedParameters.get(NyxletSession.SERVICE_AGENT_PARAMETER).get(0).isEmpty()) ? true : mergedParameters.get(NyxletSession.SERVICE_AGENT_PARAMETER).get(0).equalsIgnoreCase("true"));
            }
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        }
    }

    /**
     * This method is responsible for initializing the action handlers. It is called from within init() which
     * will pass the appropriate actionHandlerEntrySet. If you wish to implement your own custom action handler
     * registration mechanism then you can override this method.
     *
     * @param actionHandlerEntrySet
     * @throws Exception
     */
    public void loadActionHandlers (Set> actionHandlerEntrySet) throws Exception {
        final Class[] parameterArray = {STROMANyxlet.class};
        final Object[] objectArray = {this};
        Map classToObjectMap = new HashMap();
        for (Entry actionHandler : actionHandlerEntrySet) {
            ActionHandler handler = classToObjectMap.get(actionHandler.getValue().toString());
            if (handler == null) {
                handler = (ActionHandler)this.getClass().getClassLoader().loadClass(actionHandler.getValue().toString()).
                            getConstructor(parameterArray).newInstance(objectArray);
                handler.init();
                classToObjectMap.put(actionHandler.getValue().toString(), handler);
            }
            actionHandlers.put(actionHandler.getKey().toString(), handler);
        }
    }

    protected void loadActionHandlers (String propertyString) throws Exception {
        ByteArrayInputStream bis = null;
        try {
            bis = new ByteArrayInputStream(propertyString.getBytes());
            Properties props = new Properties();
            props.load(bis);
            actionHandlers.put("listactions", new ListActionsHandler(this));
            actionHandlers.put("getservicemeta", new GetServiceMetaHandler(this));
            this.loadActionHandlers(props.entrySet());
        } finally {
            try { bis.close(); } catch (Exception ignore) {}
        }
    }

    private void loadLogWriters (String propertyString) throws Exception {
        final String eLabel = "STROMANyxlet.loadLogWriters: ";
        ByteArrayInputStream bis = null;
        try {
            bis = new ByteArrayInputStream(propertyString.getBytes());
            Properties props = new Properties();
            props.load(bis);
            Enumeration keySet = props.keys();
            String writerName;
            String[] writerFields;
            LogWriter writer;
            while (keySet.hasMoreElements()) {
                writerName = (String)keySet.nextElement();
                writerFields = ((String)props.get(writerName)).split("\\|");
                if (writerFields.length != 3) {
                    throw new Exception("Invalid LogWriter entry, parse error: " + writerName);
                }
                writer = new LogWriter(getEngineContext().getCanonicalEngineDirectoryPath(writerFields[0]), writerFields[1], writerFields[2]);
                this.logWriterMap.put(writerName, writer);
            }
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        } finally {
            try {
                bis.close();
            } catch (Exception e) {
            }
        }
    }

    private void loadLoggingDelegates (String propertyString) throws Exception {
        final String eLabel = "STROMANyxlet.loadLoggingDelegates: ";
        ByteArrayInputStream bis = null;
        try {
            bis = new ByteArrayInputStream(propertyString.getBytes());
            Properties props = new Properties();
            props.load(bis);
            Enumeration keySet = props.keys();
            String loggingDelegateName;
            String[] fields;
            LoggingDelegate loggingDelegate;
            while (keySet.hasMoreElements()) {
                loggingDelegateName = (String)keySet.nextElement();
                fields = ((String)props.get(loggingDelegateName)).split("\\|");
                if (fields.length != 2) {
                    throw new Exception("Invalid LoggingDelegate entry, parse error: " + loggingDelegateName);
                }
                loggingDelegate = new LoggingDelegate(this.getLogWriter(fields[0]),
                        LoggingEnum.valueOf(this.externalProperties.getProperty("LoggingDelegate." + loggingDelegateName + ".logLevel", fields[1]).toUpperCase()));
                this.loggingDelegateMap.put(loggingDelegateName, loggingDelegate);
            }
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        } finally {
            try {
                bis.close();
            } catch (Exception e) {
            }
        }
    }

    public LogWriterInterface getLogWriter (String writerName) throws Exception {
        final String eLabel = "STROMANyxlet.getLogWriter: ";
        try {
            LogWriterInterface writer = this.logWriterMap.get(writerName);
            if (writer == null) {
                throw new Exception("Writer does not exist: " + writerName);
            }
            return writer;
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        }
    }

    public LoggingDelegate getLoggingDelegate (String loggingDelegateName) throws Exception {
        final String eLabel = "STROMANyxlet.getLoggingDelegate: ";
        try {
            LoggingDelegate loggingDelegate = this.loggingDelegateMap.get(loggingDelegateName);
            if (loggingDelegate == null) {
                throw new Exception("LoggingDelegate does not exist: " + loggingDelegateName);
            }
            return loggingDelegate;
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        }
    }

    private void closeLogWriters () throws Exception {
        final String eLabel = "STROMANyxlet.closeLogWriters: ";
        try {
            for (String key : this.logWriterMap.keySet()) {
                this.logWriterMap.get(key).close();
            }
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        }
    }

    protected void destroyActionHandlers () throws Exception {
        for (String key : this.actionHandlers.keySet()) {
            try {
                ((ActionHandler)this.actionHandlers.get(key)).destroy();
            } catch (Exception ex) {
                logError(ex.getMessage() + " " + " Continuing to destroy remaining ActionHandlers");
            }
        }
    } // end of destroyActionHandlers(...)

    @Override
    public void init () throws CycladesException {
        final String eLabel = "STROMANyxlet.init: ";
        try {
            // External Properties
            String aValue = this.getAttribute(EXTERNAL_PROPERTIES);
            if (aValue != null && !aValue.isEmpty()) {
                this.loadExternalProperties(this.getAttribute(EXTERNAL_PROPERTIES));
            } else {
                loadExternalPropertiesDefault();
            }

            // LogWriters
            aValue = this.getAttribute(LOG_WRITERS);
            if (aValue == null) {
                logInfo(eLabel + "No LogWriters defined.");
            } else {
                this.loadLogWriters(aValue);
            }

            // LoggingDelegates
            aValue = this.getAttribute(LOGGING_DELEGATES);
            if (aValue == null) {
                logInfo(eLabel + "No LoggingDelegates defined.");
            } else {
                this.loadLoggingDelegates(aValue);
            }

            // action handlers
            aValue = this.getAttribute(ACTION_HANDLERS);
            if (aValue == null) {
                logInfo(eLabel + "No ActionHandlers defined.");
            } else {
                this.loadActionHandlers(aValue);
            }
        } catch (Exception ex) {
            logError(eLabel + ex);
            throw new CycladesException(ex.getMessage(), ex);
        }
    } // end of init()

    @Override
    public void destroy () throws CycladesException {
        try {
            this.destroyActionHandlers();
        } catch (Exception ex) {
            logError(ex.toString());
        }
        try {
            this.closeLogWriters();
        } catch (Exception ex) {
            logError(ex.toString());
        }
    } // end of destroy()

    public XProperties getExternalProperties () {
        return this.externalProperties;
    }

    protected void loadExternalProperties (String path) throws Exception {
        loadExternalProperties(path.split("[,]"));
    }

    protected void loadExternalPropertiesDefault () throws Exception {
        String[] nyxletDirectories = getEngineContext().getNyxletDirectories();
        String[] propertyPaths = new String[nyxletDirectories.length];
        for (int i = 0; i < nyxletDirectories.length; i++) {
            propertyPaths[i] = nyxletDirectories[i] + "/" + getName() + ".properties";
        }
        loadExternalProperties(propertyPaths);
    }

    private void loadExternalProperties (String[] propPaths) throws Exception {
        InputStream is = null;
        for (String propPath : propPaths) {
            try {
                is = ResourceRequestUtils.getInputStream(propPath.trim(), null);
                this.externalProperties.load(is);
                // XXX - This is flexible for linking properties files...however someone can introduce an infinite loop if they do something foolish.
                if (externalProperties.containsKey(SUPPLEMENTAL_PROPERTIES)) loadExternalProperties(getEngineContext().getCanonicalEngineDirectoryPath((String)externalProperties.remove(SUPPLEMENTAL_PROPERTIES)));
                logInfo("This external properties file was successfully loaded: " + propPath);
                is.close();
            } catch (Exception ex) {
                logError(ex.toString());
            } finally {
                try { is.close();  } catch (Exception ignore ) { }
            }
        }
    }

    public boolean auth (NyxletSession sessionDelegate, Map authAttributes, Object authMetaObject) throws Exception {
        return (sessionDelegate.auth(authAttributes, authMetaObject, false) != null);
    }

    public void logDebug (String logMessage, Throwable... t) {
        log(LoggingEnum.DEBUG, logMessage, t);
    }

    public void logInfo (String logMessage, Throwable... t) {
        log(LoggingEnum.INFO, logMessage, t);
    }

    public void logWarn (String logMessage, Throwable... t) {
        log(LoggingEnum.WARN, logMessage, t);
    }

    public void logError (String logMessage, Throwable... t) {
        log(LoggingEnum.ERROR, logMessage, t);
    }

    public void logNotify (LoggingEnum level, String logMessage, Throwable... t) {
        // Log to normal channel
        try { log(level, logMessage, t); } catch (Exception e) {}
        // Send notification
        try {
            getEngineContext().sendNotification(level, (t.length > 0) ? logMessage + " " +  t[0] : logMessage);
        } catch (Exception e) {
            logger.log(LoggingEnum.ERROR.getLog4jLevel(), "Failed to send notification, we need to look into this: " + e);
            // Do we want to print out a stack trace?
        }
    }

    public void logStackTrace (Throwable t) { logStackTrace(t, true); }

    public void logStackTrace (Throwable t, boolean newLine) {
        StringBuilder sb = new StringBuilder(t.toString());
        sb.append(" > STACK_TRACE_ELEMENTS > [");
        StackTraceElement[] stes = t.getStackTrace();
        for (int i = 0; i < stes.length; i++) {
            if (newLine) sb.append("\n");
            sb.append("\"").append(stes[i].toString()).append("\"");
             sb.append((i < stes.length - 1) ? "," : (newLine) ? "\n" : "");
        }
        sb.append("]");
        log(LoggingEnum.ERROR, sb.toString());
    }

    private void log (LoggingEnum loggingEnum, String message, Throwable... t) {
        try {
            getLoggingDelegate(DEFAULT_LOGGING_DELEGATE).log(loggingEnum, "", (t.length > 0) ? message + " " +  t[0] : message);
        } catch (Exception e) {
            if (t.length > 0) {
                logger.log(loggingEnum.getLog4jLevel(), message, t[0]);
            } else {
                logger.log(loggingEnum.getLog4jLevel(), message);
            }
        }
    }

    public boolean shouldLog (LoggingEnum logLevel) throws Exception { return shouldLog(logLevel, null); }

    public boolean shouldLog (LoggingEnum logLevel, LoggingEnum userLogLevel) throws Exception {
        final String eLabel = "STROMANyxlet.shouldLog: ";
        try {
            LoggingDelegate loggingDelegate = getLoggingDelegate(DEFAULT_LOGGING_DELEGATE);
            if (loggingDelegate != null) return loggingDelegate.shouldLog(logLevel, userLogLevel);
            return LoggingEnum.shouldLogLog4jLevel(logger.getLevel(), logLevel.getLog4jLevel(), (userLogLevel != null) ? userLogLevel.getLog4jLevel() : null);
        } catch (Exception e) {
            throw new Exception(eLabel + e);
        }
    }

    private void processFieldValidators(List validationFaultElements) throws CycladesException {
        if (validationFaultElements.size() < 1) return;
        throw new CycladesException(ValidationFaultElement.toString("VALIDATION_FAULT_ELEMENTS", validationFaultElements),
                ResponseCodeEnum.REQUEST_VALIDATION_FAULT.getCode());
    }

    /**
     * Loop through all of the action handlers and test each one to see if it is healthy. Return
     * false if any are encountered that are not healthy. Subclasses can override this method and
     * do something meaningful with the return value. See the helloword example Nyxlet for a further
     * example.
     */
    @Override
    public boolean isHealthy () throws CycladesException {
        try {
            for (String key : actionHandlers.keySet()) {
                if (!((ActionHandler)actionHandlers.get(key)).isHealthy()) throw new Exception("Unhealthy action handler: " + key);
            }
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    public String[] listActionHandlers () {
        return  actionHandlers.keySet().toArray(new String[0]);
    }

    protected Map actionHandlers = new HashMap();
    private Map logWriterMap = new HashMap();
    private Map loggingDelegateMap = new HashMap();
    private XProperties externalProperties = new XProperties();
    public static final String ACTION_HANDLERS          = "actionHandlers";
    public static final String LOG_WRITERS              = "logWriters";
    public static final String LOGGING_DELEGATES        = "loggingDelegates";
    public static final String EXTERNAL_PROPERTIES      = "externalProperties";
    public static final String DEFAULT_LOGGING_DELEGATE = "general";
    public static final String SUPPLEMENTAL_PROPERTIES  = "supplementalProperties";
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy