Java tutorial
package org.apache.maven.plugins.help; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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. */ import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.MarshallingContext; import com.thoughtworks.xstream.converters.collections.PropertiesConverter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import org.apache.commons.lang.ClassUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.factory.ArtifactFactory; import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.artifact.resolver.ArtifactNotFoundException; import org.apache.maven.artifact.resolver.ArtifactResolutionException; import org.apache.maven.artifact.resolver.ArtifactResolver; import org.apache.maven.execution.MavenSession; import org.apache.maven.model.Dependency; import org.apache.maven.model.Model; import org.apache.maven.model.io.xpp3.MavenXpp3Writer; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecution; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.PluginParameterExpressionEvaluator; import org.apache.maven.plugin.descriptor.MojoDescriptor; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectBuilder; import org.apache.maven.project.ProjectBuildingException; import org.apache.maven.project.path.PathTranslator; import org.apache.maven.settings.Settings; import org.apache.maven.settings.io.xpp3.SettingsXpp3Writer; import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; import org.codehaus.plexus.components.interactivity.InputHandler; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.StringUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.TreeMap; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; /** * Evaluates Maven expressions given by the user in an interactive mode. * * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a> * @version $Id$ * @since 2.1 */ @Mojo(name = "evaluate", requiresProject = false) public class EvaluateMojo extends AbstractMojo { // ---------------------------------------------------------------------- // Mojo components // ---------------------------------------------------------------------- /** * Maven Artifact Factory component. */ @Component private ArtifactFactory artifactFactory; /** * Input handler, needed for command line handling. */ @Component private InputHandler inputHandler; /** * Maven Project Builder component. */ @Component private MavenProjectBuilder mavenProjectBuilder; /** */ @Component private PathTranslator pathTranslator; /** * Artifact Resolver component. */ @Component private ArtifactResolver resolver; /** */ @Component private LoggerRetriever loggerRetriever; // ---------------------------------------------------------------------- // Mojo parameters // ---------------------------------------------------------------------- /** * An artifact for evaluating Maven expressions. * <br/> * <b>Note</b>: Should respect the Maven format, i.e. <code>groupId:artifactId[:version][:classifier]</code>. */ @Parameter(property = "artifact") private String artifact; /** * An expression to evaluate instead of prompting. Note that this <i>must not</i> include the surrounding ${...}. */ @Parameter(property = "expression") private String expression; /** * Local Repository. */ @Parameter(defaultValue = "${localRepository}", required = true, readonly = true) protected ArtifactRepository localRepository; /** * The current Maven project or the super pom. */ @Component protected MavenProject project; /** * Remote repositories used for the project. */ @Parameter(defaultValue = "${project.remoteArtifactRepositories}", required = true, readonly = true) private List<ArtifactRepository> remoteRepositories; /** * The system settings for Maven. */ @Component protected Settings settings; /** * The current Maven session. */ @Component private MavenSession session; // ---------------------------------------------------------------------- // Instance variables // ---------------------------------------------------------------------- /** lazy loading evaluator variable */ private PluginParameterExpressionEvaluator evaluator; /** lazy loading xstream variable */ private XStream xstream; // ---------------------------------------------------------------------- // Public methods // ---------------------------------------------------------------------- /** {@inheritDoc} */ public void execute() throws MojoExecutionException, MojoFailureException { if (expression == null && !settings.isInteractiveMode()) { getLog().error("Maven is configured to NOT interact with the user for input. " + "This Mojo requires that 'interactiveMode' in your settings file is flag to 'true'."); return; } validateParameters(); if (StringUtils.isNotEmpty(artifact)) { Artifact artifactObj = getArtifact(artifact); try { project = getMavenProject(artifactObj); } catch (ProjectBuildingException e) { throw new MojoExecutionException("Unable to get the POM for the artifact '" + artifact + "'. Verify the artifact parameter."); } } if (expression == null) { while (true) { getLog().info("Enter the Maven expression i.e. ${project.groupId} or 0 to exit?:"); try { String userExpression = inputHandler.readLine(); if (userExpression == null || userExpression.toLowerCase(Locale.ENGLISH).equals("0")) { break; } handleResponse(userExpression); } catch (IOException e) { throw new MojoExecutionException("Unable to read from standard input.", e); } } } else { handleResponse("${" + expression + "}"); } } // ---------------------------------------------------------------------- // Private methods // ---------------------------------------------------------------------- /** * Validate Mojo parameters. */ private void validateParameters() { if (artifact == null) { // using project if found or super-pom getLog().info("No artifact parameter specified, using '" + project.getId() + "' as project."); } } /** * @param artifactString should respect the format <code>groupId:artifactId[:version][:classifier]</code> * @return the <code>Artifact</code> object for the <code>artifactString</code> parameter. * @throws MojoExecutionException if the <code>artifactString</code> doesn't respect the format. */ private Artifact getArtifact(String artifactString) throws MojoExecutionException { if (StringUtils.isEmpty(artifactString)) { throw new IllegalArgumentException("artifact parameter could not be empty"); } String groupId; // required String artifactId; // required String version; // optional String classifier = null; // optional String[] artifactParts = artifactString.split(":"); switch (artifactParts.length) { case 2: groupId = artifactParts[0]; artifactId = artifactParts[1]; version = Artifact.LATEST_VERSION; break; case 3: groupId = artifactParts[0]; artifactId = artifactParts[1]; version = artifactParts[2]; break; case 4: groupId = artifactParts[0]; artifactId = artifactParts[1]; version = artifactParts[2]; classifier = artifactParts[3]; break; default: throw new MojoExecutionException("The artifact parameter '" + artifactString + "' should be conform to: " + "'groupId:artifactId[:version][:classifier]'."); } if (StringUtils.isNotEmpty(classifier)) { return artifactFactory.createArtifactWithClassifier(groupId, artifactId, version, "jar", classifier); } return artifactFactory.createArtifact(groupId, artifactId, version, Artifact.SCOPE_COMPILE, "jar"); } /** * @param artifactObj not null * @return the POM for the given artifact. * @throws MojoExecutionException if the artifact has a system scope. * @throws ProjectBuildingException when building pom. */ private MavenProject getMavenProject(Artifact artifactObj) throws MojoExecutionException, ProjectBuildingException { if (Artifact.SCOPE_SYSTEM.equals(artifactObj.getScope())) { throw new MojoExecutionException("System artifact is not be handled."); } Artifact copyArtifact = ArtifactUtils.copyArtifact(artifactObj); if (!"pom".equals(copyArtifact.getType())) { copyArtifact = artifactFactory.createProjectArtifact(copyArtifact.getGroupId(), copyArtifact.getArtifactId(), copyArtifact.getVersion(), copyArtifact.getScope()); } return mavenProjectBuilder.buildFromRepository(copyArtifact, remoteRepositories, localRepository); } /** * @return a lazy loading evaluator object. * @throws MojoExecutionException if any * @throws MojoFailureException if any reflection exceptions occur or missing components. * @see #getMojoDescriptor(String, MavenSession, MavenProject, String, boolean, boolean) */ private PluginParameterExpressionEvaluator getEvaluator() throws MojoExecutionException, MojoFailureException { if (evaluator == null) { MojoDescriptor mojoDescriptor = HelpUtil.getMojoDescriptor("help:evaluate", session, project, "help:evaluate", true, false); MojoExecution mojoExecution = new MojoExecution(mojoDescriptor); evaluator = new PluginParameterExpressionEvaluator(session, mojoExecution, pathTranslator, loggerRetriever.getLogger(), project, session.getExecutionProperties()); } return evaluator; } /** * @param expr the user expression asked. * @throws MojoExecutionException if any * @throws MojoFailureException if any reflection exceptions occur or missing components. */ private void handleResponse(String expr) throws MojoExecutionException, MojoFailureException { StringBuilder response = new StringBuilder(); Object obj; try { obj = getEvaluator().evaluate(expr); } catch (ExpressionEvaluationException e) { throw new MojoExecutionException("Error when evaluating the Maven expression", e); } if (obj != null && expr.equals(obj.toString())) { getLog().warn("The Maven expression was invalid. Please use a valid expression."); return; } // handle null if (obj == null) { response.append("null object or invalid expression"); } // handle primitives objects else if (obj instanceof String) { response.append(obj.toString()); } else if (obj instanceof Boolean) { response.append(obj.toString()); } else if (obj instanceof Byte) { response.append(obj.toString()); } else if (obj instanceof Character) { response.append(obj.toString()); } else if (obj instanceof Double) { response.append(obj.toString()); } else if (obj instanceof Float) { response.append(obj.toString()); } else if (obj instanceof Integer) { response.append(obj.toString()); } else if (obj instanceof Long) { response.append(obj.toString()); } else if (obj instanceof Short) { response.append(obj.toString()); } // handle specific objects else if (obj instanceof File) { File f = (File) obj; response.append(f.getAbsolutePath()); } // handle Maven pom object else if (obj instanceof MavenProject) { MavenProject projectAsked = (MavenProject) obj; StringWriter sWriter = new StringWriter(); MavenXpp3Writer pomWriter = new MavenXpp3Writer(); try { pomWriter.write(sWriter, projectAsked.getModel()); } catch (IOException e) { throw new MojoExecutionException("Error when writing pom", e); } response.append(sWriter.toString()); } // handle Maven Settings object else if (obj instanceof Settings) { Settings settingsAsked = (Settings) obj; StringWriter sWriter = new StringWriter(); SettingsXpp3Writer settingsWriter = new SettingsXpp3Writer(); try { settingsWriter.write(sWriter, settingsAsked); } catch (IOException e) { throw new MojoExecutionException("Error when writing settings", e); } response.append(sWriter.toString()); } else { // others Maven objects response.append(toXML(expr, obj)); } getLog().info("\n" + response.toString()); } /** * @param expr the user expression. * @param obj a not null. * @return the XML for the given object. */ private String toXML(String expr, Object obj) { XStream currentXStream = getXStream(); // beautify list if (obj instanceof List) { List<?> list = (List<?>) obj; if (list.size() > 0) { Object elt = list.iterator().next(); String name = StringUtils.lowercaseFirstLetter(ClassUtils.getShortClassName(elt.getClass())); currentXStream.alias(pluralize(name), List.class); } else { // try to detect the alias from question if (expr.indexOf('.') != -1) { String name = expr.substring(expr.indexOf('.') + 1, expr.indexOf('}')); currentXStream.alias(name, List.class); } } } return currentXStream.toXML(obj); } /** * @return lazy loading xstream object. */ private XStream getXStream() { if (xstream == null) { xstream = new XStream(); addAlias(xstream); // handle Properties a la Maven xstream.registerConverter(new PropertiesConverter() { /** {@inheritDoc} */ public boolean canConvert(@SuppressWarnings("rawtypes") Class type) { return Properties.class == type; } /** {@inheritDoc} */ public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { Properties properties = (Properties) source; Map<?, ?> map = new TreeMap<Object, Object>(properties); // sort for (Map.Entry<?, ?> entry : map.entrySet()) { writer.startNode(entry.getKey().toString()); writer.setValue(entry.getValue().toString()); writer.endNode(); } } }); } return xstream; } /** * @param xstreamObject not null */ private void addAlias(XStream xstreamObject) { try { addAlias(xstreamObject, getMavenModelJarFile(), "org.apache.maven.model"); addAlias(xstreamObject, getMavenSettingsJarFile(), "org.apache.maven.settings"); } catch (MojoExecutionException e) { if (getLog().isDebugEnabled()) { getLog().debug("MojoExecutionException: " + e.getMessage(), e); } } catch (ArtifactResolutionException e) { if (getLog().isDebugEnabled()) { getLog().debug("ArtifactResolutionException: " + e.getMessage(), e); } } catch (ArtifactNotFoundException e) { if (getLog().isDebugEnabled()) { getLog().debug("ArtifactNotFoundException: " + e.getMessage(), e); } } catch (ProjectBuildingException e) { if (getLog().isDebugEnabled()) { getLog().debug("ProjectBuildingException: " + e.getMessage(), e); } } // TODO need to handle specific Maven objects like DefaultArtifact? } /** * @param xstreamObject not null * @param jarFile not null * @param packageFilter a package name to filter. */ private void addAlias(XStream xstreamObject, File jarFile, String packageFilter) { JarInputStream jarStream = null; try { jarStream = new JarInputStream(new FileInputStream(jarFile)); JarEntry jarEntry = jarStream.getNextJarEntry(); while (jarEntry != null) { if (jarEntry.getName().toLowerCase(Locale.ENGLISH).endsWith(".class")) { String name = jarEntry.getName().substring(0, jarEntry.getName().indexOf(".")); name = name.replaceAll("/", "\\."); if (name.contains(packageFilter)) { try { Class<?> clazz = ClassUtils.getClass(name); String alias = StringUtils.lowercaseFirstLetter(ClassUtils.getShortClassName(clazz)); xstreamObject.alias(alias, clazz); if (!clazz.equals(Model.class)) { xstreamObject.omitField(clazz, "modelEncoding"); // unnecessary field } } catch (ClassNotFoundException e) { getLog().error(e); } } } jarStream.closeEntry(); jarEntry = jarStream.getNextJarEntry(); } } catch (IOException e) { if (getLog().isDebugEnabled()) { getLog().debug("IOException: " + e.getMessage(), e); } } finally { IOUtil.close(jarStream); } } /** * @return the <code>org.apache.maven:maven-model</code> artifact jar file in the local repository. * @throws MojoExecutionException if any * @throws ProjectBuildingException if any * @throws ArtifactResolutionException if any * @throws ArtifactNotFoundException if any */ private File getMavenModelJarFile() throws MojoExecutionException, ProjectBuildingException, ArtifactResolutionException, ArtifactNotFoundException { return getArtifactFile(true); } /** * @return the <code>org.apache.maven:maven-settings</code> artifact jar file in the local repository. * @throws MojoExecutionException if any * @throws ProjectBuildingException if any * @throws ArtifactResolutionException if any * @throws ArtifactNotFoundException if any */ private File getMavenSettingsJarFile() throws MojoExecutionException, ProjectBuildingException, ArtifactResolutionException, ArtifactNotFoundException { return getArtifactFile(false); } /** * * @param isPom <code>true</code> to lookup the <code>maven-model</code> artifact jar, <code>false</code> to * lookup the <code>maven-settings</code> artifact jar. * @return the <code>org.apache.maven:maven-model|maven-settings</code> artifact jar file for this current * HelpPlugin pom. * @throws MojoExecutionException if any * @throws ProjectBuildingException if any * @throws ArtifactResolutionException if any * @throws ArtifactNotFoundException if any */ private File getArtifactFile(boolean isPom) throws MojoExecutionException, ProjectBuildingException, ArtifactResolutionException, ArtifactNotFoundException { @SuppressWarnings("unchecked") List<Dependency> dependencies = getHelpPluginPom().getDependencies(); for (Dependency depependency : dependencies) { if (!(depependency.getGroupId().equals("org.apache.maven"))) { continue; } if (isPom) { if (!(depependency.getArtifactId().equals("maven-model"))) { continue; } } else { if (!(depependency.getArtifactId().equals("maven-settings"))) { continue; } } Artifact mavenArtifact = getArtifact(depependency.getGroupId() + ":" + depependency.getArtifactId() + ":" + depependency.getVersion()); resolver.resolveAlways(mavenArtifact, remoteRepositories, localRepository); return mavenArtifact.getFile(); } throw new MojoExecutionException("Unable to find the 'org.apache.maven:" + (isPom ? "maven-model" : "maven-settings") + "' artifact"); } /** * @return the Maven POM for the current help plugin * @throws MojoExecutionException if any * @throws ProjectBuildingException if any */ private MavenProject getHelpPluginPom() throws MojoExecutionException, ProjectBuildingException { String resource = "META-INF/maven/org.apache.maven.plugins/maven-help-plugin/pom.properties"; InputStream resourceAsStream = EvaluateMojo.class.getClassLoader().getResourceAsStream(resource); Artifact helpPluginArtifact = null; if (resourceAsStream != null) { Properties properties = new Properties(); try { properties.load(resourceAsStream); } catch (IOException e) { if (getLog().isDebugEnabled()) { getLog().debug("IOException: " + e.getMessage(), e); } } String artifactString = properties.getProperty("groupId", "unknown") + ":" + properties.getProperty("artifactId", "unknown") + ":" + properties.getProperty("version", "unknown"); helpPluginArtifact = getArtifact(artifactString); } if (helpPluginArtifact == null) { throw new MojoExecutionException("The help plugin artifact was not found."); } return getMavenProject(helpPluginArtifact); } /** * @param name not null * @return the plural of the name */ private static String pluralize(String name) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("name is required"); } if (name.endsWith("y")) { return name.substring(0, name.length() - 1) + "ies"; } else if (name.endsWith("s")) { return name; } else { return name + "s"; } } }