Package org.jboss.arquillian.container.appengine.tools

Source Code of org.jboss.arquillian.container.appengine.tools.AppEngineToolsContainer$DeployUpdateListener$MessageHeaders

/*
* JBoss, Home of Professional Open Source
* Copyright 2013 Red Hat Inc. and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This 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 2.1 of
* the License, or (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/

package org.jboss.arquillian.container.appengine.tools;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.appengine.tools.admin.AppAdmin;
import com.google.appengine.tools.admin.AppAdminFactory;
import com.google.appengine.tools.admin.Application;
import com.google.appengine.tools.admin.GenericApplication;
import com.google.appengine.tools.admin.UpdateFailureEvent;
import com.google.appengine.tools.admin.UpdateListener;
import com.google.appengine.tools.admin.UpdateProgressEvent;
import com.google.appengine.tools.admin.UpdateSuccessEvent;
import com.google.appengine.tools.info.SdkInfo;
import com.google.appengine.tools.info.UpdateCheck;
import com.google.apphosting.utils.config.AppEngineConfigException;
import org.jboss.arquillian.container.common.AppEngineCommonContainer;
import org.jboss.arquillian.container.spi.ConfigurationException;
import org.jboss.arquillian.container.spi.client.container.DeploymentException;
import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData;
import org.jboss.shrinkwrap.api.Archive;
import org.xml.sax.SAXParseException;

/**
* Tools AppEngine container.
* <p/>
* Code taken from "http://code.google.com/p/google-plugin-for-eclipse/source/browse/trunk/plugins/com.google.appengine.eclipse.core/proxy_src/com/google/appengine/eclipse/core/proxy/AppEngineBridgeImpl.java?r=2"
* with permission from GAE team.
*
* @author GAE Team
* @author <a href="mailto:ales.justin@jboss.org">Ales Justin</a>
*/
public class AppEngineToolsContainer extends AppEngineCommonContainer<AppEngineToolsConfiguration> {
    private AppEngineToolsConfiguration configuration;

    public Class<AppEngineToolsConfiguration> getConfigurationClass() {
        return AppEngineToolsConfiguration.class;
    }

    public void setup(AppEngineToolsConfiguration configuration) {
        final String sdkDir = configuration.getSdkDir();
        if (sdkDir == null)
            throw new ConfigurationException("AppEngine SDK root is null.");

        final String userId = configuration.getUserId();
        if (userId == null)
            throw new ConfigurationException("Null userId.");

        final String password = configuration.getPassword();
        if (password == null)
            throw new ConfigurationException("Null password.");

        SdkInfo.setSdkRoot(new File(sdkDir));

        this.configuration = configuration;
    }

    protected ProtocolMetaData doDeploy(Archive<?> archive) throws DeploymentException {
        try {
            if (configuration.isUpdateCheck()) {
                UpdateCheck updateCheck = new UpdateCheck(SdkInfo.getDefaultServer());
                if (updateCheck.allowedToCheckForUpdates()) {
                    updateCheck.maybePrintNagScreen(new PrintStream(System.out, true));
                }
            }

            final GenericApplication app = readApplication();
            final AppAdmin appAdmin = createAppAdmin(app);

            final DeployUpdateListener listener = new DeployUpdateListener(
                    this,
                    new PrintWriter(System.out, true),
                    new PrintWriter(System.err, true)
            );

            getExecutor().execute(new Runnable() {
                public void run() {
                    appAdmin.update(listener);
                }
            });

            Status status;
            synchronized (this) {
                do {
                    wait(configuration.getStartupTimeout());
                    status = listener.getStatus();
                } while (status == null); // guard against spurious wakeup
            }

            if (status != Status.OK) {
                throw new DeploymentException("Cannot deploy via GAE tools: " + status);
            }

            final String id = app.getVersion() + "." + app.getAppId();

            return getProtocolMetaData(id + ".appspot.com", configuration.getPort(), archive);
        } catch (DeploymentException e) {
            throw e;
        } catch (AppEngineConfigException e) {
            if (e.getCause() instanceof SAXParseException) {
                String msg = e.getCause().getMessage();

                // have to check what the message says to distinguish a file-not-found
                // problem from some other xml problem.
                if (msg.contains("Failed to read schema document") && msg.contains("backends.xsd")) {
                    throw new IllegalArgumentException("Deploying a project with backends requires App Engine SDK 1.5.0 or greater.", e);
                } else {
                    throw e;
                }
            } else {
                throw e;
            }
        } catch (Exception e) {
            if (e instanceof InterruptedException) {
                Thread.currentThread().interrupt();
            }
            throw new DeploymentException("Cannot deploy via GAE tools.", e);
        }
    }

