package js.http.encoder;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import javax.servlet.http.HttpServletRequest;

import js.dom.Document;
import js.dom.DocumentBuilder;
import js.lang.IllegalArgumentException;
import js.util.Classes;
import js.util.Files;
import js.util.Types;

/**
 * Handle XML input stream from HTTP request. This method expects a single formal parameter, current implementation supporting
 * {@link Document} and {@link InputStream}.
 * <p>
 * If formal parameter is a XML document, create a new document instance and load it from XML input stream. If formal parameter
 * is an input stream just hand over HTTP request input stream as it is.
 *
 * @author Iulian Rotaru
 * @version final
 */
final class XmlArgumentsReader implements ArgumentsReader, ArgumentPartReader {
	/**
	 * Cache closeable object till HTTP request is processed by application logic, in order to close it after. See
	 * {@link #clean()}. Uses thread local storage since arguments reader instance is reusable and cannot hold state.
	 */
	private ThreadLocal<Closeable> threadLocal = new ThreadLocal<Closeable>();

	/** XML document builder. */
	private final DocumentBuilder documentBuilder;

	/** Construct XML argument reader. */
	public XmlArgumentsReader() {
		this.documentBuilder = Classes.loadService(DocumentBuilder.class);
	}

	/**
	 * Handle XML input stream from HTTP request. This method expects a single formal parameter, current implementation
	 * supporting {@link Document} and {@link InputStream}.
	 *
	 * @param httpRequest HTTP request,
	 * @param formalParameters formal parameter types.
	 * @return arguments array.
	 * @throws IOException if reading from HTTP request input stream fails.
	 * @throws IllegalArgumentException if formal parameters does not contain exactly one type.
	 * @throws IllegalArgumentException if formal parameter type is parameterized.
	 * @throws IllegalArgumentException if formal parameter type is not supported by this method.
	 */
	@Override
	public Object[] read(HttpServletRequest httpRequest, Type[] formalParameters) throws IOException, IllegalArgumentException {
		if (formalParameters.length != 1) {
			throw new IllegalArgumentException("Bad parameters count. Should be exactly one but is |%d|.", formalParameters.length);
		}
		if (formalParameters[0] instanceof ParameterizedType) {
			throw new IllegalArgumentException("Parameterized type |%s| is not supported.", formalParameters[0]);
		}
		return new Object[] { read(httpRequest.getInputStream(), formalParameters[0]) };
	}

	/**
	 * Process XML input stream accordingly requested type. Current implementation supports two type: XML {@link Document} and
	 * XML input stream.
	 *
	 * @param inputStream XML input stream,
	 * @param type expected Java type.
	 * @return XML stream processing result.
	 * @throws IOException if XML stream reading fails.
	 * @throws IllegalArgumentException if requested Java type is not supported.
	 */
	@Override
	public Object read(InputStream inputStream, Type type) throws IOException {
		if (Types.isKindOf(type, Document.class)) {
			return documentBuilder.loadXML(inputStream);
		} else if (Types.isKindOf(type, InputStream.class)) {
			threadLocal.set(inputStream);
			return inputStream;
		} else {
			throw new IllegalArgumentException("Unsupported formal parameter type |%s| for XML content type.", type);
		}
	}

	/** Close closeable object from current thread storage then remove the storage. */
	@Override
	public void clean() {
		Files.close(threadLocal.get());
		threadLocal.remove();
	}
}


/**
 * Copyright (C) 2015 Michael Schnell. All rights reserved.
 * http://www.fuin.org/
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library. If not, see http://www.gnu.org/licenses/.
 */
package org.fuin.srcgen4j.core.velocity;

import static org.fuin.utils4j.Utils4J.replaceVars;

import java.io.File;
import java.io.IOException;
import java.util.Map;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

import org.fuin.srcgen4j.commons.Config;
import org.fuin.srcgen4j.commons.GeneratorConfig;
import org.fuin.srcgen4j.commons.InitializableElement;
import org.fuin.srcgen4j.commons.SrcGen4JContext;
import org.fuin.utils4j.Utils4J;
import org.fuin.xmlcfg4j.AbstractElement;

/**
 * Configuration for a {@link ParameterizedTemplateParser}.
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "parameterized-template-parser")
public class ParameterizedTemplateParserConfig extends AbstractElement
        implements InitializableElement<ParameterizedTemplateParserConfig, Config<GeneratorConfig>> {

    @XmlAttribute(name = "modelPath")
    private String modelPath;

    @XmlAttribute(name = "modelFilter")
    private String modelFilter;

    @XmlAttribute(name = "templatePath")
    private String templatePath;

    @XmlAttribute(name = "templateFilter")
    private String templateFilter;

    private transient File modelDir;

    private transient File templateDir;

    /**
     * Default constructor.
     */
    public ParameterizedTemplateParserConfig() {
        super();
    }

    /**
     * Constructor with model path.
     *
     * @param modelPath
     *            Model path.
     * @param modelFilter
     *            Regular expression for selecting model files.
     */
    public ParameterizedTemplateParserConfig(final String modelPath, final String modelFilter) {
        super();
        this.modelPath = modelPath;
        this.modelFilter = modelFilter;
    }

    /**
     * Returns the model path.
     *
     * @return Model path.
     */
    public final String getModelPath() {
        return modelPath;
    }

    /**
     * Returns the model directory.
     *
     * @return Model directory or NULL.
     */
    public final File getModelDir() {
        if ((modelDir == null) && (modelPath != null)) {
            modelDir = Utils4J.getCanonicalFile(new File(modelPath));
        }
        return modelDir;
    }

    /**
     * Sets the model path to a new value.
     *
     * @param modelPath
     *            Model path to set.
     */
    public final void setModelPath(final String modelPath) {
        this.modelPath = modelPath;
    }

    /**
     * Returns the regular expression for selecting model files.
     *
     * @return Model file filter expression.
     */
    public final String getModelFilter() {
        return modelFilter;
    }

    /**
     * Sets the regular expression for selecting model files.
     *
     * @param modelFilter
     *            Model file filter expression.
     */
    public final void setModelFilter(final String modelFilter) {
        this.modelFilter = modelFilter;
    }

    /**
     * Returns the template path.
     *
     * @return Template path.
     */
    public final String getTemplatePath() {
        return templatePath;
    }

    /**
     * Returns the template directory.
     *
     * @return Template directory or NULL.
     */
    public final File getTemplateDir() {
        if ((templateDir == null) && (templatePath != null)) {
            try {
                templateDir = new File(templatePath).getCanonicalFile();
            } catch (final IOException ex) {
                throw new RuntimeException("Couldn't determine canonical template file: " + templatePath, ex);
            }
        }
        return templateDir;
    }

    /**
     * Sets the template path to a new value.
     *
     * @param templatePath
     *            Template path to set.
     */
    public final void setTemplatePath(final String templatePath) {
        this.templatePath = templatePath;
    }

    /**
     * Returns the regular expression for selecting template files.
     *
     * @return Template file filter expression.
     */
    public final String getTemplateFilter() {
        return templateFilter;
    }

    /**
     * Sets the regular expression for selecting template files.
     *
     * @param templateFilter
     *            Template file filter expression.
     */
    public final void setTemplateFilter(final String templateFilter) {
        this.templateFilter = templateFilter;
    }

    @Override
    public final ParameterizedTemplateParserConfig init(final SrcGen4JContext context, final Config<GeneratorConfig> parent,
            final Map<String, String> vars) {
        inheritVariables(vars);
        setModelPath(replaceVars(getModelPath(), getVarMap()));
        setTemplatePath(replaceVars(getTemplatePath(), getVarMap()));
        return this;
    }

}


package js.http.encoder;

import java.io.IOException;
import java.io.OutputStreamWriter;

import javax.servlet.http.HttpServletResponse;

import js.dom.Document;

/**
 * Write XML document to HTTP response.
 *
 * @author Iulian Rotaru
 * @version final
 */
final class XmlValueWriter implements ValueWriter {
	/**
	 * Serialize XML document to output stream of given HTTP response.
	 *
	 * @param httpResponse HTTP response,
	 * @param value value object is a XML document.
	 * @throws IOException if output stream write operation fails.
	 */
	@Override
	public void write(HttpServletResponse httpResponse, Object value) throws IOException {
		final Document document = (Document) value;
		document.serialize(new OutputStreamWriter(httpResponse.getOutputStream(), "UTF-8"));
	}
}


/*
 * Copyright 2010 akquinet
 * 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 de.akquinet.chameria.webview;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;

import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Invalidate;
import org.apache.felix.ipojo.annotations.Property;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Validate;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.trolltech.qt.core.QFile;
import com.trolltech.qt.core.QSize;
import com.trolltech.qt.core.Qt.ContextMenuPolicy;
import com.trolltech.qt.core.Qt.ScrollBarPolicy;
import com.trolltech.qt.gui.QApplication;
import com.trolltech.qt.gui.QDialog;
import com.trolltech.qt.gui.QFileDialog;
import com.trolltech.qt.gui.QIcon;
import com.trolltech.qt.gui.QPrintDialog;
import com.trolltech.qt.gui.QPrinter;
import com.trolltech.qt.network.QNetworkProxy;
import com.trolltech.qt.network.QNetworkReply;
import com.trolltech.qt.webkit.QWebView;

import de.akquinet.chameria.services.BrowserService;

/**
 * Main component of the web view factory.
 * This component receives the configuration and creates
 * the browser windows and the web view.
 * It also has a couple of up-calls to handle the
 * configuration properly.
 */
@Component(name="chameria.webview")
@Provides
public class WebViewFactory implements BrowserService {

    /**
     * Chameria Logger.
     */
    final Logger m_logger = LoggerFactory.getLogger("Chameria-WebViewFactory");

    /**
     * The Browser Window.
     */
    private WebWindow m_browser;

    /**
     * The initial URL.
     */
    private String m_url;

    /**
     * Configuration Property : enables/disabled
     * the full screen mode.
     * Default : false.
     */
    @Property(name="fullscreen", value="false")
    private boolean m_fullscreen;

    /**
     * Configuration Property : the window height.
     * This property is ignored if the full screen mode
     * is enabled.
     * Default: 600 px.
     */
    @Property(name="height", value="600")
    private int m_height;

