private String getEnvDefinedMavenHome(EnvVars env) {
    String mavenHome = env.get("MAVEN_HOME");
    if (StringUtils.isNotBlank(mavenHome)) {
      return mavenHome;
    }

    return env.get("M2_HOME");
  }
  private ArgumentListBuilder buildMavenCmdLine(
      AbstractBuild<?, ?> build, BuildListener listener, EnvVars env)
      throws IOException, InterruptedException {

    FilePath mavenHome = getMavenHomeDir(build, listener, env);

    if (!mavenHome.exists()) {
      listener.error("Couldn't find Maven home: " + mavenHome.getRemote());
      throw new Run.RunnerAbortedException();
    }

    ArgumentListBuilder args = new ArgumentListBuilder();

    FilePath mavenBootDir = new FilePath(mavenHome, "boot");
    FilePath[] classworldsCandidates = mavenBootDir.list("plexus-classworlds*.jar");
    if (classworldsCandidates == null || classworldsCandidates.length == 0) {
      listener.error("Couldn't find classworlds jar under " + mavenBootDir.getRemote());
      throw new Run.RunnerAbortedException();
    }

    FilePath classWorldsJar = classworldsCandidates[0];

    // classpath
    args.add("-classpath");
    // String cpSeparator = launcher.isUnix() ? ":" : ";";

    args.add(classWorldsJar.getRemote());

    // maven home
    args.addKeyValuePair("-D", "maven.home", mavenHome.getRemote(), false);

    String buildInfoPropertiesFile = env.get(BuildInfoConfigProperties.PROP_PROPS_FILE);
    boolean artifactoryIntegration = StringUtils.isNotBlank(buildInfoPropertiesFile);
    listener
        .getLogger()
        .println("Artifactory integration is " + (artifactoryIntegration ? "enabled" : "disabled"));
    String classworldsConfPath;
    if (artifactoryIntegration) {

      args.addKeyValuePair(
          "-D", BuildInfoConfigProperties.PROP_PROPS_FILE, buildInfoPropertiesFile, false);

      // use the classworlds conf packaged with this plugin and resolve the extractor libs
      File maven3ExtractorJar = Which.jarFile(Maven3BuildInfoLogger.class);
      FilePath actualDependencyDirectory =
          PluginDependencyHelper.getActualDependencyDirectory(build, maven3ExtractorJar);

      if (getMavenOpts() == null || !getMavenOpts().contains("-Dm3plugin.lib")) {
        args.addKeyValuePair("-D", "m3plugin.lib", actualDependencyDirectory.getRemote(), false);
      }

      URL classworldsResource =
          getClass()
              .getClassLoader()
              .getResource("org/jfrog/hudson/maven3/classworlds-freestyle.conf");

      File classworldsConfFile =
          new File(URLDecoder.decode(classworldsResource.getFile(), "utf-8"));
      if (!classworldsConfFile.exists()) {
        listener.error(
            "Unable to locate classworlds configuration file under "
                + classworldsConfFile.getAbsolutePath());
        throw new Run.RunnerAbortedException();
      }

      // If we are on a remote slave, make a temp copy of the customized classworlds conf
      if (Computer.currentComputer() instanceof SlaveComputer) {

        FilePath remoteClassworlds =
            build.getWorkspace().createTextTempFile("classworlds", "conf", "", false);
        remoteClassworlds.copyFrom(classworldsResource);
        classworldsConfPath = remoteClassworlds.getRemote();
      } else {
        classworldsConfPath = classworldsConfFile.getCanonicalPath();
      }
    } else {
      classworldsConfPath = new FilePath(mavenHome, "bin/m2.conf").getRemote();
    }

    args.addKeyValuePair("-D", "classworlds.conf", classworldsConfPath, false);

    // maven opts
    if (StringUtils.isNotBlank(getMavenOpts())) {
      String mavenOpts = Util.replaceMacro(getMavenOpts(), build.getBuildVariableResolver());
      args.add(mavenOpts);
    }

    // classworlds launcher main class
    args.add(CLASSWORLDS_LAUNCHER);

    // pom file to build
    String rootPom = getRootPom();
    if (StringUtils.isNotBlank(rootPom)) {
      args.add("-f", rootPom);
    }

    // maven goals
    args.addTokenized(getGoals());

    return args;
  }
  @Override
  public BuildWrapper.Environment setUp(
      AbstractBuild build, Launcher launcher, BuildListener listener)
      throws IOException, InterruptedException {
    final PrintStream logger = listener.getLogger();

    final DeviceFarmApi api = new DeviceFarmApiImpl();
    long start = System.currentTimeMillis();

    try {
      EnvVars environment = build.getEnvironment(listener);
      String expendedTag = environment.expand(tag);

      log(logger, Messages.TRYING_TO_CONNECT_API_SERVER(deviceApiUrl, expendedTag));
      api.connectApiServer(
          logger,
          deviceApiUrl,
          expendedTag,
          build.getProject().getAbsoluteUrl() + build.getNumber());

      final RemoteDevice reserved =
          api.waitApiResponse(
              logger, DEVICE_WAIT_TIMEOUT_IN_MILLIS, DEVICE_READY_CHECK_INTERVAL_IN_MS);
      log(
          logger,
          Messages.DEVICE_IS_READY(passedSeconds(start), reserved.ip, reserved.port, reserved.url));

      if (descriptor == null) {
        descriptor = Hudson.getInstance().getDescriptorByType(DescriptorImpl.class);
      }

      // Substitute environment and build variables into config
      final String androidHome = discoverAndroidSdkHome(build, launcher, listener);
      log(logger, Messages.USING_SDK(androidHome));

      AndroidSdk sdk = new AndroidSdk(androidHome, androidHome);
      final AndroidDeviceContext device =
          new AndroidDeviceContext(build, launcher, listener, sdk, reserved.ip, reserved.port);
      // disconnect first to workaround previous error
      device.disconnect();

      // connect device with adb
      device.connect(DEVICE_CONNECT_TIMEOUT_IN_MILLIS);

      device.waitDeviceReady(logger, DEVICE_CONNECT_TIMEOUT_IN_MILLIS, 1000);
      // check availability
      device.devices();

      // unlock screen
      device.unlockScreen();

      // Start dumping logcat to temporary file
      final LogcatCollector logcatCollector = new LogcatCollector(build, device);
      logcatCollector.start();

      return new BuildWrapper.Environment() {
        @Override
        public void buildEnvVars(Map<String, String> env) {
          env.put("ANDROID_IP", device.ip());
          env.put("ANDROID_HOME", androidHome);
          env.put("ANDROID_SDK_HOME", androidHome);
          env.put("ANDROID_PORT", Integer.toString(device.port()));
          env.put("ANDROID_SERIAL", device.serial());
        }

        @Override
        public boolean tearDown(AbstractBuild build, BuildListener listener)
            throws IOException, InterruptedException {
          cleanUp(build, device, api, logcatCollector);

          return true;
        }
      };

    } catch (FailedToConnectApiServerException e) {
      log(logger, Messages.FAILED_TO_CONNECT_API_SERVER());
    } catch (MalformedResponseException e) {
      log(logger, Messages.FAILED_TO_PARSE_DEVICE_FARM_RESPONSE());
    } catch (TimeoutException e) {
      log(logger, Messages.DEVICE_WAIT_TIMEOUT(passedSeconds(start)));
    } catch (NoDeviceAvailableException e) {
      log(logger, Messages.NO_SUCH_DEVICE());
    }

    build.setResult(Result.NOT_BUILT);
    cleanUp(null, null, api, null);
    return null;
  }