    protected Executor getExecutor() {
        return Executors.newSingleThreadExecutor();
    }

    protected GenericApplication readApplication() throws IOException {
        return Application.readApplication(getAppLocation().getCanonicalPath());
    }

    AppAdmin createAppAdmin(GenericApplication app) throws IOException {
        AppAdminFactory appAdminFactory = new AppAdminFactory();

        /**
         if (options.getJavaExecutableOSPath() != null) {
         appAdminFactory.setJavaExecutable(new File(options.getJavaExecutableOSPath()));
         }

         if (options.getJavaCompilerExecutableOSPath() != null) {
         appAdminFactory.setJavaCompiler(new File(options.getJavaCompilerExecutableOSPath()));
         }
         */

        final AppAdminFactory.ConnectOptions appEngineConnectOptions = new AppAdminFactory.ConnectOptions();
        String appengineServer = System.getenv("APPENGINE_SERVER");
        if (appengineServer != null) {
            appEngineConnectOptions.setServer(appengineServer);
        }
        appEngineConnectOptions.setUserId(configuration.getUserId());
        // TODO -- better prompt?
        appEngineConnectOptions.setPasswordPrompt(new AppAdminFactory.PasswordPrompt() {
            public String getPassword() {
                return configuration.getPassword();
            }
        });

        PrintWriter errorWriter = new PrintWriter(System.err, true);

        return appAdminFactory.createAppAdmin(appEngineConnectOptions, app, errorWriter);
    }

    private static final class DeployUpdateListener implements UpdateListener {
        private static final Logger log = Logger.getLogger(DeployUpdateListener.class.getName());

        /**
         * Class for getting headers representing different stages of deployments
         * bassed on console messages from the gae sdk.
         */
        private static class MessageHeaders {

            // Headers should go in the order specified in this array.
            private static final PrefixHeaderPair[] prefixHeaderPairs = new PrefixHeaderPair[]{
                    new PrefixHeaderPair("Preparing to deploy", null, "Created staging directory", "Scanning files on local disk"),
                    new PrefixHeaderPair("Deploying", null, "Uploading"),
                    new PrefixHeaderPair("Verifying availability", "Verifying availability of", "Will check again in 1 seconds."),
                    new PrefixHeaderPair("Updating datastore", null, "Uploading index")};

            /*
             * The headers should go in the sequence specified in the array,
             * so keep track of which header we're currently looking for.
             */
            private int currentPrefixHeaderPair;

            PrefixHeaderPair getMessageHeader(String msg) {
                PrefixHeaderPair php = prefixHeaderPairs[currentPrefixHeaderPair];
                for (String prefix : php.msgPrefixes) {
                    if (msg.startsWith(prefix)) {
                        currentPrefixHeaderPair = (currentPrefixHeaderPair + 1) % prefixHeaderPairs.length;
                        return php;
                    }
                }
                return null;
            }
        }

        /**
         * Class for holding the different gae sdk messages that are associated with
         * different "headers", representing the stages of deployment.
         */
        private static class PrefixHeaderPair {
            // the header that should be displayed on the console
            final String header;

            // the prefixes of console messages that trigger this header
            final String[] msgPrefixes;

