ru.anr.base.tests.GlassfishLoader.java Source code

Java tutorial

Introduction

Here is the source code for ru.anr.base.tests.GlassfishLoader.java

Source

/*
 * Copyright 2014 the original author or authors.
 * 
 * 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 ru.anr.base.tests;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.glassfish.embeddable.CommandResult;
import org.glassfish.embeddable.CommandRunner;
import org.glassfish.embeddable.Deployer;
import org.glassfish.embeddable.GlassFish;
import org.glassfish.embeddable.GlassFishException;
import org.glassfish.embeddable.GlassFishProperties;
import org.glassfish.embeddable.GlassFishRuntime;
import org.glassfish.embeddable.archive.ScatteredArchive;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;

import ru.anr.base.ApplicationException;

/**
 * Loader for Embedded Glassfish. Default configuration tries to load both
 * classpath and test classpath as a single war application.
 * <p>
 * The loader can be use only for JUnit tests and not specially usefull for
 * local web development, because unable to perform class/resources reloading.
 *
 *
 * @author Alexey Romanchuk
 * @created Nov 17, 2014
 *
 */

public class GlassfishLoader {

    /**
     * Logger
     */
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(GlassfishLoader.class);

    /**
     * Static ref to Glassfish instance
     */
    protected GlassFish glassfish;

    /**
     * Static ref to GlassfishRuntime
     */
    protected GlassFishRuntime glassfishRuntime;

    /**
     * File with domain configuration in class path
     */
    private String domainFileConfig = "domain.xml";

    /**
     * Name of application
     */
    private final String appName;

    /**
     * Constructor
     * 
     * @param appName
     *            Name of application
     */
    public GlassfishLoader(String appName) {

        super();
        this.appName = appName;
    }

    /**
     * Note: This part of code is taken from Apache Ant CommandLine
     * implementation.
     * 
     * Crack a command line.
     * 
     * @param toProcess
     *            the command line to process.
     * @return the command line broken into strings. An empty or null toProcess
     *         parameter results in a zero sized array.
     */
    public static String[] translateCommandline(String toProcess) {

        if (toProcess == null || toProcess.length() == 0) {
            // no command? no string
            return new String[0];
        }
        // parse with a simple finite state machine

        final int normal = 0;
        final int inQuote = 1;
        final int inDoubleQuote = 2;
        int state = normal;
        final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
        final ArrayList<String> result = new ArrayList<String>();
        final StringBuilder current = new StringBuilder();
        boolean lastTokenHasBeenQuoted = false;

        while (tok.hasMoreTokens()) {
            String nextTok = tok.nextToken();
            switch (state) {
            case inQuote:
                if ("\'".equals(nextTok)) {
                    lastTokenHasBeenQuoted = true;
                    state = normal;
                } else {
                    current.append(nextTok);
                }
                break;
            case inDoubleQuote:
                if ("\"".equals(nextTok)) {
                    lastTokenHasBeenQuoted = true;
                    state = normal;
                } else {
                    current.append(nextTok);
                }
                break;
            default:
                if ("\'".equals(nextTok)) {
                    state = inQuote;
                } else if ("\"".equals(nextTok)) {
                    state = inDoubleQuote;
                } else if (" ".equals(nextTok)) {
                    if (lastTokenHasBeenQuoted || current.length() != 0) {
                        result.add(current.toString());
                        current.setLength(0);
                    }
                } else {
                    current.append(nextTok);
                }
                lastTokenHasBeenQuoted = false;
                break;
            }
        }
        if (lastTokenHasBeenQuoted || current.length() != 0) {
            result.add(current.toString());
        }
        if (state == inQuote || state == inDoubleQuote) {
            throw new ApplicationException("unbalanced quotes in " + toProcess);
        }
        return result.toArray(new String[result.size()]);
    }

    /**
     * Initialization and starting of Embedded Glassfish instance
     */
    public void initialize() {

        try {

            GlassFishProperties gfProps = new GlassFishProperties();

            // Searching of domain.xml in classpath
            gfProps.setConfigFileURI(new ClassPathResource(domainFileConfig).getFile().toURI().toString());
            gfProps.setConfigFileReadOnly(true);
            gfProps.setProperty("glassfish.embedded.tmpdir", "./target/glassfish");

            // Base logger settings
            Logger.getLogger("").getHandlers()[0].setLevel(Level.INFO);

            Logger.getLogger("javax.enterprise.system.tools.deployment").setLevel(Level.INFO);
            Logger.getLogger("javax.enterprise.system").setLevel(Level.INFO);

            glassfishRuntime = GlassFishRuntime.bootstrap();

            glassfish = glassfishRuntime.newGlassFish(gfProps);
            glassfish.start();

            /*
             * Executing command-line scripts
             */
            executeScript("META-INF/glassfish.properties.txt");
            executeScript("META-INF/glassfish.txt");

        } catch (IOException | GlassFishException ex) {
            throw new ApplicationException(ex);
        }
    }