    /**
     * Configuration Property : the window width.
     * This property is ignored if the full screen mode
     * is enabled.
     * Default : 1024 px.
     */
    @Property(name="width", value="1024")
    private int m_width;

    /**
     * Configuration Property : enables / disables the
     * window 'resizability'.
     * This property is ignored if the full screen mode
     * is enabled.
     * Default : true.
     */
    @Property(name="resizable", value="true")
    private boolean m_resizable;

    /**
     * Configuration Property : enables / disables
     * the menu bar (top bar)
     * Default : true (enabled).
     */
    @Property(name="menu.bar", value="true")
    private boolean m_bar;

    /**
     * Configuration Property : enables / disables
     * the context menu
     * Default : false (disabled).
     */
    @Property(name="context.menu", value="false")
    private boolean m_contextMenu;

    /**
     * Configuration Property: sets the path
     * of the application icon. If not set, no
     * icon will be used.
     * The path is relative to the execution
     * directory.
     */
    @Property(name="application.icon")
    private String m_icon;

    /**
     * Configuration Property: sets the application
     * name used as window title.
     */
    @Property(name="application.name")
    private String m_appName;

    /**
     * Configuration Property: sets the application
     * version.
     */
    @Property(name="application.version")
    private String m_appVersion;

    /**
     * Configuration Property: enables / disables the
     * print support (window.print)
     * Default: false (disabled)
     */
    @Property(name="print", value="false")
    private boolean m_print;

    /**
     * Configuration Property: enables / disables the
     * download of unsupported content.
     * Default: false (disabled)
     */
    @Property(name="download", value="false")
    private boolean m_download;

    /**
     * Configuration Property: enables / disables the
     * opening of new windows (window.open).
     * The opened window have the same property as the parent
     * (current) one.
     * Default: false (disabled)
     */
    @Property(name="window.open", value="false")
    private boolean m_window_open;

    /**
     * Configuration Property: enables / disables the
     * inspector. The context menu must also be enabled.
     * Default: false (disabled)
     */
    @Property(name="inspector", value="false")
    private boolean m_inspector;

    /**
     * Configuration Property: enables / disables the
     * local storage (session storage, local storage,
     * client database, offline mode...)
     * Default: true (enabled)
     */
    @Property(name="storage", value="true")
    private boolean m_localStorage;

    /**
     * Configuration Property: sets the local storage
     * location (path relative to the launching directory.
     * Default: ./offline-storage
     */
    @Property(name="storage.location", value="offline-storage")
    private String m_localStorageLocation;

    /**
     * Horizontal Scrollbar policy.
     * Supported values are:
     *

     *
ScrollBarAsNeeded (default)

     *
ScrollBarAlwaysOn

     *
ScrollBarAlwaysOff

     *

     */
    @Property(name="scrollbar.horizontal", value="ScrollBarAsNeeded")
    private String m_scrollbar_horizontal;

    /**
     * Horizontal Scrollbar policy.
     * Supported values are:
     *

     *
ScrollBarAsNeeded (default)

     *
ScrollBarAlwaysOn

     *
ScrollBarAlwaysOff

     *

     */
    @Property(name="scrollbar.vertical", value="ScrollBarAsNeeded")
    private String m_scrollbar_vertical;

    /**
     * Sets the default fixed font size.
     */
    @Property(name="font.size.fixed", value="-1")
    private int m_fixed_fontsize;

    /**
     * Sets the default font size.
     */
    @Property(name="font.size", value="-1")
    private int m_fontsize;

    /**
     * Sets the proxy type. To avoid proxy, don't assign a value to
     * this property.
     * Supported values are:
     *

     *
Socks5Proxy

     *
HttpProxy

     *
HttpCachingProxy

     *

     */
    @Property(name="proxy.type")
    private QNetworkProxy.ProxyType m_proxyType;

    /**
     * Sets the proxy host name.
     * Cannot be null, if proxy is enabled.
     */
    @Property(name="proxy.hostname")
    private String m_proxyHostName;

    /**
     * Sets the proxy port.
     * Cannot be 0, if proxy is enabled.
     */
    @Property(name="proxy.port")
    private int m_proxyPort;

    /**
     * Bundle Context.
     */
    private BundleContext m_context;

    /**
     * Creates a WebViewFactory.
     * @param ctxt the bundle context.
     */
    public WebViewFactory(BundleContext ctxt) {
        m_context = ctxt;
    }

    /**
     * Configuration Property: set the initial URL.
     * This property is mandatory.
     * If the browser is already created, this web view loads
     * this new url.
     * @param url the url
     */
    @Property(name="url")
    public synchronized void setURL(final String url) {
        this.m_url = url;
        if (m_browser != null) {
            QApplication.invokeLater(new Runnable() {
                public void run() {
                    m_browser.open(url);
                }
            });
        }
    }

    /**
     * Deletes the create browser.
     */
    @Invalidate
    public synchronized void stop() {
        if (m_browser != null) {
            m_browser.disposeLater();
        }
    }

    /**
     * Starts the browser and loads the
     * set url in the web view.
     */
    @Validate
    public  void start() {
        QApplication.invokeLater(new Runnable() {
            public void run() {
                configureApplication();
                m_logger.info("Creating a web ui...");
                synchronized (WebViewFactory.this) {
                    m_browser = new WebWindow(m_url, WebViewFactory.this);

                    configureWindow(m_browser);
                    m_browser.show();
                }
                m_logger.info("Web UI created.");
            }
        });
    }

    /**
     * Configures the application:
     *

     *
sets the application name (default: 'akquinet ChameRIA')

     *
sets the application version (default: current web view factory version)

     *
sets the application icon (default : no icon)

     *

     */
    private void configureApplication() {
        if (m_appName != null) {
            QApplication.setApplicationName(m_appName);
        } else {
            QApplication.setApplicationName("akquinet ChameRIA");
        }
        if (m_appVersion != null) {
            QApplication.setApplicationVersion(m_appVersion);
        } else {
            QApplication.setApplicationVersion(m_context.getBundle().getVersion().toString());
        }

        if (m_icon != null) {
            QFile file = new QFile(m_icon);

            QIcon icon = new QIcon(file.fileName());
            QApplication.setWindowIcon(icon);
        }

        QApplication.setOrganizationName("akquinet A.G.");

        // Configure the proxy
        if (m_proxyType != null) {
            m_logger.warn("Set application proxy : " + m_proxyType);
            if (m_proxyHostName == null || m_proxyPort == 0) {
                m_logger.error("Cannot configure proxy : hostname or port not set : " + m_proxyHostName + ":" + m_proxyPort);
            } else {
                QNetworkProxy proxy = new QNetworkProxy(m_proxyType, m_proxyHostName, m_proxyPort);
                QNetworkProxy.setApplicationProxy(proxy);
                m_logger.warn("Application proxy set " + m_proxyType + " on " + m_proxyHostName + ":" + m_proxyPort);
            }
        }

    }

    /**
     * Configures the browser window.
     * @param web the browser window
     */
    private void configureWindow(WebWindow web) {

        if (m_fullscreen) {
            // We need to store the previous width and height values
            m_width = web.width();
            m_height = web.height();
            web.showFullScreen();
        } else {
            web.showNormal();
            if (! m_resizable) {
               web.setFixedSize(new QSize(m_width, m_height));
            } else {
               web.setBaseSize(new QSize(m_width, m_height));
            }
            web.resize(m_width, m_height);
        }

        if (! m_bar) {
            web.menuBar().setVisible(false);
        } else {
            web.menuBar().setVisible(true);
            if (m_icon != null) {
                QIcon icon = new QIcon(m_icon);
                web.setWindowIcon(icon);
            }
            web.setWindowTitle(m_appName);
        }

        if (! m_contextMenu) {
            web.setContextMenuPolicy(ContextMenuPolicy.PreventContextMenu);
        } else {
            web.setContextMenuPolicy(ContextMenuPolicy.DefaultContextMenu);
        }
    }

    /**
     * Print callback.
     * Checks if the print feature is enabled. If so, launch the system
     * print job.
     * @param view the view to print
     */
    public void print(QWebView view) {
        if (m_print) {
            QPrinter printer = new QPrinter();
            QPrintDialog printDialog = new QPrintDialog(printer, view);
            if (printDialog.exec() == QDialog.DialogCode.Accepted.value()) {
                // print ...
                view.print(printer);
            }
        } else {
            m_logger.warn("Print disabled");
        }
    }

    /**
     * Save callback (for unsupported content).
     * Checks if the 'save' feature is enabled. If so, launch the system
     * save dialog.
     * @param reply the url to save
     */
    public void save(QNetworkReply reply) {
        if (m_download) {
            String fn = QFileDialog.getSaveFileName();
            if (fn != null && fn.length()>0) {
                m_logger.info("File name : " + fn);
                try {
                    URL u = new URL(reply.url().toString());
                    FileOutputStream out = new FileOutputStream(new File(fn));
                    InputStream in = u.openStream();
                    write(in, out);
                } catch (IOException e) {
                    m_logger.error("Cannot download file " + e.getMessage(), e);
                }
            } else {
                m_logger.warn("No File Name - Download request cancelled");
            }
        } else {
            m_logger.warn("Download disabled");
        }

    }

    /**
     * Utility method to copy a stream to another stream.
     * @param in the stream to copy
     * @param out the destination
     * @throws IOException the stream cannot be copied
     */
    public static void write(InputStream in, OutputStream out)
            throws IOException {
        byte[] b = new byte[4096];
        for (int n; (n = in.read(b)) != -1;) {
            out.write(b, 0, n);
        }
        in.close();
        out.close();
    }

    /**
     * Open callback (window.open).
     * Checks if the 'open new window' feature is enabled. If so, creates the
     * web view
     */
    public QWebView openWindow() {
        if (m_window_open) {
            // We share the same factory
            QWebView newwindow = new ChameriaWebView(this);
            return newwindow;
        } else {
            m_logger.warn("Open new window disabled");
            return null;
        }
    }

    /**
     * Is the context menu supported ?
     * @return true if the context menu is supported,
     * otherwise false
     */
    public boolean isContextMenuSupported() {
        return m_contextMenu;
    }