            // the header that should be displayed on the progress dialog, mainly for
            // displaying "verifying availability" on the console and displaying
            // "verifying availability of "backend""
            final String taskHeader;

            PrefixHeaderPair(String header, String taskHeader, String... msgPrefixes) {
                this.msgPrefixes = msgPrefixes;
                this.header = header;
                if (taskHeader == null) {
                    this.taskHeader = header;
                } else {
                    this.taskHeader = taskHeader;
                }
            }
        }

        /**
         * Attempts to reflectively call getDetails() on the event object received
         * by the onFailure or onSuccess callback. That method is only supported by
         * App Engine SDK 1.2.1 or later. If we are able to call getDetails we
         * return the details message; otherwise we return <code>null</code>.
         */
        private static String getDetailsIfSupported(Object updateEvent) {
            try {
                Method method = updateEvent.getClass().getDeclaredMethod("getDetails");
                return (String) method.invoke(updateEvent);
            } catch (NoSuchMethodException e) {
                // Expected on App Engine SDK 1.2.0; no need to log
            } catch (Exception e) {
                log.log(Level.SEVERE, e.getMessage(), e);
            }
            return null;
        }

        /**
         * Reflectively checks to see if an exception is a JspCompilationException,
         * which is only supported by App Engine SDK 1.2.1 or later.
         */
        private static boolean isJspCompilationException(Throwable ex) {
            if (ex != null) {
                try {
                    Class<?> jspCompilationExceptionClass = Class.forName("com.google.appengine.tools.admin.JspCompilationException");
                    return jspCompilationExceptionClass.isAssignableFrom(ex.getClass());
                } catch (ClassNotFoundException e) {
                    // Expected on App Engine SDK 1.2.0; no need to log
                }
            }
            return false;
        }

        private final Object waiter;
        private final PrintWriter errorWriter;
        private final PrintWriter outputWriter;

        private MessageHeaders messageHeaders;
        private int percentDone = 0;
        private Status status = null;

        private DeployUpdateListener(Object waiter, PrintWriter outputWriter, PrintWriter errorWriter) {
            this.waiter = waiter;
            this.outputWriter = outputWriter;
            this.errorWriter = errorWriter;
            this.messageHeaders = new MessageHeaders();
        }

        public Status getStatus() {
            return status;
        }

        public void onFailure(UpdateFailureEvent event) {
            // Create status object and print error message to the writer
            status = Status.ERROR;
            outputWriter.println(event.getFailureMessage());

            // Only print the details for JSP compilation errors
            if (isJspCompilationException(event.getCause())) {
                String details = getDetailsIfSupported(event);
                if (details != null) {
                    outputWriter.println(details);
                }
            }

            synchronized (waiter) {
                waiter.notify();
            }
        }

        public void onProgress(UpdateProgressEvent event) {
            // Update the progress monitor
            int worked = event.getPercentageComplete() - percentDone;
            percentDone += worked;

            String msg = event.getMessage();
            PrefixHeaderPair php = messageHeaders.getMessageHeader(msg);

            if (php != null) {
                outputWriter.println("\n" + php.header + ":");
            }
            outputWriter.println("\t" + msg);
        }

        public void onSuccess(UpdateSuccessEvent event) {
            status = Status.OK;

            percentDone = 0; // reset

            String details = getDetailsIfSupported(event);
            if (details != null) {
                // Note that unlike in onFailure, we're writing to the log file here,
                // not to the console. This is so we don't clutter our deployment
                // console with a bunch of info or warning messages from JSP
                // compilation, if we deployed successfully.
                errorWriter.println(details);
            }

            outputWriter.println("\nDeployment completed successfully");

            synchronized (waiter) {
                waiter.notify();
            }
        }

        /**
         * Println's the given string to this DeployUpdateListener's output writer.
         */
        public void println(String s) {
            outputWriter.println(s);
        }
    }
}
TOP

Related Classes of org.jboss.arquillian.container.appengine.tools.AppEngineToolsContainer$DeployUpdateListener$MessageHeaders

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.