/*
* Copyright 2012 James Moger
*
* 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.moxie.ant;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.moxie.Build;
import org.moxie.Dependency;
import org.moxie.MoxieException;
import org.moxie.Scope;
import org.moxie.Toolkit;
import org.moxie.Toolkit.Key;
import org.moxie.console.Console;
import org.moxie.utils.FileUtils;
import org.moxie.utils.StringUtils;
public abstract class MxTask extends Task {
private Console console;
private Boolean verbose;
private boolean requiredGoal;
public MxTask() {
super();
requiredGoal = true;
}
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public boolean isVerbose() {
if (verbose == null) {
Build build = getBuild();
if (build == null) {
verbose = false;
} else {
verbose = build.getConfig().isVerbose();
}
}
return verbose;
}
private Boolean showtitle;
public void setShowtitle(boolean value) {
this.showtitle = value;
}
public boolean isShowTitle() {
return showtitle == null || showtitle;
}
public boolean isRequiredGoal() {
return requiredGoal;
}
public void setRequiredGoal(boolean value) {
requiredGoal = value;
}
public void title(String title) {
if (isShowTitle()) {
getConsole().title(title);
}
}
public void title(String title, String parameter) {
if (isShowTitle()) {
getConsole().title(title, parameter);
}
}
public void titleClass() {
if (isShowTitle()) {
getConsole().title(getClass());
}
}
public void titleClass(String parameter) {
if (isShowTitle()) {
getConsole().title(getClass(), parameter);
}
}
/**
* Console offset is a one-time correction factor
* to improve readability of the output.
* @return
*/
public int getConsoleOffset() {
int consoleOffset = 0;
String offset = getProject().getProperty("console.offset");
if (!StringUtils.isEmpty(offset)) {
consoleOffset = Integer.parseInt(offset);
}
return consoleOffset;
}
public void setConsoleOffset(int value) {
getProject().setProperty("console.offset", "" + value);
}
public Build getBuild() {
Build build = (Build) getProject().getReference(Key.build.referenceId());
return build;
}
protected Console getConsole() {
if (console == null) {
Build build = getBuild();
if (build == null) {
console = new Console();
} else {
console = build.getConsole();
}
}
return console;
}
protected void setProjectProperty(Key prop, String value) {
if (!StringUtils.isEmpty(value)) {
getProject().setProperty(prop.projectId(), value);
log(prop.projectId(), value, false);
}
}
protected void setProperty(String prop, String value) {
if (!StringUtils.isEmpty(value)) {
getProject().setProperty(prop, value);
log(prop, value, false);
}
}
protected void setReference(Key prop, Object obj) {
if (obj == null) {
return;
}
getProject().addReference(prop.referenceId(), obj);
}
protected void setPathReference(Key prop, Object obj, boolean split) {
// set path as both a property and a reference
// the property is accessible from all Ants
// the reference is accessible internally from mx tasks
// and when using Moxie on the Ant classpath (-lib and Moxie+Ant)
getProject().setProperty(prop.projectId(), obj.toString());
log(prop.projectId(), obj.toString(), split);
setReference(prop, obj);
}
protected void log(String key, String value, boolean split) {
if (isVerbose()) {
int indent = 30;
if (split) {
String [] paths = value.split(File.pathSeparator);
getConsole().key(StringUtils.leftPad(key, indent, ' '), paths[0]);
for (int i = 1; i < paths.length; i++) {
getConsole().key(StringUtils.leftPad("", indent, ' '), paths[i]);
}
} else {
getConsole().key(StringUtils.leftPad(key, indent, ' '), value);
}
}
}
protected String readResourceAsString(String resource) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
InputStream is = getClass().getResourceAsStream("/" + resource);
byte [] buffer = new byte[32767];
int len = 0;
while ((len = is.read(buffer)) > -1) {
os.write(buffer, 0, len);
}
} catch (Exception e) {
getConsole().error(e, "Can not extract \"{0}\"!", resource);
}
return os.toString();
}
protected byte [] readResourceAsBytes(String resource) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
InputStream is = getClass().getResourceAsStream("/" + resource);
byte [] buffer = new byte[32767];
int len = 0;
while ((len = is.read(buffer)) > -1) {
os.write(buffer, 0, len);
}
} catch (Exception e) {
getConsole().error(e, "Can not extract \"{0}\"!", resource);
}
return os.toByteArray();
}
protected boolean extractResource(File outputFolder, String resource) {
return extractResource(outputFolder, resource, resource, true);
}
protected boolean extractResource(File outputFolder, String resource, String asResource, boolean overwrite) {
File targetFile = new File(outputFolder, asResource);
if (targetFile.exists() && !overwrite) {
return false;
}
byte [] content = readResourceAsBytes(resource);
targetFile.getParentFile().mkdirs();
FileUtils.writeContent(targetFile, content);
return true;
}
protected boolean hasClass(String classname) {
try {
return Class.forName(classname) != null;
} catch (Throwable t) {
}
return false;
}
protected void updateExecutionClasspath() {
Build build = getBuild();
Set<Dependency> executionDependencies = new LinkedHashSet<Dependency>();
executionDependencies.addAll(build.getSolver().getDependencies(Scope.build));
List<String> specialCases = Arrays.asList(Toolkit.TEST_BUILD_CLASSPATH_CASES);
for (Dependency dep : build.getSolver().getDependencies(Scope.test)) {
if (specialCases.contains(dep.getManagementId())) {
executionDependencies.add(dep);
}
}
updateExecutionClasspath(build, executionDependencies);
}
public String getProjectTitle() {
String name = getBuild().getPom().getCoordinates();
if (!StringUtils.isEmpty(getBuild().getPom().getName())) {
name = getBuild().getPom().getName() + " (" + name + ")";
}
return name;
}
public void sharePaths(String... paths) {
getProject().setProperty("mxshared.path", StringUtils.flattenStrings(
Arrays.asList(paths), File.pathSeparator));
}
public Path getSharedPaths() {
Path path = new Path(getProject());
String paths = getProject().getProperty("mxshared.path");
if (!StringUtils.isEmpty(paths)) {
for (String fp : paths.split(File.pathSeparator)) {
FileSet fs = new FileSet();
fs.setProject(getProject());
File file = new File(fp);
if (file.isDirectory()) {
fs.setDir(file);
} else {
fs.setFile(file);
}
path.add(fs);
}
}
return path;
}
public Path consumeSharedPaths() {
Path path = getSharedPaths();
getProject().setProperty("mxshared.path", "");
return path;
}
public static void loadRuntimeDependencies(Build build, Dependency... dependencies) {
Collection<Dependency> downloaded = build.getSolver().getRuntimeDependencies(dependencies);
updateExecutionClasspath(build, downloaded);
}
public static void updateExecutionClasspath(Build build, Collection<Dependency> dependencies) {
Set<String> cp = new LinkedHashSet<String>();
for (Dependency dep : dependencies) {
File file = build.getSolver().getArtifact(dep);
if (file == null || !file.exists()) {
build.getConsole().warn("Failed to find {0} artifact, excluding from Moxie classpath", dep.getCoordinates());
continue;
}
try {
cp.add(file.getCanonicalPath());
} catch (Exception e) {
cp.add(file.getAbsolutePath());
}
}
ClassLoader loader = build.getClass().getClassLoader();
if (loader instanceof AntClassLoader) {
// running Moxie using taskdef notation from a script
AntClassLoader antLoader = (AntClassLoader) loader;
build.getConsole().debug("updating Moxie classpath via AntClassLoader");
build.getConsole().debug(antLoader.getClasspath());
// add file objects
for (String path : cp) {
antLoader.addPathComponent(new File(path));
build.getConsole().debug(1, "{0}", path);
}
} else if (loader instanceof URLClassLoader) {
// running Moxie bundled with Ant or with -lib parameter
build.getConsole().debug("updating Moxie classpath via URLClassLoader");
URLClassLoader sysloader = (URLClassLoader) loader;
Class<?> sysclass = URLClassLoader.class;
Method addURL = null;
try {
addURL = sysclass.getDeclaredMethod("addURL", new Class[] { URL.class });
addURL.setAccessible(true);
} catch (Throwable t) {
throw new MoxieException("Error, could not access class loader!", t);
}
for (String path : cp) {
try {
addURL.invoke(sysloader, new Object[] { new File(path).toURI().toURL() });
build.getConsole().debug(1, "{0}", path);
} catch (Throwable t) {
throw new MoxieException(MessageFormat.format(
"Error, could not add {0} to classloader", path), t);
}
}
} else {
build.getConsole().error("Skipping update classpath. Unexpected class loader {0}", loader.getClass().getName());
}
}
}