    /**
     * Gets the horizontal scroll bar policy.
     * @return the horizontal bar policy
     */
    public ScrollBarPolicy getHorizontalScrollBarQTPolicy() {
        return ScrollBarPolicy.valueOf(m_scrollbar_horizontal);
    }

    /**
     * Gets the vertical scroll bar policy.
     * @return the vertical bar policy
     */
    public ScrollBarPolicy getVerticalScrollBarQTPolicy() {
        return ScrollBarPolicy.valueOf(m_scrollbar_vertical);
    }

    /**
     * Checks if the inspector is enabled.
     * @return true if the inspector
     * is enabled.
     */
    public boolean isInspectorEnabled() {
        return m_inspector;
    }

    /**
     * Checks if the local storage is enabled.
     * @return true if the local storage
     * is enabled.
     */
    public boolean isLocalStorageEnabled() {
        return m_localStorage;
    }

    /**
     * Gets the local storage location.
     * @return the local storage location
     */
    public String getLocalStorageLocation() {
        return m_localStorageLocation;
    }

    public int getHeight() {
        return m_height;
    }

    public String getURL() {
        return m_url;
    }

    public int getWidth() {
        return m_width;
    }

    public int getDefaultFixedFontSize() {
        return m_fixed_fontsize;
    }

    public int getDefaultFontSize() {
        return m_fontsize;
    }

    public boolean isContextMenuEnabled() {
        return m_contextMenu;
    }

    public boolean isDownloadSupported() {
        return m_download;
    }

    public boolean isFullScreen() {
        return m_fullscreen;
    }

    public boolean isMenuBarEnabled() {
        return m_bar;
    }

    public boolean isOpenWindowSupported() {
        return m_window_open;
    }

    public boolean isPrintSupported() {
        return m_print;
    }

    public boolean isResizable() {
        return m_resizable;
    }

    public void setContextMenu(boolean enabled) {
        m_contextMenu = enabled;
        QApplication.invokeLater(new Runnable() {
            public void run() {
                configureWindow(m_browser);
            }
        });
    }

    public void setDownloadSupport(boolean enabled) {
        m_download = enabled;
    }

    public void setFullScreen(boolean fullscreen) {
        m_fullscreen = fullscreen;
        QApplication.invokeLater(new Runnable() {
            public void run() {
                configureWindow(m_browser);
            }
        });
    }

    public void setHorizontalScrollBarPolicy(String policy) {
        m_scrollbar_horizontal = policy;
    }

    public void setMenuBar(boolean enabled) {
        m_bar = enabled;
        QApplication.invokeLater(new Runnable() {
            public void run() {
                configureWindow(m_browser);
            }
        });

    }

    public void setOpenWindowSupport(boolean enabled) {
        m_window_open = enabled;
    }

    public void setPrintSupport(boolean enabled) {
        m_print = enabled;
    }

    public void setResizable(boolean resizable) {
        m_resizable = resizable;
        QApplication.invokeLater(new Runnable() {
            public void run() {
                configureWindow(m_browser);
            }
        });
    }

    public void setSize(int width, int height) {
        m_width = width;
        m_height = height;
        QApplication.invokeLater(new Runnable() {
            public void run() {
                configureWindow(m_browser);
            }
        });
    }

    public void setVerticalScrollBarPolicy(String policy) {
        m_scrollbar_vertical = policy;
    }

    public String getHorizontalScrollBarPolicy() {
        return m_scrollbar_horizontal;
    }

    public String getVerticalScrollBarPolicy() {
        return m_scrollbar_vertical;
    }

}


package js.container;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import js.core.AppFactory;

/**
 * Adapter for dependency with shorter scope hosted into managed instance with larger scope. If dependency scope, i.e. life span
 * is shorter than hosting instance is possible for dependency instance to become invalid, that is out of scope or scope
 * changed, while hosting instance is still valid. For example, a dependency with {@link InstanceScope#SESSION} scope hosted in
 * a controller with {@link InstanceScope#APPLICATION} scope: controller should use the managed instance attached to current
 * HTTP request only.
 *

 * This proxy uses parent application factory to retrieve managed instance before every method invocation.
 * {@link AppFactory#getInstance(Class, Object...)} takes care about managed instance scope.
 *

 * This class is designed specifically for {@link InstanceFieldsInjectionProcessor}.
 *
 * @author Iulian Rotaru
 * @param <T> managed class type.
 * @version final
 */
final class ScopeProxyHandler<T> implements InvocationHandler {
	/** Parent application factory. */
	private final AppFactory appFactory;
	/** Managed class interface. */
	private final Class<T> interfaceClass;

	/**
	 * Construct scope proxy handler with parent application factory and managed class interface.
	 *
	 * @param appFactory parent application factory,
	 * @param interfaceClasss managed class interface.
	 */
	public ScopeProxyHandler(AppFactory appFactory, Class<T> interfaceClasss) {
		this.appFactory = appFactory;
		this.interfaceClass = interfaceClasss;
	}

	/**
	 * Retrieve managed instance from application factory and invoke given method on that instance.
	 *
	 * @param proxy Java Proxy instance using this handler, unused by current implementation,
	 * @param method reflexive method invoked by proxy instance caller,
	 * @param args method invocation arguments.
	 * @return value returned by method execution, possible null.
	 * @throws Throwable any exception generated by method execution is bubbled up.
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		T instance = appFactory.getInstance(interfaceClass);
		return method.invoke(instance, args);
	}
}


package js.servlet;

import java.util.Enumeration;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import js.container.ContainerSPI;
import js.container.InstanceScope;
import js.lang.BugError;
import js.log.Log;
import js.log.LogFactory;

/**
 * Request context stored on current HTTP servlet request thread. This class allows access to container public services and to
 * relatively low level servlet abstractions like {@link HttpServletRequest}. Since exposes tiny container and servlet APIs this
 * class is primarily for framework internal needs. Anyway, there is no formal restriction on using it from applications.
 *

 * Request context instance has {@link InstanceScope#THREAD} scope but is valid to be used only within HTTP request boundaries.
 * Request context instance is {@link #attach(HttpServletRequest, HttpServletResponse)}ed on new HTTP request thread and
 * {@link #detach()}ed on HTTP request end.
 *

 * RequestContext is a managed class and can be retrieved from application context or can be injected on fields or constructors.
 * In sample below request context instance is retrieved from application context. Request context is attached to HTTP request
 * thread that runs addSuggestion method .
 *
 *

 * class Controller {
 * 	@Inject
 * 	private AppContext appContext;
 *
 * 	void addSuggestion(Suggestion suggestion) {
 * 		RequestContext requestContext = appContext.getInstance(RequestContext.class);
 * 		log.debug("Request from |%s|.", requestContext.getRemoteAddr());
 * 	}
 * 	...
 * }
 *

 *

 * Request context can also be injected into field. Now, controller is a managed class with {@link InstanceScope#APPLICATION}
 * scope whereas request context scope is {@link InstanceScope#THREAD}. Since request context life span is smaller than
 * controller, request context may become invalid while controller still handles requests. Container dependency injection takes
 * care and replace injected request context instance with a Java proxy.
 *
 *

 * class Controller {
 * 	@Inject
 * 	private RequestContext context;
 * 	...
 * }
 *

 *

 * The same goes for constructor injection: request context instance is wrapped by a Java proxy that routes method invocations
 * to the instance attached to current HTTP request.
 *
 *

 * class Controller {
 * 	private final RequestContext context;
 *
 * 	Controller(RequestContext context) {
 * 		this.context = context;
 * 	}
 * 	...
 * }
 *

 *

 * RequestContext is usable only inside HTTP request. Trying to used this class from user defined threads, perhaps with
 * life span exceeding request thread, is both meaningless and dangerous; for this reason is forbidden. In fact bug error is
 * thrown if attempt to use any method of this class from outside request thread. One can use {@link #isAttached()} to test if
 * request context is safe to use.
 *
 * @author Iulian Rotaru
 * @version final
 */
public class RequestContext {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(RequestContext.class);

	/** Parent container. */
	private final ContainerSPI container;

	/**
	 * Original, not pre-processed by {@link RequestPreprocessor} request URI including query string, if any. This value is
	 * merely used for request logging.
	 */
	private String requestURL;

	/** Request locale. Locale value can be loaded by {@link RequestPreprocessor} or from HTTP request. */
	private Locale locale;

	/**
	 * Optional security domain set if application is configured with servlet container provided security. See
	 * {@link RequestPreprocessor} for security domain description.
	 */
	private String securityDomain;

	/**
	 * Request path is the part of request URI that identifies managed method to be invoked. It is the last part of the request
	 * URI - after context path, optional locale and security domain, and its structure depends on HTTP request protocol.
	 * Anyway, it always starts with path separator.
	 *

	 * If application is configured with request filter, this request path is computed by {@link RequestPreprocessor}. If not,
	 * is loaded from request URI when attach this request context.
	 */
	private String requestPath;

	/** Wrapped HTTP servlet request. */
	private HttpServletRequest httpRequest;

	/** HTTP servlet response related to wrapped HTTP servlet request. */
	private HttpServletResponse httpResponse;

	/** Current request cookies. */
	private Cookies cookies;

	/**
	 * Flag true while request context instance is attached to HTTP servlet request. It is set to true by
	 * {@link #attach(HttpServletRequest, HttpServletResponse)} and reset to false by {@link #detach()}.
	 */
	private volatile boolean attached;

	/**
	 * Create request context instance for given parent container.
	 *
	 * @param container parent container.
	 */
	public RequestContext(ContainerSPI container) {
		this.container = container;
	}

	/**
	 * Get parent container.
	 *
	 * @return parent container.
	 * @see #container
	 */
	public ContainerSPI getContainer() {
		return container;
	}

	/**
	 * Set request URL. This setter is invoked by {@link RequestPreprocessor} with original request URI and query string.
	 *
	 * @param requestURL original request URI with optional query string.
	 * @see #requestURL
	 */
	public void setRequestURL(String requestURL) {
		this.requestURL = requestURL;
	}

	/**
	 * Set request locale. This setter is invoked if application is configured with multiple locale support. See
	 * {@link RequestPreprocessor} for multiple locale configuration.
	 *
	 * @param locale request locale to set.
	 * @see #locale
	 */
	public void setLocale(Locale locale) {
		this.locale = locale;
	}

