org.jboss.arquillian.android.drone.impl.AndroidDroneArchiveDeployer.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.arquillian.android.drone.impl.AndroidDroneArchiveDeployer.java

Source

/*
 * JBoss, Home of Professional Open Source
 * Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual
 * contributors by the @authors tag. See the copyright.txt in the
 * distribution for a full listing of individual contributors.
 *
 * 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 org.jboss.arquillian.android.drone.impl;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.FileUtils;
import org.jboss.arquillian.android.drone.configuration.AndroidDroneConfiguration;
import org.jboss.arquillian.android.drone.configuration.Command;
import org.jboss.arquillian.android.drone.utils.FileIdentifierGenerator;
import org.jboss.arquillian.android.drone.utils.IdentifierType;
import org.jboss.arquillian.android.spi.event.AndroidDeployArchive;
import org.jboss.arquillian.android.spi.event.AndroidUndeployArchive;
import org.jboss.arquillian.container.android.api.AndroidArchiveDeployer;
import org.jboss.arquillian.container.android.api.AndroidDevice;
import org.jboss.arquillian.container.android.api.AndroidExecutionException;
import org.jboss.arquillian.container.android.managed.configuration.AndroidSDK;
import org.jboss.arquillian.container.android.managed.impl.ProcessExecutor;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.shrinkwrap.api.Archive;

/**
 * Deploys and undeploys modified and resigned Selendroid application and application under test
 *
 * @author <a href="mailto:smikloso@redhat.com">Stefan Miklosovic</a>
 *
 */
public class AndroidDroneArchiveDeployer implements AndroidArchiveDeployer {

    private static final Logger logger = Logger.getLogger(AndroidDroneArchiveDeployer.class.getName());

    private static final String SELENDROID_SERVER_ACTIVITY = "org.openqa.selendroid/.ServerInstrumentation";

    @Inject
    private Instance<AndroidDevice> androidDevice;

    @Inject
    private Instance<AndroidSDK> androidSDK;

    @Inject
    private Instance<AndroidDroneConfiguration> androidDroneConfiguration;

    @Inject
    private Instance<ProcessExecutor> processExecutor;

    private File modifiedSelendroid = null;

    private File modifiedApplicationUnderTest = null;

    private File applicationUnderTest = null;

    private File tmpDir;

    /**
     * Observer deploy event and fires deployment method
     *
     * @param event
     * @throws AndroidExecutionException
     */
    public void listenDeployEvent(@Observes AndroidDeployArchive event) throws AndroidExecutionException {
        deploy(event.getArchive());
    }

    /**
     * Observe undeploymente event and fires undeployment method
     *
     * @param event
     * @throws AndroidExecutionException
     */
    public void listenUndeployEvent(@Observes AndroidUndeployArchive event) throws AndroidExecutionException {
        undeploy(event.getArchive());
    }