    /**
     * Executes a file with asadmin commands
     * 
     * @param fileName
     *            Name of a file
     * @throws IOException
     *             In case of File not found
     * @throws GlassFishException
     *             In case of Glassfish error
     */
    private void executeScript(String fileName) throws IOException, GlassFishException {

        ClassPathResource script = new ClassPathResource(fileName);

        if (script.exists()) {

            List<String> lines = IOUtils.readLines(script.getInputStream());
            CommandRunner cmd = glassfish.getCommandRunner();

            for (String l : lines) {

                String[] tokens = translateCommandline(l);

                if (tokens.length == 0 || "#".equals(tokens[0])) {
                    continue;
                }

                String[] args = ArrayUtils.subarray(tokens, 1, tokens.length);

                logger.info("Runnning a command: {} {}", tokens[0], args);
                CommandResult r = cmd.run(tokens[0], args);

                switch (r.getExitStatus()) {
                case SUCCESS:
                    logger.info("SUCCESS >> {}", r.getOutput());
                    break;
                case WARNING:
                    logger.info("WARNING >> {} ( {} )", r.getOutput(), r.getFailureCause());
                    break;
                case FAILURE:
                    logger.info("ERROR >> \n{}", r.getOutput(), r.getFailureCause());
                    throw new ApplicationException(r.getFailureCause());
                default:
                }
            }
        } else {
            logger.info("'glassfish.txt' file not found");
        }
    }

    /**
     * Shutdown instance
     */
    public void shutdown() {

        try {

            glassfish.dispose();
            glassfishRuntime.shutdown();

        } catch (GlassFishException ex) {
            throw new ApplicationException(ex);
        }

    }

    /**
     * Deploying all applications (EJB, Servlets) localed both in classpath and
     * test classpath.
     * 
     * @param deployer
     * @throws IOException
     * @throws GlassFishException
     */
    public void deployApps() {

        try {

            Deployer deployer = glassfish.getDeployer();

            // Create a scattered web application.
            ScatteredArchive webmodule = new ScatteredArchive(appName, ScatteredArchive.Type.WAR,
                    new File("src/main/webapp"));

            // target/classes directory contains my complied servlets
            webmodule.addClassPath(new File("target", "classes"));
            webmodule.addClassPath(new File("target", "test-classes"));

            // WEB application if required
            safeAddMetadata(webmodule, new File("src/main/webapp/WEB-INF", "sun-web.xml"));
            safeAddMetadata(webmodule, new File("src/main/webapp/WEB-INF", "web.xml"));

            // EJB application if required
            safeAddMetadata(webmodule, new File("target/classes/META-INF", "ejb-jar.xml"));
            safeAddMetadata(webmodule, new File("target/test-classes/META-INF", "ejb-jar.xml"));

            safeAddMetadata(webmodule, new File("target/classes/META-INF", "glassfish-ejb-jar.xml"));
            safeAddMetadata(webmodule, new File("target/test-classes/META-INF", "glassfish-ejb-jar.xml"));

            // JPA config if required
            safeAddMetadata(webmodule, new File("target/classes/META-INF", "persistence.xml"));
            safeAddMetadata(webmodule, new File("target/test-classes/META-INF", "persistence.xml"));

            deployer.deploy(webmodule.toURI());

        } catch (IOException | GlassFishException ex) {
            throw new ApplicationException(ex);
        }
    }

    /**
     * Safe adding item to avoid {@link IOException} in case of absence some
     * configuration. For example a test application can be web only without
     * EJB.
     * 
     * @param webmodule
     *            WAR module
     * @param file
     *            File to add
     */
    private void safeAddMetadata(ScatteredArchive webmodule, File file) {

        try {

            logger.debug("Trying to load: {}", file);
            webmodule.addMetadata(file);
            logger.info("Loaded: {}", file);

        } catch (IOException ex) {
            logger.info("File : {} not found, ignoring", file);
        }
    }

    // /////////////////////////////////////////////////////////////////////////
    // /// getters/setters
    // /////////////////////////////////////////////////////////////////////////

    /**
     * @param domainFileConfig
     *            the domainFileConfig to set
     */
    public void setDomainFileConfig(String domainFileConfig) {

        this.domainFileConfig = domainFileConfig;
    }

}