	/**
	 * Set security domain. This setter is invoked if application has security provided by servlet container. See
	 * {@link RequestPreprocessor} for security domain description.
	 *
	 * @param securityDomain request security domain.
	 * @see #securityDomain
	 */
	public void setSecurityDomain(String securityDomain) {
		this.securityDomain = securityDomain;
	}

	/**
	 * Set request path used to identify managed method to be invoked. This setter is invoked by {@link RequestPreprocessor}
	 * after removing context path and optional locale and security domain from request URI.
	 *
	 * @param requestPath request path.
	 * @see #requestPath
	 */
	public void setRequestPath(String requestPath) {
		this.requestPath = requestPath;
	}

	/**
	 * Attach this instance to HTTP servlet request. Load this instance state from HTTP servlet request and mark it as attached.
	 *
	 * @param httpRequest current HTTP request object,
	 * @param httpResponse HTTP response object associated to current request.
	 */
	public void attach(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
		// takes care to not override request URL, locale and request path values if set by request pre-processor
		if (requestURL == null) {
			requestURL = httpRequest.getRequestURI();
		}
		if (locale == null) {
			locale = httpRequest.getLocale();
		}
		if (requestPath == null) {
			// request URI and context path cannot ever be null
			requestPath = httpRequest.getRequestURI().substring(httpRequest.getContextPath().length());
		}

		this.httpRequest = httpRequest;
		this.httpResponse = httpResponse;
		this.attached = true;
	}

	/**
	 * Detach request context instance from HTTP servlet request. Invoking getters on this instance after detaching is
	 * considered a bug.
	 */
	public void detach() {
		attached = false;
		locale = null;
		securityDomain = null;
		cookies = null;
		requestPath = null;
		requestURL = null;
	}

	/**
	 * Get instance attached state.
	 *
	 * @return instance attached state.
	 * @see #attached
	 */
	public boolean isAttached() {
		return attached;
	}

	/**
	 * Get request locale. Request locale is loaded by {@link RequestPreprocessor} filter or from HTTP servlet request if
	 * application is not configured with multiple locale.
	 *
	 * @return request locale.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 * @see #locale
	 */
	public Locale getLocale() {
		assertAttached();
		return locale;
	}

	/**
	 * Get security domain from request URI or null if servlet container security is not enabled. See
	 * {@link RequestPreprocessor} for security domain description.
	 *
	 * @return request URI security domain, possible null.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 * @see #securityDomain
	 */
	public String getSecurityDomain() {
		assertAttached();
		return securityDomain;
	}

	/**
	 * Get request path used to identify managed method to be invoked.
	 *
	 * @return request path.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 * @see #requestPath
	 */
	public String getRequestPath() {
		assertAttached();
		return requestPath;
	}

	/**
	 * Get URI of the request that trigger this request context creation. If application has request filter active request URI
	 * is pre-processed by {@link RequestPreprocessor}.
	 *
	 * @return request URI.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 */
	public String getRequestURI() {
		assertAttached();
		return httpRequest.getRequestURI();
	}

	/**
	 * Get request URI including query string, if any. If application has request filter active request URI is pre-processed by
	 * {@link RequestPreprocessor}. This method returns null if request URL is not initialized by {@link RequestPreprocessor}.
	 *
	 * @return request URI with optional query string or null if request URL is not initialized.
	 * @see #requestURL
	 */
	public String getRequestURL() {
		return requestURL;
	}

	/**
	 * Get request cookies.
	 *
	 * @return request cookies.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 */
	public Cookies getCookies() {
		assertAttached();
		if (cookies == null) {
			cookies = new Cookies(httpRequest, httpResponse);
		}
		return cookies;
	}

	/**
	 * Returns the host name of the Internet Protocol (IP) interface on which the request was received.
	 *
	 * @return IP address on which the request was received.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 */
	public String getLocalName() {
		assertAttached();
		return httpRequest.getLocalName();
	}

	/**
	 * Returns the IP port number of the interface on which HTTP request was received.
	 *
	 * @return an integer specifying the port number.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 */
	public int getLocalPort() {
		assertAttached();
		return httpRequest.getLocalPort();
	}

	/**
	 * Returns the fully qualified name of the client or the last proxy that sent the request. If the engine cannot or chooses
	 * not to resolve the hostname (to improve performance), this method returns the dotted-string form of the IP address.
	 *
	 * @return IP address of the client that sent HTTP request.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 */
	public String getRemoteHost() {
		assertAttached();
		return httpRequest.getRemoteHost();
	}

	/**
	 * Returns the IP source port of the client or last proxy that sent HTTP request.
	 *
	 * @return an integer specifying the port number.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 */
	public int getRemotePort() {
		assertAttached();
		return httpRequest.getRemotePort();
	}

	/**
	 * Allows access to servlet request interface.
	 *
	 * @return low level servlet request.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 */
	public HttpServletRequest getRequest() {
		assertAttached();
		return httpRequest;
	}

	/**
	 * Allows access to servlet response interface.
	 *
	 * @return low level servlet response.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 */
	public HttpServletResponse getResponse() {
		assertAttached();
		return httpResponse;
	}

	/**
	 * Get HTTP session this request context is part of or null if session is not or could not be created. If
	 * create flag is not provided this method return current HTTP session as it is, that is, return it if already
	 * created or null if not.
	 *

	 * If create flag is present and is true this method returns current HTTP session, if there is one associated
	 * with current request, or create a new one. This method never returns null if requested to create session but can throw
	 * illegal state exception if attempt to create session after response commit.
	 *
	 * @param create optional create flag.
	 * @return current HTTP session possible null.
	 * @throws IllegalStateException if attempt to create new HTTP session after response was committed.
	 * @throws BugError if attempt to use this getter on instance not attached to a HTTP servlet request.
	 */
	public HttpSession getSession(boolean... create) {
		assertAttached();
		if (create.length == 0) {
			return httpRequest.getSession();
		}
		// if create flag is true next call can throw IllegalStateException if HTTP response is committed
		return httpRequest.getSession(create[0]);
	}

	/** Dump this request context state to error logger. If this instance is not attached this method is NOP. */
	public void dump() {
		if (!attached) {
			return;
		}
		StringBuilder message = new StringBuilder();
		message.append("Request context |");
		message.append(httpRequest.getRequestURI());
		message.append("|:");

		message.append(System.lineSeparator());
		message.append("\t- remote-address: ");
		message.append(httpRequest.getRemoteHost());

		message.append(System.lineSeparator());
		message.append("\t- method: ");
		message.append(httpRequest.getMethod());

		message.append(System.lineSeparator());
		message.append("\t- query-string: ");
		if (httpRequest.getQueryString() != null) {
			message.append(httpRequest.getQueryString());
		}

		Enumeration<String> headerNames = httpRequest.getHeaderNames();
		while (headerNames.hasMoreElements()) {
			message.append(System.lineSeparator());
			String headerName = headerNames.nextElement();
			message.append("\t- ");
			message.append(headerName);
			message.append(": ");
			message.append(httpRequest.getHeader(headerName));
		}
		log.error(message.toString());
	}

	/**
	 * Assert this instance is attached to a HTTP servlet request.
	 *
	 * @throws BugError if this instance is not attached.
	 * @see #attached
	 */
	private void assertAttached() {
		if (!attached) {
			throw new BugError("Attempt to use request context outside HTTP request thread.");
		}
	}
}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.cologneintelligence.fitgoodies.typehandler;

import de.cologneintelligence.fitgoodies.ActionFixture;
import de.cologneintelligence.fitgoodies.util.DependencyManager;

/**
 * Fixture that contains one single command: load.
 * <p>
 * The load command loads an ActionFixture by its name.
 */
public class SetupFixture extends ActionFixture {
	/**
	 * Calls {@link #load(String)} using the 2. column as argument.
	 *
	 * @throws Exception propagated to fit
	 */
	public void load() throws Exception {
		transformAndEnter();
	}

	/**
	 * Registers the {@link TypeHandler} which is provided by
	 * {@code className}. After processing this row, the TypeAdapter
	 * will be automatically used when the destination type matches
	 * {@link TypeHandler#getType()}.
	 *
	 * @param className fully qualified name of a class
	 * @throws Exception propagated to fit, in case of unknown classes,
	 *                   permission errors, etc.
	 */
	@SuppressWarnings("unchecked")
	public void load(final String className) throws Exception {
		Class<? extends TypeHandler<?>> clazz = (Class<? extends TypeHandler<?>>) Class.forName(className);
		TypeHandlerFactory helper = DependencyManager.getOrCreate(TypeHandlerFactory.class);
		helper.register(clazz);
	}
}


package js.container;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import js.core.AppContext;
import js.core.Factory;
import js.lang.Config;
import js.lang.Configurable;
import js.lang.ManagedLifeCycle;
import js.log.Log;
import js.log.LogFactory;

/**
 * Performance meters observer. Collect periodically application invocation meters and dump values to meter logger. In order to
 * enable observer one should declare it into application descriptor and configure observer logger appender.
 *

 * Enable observer and set period to one minute. Note that period is in milliseconds and that there is no guarantee on strict
 * period precision.
 *
 *

 * <app-descriptor>
 * 	<observer period="600000" />
 * 	...
 * </app-descriptor>
 *

 *

 * Here is a sample logging configuration with appender for meters dump.
 *
 *

 * <appender name="OBS">
 * 	<class>org.apache.log4j.RollingFileAppender</class>
 * 	<format>%d{dd HH:mm:ss,SSS} %m%n</format>
 * 	<parameters>
 * 		<file>${logs}/meters.log</file>
 * 		...
 * 	</parameters>
 * </appender>
 * ...
 * <logger name="js.core.Meter">
 * 	<appender>OBS</appender>
 * 	<level>INFO</level>
 * </logger>
 *

 *
 * @author Iulian Rotaru
 * @version draft
 */
final class Observer implements Configurable, ManagedLifeCycle, Runnable {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(Observer.class);

	/** Meters logger. Into log configuration this logger can be retrieved via {@link Meter} class. */
	private static final Log meterLog = LogFactory.getLog(Meter.class);