    /*
     * (non-Javadoc)
     *
     * @see org.jboss.arquillian.container.android.api.AndroidArchiveDeployer#deploy(org.jboss.shrinkwrap.api.Archive)
     */
    @Override
    public void deploy(Archive<?> deploymentArchive) {

        ProcessExecutor processExecutor = this.processExecutor.get();
        AndroidSDK androidSDK = this.androidSDK.get();
        AndroidDroneConfiguration androidDroneConfiguration = this.androidDroneConfiguration.get();
        AndroidDevice androidDevice = this.androidDevice.get();
        AndroidApplicationHelper applicationHelper = new AndroidApplicationHelper(processExecutor, androidSDK);
        SelendroidHelper selendroidHelper = new SelendroidHelper(androidDevice,
                androidDroneConfiguration.getServerLogFile());

        // creates temporary directory where every modified application and resources are put
        tmpDir = createWorkingDir(androidDroneConfiguration.getTmpDir());

        SelendroidRebuilder selendroidRebuilder = new SelendroidRebuilder(processExecutor, androidSDK,
                androidDroneConfiguration, applicationHelper, tmpDir);

        // export archive with application to tmpDir/random.file.apk
        applicationUnderTest = applicationHelper.exportArchiveToFile(
                new File(tmpDir, AndroidApplicationHelper.getRandomAPKFileName()), deploymentArchive);

        copyFileToDirectory(androidDroneConfiguration.getAndroidServerApk(), tmpDir);

        File selendroidWorkingCopy = new File(tmpDir, androidDroneConfiguration.getAndroidServerApk().getName());

        selendroidRebuilder
                .setApplicationBasePackage(applicationHelper.getApplicationBasePackage(applicationUnderTest));

        APKSigner signer = new APKSigner(processExecutor, androidSDK, androidDroneConfiguration, applicationHelper);

        // signs rebuilt Selendroid into modifiedSelendroid
        modifiedSelendroid = new File(tmpDir, AndroidApplicationHelper.getRandomAPKFileName());
        signer.sign(selendroidRebuilder.rebuild(selendroidWorkingCopy), modifiedSelendroid);

        // signs application under test
        modifiedApplicationUnderTest = new File(tmpDir, AndroidApplicationHelper.getRandomAPKFileName());
        signer.reSign(applicationUnderTest, modifiedApplicationUnderTest);

        // command for installation of modified Selendroid
        Command selendroidInstallCommand = new Command();
        selendroidInstallCommand.add(androidSDK.getAdbPath()).add("-s").add(androidDevice.getSerialNumber())
                .add("install").add(modifiedSelendroid.getAbsolutePath());

        logger.info("Selendroid server install command: " + selendroidInstallCommand.toString());

        // command for installation of application under test
        Command applicationInstallCommand = new Command();
        applicationInstallCommand.add(androidSDK.getAdbPath()).add("-s").add(androidDevice.getSerialNumber())
                .add("install").add(modifiedApplicationUnderTest.getAbsolutePath());

        logger.info("Application under test install command: " + applicationInstallCommand.toString());

        // install Selendroid
        try {
            processExecutor.execute(selendroidInstallCommand.getAsList().toArray(new String[0]));
        } catch (InterruptedException ex) {
            throw new AndroidExecutionException("Selendroid installation was interrupted.");
        } catch (ExecutionException ex) {
            throw new AndroidExecutionException("Unable to execute Selendroid deployment process.");
        }

        // check Selendroid is installed
        if (!androidDevice.isPackageInstalled(applicationHelper.getApplicationBasePackage(modifiedSelendroid))) {
            throw new AndroidExecutionException("Modified Selendroid server was not installed on device!");
        }

        // install application under test
        try {
            processExecutor.execute(applicationInstallCommand.getAsList().toArray(new String[0]));
        } catch (InterruptedException e) {
            throw new AndroidExecutionException("Installation of application under test was interrupted.");
        } catch (ExecutionException e) {
            throw new AndroidExecutionException(
                    "Unable to execute installation command of application under test.");
        }

        // check application under test is installed
        if (!androidDevice.isPackageInstalled(applicationHelper.getApplicationBasePackage(applicationUnderTest))) {
            throw new AndroidExecutionException("Application under test was not installed on device!");
        }

        // create port forwarding
        logger.log(Level.INFO, "Creating port forwarding from {0} to {1}",
                new Object[] { androidDevice.getDroneHostPort(), androidDevice.getDroneGuestPort() });

        androidDevice.createPortForwarding(androidDevice.getDroneHostPort(), androidDevice.getDroneGuestPort());

        // command for starting instrumentation of package under test by Selendroid
        Command startApplicationInstrumentationCommand = new Command();
        startApplicationInstrumentationCommand.add("am").add("instrument").add("-e").add("main_activity")
                .add("\'" + applicationHelper.getApplicationMainActivity(applicationUnderTest) + "\'")
                .add(SELENDROID_SERVER_ACTIVITY);

        logger.info(startApplicationInstrumentationCommand.toString());

        selendroidHelper.startInstrumentation(startApplicationInstrumentationCommand,
                applicationHelper.getApplicationBasePackage(applicationUnderTest));

        selendroidHelper.waitForServerHTTPStart();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.jboss.arquillian.container.android.api.AndroidArchiveDeployer#undeploy(org.jboss.shrinkwrap.api.Archive)
     */
    @Override
    public void undeploy(Archive<?> archive) {

        ProcessExecutor processExecutor = this.processExecutor.get();
        AndroidDevice androidDevice = this.androidDevice.get();
        AndroidSDK androidSDK = this.androidSDK.get();
        AndroidDroneConfiguration androidDroneConfiguration = this.androidDroneConfiguration.get();

        AndroidApplicationHelper applicationHelper = new AndroidApplicationHelper(processExecutor, androidSDK);
        SelendroidHelper selendroidHelper = new SelendroidHelper(androidDevice,
                androidDroneConfiguration.getServerLogFile());

        String selendroidBasePackage = applicationHelper.getApplicationBasePackage(modifiedSelendroid);
        String applicationBasePackage = applicationHelper.getApplicationBasePackage(applicationUnderTest);

        // command for stopping instrumentation
        Command stopApplicationInstrumentationCommand = new Command();
        stopApplicationInstrumentationCommand.add("am").add("force-stop").add(selendroidBasePackage);

        // stop Selendroid instrumentation
        selendroidHelper.stopInstrumentation(stopApplicationInstrumentationCommand);

        // command for uninstalling Selendroid server
        Command selendroidUninstallCommand = new Command();
        selendroidUninstallCommand.add("pm").add("uninstall").add(selendroidBasePackage);

        // uninstall Selendroid server
        selendroidHelper.uninstallSelendroid(selendroidUninstallCommand);

        // command for uninstalling application under test
        Command applicationUninstallCommand = new Command();
        applicationUninstallCommand.add("pm").add("uninstall").add(applicationBasePackage);

        // uninstall application under test
        selendroidHelper.uninstallApplicationUnderTest(applicationUninstallCommand);

        // remove port forwarding
        androidDevice.removePortForwarding(androidDevice.getDroneHostPort(), androidDevice.getDroneGuestPort());

        if (androidDroneConfiguration.getRemoveTmpDir()) {
            removeWorkingDir(tmpDir);
        }
    }

    private void removeWorkingDir(File dir) {
        try {
            FileUtils.deleteDirectory(dir);
        } catch (IOException e) {
            logger.log(Level.INFO, "Unable to delete temporary working dir {0}. Reason: {1}",
                    new Object[] { dir.getAbsolutePath(), e.getMessage() });
        }
    }

    /**
     * Creates directory with random name in {@code System.getProperty("java.io.tmpdir")}
     *
     * @return directory in system temporary directory
     */
    private File createWorkingDir(File parent) {
        FileIdentifierGenerator fig = new FileIdentifierGenerator();
        File temp;

        try {
            do {
                temp = new File(parent, fig.getIdentifier(IdentifierType.FILE.getClass()));
            } while (!temp.mkdir());
        } catch (SecurityException ex) {
            logger.severe("Security manager denies to create the working dir in " + parent.getAbsolutePath());
            throw new RuntimeException("Unable to create working directory in " + parent.getAbsolutePath());
        }

        return temp;
    }

    /**
     * Copies file to directory
     *
     * @param src source file
     * @param dest destination directory
     */
    private void copyFileToDirectory(File src, File dest) {
        try {
            FileUtils.copyFileToDirectory(src, dest);
        } catch (IOException ex) {
            throw new RuntimeException("Unable to copy " + src.getAbsolutePath() + " to " + dest.getAbsolutePath());
        }
    }
}