com.frameworkx.AbstractApplication.java Source code

Java tutorial

Introduction

Here is the source code for com.frameworkx.AbstractApplication.java

Source

/**
 * Framework X - Java Web Application Framework
 * Copyright (C) 2011 Robert Hollencamp
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.frameworkx;

import com.frameworkx.mvc.ControllerRouteHandler;
import com.frameworkx.mvc.ViewResult;
import java.io.IOException;
import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;

/**
 * Base class for all Framework X Applications
 *
 * @author robert.hollencamp
 */
public abstract class AbstractApplication {
    private final Properties properties = new Properties();
    private String instance;
    private final Map<Pattern, RouteHandler> routes = new LinkedHashMap<Pattern, RouteHandler>();
    private final List<Plugin> plugins = new LinkedList<Plugin>();
    private ServletContext servletContext;

    /**
     * Initialize the framework
     *
     * This should not be called from client code
     *
     * @param servletContext
     */
    public final void init(final ServletContext servletContext) {
        this.servletContext = servletContext;

        this.loadDefaultProperties();
        this.loadAppProperties();

        this.instance = this.resolveInstance();
        if (this.instance == null || this.instance.isEmpty()) {
            throw new IllegalStateException("Instance can not be null or empty");
        }
        this.loadInstanceProperties();

        this.setStaticConfig();

        this.loadPlugins();

        this.registerRoutes();
    }

    /**
     * Get the servlet context for the application
     *
     * @return
     */
    public ServletContext getServletContext() {
        return this.servletContext;
    }

    /**
     * Access configuration values
     *
     * @param key
     * @return
     */
    public String getProperty(final String key) {
        return this.properties.getProperty(key);
    }

    /**
     * Read the default properties file
     *
     * @throws RuntimeException if default properties file can not be read
     */
    private void loadDefaultProperties() {
        final InputStream is = AbstractApplication.class
                .getResourceAsStream("/com/frameworkx/framework-x.properties");
        try {
            if (is != null) {
                this.properties.load(is);
            } else {
                throw new RuntimeException("Unable to load default properties");
            }
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * Load Application properties
     *
     * @throws RuntimeException if application properties file can not be read
     */
    private void loadAppProperties() {
        final InputStream is = this.servletContext.getResourceAsStream("/WEB-INF/framework-x.properties");
        try {
            if (is != null) {
                this.properties.load(is);
            }
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * Load Application Instance properties
     *
     * @throws RuntimeException if an IOException occurs while reading instance properties file
     */
    private void loadInstanceProperties() {
        final InputStream is = this.servletContext
                .getResourceAsStream("/WEB-INF/instance-" + this.instance + ".properties");
        try {
            if (is != null) {
                this.properties.load(is);
            }
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * Register a route
     *
     * @param p
     * @param rh
     */
    protected void registerRoute(final Pattern p, final RouteHandler rh) {
        if (p == null) {
            throw new IllegalArgumentException("Pattern can not be null");
        }
        if (rh == null) {
            throw new IllegalArgumentException("Route Handler can not be null");
        }

        this.routes.put(p, rh);
    }

    /**
     * Iterate through the routing table and find a matching entry. If a matching route is not found,
     * a 404 is generated
     *
     * @param request
     * @param response
     */
    public void execute(final HttpServletRequest request, final HttpServletResponse response) {
        final String path = request.getRequestURI().substring(request.getContextPath().length());

        // plugin requestReceived hook
        for (Plugin p : this.plugins) {
            p.onRequestReceived(request, response);
        }

        try {
            for (Map.Entry<Pattern, RouteHandler> kvp : this.routes.entrySet()) {
                Matcher m = kvp.getKey().matcher(path);
                if (m.matches()) {
                    kvp.getValue().execute(request, response, m);
                    return;
                }
            }

            // @todo 404
        } catch (Exception ex) {
            for (Plugin p : this.plugins) {
                p.onUncaughtException(request, response);
            }

            // @todo 500
            throw new RuntimeException(ex);
        } finally {
            for (Plugin p : this.plugins) {
                p.onRequestFinally(request, response);
            }
        }
    }

    /**
     * Determine what instance we are running in. Instance is used to determine what config file to
     * read (instance-{instance}.properties), and can also be referenced in client code.
     *
     * Common instance names are "dev", "qa", "stage", "live"
     *
     * Returned string can not be null or empty
     *
     * @return
     */
    protected abstract String resolveInstance();

    /**
     * Fill out the route table
     */
    protected abstract void registerRoutes();

    /**
     * Initialize static configuration
     */
    private void setStaticConfig() {
        ViewResult.init(this);
        ControllerRouteHandler.init(this);
    }

    /**
     * Load and initialize plugins
     */
    private void loadPlugins() {
        // load the powered by plugin by default
        if ("Y".equals(this.properties.getProperty("plugins.poweredBy"))) {
            // add the powered by plugin
            Plugin p = new PoweredByPlugin();
            p.init(null, this);
            this.plugins.add(p);
        }

        // get list of other plugins
        for (String name : this.properties.getProperty("plugins", "").split(",")) {
            if (!name.isEmpty()) {
                this.loadPlugin(name);
            }
        }
    }

    /**
     * Load the plugin with the given name
     *
     * @param name
     */
    private void loadPlugin(final String name) {
        try {
            final String pluginClassName = this.properties.getProperty("plugin." + name + ".class");
            if (pluginClassName == null || pluginClassName.isEmpty()) {
                throw new Exception("Plugin Class not specified");
            }
            Class<? extends Plugin> pluginClass = Class.forName(pluginClassName).asSubclass(Plugin.class);

            Plugin p = pluginClass.getConstructor().newInstance();
            p.init(name, this);
            this.plugins.add(p);
        } catch (Exception ex) {
            throw new RuntimeException("Unable to initialize plugin " + name, ex);
        }
    }
}