	/** Timeout for observer thread stop. */
	private static final int THREAD_STOP_TIMEOUT = 4000;

	/** Application name displayed into meters dump header. */
	private final String appName;

	/** Observer worker thread. */
	private Thread thread;

	/** Flag indicating that worker thread is running. */
	private volatile boolean running;

	/** Meters sampling period, in milliseconds. */
	private int period;

	/**
	 * Construct instrumentation manager for given parent application.
	 *
	 * @param app parent application.
	 */
	private Observer(AppContext app) {
		log.trace("Observer(App)");
		this.appName = app.getAppName();
	}

	@Override
	public void config(Config config) {
		log.trace("config(Config.Element)");
		period = config.getAttribute("period", int.class, 0);
	}

	@Override
	public void postConstruct() throws Exception {
		log.trace("postConstruct()");
		// start observer thread only if application is configured with observer period
		if (period > 0) {
			running = true;
			thread = new Thread(this, getClass().getSimpleName());
			thread.start();
		}
	}

	@Override
	public void preDestroy() throws Exception {
		log.trace("preDestroy()");
		if (running) {
			synchronized (this) {
				running = false;
				thread.interrupt();
				this.wait(THREAD_STOP_TIMEOUT);
			}
		}
	}

	@Override
	public void run() {
		log.debug("Start meters observer on thread |%s|.", thread);

		for (;;) {
			try {
				Thread.sleep(period);
			} catch (InterruptedException unused) {
				if (!running) {
					break;
				}
			}

			List<InvocationMeter> invocationMeters = getMeters();
			Collections.sort(invocationMeters, new Comparator<InvocationMeter>() {
				@Override
				public int compare(InvocationMeter m1, InvocationMeter m2) {
					return ((Long) m1.getMaxProcessingTime()).compareTo(m2.getMaxProcessingTime());
				}
			});

			meterLog.info("Start observer meters dump for %s:", appName);
			for (InvocationMeter meter : invocationMeters) {
				if (meter.getInvocationsCount() != 0) {
					meterLog.info(meter.toExternalForm());
				}
			}
		}

		synchronized (this) {
			this.notify();
		}
		log.debug("Stop meters observer on thread |%s|.", thread);
	}

	/**
	 * Collect invocation meters from application managed classes.
	 *
	 * @return application invocation meters.
	 */
	private static List<InvocationMeter> getMeters() {
		List<InvocationMeter> invocationMeters = new ArrayList<InvocationMeter>();
		ContainerSPI container = (ContainerSPI) Factory.getAppFactory();
		for (ManagedMethodSPI managedMethod : container.getManagedMethods()) {
			invocationMeters.add(((ManagedMethod) managedMethod).getMeter());
		}
		return invocationMeters;
	}
}


package js.net;

import java.io.Closeable;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;

import js.converter.ConverterRegistry;
import js.json.Json;
import js.lang.BugError;
import js.lang.Event;
import js.log.Log;
import js.log.LogFactory;
import js.util.Base64;
import js.util.Classes;
import js.util.Strings;

/**
 * Push W3C Server-Sent Events to a writer that is connecting an event stream client. {@link EventStreamServlet} creates an
 * instance of this class for every event stream request and block on it while {@link #loop()} returns true.
 *

 * As mentioned, event stream instance is created when a HTTP request arrive for an event stream. In order to create an
 * application defined event stream one needs to extends this class. Please remember to declare subclass into application
 * descriptor, managed-classes section like in snippet below. Event stream declaration is in fact a managed class
 * overload since it is already declared into library descriptor with LOCAL scope; it should be local because a new instance is
 * created for every request.
 *
 *

 *  <managed-classes>
 *      <event-stream class="AppEventStream" interface="js.net.EventStream" />
 *      ...
 *

 *

 * Once custom event stream created there are two usage patterns. First is to create new thread that observe system state,
 * create events and push them using {@link #push(Event)} method.
 *
 *

 * public class AppEventStream extends EventStream implements Runnable {
 * 	void run() {
 * 		// wait for system state updates, create event then push it
 * 		pushEvent(event);
 * 	}
 * }
 *

 *
 * The second option is to override {@link #loop()} method that is executed into HTTP request thread.
 *
 *

 * public class AppEventStream extends EventStream {
 * 	protected boolean loop() {
 * 		// wait for system state updates and create event then invoke inherited event sender
 * 		sendEvent(event);
 * 		// returns true to allow for event stream loop continuation
 * 		return true;
 * 	}
 * }
 *

 *
 * First usage pattern is using internal events queue and rely on implemented loop method to send keep alive events but is using
 * a separated thread. The second uses HTTP request thread but user code should take care of keep alive sending.
 *
 *
Keep Alive
 This events stream is basically a long connection using chunked transfer. Client sends HTTP request then
 * block on HTTP response waiting for events. If server side or network goes down client logic has no means to realize this; it
 * simply continue waiting. For this reason event stream takes care to periodically send KeepAliveEvent.
 *

 * The second reason for keep alive events is routers idle connection timeout. Although TCP/IP does not require keep alive or
 * dead connection detect packets there is usual practice for routers to disconnect idle connections in order to free resources.
 *

 * This event stream takes care to send a keep alive event immediately a client is connected, see {@link #onOpen()}. This way
 * client side has the chance to quickly know that event stream is properly initialized.
 *
 * @author Iulian Rotaru
 * @version final
 */
public class EventStream implements Closeable {
	/** Class logger. */
	private static final Log log = LogFactory.getLog(EventStream.class);

	/** Events queue timeout used to guard queue offer operation, in milliseconds. */
	private static final int EVENTS_QUEUE_PUSH_TIMEOUT = 4000;

	/**
	 * Default keep alive timeout loaded from application descriptor. See {@link #keepAlivePeriod} for a discussion about this
	 * timeout value and configuration.
	 */
	private static int KEEP_ALIVE_TIMEOUT = 40000;

	/**
	 * Every event stream has its own unique ID used merely for debugging. Stream ID is unique only on current application run;
	 * it is not preserved after application restart.
	 */
	private static int STREAM_ID;

	/** JSON serializer. */
	private final JsonSerializer json;

	/** Flag indicating this event stream instance is active. */
	private final AtomicBoolean active = new AtomicBoolean(true);

	/**
	 * Blocking events queue. This queue is used from two threads: this events stream thread is waiting for the queue to acquire
	 * an event, see {@link #loop()} and second, the event service thread uses {@link #push(Event)} to actually enqueue the
	 * event.
	 */
	private BlockingQueue<Event> eventsQueue;

	/**
	 * Underlying characters stream used to actually carry serialized event to the client. Please remember that Java print
	 * writer does not throw IO exceptions and provide {@link PrintWriter#checkError()} for that. Event stream uses error
	 * checking mechanism to break {@link #loop()} on errors. This helps closing event stream and parent servlet instance when
	 * remote client closes its connection.
	 */
	private PrintWriter writer;

	/** Optional symmetric key used to encrypt the event message using AES chiper. Default to null. */
	private SecretKey secretKey;

	/**
	 * Send keep alive if there are no events sent for this amount of time, in milliseconds. Keep alive events have double role:
	 *

	 *
Allows for both client and this server side event stream to know that peer is up,
	 *
Avoid routers with idle timeout to drop connections when no events are sent.
	 *

	 *

	 * For second case this timeout value is critical. If is larger than router idle connection timeout, connection will be lost
	 * and client will reconnect periodically; in this case events stream behaves like a polling mechanism with resources waste
	 * and bad responsiveness.
	 *

	 * Routers idle connection timeout is manufacturer specific and / or network administrator customizable and can vary
	 * greatly, including complete disabling. In fact it is a not standard behavior. TCP/IP does not require such timeout; it is
	 * merely a technological limitation used to save routers internal resources. Anyway, from practice is observed that the
	 * lower idle connection timeout value found is one minute. This event stream class uses 40 seconds in order to cope with
	 * delays generated by server heavy loading and network latency. It is a heuristic / brute force approach. A better solution
	 * may be an adaptive timeout that client may learn and communicate to server when opens events stream.
	 *

	 * Now, some considerations about keep alive packages bandwidth consumption. Believe me or not, keep alive event data length
	 * is exactly 40 bytes. With current 40 seconds period results in exactly one byte per second, a perfectly acceptable
	 * overload. And even if include TCP/IP headers and acknowledge packet bandwidth consumption will not exceed 3 B/s.
	 *

	 * Finally, events stream implementation is a managed class and this keep alive timeout value is customizable per server,
	 * like in sample code below.
	 *
	 *

	 *    <events-stream>
	 *        <static-field name="KEEP_ALIVE_TIMEOUT" value="300" />
	 *    </events-stream>
	 *

	 */
	private int keepAlivePeriod = KEEP_ALIVE_TIMEOUT;

	/** Parameters map for request used to enact this event stream service. */
	private Map<String, String> parameters;

	/** This events stream string representation used mostly for debugging. */
	private String string;

	// --------------------------------------------------------------------------------------------
	// CONSTRUCTION AND CONFIGURATION

	/** Default constructor. This class should not be instantiated using new operator. */
	protected EventStream() {
		this.json = new JsonSerializer(Classes.loadService(Json.class));
		this.eventsQueue = new LinkedBlockingQueue<>();
	}

	/**
	 * Configure events stream instance from configuration object.
	 *
	 * @param config event stream configuration object.
	 */
	protected void config(EventStreamConfig config) {
		if (config.hasSecretKey()) {
			secretKey = config.getSecretKey();
		}
		if (config.hasKeepAlivePeriod()) {
			keepAlivePeriod = config.getKeepAlivePeriod();
		}
		parameters = config.getParameters();
	}

	// --------------------------------------------------------------------------------------------
	// PUBLIC METHODS

