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

Java tutorial

Introduction

Here is the source code for org.jboss.arquillian.android.drone.impl.SelendroidRebuilder.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.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.jboss.arquillian.android.drone.configuration.AndroidDroneConfiguration;
import org.jboss.arquillian.android.drone.configuration.Command;
import org.jboss.arquillian.android.drone.configuration.Validate;
import org.jboss.arquillian.container.android.managed.configuration.AndroidSDK;
import org.jboss.arquillian.container.android.managed.impl.ProcessExecutor;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;

/**
 * Rebuilds Selendroid application.
 *
 * @author <a href="mailto:smikloso@redhat.com">Stefan Miklosovic</a>
 */
public class SelendroidRebuilder {

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

    private File workingDir = null;

    private ProcessExecutor processExecutor;

    private AndroidSDK androidSDK;

    private AndroidApplicationHelper androidApplicationHelper;

    private String applicationBasePackage;

    /**
     *
     * @param processExecutor
     * @param androidSDK
     * @param androidDroneConfiguration
     * @param androidApplicationHelper
     * @param workingDir directory where all files and resources this class is working with are put
     */
    public SelendroidRebuilder(ProcessExecutor processExecutor, AndroidSDK androidSDK,
            AndroidDroneConfiguration androidDroneConfiguration, AndroidApplicationHelper androidApplicationHelper,
            File workingDir) {
        Validate.notNull(processExecutor, "Process exeuctor for Selendroid rebuilder can't be null!");
        Validate.notNull(androidSDK, "Android SDK for Selendroid rebuilder can't be null!");
        Validate.notNull(androidDroneConfiguration,
                "Android drone configuration for Selendroid rebuilder can't be null!");
        Validate.notNull(androidApplicationHelper,
                "Android applicatio helper for Selendroid rebuilder can't be null!");
        Validate.notNull(workingDir,
                "Working dir for Selendroid rebuilder can't be null!. Set it e.g. to /tmp/ ...");
        this.processExecutor = processExecutor;
        this.androidSDK = androidSDK;
        this.androidApplicationHelper = androidApplicationHelper;
        this.workingDir = workingDir;
    }

    /**
     * Rebuilds Selendroid package.
     *
     * Changes AndroidManifest.xml to reflect application under test and repackages it.
     *
     * @param selendroidWorkingCopy Selendroid working copy for making modifications
     *
     * @return modified Selendroid application which waits to be resigned
     */
    @SuppressWarnings("resource")
    public File rebuild(File selendroidWorkingCopy) {

        try {

            File toBeReplacedAndroidManifest = new File(workingDir, "AndroidManifestToBeReplaced.xml");
            File finalAndroidManifest = new File(workingDir, "AndroidManifest.xml");
            File dummyAPK = new File(workingDir, "dummy.apk");

            // ugly hack for copying AndroidManifest.xml from resources to tmpDir
            new FileOutputStream(toBeReplacedAndroidManifest.getAbsoluteFile()).write(IOUtils
                    .toByteArray(this.getClass().getClassLoader().getResourceAsStream("AndroidManifest.xml")));

            filterManifestFile(toBeReplacedAndroidManifest, finalAndroidManifest);

            // create dummy package in order to get compiled AndroidManifest.xml

            createDummyAPK(dummyAPK, finalAndroidManifest);

            // extract AndroidManifest.xml from that dummy.apk package and add it to selendroid working copy
            Archive<?> dummyArchive = ShrinkWrap.createFromZipFile(JavaArchive.class, dummyAPK);
            Archive<?> finalArchive = ShrinkWrap.createFromZipFile(JavaArchive.class, selendroidWorkingCopy);

            finalArchive.delete("AndroidManifest.xml");
            finalArchive.add(dummyArchive.get("AndroidManifest.xml").getAsset(), "AndroidManifest.xml");

            File targetFile = new File(workingDir, AndroidApplicationHelper.getRandomAPKFileName());

            return androidApplicationHelper.exportArchiveToFile(targetFile, finalArchive);
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     *
     * @param applicationBasePackage
     */
    public void setApplicationBasePackage(String applicationBasePackage) {
        this.applicationBasePackage = applicationBasePackage;
    }

    /**
     * Creates dummy APK file to get compiler AndroidManifest.xml
     *
     * @param dummyAPK APK to store dummy APK to
     * @param androidManifest AndroidManifest.xml to be used while creating dummy APK
     */
    private void createDummyAPK(File dummyAPK, File androidManifest) {
        Command createDummyPackage = new Command();
        createDummyPackage.add(androidSDK.getAaptPath()).add("package").add("-M")
                .add(androidManifest.getAbsolutePath()).add("-I").add(androidSDK.getPlatform() + "/android.jar")
                .add("-F").add(dummyAPK.getAbsolutePath());

        logger.log(Level.INFO, createDummyPackage.toString());

        try {
            processExecutor.execute(createDummyPackage.getAsList().toArray(new String[0]));
        } catch (InterruptedException e) {
            logger.severe("Command was interrupted: " + createDummyPackage.toString());
        } catch (ExecutionException e) {
            logger.severe("Command failed to execute: " + createDummyPackage.toString());
        }
    }

    /**
     * Replaces AndroidManifest.xml file which contains default testapp base package to use the propper one
     *
     * @param toBeReplaced
     * @param finalManifest
     */
    @SuppressWarnings("unchecked")
    private void filterManifestFile(File toBeReplaced, File finalManifest) {
        try {
            List<String> oldManifest = FileUtils.readLines(toBeReplaced);
            List<String> finalManifestList = Replacer.replace(oldManifest, "org\\.openqa\\.selendroid\\.testapp",
                    applicationBasePackage);
            FileUtils.writeLines(finalManifest, finalManifestList);
        } catch (IOException e) {
            logger.severe(e.getMessage());
        }
    }

    /**
     * Replaces strings in list of strings.
     *
     * @author <a href="mailto:smikloso@redhat.com">Stefan Miklosovic</a>
     *
     */
    private static class Replacer {
        public static List<String> replace(List<String> fileLines, String toReplace, String replacement) {
            if (fileLines == null || fileLines.size() == 0) {
                return null;
            }

            List<String> afterReplace = new ArrayList<String>();
            for (String line : fileLines) {
                afterReplace.add(line.replaceAll(toReplace, replacement));
            }

            return afterReplace;
        }
    }
}