Java tutorial
/** * Copyright 2005-2014 Red Hat, Inc. * * Red Hat licenses this file to you 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 io.fabric8.maven; import java.io.Console; import java.io.File; import java.io.FileInputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.jar.ContainerManifest; import java.util.regex.Matcher; import java.util.regex.Pattern; import io.fabric8.utils.Files; import io.fabric8.utils.Strings; import io.fabric8.deployer.dto.DependencyDTO; import io.fabric8.deployer.dto.ProjectRequirements; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactCollector; import org.apache.maven.artifact.resolver.ArtifactResolutionRequest; import org.apache.maven.artifact.resolver.ArtifactResolver; import org.apache.maven.artifact.resolver.filter.ArtifactFilter; import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.shared.dependency.tree.DependencyNode; import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException; import org.codehaus.plexus.util.IOUtil; import org.osgi.framework.Constants; /** * Abstract base class for Profile based mojos */ public abstract class AbstractProfileMojo extends AbstractMojo { /** * The set of packaging types that should be omitted from the bundle spec * <ul> * <li>'jar' - because the resolve implicitly searches for jars when no type is specified</li> * <li>'bundle' - because a bundle is a jar, but the resolver doesn't account for this</li> * </ul> */ private static final String[] OMITTED_BUNDLE_TYPES = new String[] { "jar", "bundle" }; /** * The folder used for defining project specific files */ @Parameter(property = "profileConfigDir", defaultValue = "${basedir}/src/main/fabric8") protected File profileConfigDir; @Component protected MavenProject project; @Component protected ArtifactCollector artifactCollector; @Component protected ArtifactFactory artifactFactory; @Component protected DependencyTreeBuilder dependencyTreeBuilder; @Component protected ArtifactMetadataSource metadataSource; @Component protected ArtifactResolver resolver; @Parameter(property = "localRepository", readonly = true, required = true) protected ArtifactRepository localRepository; @Parameter(property = "project.remoteArtifactRepositories") protected List remoteRepositories; /** * The scope to filter by when resolving the dependency tree */ @Parameter(property = "fabric8.scope", defaultValue = "compile") private String scope; /** * The profile ID to deploy to. If not specified then it defaults to the groupId-artifactId of the project */ @Parameter(property = "fabric8.profile") private String profile; /** * Whether the profile is abstract */ @Parameter(property = "fabric8.abstractProfile", defaultValue = "false") private boolean abstractProfile; /** * The profile version to deploy to. If not specified then the current latest version is used. */ @Parameter(property = "fabric8.profileVersion") private String version; /** * The profile base version used if the version specified is a new version. */ @Parameter(property = "fabric8.baseVersion") private String baseVersion; /** * The space separated list of parent profile IDs to use for the profile */ @Parameter(property = "fabric8.parentProfiles") private String parentProfiles; /** * The space separated list of bundle URLs (in addition to the project artifact) which should be added to the profile */ @Parameter(property = "fabric8.bundles") private String bundles; /** * The space separated list of features to be added to the profile */ @Parameter(property = "fabric8.features") private String features; /** * The space separated list of feature repository URLs to be added to the profile */ @Parameter(property = "fabric8.featureRepos") private String featureRepos; /** * If enabled then the OSGi resolver is used to try deduce extra bundles or features * required to be added from the transitive maven dependencies. */ @Parameter(property = "fabric8.useResolver", defaultValue = "true") private boolean useResolver; /** * Specifies whether or not to specify that the profile should be locked. */ @Parameter(property = "fabric8.locked") private Boolean locked; /** * The minimum number of instances of this profile which we require to run. */ @Parameter(property = "fabric8.minInstanceCount", defaultValue = "1") private Integer minInstanceCount; /** * Whether or not we should add the maven deployment unit to the fabric profile */ @Parameter(property = "fabric8.includeArtifact", defaultValue = "true") private boolean includeArtifact; /** * Type to use for the project artifact bundle reference */ @Parameter(property = "fabric8.artifactBundleType") private String artifactBundleType; /** * Classifier to use for the project artifact bundle reference */ @Parameter(property = "fabric8.artifactBundleClassifier") private String artifactBundleClassifier; /** * Whether or not we should ignoreProject this maven project from goals like fabric8:deploy or fabric8:zip */ @Parameter(property = "fabric8.ignoreProject", defaultValue = "false") private boolean ignoreProject; /** * Whether or not we should upload the project readme file if no specific readme file exists in the {@link #profileConfigDir} */ @Parameter(property = "fabric8.includeReadMe", defaultValue = "true") protected boolean includeReadMe; /** * Whether or not we should generate a <code>Summary.md</code> file from the pom.xml <description> element text value. */ @Parameter(property = "fabric8.generateSummaryFile", defaultValue = "true") protected boolean generateSummaryFile; /** * The context path to use for web applications for projects using the <code>war</code> packaging */ @Parameter(property = "fabric8.webContextPath", defaultValue = "${project.artifactId}") private String webContextPath; /** * If provided then any links in the readme.md files will be replaced to include the given prefix */ @Parameter(property = "fabric8.replaceReadmeLinksPrefix") protected String replaceReadmeLinksPrefix; protected static boolean isFile(File file) { return file != null && file.exists() && file.isFile(); } public static void combineProfileFilesToFolder(MavenProject reactorProject, File buildDir, Log log, String reactorProjectOutputPath) throws IOException { File basedir = reactorProject.getBasedir(); if (!basedir.exists()) { log.warn("No basedir " + basedir.getAbsolutePath() + " for project + " + reactorProject); return; } File outDir = new File(basedir, reactorProjectOutputPath); if (!outDir.exists()) { log.warn("No profile output dir at: " + outDir.getAbsolutePath() + " for project + " + reactorProject + " so ignoring this project."); return; } log.info("Copying profiles from " + outDir.getAbsolutePath() + " into the output directory: " + buildDir); appendProfileConfigFiles(outDir, buildDir); } /** * Combines any files from the profileSourceDir into the output directory */ public static void appendProfileConfigFiles(File profileSourceDir, File outputDir) throws IOException { if (profileSourceDir.exists() && profileSourceDir.isDirectory()) { File[] files = profileSourceDir.listFiles(); if (files != null) { outputDir.mkdirs(); for (File file : files) { File outFile = new File(outputDir, file.getName()); if (file.isDirectory()) { appendProfileConfigFiles(file, outFile); } else { if (outFile.exists() && file.getName().endsWith(".properties")) { System.out.println("Combining properties: file " + file.getAbsolutePath()); combinePropertiesFiles(file, outFile); } else { System.out.println("Copying file " + file.getAbsolutePath()); Files.copy(file, outFile); } } } } } } /** * For 2 properties files the source and dest file, lets combine the values so that all the values of the sourceFile are in the dest file */ public static void combinePropertiesFiles(File sourceFile, File destFile) throws IOException { Properties source = loadProperties(sourceFile); Properties dest = loadProperties(destFile); Set<Map.Entry<Object, Object>> entries = source.entrySet(); for (Map.Entry<Object, Object> entry : entries) { Object key = entry.getKey(); Object value = entry.getValue(); if (key != null && value != null) { String keyText = key.toString(); String valueText = value.toString(); String oldValue = dest.getProperty(keyText); if (oldValue == null || oldValue.trim().length() == 0) { dest.setProperty(keyText, valueText); } else { if (oldValue.contains(valueText)) { // we've already added it so ignoreProject! } else { String newValue = oldValue + " " + valueText; dest.setProperty(keyText, newValue); } } } } dest.store(new FileWriter(destFile), "Generated by fabric8:full-zip plugin at " + new Date()); } private static Properties loadProperties(File file) throws IOException { Properties answer = new Properties(); answer.load(new FileReader(file)); return answer; } public boolean isIncludeArtifact() { return includeArtifact && !"pom".equals(project.getPackaging()); } public boolean isIgnoreProject() { return ignoreProject; } public String getWebContextPath() { return webContextPath; } public void setWebContextPath(String webContextPath) { this.webContextPath = webContextPath; } protected static List<String> parameterToStringList(String parameterValue) { List<String> answer = new ArrayList<String>(); if (Strings.isNotBlank(parameterValue)) { String[] split = parameterValue.split("\\s"); for (String text : split) { if (Strings.isNotBlank(text)) { answer.add(text); } } } return answer; } protected String readInput(String prompt) { Console console = System.console(); System.out.print(prompt); return console.readLine(); } protected String readPassword(String prompt) { Console console = System.console(); System.out.print(prompt); char[] pw = console.readPassword(); return new String(pw); } protected void configureRequirements(ProjectRequirements requirements) throws MojoExecutionException { if (Strings.isNotBlank(profile)) { requirements.setProfileId(profile); } else { requirements.setProfileId(project.getGroupId() + "-" + project.getArtifactId()); } requirements.setAbstractProfile(abstractProfile); String description = project.getDescription(); if (Strings.isNotBlank(description)) { requirements.setDescription(description); } if (Strings.isNotBlank(version)) { requirements.setVersion(version); } if (Strings.isNotBlank(baseVersion)) { requirements.setBaseVersion(baseVersion); } if (Strings.isNotBlank(webContextPath)) { requirements.setWebContextPath(webContextPath); } if (locked != null) { requirements.setLocked(locked); } List<String> bundleList = parameterToStringList(bundles); if (parentProfiles == null || parentProfiles.length() <= 0) { parentProfiles = defaultParentProfiles(requirements); } List<String> profileParentList = parameterToStringList(parentProfiles); List<String> featureList = parameterToStringList(features); List<String> featureReposList = parameterToStringList(featureRepos); requirements.setParentProfiles(profileParentList); requirements.setBundles(bundleList); requirements.setFeatures(featureList); requirements.setFeatureRepositories(featureReposList); if (minInstanceCount != null) { requirements.setMinimumInstances(minInstanceCount); } if (useResolver) { requirements.setUseResolver(Boolean.TRUE); } } protected String defaultParentProfiles(ProjectRequirements requirements) throws MojoExecutionException { // TODO lets try figure out the best parent profile based on the project String packaging = project.getPackaging(); if (packaging != null) { if ("jar".equals(packaging)) { // lets use the java container List<File> files = new ArrayList<File>(); Set<String> classNames = findMainClasses(files); int classNameSize = classNames.size(); if (classNameSize > 0) { if (classNameSize > 1) { getLog().warn("We found more than one executable main: " + classNames); } // TODO if we've a single className and we've not specified one via a properties file // lets add it to the properties file? } List<URL> urls = new ArrayList<URL>(); try { for (File file : files) { URL url = file.toURI().toURL(); urls.add(url); } return resolveProfileFromJars(urls); } catch (MalformedURLException e) { getLog().warn("Failed to create URLClassLoader from files: " + files); } return "containers-java"; } else if ("war".equals(packaging)) { return "containers-tomcat"; } else if ("ear".equals(packaging)) { return "containers-wildfly"; } } return findOSGiDefaultParentProfiles(requirements); } protected String resolveProfileFromJars(List<URL> urls) { URLClassLoader classLoader = createURLClassLoader(urls); Map<String, String> mainToProfileMap = getDefaultJavaClassToParentProfileMap(); String defaultProfile = "containers-java"; return resolveProfileFromClassMap(classLoader, mainToProfileMap, defaultProfile); } protected String findOSGiDefaultParentProfiles(ProjectRequirements requirements) throws MojoExecutionException { URLClassLoader classLoader = getCompileClassLoader(); Map<String, String> classToProfileMap = getDefaultOSGiClassToParentProfileMap(); Set<Map.Entry<String, String>> entries = classToProfileMap.entrySet(); List<String> parentProfileNames = new ArrayList<>(); for (Map.Entry<String, String> entry : entries) { String className = entry.getKey(); String profileName = entry.getValue(); if (hasClass(classLoader, className)) { getLog().info("Found class: " + className + " so adding the parent profile: " + profileName); parentProfileNames.add(profileName); } } if (parentProfileNames.isEmpty()) { return "karaf"; } else { return Strings.join(parentProfileNames, " "); } } protected Map<String, String> getDefaultOSGiClassToParentProfileMap() { Map<String, String> classToProfileMap = new LinkedHashMap<String, String>(); // TODO it'd be nice to find these automatically by querying the fabric itself for profiles // for a PID? classToProfileMap.put("org.apache.camel.CamelContext", "feature-camel"); classToProfileMap.put("org.apache.cxf.Bus", "feature-cxf"); return classToProfileMap; } protected String resolveProfileFromClassMap(URLClassLoader classLoader, Map<String, String> mainToProfileMap, String defaultProfile) { Set<Map.Entry<String, String>> entries = mainToProfileMap.entrySet(); for (Map.Entry<String, String> entry : entries) { String mainClass = entry.getKey(); String profileName = entry.getValue(); if (hasClass(classLoader, mainClass)) { getLog().info("Found class: " + mainClass + " so defaulting the parent profile: " + profileName); return profileName; } } return defaultProfile; } protected URLClassLoader getCompileClassLoader() throws MojoExecutionException { List<URL> urls = new ArrayList<>(); try { for (Object object : project.getCompileClasspathElements()) { if (object != null) { String path = object.toString(); File file = new File(path); URL url = file.toURI().toURL(); urls.add(url); } } } catch (Exception e) { throw new MojoExecutionException("Failed to resolve classpath: " + e, e); } return createURLClassLoader(urls); } protected static URLClassLoader createURLClassLoader(Collection<URL> jars) { return new URLClassLoader(jars.toArray(new URL[jars.size()])); } protected Map<String, String> getDefaultJavaClassToParentProfileMap() { Map<String, String> classToProfileMap = new LinkedHashMap<String, String>(); // TODO it'd be nice to find these automatically by querying the fabric itself for profiles // for the PID and "mainClass" value? classToProfileMap.put("org.springframework.boot.SpringApplication", "containers-java.spring.boot"); classToProfileMap.put("io.fabric8.process.spring.boot.container.FabricSpringApplication", "containers-java.spring.boot"); classToProfileMap.put("org.apache.camel.spring.Main", "containers-java.camel.spring"); classToProfileMap.put("org.osgi.framework.BundleContext", "containers-java.pojosr"); classToProfileMap.put("org.apache.camel.blueprint.ErrorHandlerType", "containers-java.pojosr"); classToProfileMap.put("javax.enterprise.context.ApplicationScoped", "containers-java.weld"); return classToProfileMap; } protected boolean hasClass(URLClassLoader classLoader, String className) { try { classLoader.loadClass(className); return true; } catch (Throwable e) { return false; } } protected Set<String> findMainClasses(List<File> files) throws MojoExecutionException { Set<String> classNames = new HashSet<String>(); Artifact artifact = project.getArtifact(); if (artifact != null) { File artifactFile = artifact.getFile(); addMainClass(classNames, files, artifactFile); } try { for (Object object : project.getCompileClasspathElements()) { if (object != null) { String path = object.toString(); File file = new File(path); addMainClass(classNames, files, file); } } } catch (Exception e) { throw new MojoExecutionException("Failed to resolve classpath: " + e, e); } return classNames; } protected void addMainClass(Set<String> classNames, List<File> files, File file) { if (file != null && file.exists() && file.isFile()) { files.add(file); try { JarFile jarFile = new JarFile(file); ContainerManifest manifest = jarFile.getContainerManifest(); if (manifest != null) { Attributes attributes = manifest.getMainAttributes(); if (attributes != null) { String className = attributes.getValue(Attributes.Name.MAIN_CLASS); if (className != null && className.length() > 0) { getLog().debug("found main class " + className + " in " + file); className = className.trim(); if (className.length() > 0) { classNames.add(className); } } } } } catch (IOException e) { getLog().warn("Failed to parse manifest for " + file + ". " + e, e); } } } protected void addProjectArtifactBundle(ProjectRequirements requirements) throws MojoFailureException { DependencyDTO rootDependency = requirements.getRootDependency(); if (rootDependency != null) { // we need url with type, so when we deploy war files the mvn url is correct StringBuilder urlBuffer = new StringBuilder(rootDependency.toBundleUrl()); String apparentType = rootDependency.getType(); String apparentClassifier = rootDependency.getClassifier(); for (String omit : OMITTED_BUNDLE_TYPES) { if (omit.equals(apparentType)) { apparentType = null; break; } } handleArtifactBundleType(urlBuffer, apparentType); handleArtifactBundleClassifier(urlBuffer, apparentClassifier); String urlString = urlBuffer.toString(); if (!requirements.getBundles().contains(urlString)) { requirements.getBundles().add(urlString); } } } private void handleArtifactBundleType(StringBuilder urlBuffer, String apparentType) { if (artifactBundleType != null) { urlBuffer.append("/" + artifactBundleType); } else { if (apparentType != null) { urlBuffer.append("/" + apparentType); } } } private void handleArtifactBundleClassifier(StringBuilder urlBuffer, String apparentClassifier) throws MojoFailureException { String nextUrlComponent = ""; if (apparentClassifier != null) { nextUrlComponent = "/" + apparentClassifier; } if (artifactBundleClassifier != null) { if (artifactBundleType != null) { nextUrlComponent = "/" + artifactBundleClassifier; } else { throw new MojoFailureException("The property artifactBundleClassifier was specified as '" + artifactBundleClassifier + "' without also specifying artifactBundleType"); } } urlBuffer.append(nextUrlComponent); } protected DependencyDTO loadRootDependency() throws DependencyTreeBuilderException { ArtifactFilter artifactFilter = createResolvingArtifactFilter(); DependencyNode dependencyNode = dependencyTreeBuilder.buildDependencyTree(project, localRepository, artifactFactory, metadataSource, artifactFilter, artifactCollector); return buildFrom(dependencyNode); } private DependencyDTO buildFrom(DependencyNode node) { Artifact artifact = node.getArtifact(); if (artifact != null) { DependencyDTO answer = new DependencyDTO(); answer.setGroupId(artifact.getGroupId()); answer.setArtifactId(artifact.getArtifactId()); answer.setVersion(artifact.getVersion()); answer.setClassifier(artifact.getClassifier()); String scope = artifact.getScope(); answer.setScope(scope); answer.setType(artifact.getType()); // there is a bug if we try to resolve the current projects artifact for a "jar" packaging // before we've installed it then this operation will force the jar not be installed // so lets ignore this for the maven project's artifact if (artifact.getClassifier() == null && "jar".equals(artifact.getType())) { if (project.getArtifact().equals(artifact)) { getLog().debug("Ignoring bundle check on the maven project artifact: " + artifact + " as this causes issues with the maven-install-plugin and we can assume the project packaging is accurate"); } else { try { ArtifactResolutionRequest request = new ArtifactResolutionRequest(); request.setArtifact(artifact); request.setRemoteRepositories(remoteRepositories); request.setLocalRepository(localRepository); resolver.resolve(request); JarInputStream jis = new JarInputStream(new FileInputStream(artifact.getFile())); ContainerManifest man = jis.getContainerManifest(); String bsn = man.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME); if (bsn != null) { answer.setType("bundle"); } else { // Try to find a matching servicemix bundle for it /* Map<String, String> bundles = getAllServiceMixBundles(); getLog().debug("Trying to find a matching bundle for " + artifact); String match = bundles.get(artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion()); if (match != null) { String[] parts = match.split(":"); answer.setGroupId(parts[0]); answer.setArtifactId(parts[1]); answer.setVersion(parts[2]); getLog().info("Replacing artifact " + artifact + " with servicemix bundle " + match); } */ } } catch (Exception e) { getLog().debug("Error checking artifact type for " + artifact, e); } } } answer.setOptional(artifact.isOptional()); String type = answer.getType(); if (type != null && type.equals("pom")) { getLog().debug("Ignoring pom.xml for " + answer); return null; } int state = node.getState(); if (state != DependencyNode.INCLUDED) { getLog().debug("Ignoring " + node); return null; } if (isWarProject()) { if (scope != null && !scope.equals("provided")) { getLog().debug("WAR packaging so ignoring non-provided scope " + scope + " for " + node); return null; } } List children = node.getChildren(); for (Object child : children) { if (child instanceof DependencyNode) { DependencyNode childNode = (DependencyNode) child; if (childNode.getState() == DependencyNode.INCLUDED) { String childScope = childNode.getArtifact().getScope(); if (!"test".equals(childScope) && !"provided".equals(childScope)) { DependencyDTO childDTO = buildFrom(childNode); if (childDTO != null) { answer.addChild(childDTO); } } else { getLog().debug( "Ignoring artifact " + childNode.getArtifact() + " with scope " + childScope); } } } } return answer; } return null; } private synchronized Map<String, String> getAllServiceMixBundles() throws InterruptedException { if (servicemixBundles == null) { servicemixBundles = doGetAllServiceMixBundles(); } return servicemixBundles; } private Map<String, String> servicemixBundles; private Map<String, String> doGetAllServiceMixBundles() throws InterruptedException { getLog().info("Retrieving ServiceMix bundles on maven central"); final Map<String, String> bundles = new HashMap<String, String>(); final ExecutorService executor = Executors.newCachedThreadPool(); try { String md = IOUtil.toString( new URL("http://central.maven.org/maven2/org/apache/servicemix/bundles/").openStream()); Matcher matcher = Pattern.compile("<a href=\"(org\\.apache\\.servicemix\\.bundles\\.[^\"]*)/\">") .matcher(md); while (matcher.find()) { final String artifactId = matcher.group(1); executor.execute(new Runnable() { @Override public void run() { try { String mda = IOUtil.toString(new URL( "http://central.maven.org/maven2/org/apache/servicemix/bundles/" + artifactId) .openStream()); Matcher matcher = Pattern.compile("<a href=\"([^\\.][^\"]*)/\">").matcher(mda); while (matcher.find()) { final String version = matcher.group(1); executor.execute(new Runnable() { @Override public void run() { try { String pom = IOUtil.toString(new URL( "http://central.maven.org/maven2/org/apache/servicemix/bundles/" + artifactId + "/" + version + "/" + artifactId + "-" + version + ".pom").openStream()); String pkgGroupId = extract(pom, "<pkgGroupId>(.*)</pkgGroupId>"); String pkgArtifactId = extract(pom, "<pkgArtifactId>(.*)</pkgArtifactId>"); String pkgVersion = extract(pom, "<pkgVersion>(.*)</pkgVersion>"); if (pkgGroupId != null && pkgArtifactId != null && pkgVersion != null) { String key = pkgGroupId + ":" + pkgArtifactId + ":" + pkgVersion; getLog().info("Found ServiceMix bundle for " + key + " in version " + version); synchronized (bundles) { String cur = bundles.get(key); if (cur == null) { bundles.put(key, "org.apache.servicemix.bundles:" + artifactId + ":" + version); } else { int v1 = extractBundleRelease(cur); int v2 = extractBundleRelease(version); if (v2 > v1) { bundles.put(key, "org.apache.servicemix.bundles:" + artifactId + ":" + version); } } } } } catch (IOException e) { getLog().warn("Error retrieving ServiceMix bundles list", e); } } }); } } catch (IOException e) { getLog().warn("Error retrieving ServiceMix bundles list", e); } } }); } executor.awaitTermination(5, TimeUnit.MINUTES); } catch (IOException e) { getLog().warn("Error retrieving ServiceMix bundles list", e); } return bundles; } private int extractBundleRelease(String version) { int i0 = version.lastIndexOf('_'); int i1 = version.lastIndexOf('-'); int i = Math.max(i0, i1); if (i > 0) { return Integer.parseInt(version.substring(i + 1)); } return -1; } private String extract(String string, String regexp) { Matcher matcher = Pattern.compile(regexp).matcher(string); return matcher.find() ? matcher.group(1) : null; } /** * Returns true if this project builds a war */ protected boolean isWarProject() { if (project != null) { String packaging = project.getPackaging(); return packaging != null && packaging.equals("war"); } return false; } protected void walkTree(DependencyNode node, int level) { if (node == null) { getLog().warn("Null node!"); return; } getLog().info(indent(level) + node.getArtifact()); List children = node.getChildren(); for (Object child : children) { if (child instanceof DependencyNode) { walkTree((DependencyNode) child, level + 1); } else { getLog().warn("Unknown class " + child.getClass()); } } } protected String indent(int level) { StringBuilder builder = new StringBuilder(); while (level-- > 0) { builder.append(" "); } return builder.toString(); } /** * Gets the artifact filter to use when resolving the dependency tree. * * @return the artifact filter */ private ArtifactFilter createResolvingArtifactFilter() { ArtifactFilter filter; if (scope != null) { getLog().debug("+ Resolving dependency tree for scope '" + scope + "'"); filter = new ScopeArtifactFilter(scope); } else { filter = null; } return filter; } protected void createAggregatedZip(List<MavenProject> reactorProjectList, File projectBaseDir, File projectBuildDir, String reactorProjectOutputPath, File projectOutputFile, boolean includeReadMe, List<MavenProject> pomZipProjects) throws IOException { projectBuildDir.mkdirs(); for (MavenProject reactorProject : reactorProjectList) { // ignoreProject the execution root which just aggregates stuff if (!reactorProject.isExecutionRoot()) { Log log = getLog(); combineProfileFilesToFolder(reactorProject, projectBuildDir, log, reactorProjectOutputPath); } } // we may want to include readme files for pom projects if (includeReadMe) { Map<String, File> pomNames = new HashMap<String, File>(); for (MavenProject pomProjects : pomZipProjects) { File src = pomProjects.getFile().getParentFile(); // must include first dir as prefix String root = projectBaseDir.getName(); String relativePath = Files.getRelativePath(projectBaseDir, pomProjects.getBasedir()); relativePath = root + File.separator + relativePath; // we must use dot instead of dashes in profile paths relativePath = pathToProfilePath(relativePath); File outDir = new File(projectBuildDir, relativePath); File copiedFile = copyReadMe(src, outDir); if (copiedFile != null) { String key = getReadMeFileKey(relativePath); pomNames.put(key, copiedFile); } } if (replaceReadmeLinksPrefix != null) { // now parse each readme file and replace github links for (Map.Entry<String, File> entry : pomNames.entrySet()) { File file = entry.getValue(); String key = entry.getKey(); boolean changed = false; List<String> lines = Files.readLines(file); for (int i = 0; i < lines.size(); i++) { String line = lines.get(i); String newLine = replaceGithubLinks(pomNames.keySet(), key, line); if (newLine != null) { lines.set(i, newLine); changed = true; } } if (changed) { Files.writeLines(file, lines); getLog().info("Replaced github links to fabric profiles in reaadme file: " + file); } } } } Zips.createZipFile(getLog(), projectBuildDir, projectOutputFile); String relativePath = Files.getRelativePath(projectBaseDir, projectOutputFile); while (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } getLog().info("Created profile zip file: " + relativePath); } private static String pathToProfilePath(String path) { // TODO: use some common fabric util return path.replace('-', '.'); } protected static File copyReadMe(File src, File profileBuildDir) throws IOException { File[] files = src.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.toLowerCase(Locale.ENGLISH).startsWith("readme."); } }); if (files != null && files.length == 1) { File readme = files[0]; File outFile = new File(profileBuildDir, readme.getName()); Files.copy(readme, outFile); return outFile; } return null; } private String getReadMeFileKey(String relativePath) { String answer = relativePath; if (Strings.isNullOrBlank(answer)) { return "<root>"; } // remove leading path which can be either unix or windows style int pos = relativePath.indexOf('/'); int pos2 = relativePath.indexOf('\\'); if (pos > 0 && pos2 > 0) { pos = Math.max(pos, pos2); } else if (pos2 > 0) { pos = pos2; } if (pos > -1) { answer = relativePath.substring(pos); } // and remove any leading path separators answer = Files.stripLeadingSeparator(answer); if (Strings.isNullOrBlank(answer)) { answer = "<root>"; } return answer; } /** * Replacing github links with fabric profiles links for our quickstarts */ protected String replaceGithubLinks(Set<String> names, String relativePath, String line) { boolean changed = false; Pattern pattern = Pattern.compile("\\[(.*?)\\]\\((.*?)\\)"); Matcher matcher = pattern.matcher(line); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String s2 = matcher.group(2); if (s2.startsWith("http:") || s2.startsWith("https:")) { // leave it as-is matcher.appendReplacement(sb, "[$1]($2)"); } else { if (names.contains(s2) || names.contains(relativePath + s2) || names.contains(relativePath + "/" + s2)) { // need to ensure path is profile friendly s2 = pathToProfilePath(s2); if (relativePath != null && !"<root>".equals(relativePath)) { s2 = addToPath(relativePath, s2); } // its a directory matcher.appendReplacement(sb, "[$1](" + replaceReadmeLinksPrefix + s2 + ")"); } else { // need to ensure path is profile friendly s2 = pathToProfilePath(s2); if (relativePath != null && !"<root>".equals(relativePath)) { s2 = addToPath(relativePath, s2); } // its a profile matcher.appendReplacement(sb, "[$1](" + replaceReadmeLinksPrefix + s2 + ".profile)"); } changed = true; } } matcher.appendTail(sb); if (changed) { return sb.toString(); } else { return null; } } private static String addToPath(String path, String add) { if (add.startsWith("/") || path.endsWith("/")) { return path + add; } else { return path + "/" + add; } } }