	/**
	 * Push event to this events stream client. This method just stores the event on {@link #eventsQueue events queue} being
	 * executed into invoker thread. This events stream thread is blocked on the events queue; after this method execution it
	 * will unblock and process the event, see {@link #loop()} method.
	 *

	 * Queue offer operation is guarded by {@link #EVENTS_QUEUE_PUSH_TIMEOUT}. This timeout can occur only in a very improbable
	 * condition of events flood combined with system resources starvation. For this reason there is no attempt to recover;
	 * event is simple lost with warning on application logger.
	 *
	 * @param event event instance to push on event stream.
	 * @throws BugError if trying to use this method after stream close.
	 */
	public void push(Event event) {
		if (!active.get()) {
			throw new BugError("Event stream |%s| is closed.", this);
		}
		// BlockingQueue is thread safe so we do not need to synchronize this method
		try {
			if (!eventsQueue.offer(event, EVENTS_QUEUE_PUSH_TIMEOUT, TimeUnit.MILLISECONDS)) {
				log.warn("Timeout trying to push event on events queue. Event |%s| not processed.", event);
			}
		} catch (InterruptedException unused) {
			log.warn("Thread interruption on event stream |%s| while trying to push event to queue. Event |%s| not processed.", this, event);
			Thread.currentThread().interrupt();
		}
	}

	/** Close this event stream loop and waits for associated {@link EventStreamServlet} exit. */
	@Override
	public void close() {
		if (!active.get()) {
			return;
		}
		log.debug("Closing event stream |%s| ...", this);

		push(new ShutdownEvent());
		active.set(false);
		log.debug("Event stream |%s| was closed.", this);
	}

	/**
	 * Get event stream instance string representation.
	 *
	 * @return this event stream instance string representation.
	 */
	@Override
	public String toString() {
		return string;
	}

	// --------------------------------------------------------------------------------------------
	// PROTECTED METHODS

	/**
	 * Set the host address for client connected to this event stream.
	 *
	 * @param remoteHost client remote host address.
	 */
	protected void setRemoteHost(String remoteHost) {
		if (string == null) {
			string = Strings.concat('#', STREAM_ID++, ':', remoteHost);
		}
	}

	/**
	 * Set the output characters stream used to convey the events.
	 *
	 * @param writer output characters stream.
	 */
	protected void setWriter(PrintWriter writer) {
		this.writer = writer;
	}

	/**
	 * Event stream main loop repeated for every event. Waits for an event and push it to the client via {@link #writer}, writer
	 * initialized by {@link EventStreamServlet} on this event stream creation. Note that this logic is executed into HTTP
	 * request thread; it blocks the thread till an event become available. This method always returns true allowing event
	 * stream to continue. It returns false only when got {@link ShutdownEvent}, pushed on queue by {@link #close()} method or
	 * there is an error on print writer. Checking for print writer errors is especially useful to detect that remote client
	 * closes its socket and to gracefully close this stream and release parent servlet instance.
	 *

	 * Also this method takes care to periodically send keep alive events, see {@link #keepAlivePeriod}. Keep alive is used to
	 * ensure server side and client logic that peer is still running and to avoid connection drop due to routers idle
	 * connection timeout.
	 *
	 * @return true if event stream should continue running.
	 */
	protected boolean loop() {
		Event event = null;
		try {
			event = eventsQueue.poll(keepAlivePeriod, TimeUnit.MILLISECONDS);
		} catch (InterruptedException unused) {
			if (!active.get()) {
				log.debug("Events stream |%s| thread is interrupted. Break events stream loop.", this);
				return false;
			}
			// in a perfect world, now would be the right moment to stop the events stream, returning false...
			// but i'm not sure interruption occurs only when current thread is interrupted
			// for now i play safe, allowing events stream to continue and use shutdown event to break it
			log.warn("Events stream |%s| thread is interrupted. Continue events stream loop.", this);
			return true;
		}

		if (event == null) {
			// we are here due to keep-alive period expiration
			// returns true to signal event stream should continue
			sendKeepAlive();
			log.debug("Keep-alive was sent to event stream |%s|.", this);
			return !writer.checkError();
		}

		// close method puts this event into queue; returns false to break this events stream loop
		if (event instanceof ShutdownEvent) {
			log.debug("Got shutdown event. Break event stream loop.");
			return false;
		}

		sendEvent(event);
		onSent(event);
		log.debug("Event |%s| was sent to event stream |%s|.", event, this);
		return !writer.checkError();
	}

	/**
	 * Get named parameter throwing exception if not found.
	 *
	 * @param name requested parameter name,
	 * @param type desired parameter type.
	 * @param <T> parameter type.
	 * @return named parameter instance.
	 * @throws BugError if named parameter does not exist.
	 */
	protected <T> T getParameter(String name, Class<T> type) {
		if (parameters == null) {
			throw new BugError("Event stream |%s| parameters not configured.", this);
		}
		String value = parameters.get(name);
		if (value == null) {
			throw new BugError("Missing event stream parameter |%s| of expected type |%s|.", name, type);
		}
		return ConverterRegistry.getConverter().asObject(value, type);
	}

	/**
	 * Hook method invoked at event stream instance creation. This method is intended to be overridden by subclasses. Anyway, it
	 * takes care to send a keep alive event so that client can quickly know event stream is properly working. If subclass needs
	 * this feature it should explicitly call super from its overriding method.
	 */
	protected void onOpen() {
		sendKeepAlive();
	}

	/**
	 * Hook method invoked on this event stream closing. This method is called by {@link EventStreamServlet} just before HTTP
	 * request processing end. After this method execution event stream instance becomes invalid and attempting to use any of
	 * its methods is considered a bug.
	 *

	 * If subclass override this method it is its responsibility to call super.
	 */
	protected void onClose() {
	}

	/**
	 * Hook method invoked just after event sent. This method is called by {@link #loop()} immediately after event was sent to
	 * output stream and stream flushed.
	 *
	 * @param event event that was sent.
	 */
	protected void onSent(Event event) {
	}

	/**
	 * Send event instance to this event stream consumer. Compile a W3C Server-Sent event from event instance
	 * argument and write it to this {@link #writer}. This event stream implementation does not use all W3C server-sent event
	 * fields: only event and data, as folows:
	 *

	 *
event field stores event argument canonical class name,
	 *
data field value is event argument instance serialized JSON.
	 *

	 * Just for those curious here is an sample of a serialized hypothetical event, as it is on wire:
	 *
	 *

	 * event:bb.hub.event.NotificationCRLF
	 * data:{"id":213,"title":"Baby Server Admin","text":"Baby server was stopped.","timestamp":"2014-04-10T19:07:05Z","priority":"DEFAULT"}CRLF
	 * CRLF
	 *

	 *
	 * @param event event instance.
	 */
	protected void sendEvent(Event event) {
		write("data:");

		if (secretKey == null) {
			json.serialize(writer, event);
		} else {
			try {
				Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
				cipher.init(Cipher.ENCRYPT_MODE, secretKey);

				// this overload of doFinal from Cipher class is able to perform single step encrypting
				// excerpt from API:
				// Encrypts or decrypts data in a single-part operation, or finishes a multiple-part operation.
				byte[] encryptedMessage = cipher.doFinal(json.serialize(event));

				// encode encrypted event message with Base64 since content type should be text
				write(Base64.encode(encryptedMessage));
			} catch (InvalidKeyException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) {
				// we step here only for bugs on encryption provider:
				// secret key size is incorrect or key is not properly initialized
				// single step encryption using directly Cipher#doFinal is supposed to process all input at once
				// missing JVM default padding mechanism
				// bad padding occurring on encryption
				throw new BugError(e);
			} catch (NoSuchAlgorithmException e) {
				throw new BugError("Missing support for |%s| cryptographic algorithm.", secretKey.getAlgorithm());
			}
		}

		// end data field
		crlf();

		// single end of line is the mark for event end
		crlf();
		flush();
	}

	/**
	 * Send keep alive to this event stream counterpart. Create a W3C Server-Sent event with event field containing
	 * canonical class name for KeepAliveEvent. On the wire things looks like bellow, so minimal of overhead. Note
	 * that specification mandates data field, for which reason envoy it with an empty JSON object.
	 *
	 *

	 * event:js.net.KeepAliveEventCRLF
	 * data:{}CRLF
	 * CRLF
	 *

	 */
	protected void sendKeepAlive() {
		write("data:");
		crlf();

		// single end of line is the mark for event end
		crlf();
		flush();
	}

	// --------------------------------------------------------------------------------------------
	// PRIVATE METHODS

	/**
	 * Write given string to this event stream.
	 *
	 * @param string string to send.
	 */
	private void write(String string) {
		writer.write(string);
	}

	/**
	 * Write end of line to this event stream. It is used to mark end of the field; remember that an empty event field, that is,
	 * only end of line is used to mark the event end. Note that W3C accepts single CR or LF as end of line; this implementation
	 * always uses CRLF.
	 */
	private void crlf() {
		write("\r\n");
	}

	/** Flush this event stream. This method is called after every event sent, including keep alive. */
	private void flush() {
		writer.flush();
	}

	// --------------------------------------------------------------------------------------------
	// UTILITY CLASSES

	/**
	 * JSON serializer for event objects. Thin wrapper for {@link Json} service used with print writer that does not generate IO
	 * exceptions.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private static final class JsonSerializer {
		/** JSON service implementation. */
		private final Json json;

		/**
		 * Create JSON serializer instance.
		 *
		 * @param json JSON service implementation.
		 */
		public JsonSerializer(Json json) {
			this.json = json;
		}

		/**
		 * Serialize event as JSON stream to given writer. Since writer is a {@link PrintWriter} it does not throw
		 * {@link IOException}. As a consequence this method does not throw exception too.
		 *
		 * @param writer print writer,
		 * @param event event object.
		 */
		public void serialize(PrintWriter writer, Event event) {
			try {
				json.stringifyObject(writer, event);
			} catch (IOException e) {
				// print writer never throws IO exceptions
				throw new BugError(e);
			}
		}

		/**
		 * Serialize event object to JSON string representation and return UTF-8 bytes.
		 *
		 * @param event event object.
		 * @return bytes of event object converted to JSON string.
		 */
		public byte[] serialize(Event event) {
			StringWriter jsonEvent = new StringWriter();
			// accordingly API there is no need to close string writer
			try {
				json.stringifyObject(jsonEvent, event);
				return jsonEvent.toString().getBytes("UTF-8");
			} catch (IOException e) {
				// hard to believe erroneous conditions:
				// - IO exceptions on string writer
				// - JVM with not support for UTF-8
				throw new BugError(e);
			}
		}
	}

