Java tutorial
/** * 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. */ package org.apache.camel.maven; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import org.apache.camel.impl.DefaultPackageScanClassResolver; import org.apache.camel.util.ObjectHelper; import org.apache.maven.artifact.Artifact; 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.artifact.versioning.InvalidVersionSpecificationException; import org.apache.maven.artifact.versioning.VersionRange; import org.apache.maven.doxia.module.xhtml.decoration.render.RenderingContext; import org.apache.maven.doxia.sink.Sink; import org.apache.maven.doxia.site.decoration.Body; import org.apache.maven.doxia.site.decoration.DecorationModel; import org.apache.maven.doxia.site.decoration.Skin; import org.apache.maven.doxia.siterenderer.Renderer; import org.apache.maven.doxia.siterenderer.RendererException; import org.apache.maven.doxia.siterenderer.SiteRenderingContext; import org.apache.maven.doxia.siterenderer.sink.SiteRendererSink; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.project.MavenProject; import org.apache.maven.reporting.AbstractMavenReport; import org.apache.maven.reporting.MavenReportException; /** * Generate report of available type conversions. * * @goal converters-report * @requiresDependencyResolution runtime * @phase verify */ public class ConvertersMojo extends AbstractMavenReport { private static final String WIKI_TYPECONVERER_URL = "http://camel.apache.org/type-converter.html"; private static final String CONVERTER_TYPE_STATIC = "org.apache.camel.impl.converter.StaticMethodTypeConverter"; private static final String CONVERTER_TYPE_INSTANCE = "org.apache.camel.impl.converter.InstanceMethodTypeConverter"; private static final String REPORT_METHOD_STATIC = "STATIC"; private static final String REPORT_METHOD_INSTANCE = "INSTANCE"; private static final String REPORT_METHOD_UNKNOWN = "UNKNOWN"; /** * Remote repositories which will be searched for source attachments. * * @parameter expression="${project.remoteArtifactRepositories}" * @required * @readonly */ protected List remoteArtifactRepositories; /** * Local maven repository. * * @parameter expression="${localRepository}" * @required * @readonly */ protected ArtifactRepository localRepository; /** * The component that is used to resolve additional artifacts required. * * @component */ protected ArtifactResolver artifactResolver; /** * The component used for creating artifact instances. * * @component */ protected ArtifactFactory artifactFactory; /** * Base output directory for reports. * * @parameter default-value="${project.build.directory}/site" * @readonly * @required */ private File outputDirectory; /** * Reference to Maven 2 Project. * * @parameter expression="${project}" * @required * @readonly */ private MavenProject project; /** * Doxia SiteRenderer. * * @component */ private Renderer renderer; /** * Gets resource bundle for given locale. * * @param locale locale * @return resource bundle */ protected ResourceBundle getBundle(final Locale locale) { return ResourceBundle.getBundle("camel-maven-plugin", locale, this.getClass().getClassLoader()); } /** * @param locale * report locale. * @return report description. * @see org.apache.maven.reporting.MavenReport#getDescription(Locale) */ public String getDescription(final Locale locale) { return getBundle(locale).getString("report.converters.description"); } /** * @see org.apache.maven.reporting.MavenReport#getName(Locale) */ public String getName(final Locale locale) { return getBundle(locale).getString("report.converters.name"); } public String getOutputName() { return "camel-converters"; } @Override protected String getOutputDirectory() { return outputDirectory.getAbsolutePath(); } @Override protected MavenProject getProject() { return this.project; } @Override protected Renderer getSiteRenderer() { return renderer; } public void execute() throws MojoExecutionException { if (!canGenerateReport()) { return; } try { DecorationModel model = new DecorationModel(); model.setBody(new Body()); Map<String, Object> attributes = new HashMap<String, Object>(); attributes.put("outputEncoding", "UTF-8"); attributes.put("project", project); Locale locale = Locale.getDefault(); SiteRenderingContext siteContext = renderer.createContextForSkin(getSkinArtifactFile(model), attributes, model, getName(locale), locale); RenderingContext context = new RenderingContext(getReportOutputDirectory(), getOutputName() + ".html"); SiteRendererSink sink = new SiteRendererSink(context); generate(sink, locale); Writer writer = new OutputStreamWriter( new FileOutputStream(new File(getReportOutputDirectory(), getOutputName() + ".html")), "UTF-8"); renderer.generateDocument(writer, sink, siteContext); renderer.copyResources(siteContext, new File(project.getBasedir(), "src/site/resources"), outputDirectory); } catch (IOException e) { throw new MojoExecutionException("Error copying resources.", e); } catch (RendererException e) { throw new MojoExecutionException("Error while rendering report.", e); } catch (MojoFailureException e) { throw new MojoExecutionException("Cannot find skin artifact for report.", e); } catch (MavenReportException e) { throw new MojoExecutionException("Error generating report.", e); } } @Override protected void executeReport(Locale locale) throws MavenReportException { if (!createOutputDirectory(outputDirectory)) { throw new MavenReportException( "Failed to create report directory " + outputDirectory.getAbsolutePath()); } ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); try { // TODO: this badly needs some refactoring // mojo.createClassLoader creates a URLClassLoader with whatever is in // ${project.testClasspathElements}, reason why we don't see all converters // in the report. First we need a list of classpath elements the user // could customize via plugin configuration, and elements of that list // be added to the URLClassLoader. This should also be factored out into // a utility class. // TODO: there is some interference with the site plugin that needs investigated. List<?> list = project.getTestClasspathElements(); EmbeddedMojo mojo = new EmbeddedMojo(); mojo.setClasspathElements(list); ClassLoader newClassLoader = mojo.createClassLoader(oldClassLoader); Thread.currentThread().setContextClassLoader(newClassLoader); ReportingTypeConverterLoader loader = new ReportingTypeConverterLoader( new DefaultPackageScanClassResolver()); ReportingTypeConverterRegistry registry = new ReportingTypeConverterRegistry(); loader.load(registry); getLog().error("FOUND type mapping; count = " + loader.getTypeConversions().length); String[] errors = registry.getErrors(); for (String error : errors) { getLog().error(error); } generateReport(getSink(), locale, loader.getTypeConversions()); } catch (Exception e) { throw new MavenReportException("Failed to generate TypeConverters report", e); } finally { Thread.currentThread().setContextClassLoader(oldClassLoader); } } private boolean createOutputDirectory(final File outputDir) { if (outputDir.exists()) { if (!outputDir.isDirectory()) { getLog().error("File with same name already exists: " + outputDir.getAbsolutePath()); return false; } } else { if (!outputDir.mkdirs()) { getLog().error("Cannot make output directory at: " + outputDir.getAbsolutePath()); return false; } } return true; } private File getSkinArtifactFile(DecorationModel decoration) throws MojoFailureException { Skin skin = decoration.getSkin(); if (skin == null) { skin = Skin.getDefaultSkin(); } String version = skin.getVersion(); Artifact artifact; try { if (version == null) { version = Artifact.RELEASE_VERSION; } VersionRange versionSpec = VersionRange.createFromVersionSpec(version); artifact = artifactFactory.createDependencyArtifact(skin.getGroupId(), skin.getArtifactId(), versionSpec, "jar", null, null); artifactResolver.resolve(artifact, remoteArtifactRepositories, localRepository); return artifact.getFile(); } catch (InvalidVersionSpecificationException e) { throw new MojoFailureException("The skin version '" + version + "' is not valid: " + e.getMessage()); } catch (ArtifactResolutionException e) { throw new MojoFailureException("Unable to fink skin: " + e.getMessage()); } catch (ArtifactNotFoundException e) { throw new MojoFailureException("The skin does not exist: " + e.getMessage()); } } private String converterType(String converterClassName) { if (CONVERTER_TYPE_STATIC.equals(converterClassName)) { return REPORT_METHOD_STATIC; } else if (CONVERTER_TYPE_INSTANCE.equals(converterClassName)) { return REPORT_METHOD_INSTANCE; } else { return REPORT_METHOD_UNKNOWN; } } private void generateReport(Sink sink, Locale locale, ReportingTypeConverterLoader.TypeMapping[] mappings) throws MojoExecutionException { beginReport(sink, locale); Set<String> classes; Map<String, Set<String>> packages = new TreeMap<String, Set<String>>(); Class<?> prevFrom = null; Class<?> prevTo = null; sink.table(); tableHeader(sink, locale); for (ReportingTypeConverterLoader.TypeMapping mapping : mappings) { boolean ignored = false; Class<?> from = mapping.getFromType(); Class<?> to = mapping.getToType(); if (ObjectHelper.equal(from, prevFrom) && ObjectHelper.equal(to, prevTo)) { ignored = true; } prevFrom = from; prevTo = to; Method method = mapping.getMethod(); Class<?> methodClass = method.getDeclaringClass(); String packageName = methodClass.getPackage().getName(); if (packages.containsKey(packageName)) { classes = packages.get(packageName); } else { classes = new TreeSet<String>(); packages.put(packageName, classes); } classes.add(methodClass.getName()); if (ignored) { sink.italic(); this.tableRow(sink, from.getSimpleName(), to.getSimpleName(), method.getName(), methodClass, mapping.getConverterType().getName()); sink.italic_(); } else { this.tableRow(sink, from.getSimpleName(), to.getSimpleName(), method.getName(), methodClass, mapping.getConverterType().getName()); } } sink.table_(); generatePackageReport(sink, packages); endReport(sink); } private void generatePackageReport(Sink sink, Map<String, Set<String>> packages) { for (Map.Entry<String, Set<String>> entry : packages.entrySet()) { sink.section2(); sink.sectionTitle2(); sink.text(entry.getKey()); sink.sectionTitle2_(); sink.list(); for (String clazz : entry.getValue()) { sink.listItem(); sink.anchor(clazz); sink.text(clazz); sink.anchor_(); sink.listItem_(); } sink.list_(); sink.section2_(); } } private void beginReport(Sink sink, Locale locale) { String title = getBundle(locale).getString("report.converters.report.title"); String header = getBundle(locale).getString("report.converters.report.header"); String intro = getBundle(locale).getString("report.converters.report.intro"); String seealso = getBundle(locale).getString("report.converters.report.seealso"); sink.head(); sink.title(); sink.text(title); sink.title_(); sink.head_(); sink.body(); sink.section1(); sink.sectionTitle1(); sink.text(header); sink.sectionTitle1_(); sink.paragraph(); sink.text(intro); sink.paragraph_(); sink.paragraph(); sink.text(seealso); sink.list(); sink.listItem(); sink.link(WIKI_TYPECONVERER_URL); sink.text(WIKI_TYPECONVERER_URL); sink.link_(); sink.listItem_(); sink.list_(); sink.paragraph_(); } private void tableHeader(Sink sink, Locale locale) { String caption = getBundle(locale).getString("report.converters.report.table.caption"); String head1 = getBundle(locale).getString("report.converters.report.table.head1"); String head2 = getBundle(locale).getString("report.converters.report.table.head2"); String head3 = getBundle(locale).getString("report.converters.report.table.head3"); String head4 = getBundle(locale).getString("report.converters.report.table.head4"); String head5 = getBundle(locale).getString("report.converters.report.table.head5"); sink.tableCaption(); sink.text(caption); sink.tableCaption_(); sink.tableRow(); sink.tableHeaderCell(); sink.text(head1); sink.tableHeaderCell_(); sink.tableHeaderCell(); sink.text(head2); sink.tableHeaderCell_(); sink.tableHeaderCell(); sink.text(head3); sink.tableHeaderCell_(); sink.tableHeaderCell(); sink.text(head4); sink.tableHeaderCell_(); sink.tableHeaderCell(); sink.text(head5); sink.tableHeaderCell_(); sink.tableRow(); } private void tableRow(Sink sink, String from, String to, String method, Class<?> clazz, String type) { sink.tableRow(); sink.tableCell(); sink.text(from); sink.tableCell_(); sink.tableCell(); sink.text(to); sink.tableCell_(); sink.tableCell(); sink.text(method); sink.tableCell_(); sink.tableCell(); sink.link(clazz.getName()); sink.text(clazz.getSimpleName()); sink.link_(); sink.tableCell_(); sink.tableCell(); sink.text(converterType(type)); sink.tableCell_(); sink.tableRow(); } private void endReport(Sink sink) { sink.section1_(); sink.body_(); sink.flush(); sink.close(); } }