	/**
	 * Internal event used to shutdown events stream. It is pushed on events queue by {@link EventStream#close()}; as a result
	 * events stream {@link EventStream#loop() loop} is stopped and {@link EventStreamServlet} is released.
	 *
	 * @author Iulian Rotaru
	 * @version final
	 */
	private static final class ShutdownEvent implements Event {
	}
}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
*/

package de.cologneintelligence.fitgoodies.file;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * Helper class which is used to process paths and directories.
 */
public class FileSystemDirectoryHelper {
	/**
	 * Removes a prefix from a path. If the path does not start with the prefix,
	 * the unchanged path is returned.
	 *
	 * @param path   the full path
	 * @param prefix prefix to remove
	 * @return {@code path} without prefix, or {@code path} itself
	 * unless it doesn't start with the prefix.
	 */
	public String removePrefix(final String path, final String prefix) {
		String pathWithoutPrefix = path;

		if (pathWithoutPrefix.startsWith(prefix)) {
			pathWithoutPrefix = pathWithoutPrefix.substring(prefix.length());

			while (pathWithoutPrefix.startsWith("/") || pathWithoutPrefix.startsWith("\\")) {
				pathWithoutPrefix = pathWithoutPrefix.substring(1);
			}
		}

		return pathWithoutPrefix;
	}

	/**
	 * Checks whether {@code subDir} is a sub directory of {@code parentDir}.
	 * Note: the comparison is case sensitive.
	 *
	 * @param subDir    potential sub directory
	 * @param parentDir parent directory
	 * @return {@code true} if {@code subDir} is a subDirectory of
	 * {@code parentDir}, false otherwise.
	 * @throws java.io.IOException Thrown when paths cannot be normalized.
	 */
	public boolean isSubDir(final File subDir, final File parentDir) throws IOException {
		int parentDirLength = parentDir.getCanonicalFile().getAbsolutePath().length();

		File currentDir = subDir.getCanonicalFile().getAbsoluteFile();
		while (currentDir.getAbsolutePath().length() > parentDirLength) {
			currentDir = currentDir.getParentFile();
		}

		return currentDir.equals(parentDir.getAbsoluteFile());
	}

	/**
	 * Returns all directory paths between {@code fromDir} and {@code toDir}.
	 *
	 * @param fromDir directory to start from
	 * @param toDir   last directory path
	 * @return an array of all generated paths
	 * @throws java.io.IOException Thrown when paths cannot be normalized.
	 */
	public File[] getParentDirs(final File fromDir, final File toDir) throws IOException {
		List<File> result = new LinkedList<>();
		final File fromDirCanonical = fromDir.getCanonicalFile();
		for (File current = toDir.getCanonicalFile().getAbsoluteFile(); !current.equals(fromDirCanonical); current = current.getParentFile()) {
			result.add(0, current);
		}

		return result.toArray(new File[result.size()]);
	}

	/**
	 * Gets the longest common parent directory path of two paths.
	 *
	 * @param dir1 first path
	 * @param dir2 second path
	 * @return longest common path in {@code dir1} and {@code dir2}
	 * @throws java.io.IOException Exception which is thrown if paths cannot be normalized
	 */
	public File getCommonDir(final File dir1, final File dir2) throws IOException {
		List<File> parts1 = getParentDirs(dir1);
		List<File> parts2 = getParentDirs(dir2);
		File matched = null;

		final int maxCommonSize = Math.min(parts1.size(), parts2.size());
		for (int i = 0; i < maxCommonSize; ++i) {
			if (parts1.get(i).equals(parts2.get(i))) {
				matched = parts1.get(i);
			} else {
				break;
			}
		}

		return matched;
	}

	private List<File> getParentDirs(File dir) throws IOException {
		List<File> dirs = new LinkedList<>();

		File currentDir = dir.getCanonicalFile();
		File lastDir;
		do {
			dirs.add(0, currentDir);
			lastDir = currentDir;
			currentDir = currentDir.getParentFile();
		} while (currentDir != null && !currentDir.equals(lastDir));

		return dirs;
	}

	/**
	 * Converts an absolute path into a relative one.
	 *
	 * @param basePath path to start from
	 * @param absPath  path to convert
	 * @return {@code absPath} as a relative path starting from {@code basePath}
	 */
	public String abs2rel(final String basePath, final String absPath) {
		if (!isAbsolutePath(absPath)) {
			return absPath;
		}

		if (isWindowsDrive(absPath) && isWindowsDrive(basePath) && absPath.charAt(0) != basePath.charAt(0)) {
			return absPath;
		}

		StringBuilder result = new StringBuilder();
		String[] baseParts = getParts(basePath);
		String[] absParts = getParts(absPath);

		// extract common prefix
		int start = 0;
		for (int i = 0; i < Math.min(baseParts.length, absParts.length); ++i) {
			if (baseParts[i].equals(absParts[i])) {
				start = i + 1;
			}
		}

		for (int i = start; i < baseParts.length; ++i) {
			if (result.length() > 0) {
				result.append(File.separator);
			}
			result.append("..");
		}

		for (int i = start; i < absParts.length; ++i) {
			if (result.length() > 0) {
				result.append(File.separator);
			}
			result.append(absParts[i]);
		}

		return result.toString();
	}

	private boolean isAbsolutePath(String path) {
		return isWindowsDrive(path) || path.startsWith("\\") || path.startsWith("/");
	}

	private String[] getParts(final String path) {
		String newPath = path;
		if (newPath.startsWith("/") || newPath.startsWith("\\")) {
			newPath = newPath.substring(1);
		}

		if (newPath.endsWith("/") || newPath.endsWith("\\")) {
			newPath = newPath.substring(0, newPath.length() - 1);
		}

		return newPath.split("[/\\\\]");
	}

	/**
	 * Converts an relative path into an absolute one.
	 *
	 * @param basePath path to start from
	 * @param relPath  path to convert
	 * @return {@code relPath} as a absolute path
	 */
	public File rel2abs(final String basePath, final String relPath) {
		String[] baseParts = getParts(basePath);
		String[] relParts = getParts(relPath);

		if (isAbsolutePath(relPath)) {
			return new File(relPath);
		}

		List<String> parts = new ArrayList<>();
		for (int i = 0; i < baseParts.length; ++i) {
			if (i > 0 || !isWindowsDrive(basePath)) {
				parts.add(baseParts[i]);
			}
		}

		for (String part : relParts) {
			if (part.equals("..") && parts.size() > 0) {
				parts.remove(parts.size() - 1);
			} else if (!part.equals(".") && !part.equals("..")) {
				parts.add(part);
			}
		}

		StringBuilder result = new StringBuilder();
		if (isWindowsDrive(basePath)) {
			result.append(baseParts[0]);
		}

		for (String part : parts) {
			result.append(File.separator);
			result.append(part);
		}

		return new File(result.toString());
	}

	private boolean isWindowsDrive(final String pathElement) {
		return pathElement.length() > 1 && pathElement.charAt(1) == ':';
	}

	/**
	 * Counts the number of directories in a given path.
	 *
	 * @param path the path to analyze
	 * @return number of directories
	 */
	public int dirDepth(final File path) {
		final String stringPath = path.getPath();
		return stringPath.length() - stringPath.replaceAll("[/\\\\]", "").length();
	}

	// FIXME: this method only exists for mocking in tests
	public File subdir(File baseDir, String file) {
		return new File(baseDir, file);
	}
}


package js.servlet;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicInteger;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import js.container.AuthorizationException;
import js.container.ContainerSPI;
import js.core.Factory;
import js.http.ContentType;
import js.http.HttpHeader;
import js.json.Json;
import js.lang.InvocationException;
import js.log.Log;
import js.log.LogContext;
import js.log.LogFactory;
import js.rmi.BusinessException;
import js.rmi.RemoteException;
import js.util.Classes;
import js.util.Strings;

/**
 * Base for all application's servlets. Implements common request processing and provides utility functions. Concrete servlet
 * implementation should focus only on request handling, see {@link #handleRequest(RequestContext)}.
 *

 * This base servlet does not deal directly with {@link AuthorizationException} but provides utility function for sending
 * unauthorized access response. For this reason abstract request handler does not accept authorization exception in its
 * signature. Rationale for this design is that security handling depends on specific protocol employed by concrete servlet. For
 * example, rejecting an access for a resource is handled by redirecting to login page whereas for a service simple send back
 * unauthorized access response. Also a protocol may be designed to send back HTTP OK and serialize authorization exception in
 * its own format.
 *

 * Usually XHR requests comes from tight coupled clients that are part of a client-server application. It is expected that a
 * well behaving client to not send XHR requests for private resources outside an authenticated scope. Anyway, if XHR request
 * breaks security this framework logic enact {@link #sendUnauthorized(RequestContext)}.
 *

 * XHR specification mandates for client agent to handle transparently unauthorized access. This results into browser login
 * form, that is not always the desired solution. This framework choose to send 200 OK with {@link HttpHeader#X_HEADER_LOCATION}
 * custom header. Client script can redirect now to sent location. Anyway, for this solution to work application should have
 * login support, see {@link ContainerSPI#getLoginPage()}. If application login page is not configured unauthorized XHR is still
 * handled by client agent form.
 *

 * Also this base class takes care to initialize logger context, {@link #logContext} with context path, remote address and
 * request ID for current processing request. This diagnostic context data is bound to current thread and accessible to all log
 * messages generated by methods involved in request processing, no matter nesting level.
 *
 * @author Iulian Rotaru
 * @version final
 */
public abstract class AppServlet extends HttpServlet {
	/** Java serialization version. */
	private static final long serialVersionUID = -1261992764546153452L;

	/** Class logger. */
	private static final Log log = LogFactory.getLog(AppServlet.class);

	/** Logger diagnostic context stores contextual information regarding current request. */
	private static final LogContext logContext = LogFactory.getLogContext();
	/** Diagnostic context name for context path, aka application. */
	private static final String LOG_CONTEXT_APP = "app";
	/** Diagnostic context name for remote host, aka IP address. */
	private static final String LOG_CONTEXT_IP = "ip";
	/** Diagnostic context name for current request ID. */
	private static final String LOG_CONTEXT_ID = "id";

	/** Sequence generator for request ID. Every new HTTP request got an ID guaranteed to be unique for practical purposes. */
	private static final AtomicInteger requestID = new AtomicInteger();

	/**
	 * Request handler should be implemented by concrete servlet. It basically should extract data from request, process it and
	 * send back results on response. Also handler implementation should deal with exception like resource not found and
	 * unauthorized access. Anyway, exception that are not directly related to request processing are bubbled up, e.g.
	 * exceptions on IO and servlet container services.
	 *
	 * @param context execution context for current request.
	 * @throws IOException if reading from request or writing to response fails.
	 * @throws ServletException for fails on executing servlet container services.
	 */
	protected abstract void handleRequest(RequestContext context) throws IOException, ServletException;

	/** Servlet name, merely for logging. */
	protected String servletName;

	/**
	 * Container of the application on behalf of which this servlet instance is acquired. From servlet instance perspective
	 * container is a singleton and its reference can be safely stored.
	 */
	protected transient ContainerSPI container;

	/**
	 * Servlet life cycle callback executed at this servlet instance initialization. Mainly takes care to initialize parent
	 * container reference. If there is no servlet context attribute with the name {@link TinyContainer#ATTR_INSTANCE} this
	 * initialization fails with servlet permanently unavailable.
	 *

	 * Parent container instance has application life span and its reference is valid for entire life span of this servlet
	 * instance.
	 *
	 * @param config servlet configuration object.
	 * @throws UnavailableException if tiny container is not properly initialized.
	 */
	@Override
	public void init(ServletConfig config) throws UnavailableException {
		container = (ContainerSPI) config.getServletContext().getAttribute(TinyContainer.ATTR_INSTANCE);
		if (container == null) {
			log.fatal("Tiny container instance not properly created, probably misconfigured. Servlet |%s| permanently unvailable.", config.getServletName());
			throw new UnavailableException("Tiny container instance not properly created, probably misconfigured.");
		}

		servletName = Strings.concat(config.getServletContext().getServletContextName(), '#', config.getServletName());
		log.trace("Initialize servlet |%s|.", servletName);
	}

	/**
	 * Servlet life cycle callback executed on servlet instance destroying. Current implementation just log the event for
	 * debugging.
	 */
	@Override
	public void destroy() {
		log.trace("Destroy servlet |%s|.", servletName);
		super.destroy();
	}

	/**
	 * Prepare execution context and delegates the actual HTTP request processing to abstract handler. Initialize request
	 * context bound to current thread and delegates {@link #handleRequest(RequestContext)}. After request handler execution
	 * takes care to cleanup request context.
	 *

	 * This method also initialize logger context, see {@link #logContext} with remote address of current request so that
	 * logging utility can include contextual diagnostic data into log messages. Just before exiting, this service request
	 * cleanups the logger context.
	 *
	 * @throws IOException if reading from request or writing to response fail.
	 * @throws ServletException for fails on executing servlet container services.
	 */
	@Override
	protected void service(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException {
		// push context path and remote address of the requested processed by this thread to logger diagnostic context
		logContext.put(LOG_CONTEXT_APP, httpRequest.getContextPath().isEmpty() ? TinyContainer.ROOT_CONTEXT : httpRequest.getContextPath().substring(1));
		logContext.put(LOG_CONTEXT_IP, httpRequest.getRemoteHost());
		logContext.put(LOG_CONTEXT_ID, Integer.toString(requestID.getAndIncrement(), Character.MAX_RADIX));
		if (isEmptyUriRequest(httpRequest)) {
			log.debug("Empty URI request for |%s|. Please check for  with empty 'src' or <link>,


/**
 * Copyright (C) 2015 Michael Schnell. All rights reserved.
 * http://www.fuin.org/
 *
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library. If not, see http://www.gnu.org/licenses/.
 */
package org.fuin.srcgen4j.core.velocity;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;

import org.fuin.srcgen4j.commons.SrcGen4JContext;
import org.fuin.utils4j.Utils4J;

/**
 * A collection of multiple {@link ParameterizedTemplateModel} objects.
 */
public final class ParameterizedTemplateModels {

    @Valid
    @NotEmpty
    private List<ParameterizedTemplateModel> paramTemplates;

    /**
     * Default constructor.
     */
    public ParameterizedTemplateModels() {
        super();
    }

    /**
     * Returns the list of templates.
     *
     * @return Template list.
     */
    public final List<ParameterizedTemplateModel> getModelList() {
        return paramTemplates;
    }

    /**
     * Sets the list of templates to a new value.
     *
     * @param paramTemplates
     *            Template list to set.
     */
    public final void setParamTemplates(final List<ParameterizedTemplateModel> paramTemplates) {
        this.paramTemplates = paramTemplates;
    }

    /**
     * Adds another template to the list. If the list does not exist,it will be created.
     *
     * @param paramTemplate
     *            Template to add - Cannot be NULL.
     */
    public final void addParamTemplate(final ParameterizedTemplateModel paramTemplate) {
        if (paramTemplates == null) {
            paramTemplates = new ArrayList<ParameterizedTemplateModel>();
        }
        paramTemplates.add(paramTemplate);
    }

    /**
     * Adds all templates to the list. If the list does not exist,it will be created.
     *
     * @param list
     *            LIst of templates to add - Cannot be NULL.
     */
    public final void addParamTemplates(final List<ParameterizedTemplateModel> list) {
        if (list != null) {
            for (final ParameterizedTemplateModel template : list) {
                addParamTemplate(template);
            }
        }
    }

    /**
     * Initalizes the object.
     *
     * @param context
     *            Current context.
     * @param vars
     *            Variables to use.
     */
    public final void init(final SrcGen4JContext context, final Map<String, String> vars) {
        if (paramTemplates != null) {
            for (final ParameterizedTemplateModel paramTemplate : paramTemplates) {
                paramTemplate.init(context, vars);
            }
        }
    }

    /**
     * Returns a list that contains all models that reference the given template.
     *
     * @param templateDir
     *            Directory where all template files are located - Cannot be NULL.
     * @param templateFile
     *            File to find references to - Cannot be NULL.
     *
     * @return List - Never NULL, but may be empty.
     */
    public final List<ParameterizedTemplateModel> findReferencesTo(final File templateDir, final File templateFile) {

        final List<ParameterizedTemplateModel> result = new ArrayList<ParameterizedTemplateModel>();
        if ((paramTemplates != null) && Utils4J.fileInsideDirectory(templateDir, templateFile)) {
            for (final ParameterizedTemplateModel paramTemplate : paramTemplates) {
                if (paramTemplate.hasReferenceTo(templateDir, templateFile)) {
                    result.add(paramTemplate);
                }
            }
        }
        return result;
    }

}


/*
 * Copyright (c) 2002 Cunningham & Cunningham, Inc.
 * Copyright (c) 2009-2015 by Jochen Wierum & Cologne Intelligence
 *
 * This file is part of FitGoodies.
 *
 * FitGoodies is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FitGoodies is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FitGoodies.  If not, see <http://www.gnu.org/licenses/>.
 */

package de.cologneintelligence.fitgoodies.typehandler;

/**
 * Implementation of {@code AbstractTypeAdapter} which is able to
 * handle StringBuilder.
 */
public class StringBuilderTypeHandler extends TypeHandler<StringBuilder> {
	/**
	 * Generates a new TypeAdapter based on {@code ta}.
	 *
	 * @param parameter the given column/row parameter - ignored
	 */
	public StringBuilderTypeHandler(final String parameter) {
		super(parameter);
	}

	/**
	 * Returns the data type handled by the class.
	 *
	 * @return StringBuilder.class
	 */
	@Override
	public final Class<StringBuilder> getType() {
		return StringBuilder.class;
	}

	/**
	 * Checks whether two StringBuilder {@code a} and {@code b} are
	 * equal.
	 * <p>
	 * This method removes whitespaces around both strings first.
	 *
	 * @param a first StringBuilder
	 * @param b second StringBuilder
	 * @return true if they are equal or both are null, false otherwise
	 */
    @Override
	public final boolean unsafeEquals(final StringBuilder a, Object b) {
		return a.toString().trim().equals(b.toString().trim());
	}

	/**
	 * Returns the String representation of an StringBuilder.
	 *
	 * @param o StringBuilder to convert
	 * @return {@code o} as a String
	 */
	public final String toString(final StringBuilder o) {
		if (o == null) {
			return "null";
		}
		return o.toString();
	}

	/**
	 * Converts a {@code String} into a {@code StringBuilder}.
	 *
	 * @param s the {@code String} to convert
	 * @return a {@code StringBuilder} which contains {@code s}
	 */
	@Override
	public StringBuilder unsafeParse(final String s) {
		return new StringBuilder(s);
	}
}


package js.http.encoder;

import java.io.IOException;
import java.io.OutputStream;

import javax.servlet.http.HttpServletResponse;

import js.io.StreamHandler;

/**
 * Write bytes to output stream of the HTTP response. This return value writer uses {@link StreamHandler} to inject bytes from
 * application code to HTTP response output stream.
 *

 * This value writer executes {@link StreamHandler#invokeHandler(OutputStream)} with HTTP response output stream. Application
 * code implements stream handler that gets output stream provided by this value writer.
 *
 *

 * class Controller {
 * 	public StreamHandler<OutputStream> downloadStream() {
 * 		return new StreamHandler<OutputStream>(OutputStream.class) {
 * 			@Override
 * 			public void handle(OutputStream outputStream) throws IOException {
 * 				// output stream is from HTTP response
 * 				outputStream.write("output stream".getBytes("UTF-8"));
 * 			}
 * 		};
 * 	}
 * }
 *

 *
 * @author Iulian Rotaru
 * @version final
 */
final class StreamValueWriter implements ValueWriter {
	/**
	 * Write bytes to output stream of HTTP response. This method invokes {@link StreamHandler#invokeHandler(OutputStream)} with
	 * HTTP response output stream. Stream handler instance is created by application and allows application logic access to
	 * HTTP response stream.
	 *
	 * @param httpResponse HTTP response,
	 * @param value object value is a stream handler instance created by application code.
	 * @throws IOException if writing to HTTP response output stream fails.
	 */
	@Override
	public void write(HttpServletResponse httpResponse, Object value) throws IOException {
		((StreamHandler<?>) value).invokeHandler(httpResponse.getOutputStream());
	}
}