blob: a1aa5c40f689e0c21bab4cb49126dae1b025c03b [file] [log] [blame]
/*
* 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.uima.fit.maven;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Modifier;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.uima.analysis_engine.AnalysisEngineDescription;
import org.apache.uima.collection.CollectionReaderDescription;
import org.apache.uima.fit.factory.AnalysisEngineFactory;
import org.apache.uima.fit.factory.CollectionReaderFactory;
import org.apache.uima.fit.factory.TypeSystemDescriptionFactory;
import org.apache.uima.fit.maven.util.Util;
import org.apache.uima.resource.ResourceCreationSpecifier;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceSpecifier;
import org.apache.uima.resource.metadata.ProcessingResourceMetaData;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.codehaus.plexus.util.FileUtils;
import org.sonatype.plexus.build.incremental.BuildContext;
import org.xml.sax.SAXException;
/**
* Generate descriptor files for uimaFIT-based UIMA components.
*/
@Mojo(name = "generate", defaultPhase = LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution = ResolutionScope.COMPILE, requiresDependencyCollection = ResolutionScope.COMPILE)
public class GenerateDescriptorsMojo extends AbstractMojo {
@Component
private MavenProject project;
@Component
private BuildContext buildContext;
private ClassLoader componentLoader;
/**
* Path where the generated resources are written.
*/
@Parameter(defaultValue = "${project.build.directory}/classes", required = true)
private File outputDirectory;
/**
* Skip generation of META-INF/org.apache.uima.fit/components.txt
*/
@Parameter(defaultValue = "false", required = true)
private boolean skipComponentsManifest;
/**
* Source file encoding.
*/
@Parameter(defaultValue = "${project.build.sourceEncoding}", required = true)
private String encoding;
/**
* Fail on error.
*/
@Parameter(defaultValue = "true", required = true)
private boolean failOnError;
enum TypeSystemSerialization {
NONE, EMBEDDED
}
/**
* Mode of adding type systems found on the classpath via the uimaFIT detection mechanism at
* compile time to the generated descriptor. By default, no type systems are added.
*/
@Parameter(defaultValue = "NONE")
private TypeSystemSerialization addTypeSystemDescriptions;
@Override
public void execute() throws MojoExecutionException {
// add the generated sources to the build
if (!outputDirectory.exists()) {
outputDirectory.mkdirs();
buildContext.refresh(outputDirectory);
}
// Get the compiled classes from this project
String[] files = FileUtils.getFilesFromExtension(project.getBuild().getOutputDirectory(),
new String[] { "class" });
componentLoader = Util.getClassloader(project, getLog());
// List of components that is later written to META-INF/org.apache.uima.fit/components.txt
StringBuilder componentsManifest = new StringBuilder();
int countGenerated = 0;
for (String file : files) {
String base = file.substring(0, file.length() - 6);
String clazzPath = base.substring(project.getBuild().getOutputDirectory().length() + 1);
String clazzName = clazzPath.replace(File.separator, ".");
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
Class clazz = componentLoader.loadClass(clazzName);
// Make the componentLoader available to uimaFIT e.g. to resolve imports
Thread.currentThread().setContextClassLoader(componentLoader);
// Do not generate descriptors for abstract classes, they cannot be instantiated.
if (Modifier.isAbstract(clazz.getModifiers())) {
continue;
}
ResourceCreationSpecifier desc = null;
ProcessingResourceMetaData metadata = null;
switch (Util.getType(componentLoader, clazz)) {
case ANALYSIS_ENGINE:
AnalysisEngineDescription aeDesc = AnalysisEngineFactory.createEngineDescription(clazz);
metadata = aeDesc.getAnalysisEngineMetaData();
desc = aeDesc;
break;
case COLLECTION_READER:
CollectionReaderDescription crDesc = CollectionReaderFactory
.createReaderDescription(clazz);
metadata = crDesc.getCollectionReaderMetaData();
desc = crDesc;
default:
// Do nothing
}
if (desc != null) {
switch (addTypeSystemDescriptions) {
case EMBEDDED:
embedTypeSystems(metadata);
break;
case NONE: // fall-through
default:
// Do nothing
}
File out = new File(outputDirectory, clazzPath + ".xml");
out.getParentFile().mkdirs();
toXML(desc, out.getPath());
countGenerated++;
// Remember component
componentsManifest.append("classpath*:").append(clazzPath + ".xml").append('\n');
}
} catch (SAXException e) {
handleError("Cannot serialize descriptor for [" + clazzName + "]", e);
} catch (IOException e) {
handleError("Cannot write descriptor for [" + clazzName + "]", e);
} catch (ClassNotFoundException e) {
handleError("Cannot analyze class [" + clazzName + "]", e);
} catch (ResourceInitializationException e) {
handleError("Cannot generate descriptor for [" + clazzName + "]", e);
} finally {
Thread.currentThread().setContextClassLoader(classLoader);
}
}
getLog().info(
"Generated " + countGenerated + " descriptor" + (countGenerated != 1 ? "s." : "."));
// Write META-INF/org.apache.uima.fit/components.txt unless skipped and unless there are no
// components
if (!skipComponentsManifest && componentsManifest.length() > 0) {
File path = new File(outputDirectory, "META-INF/org.apache.uima.fit/components.txt");
FileUtils.mkdir(path.getParent());
try {
FileUtils.fileWrite(path.getPath(), encoding, componentsManifest.toString());
} catch (IOException e) {
handleError("Cannot write components manifest to [" + path + "]"
+ ExceptionUtils.getRootCauseMessage(e), e);
}
}
}
private void handleError(String message, Exception e) throws MojoExecutionException {
if (failOnError) {
throw new MojoExecutionException(message, e);
}
getLog().error(message, e);
}
private void embedTypeSystems(ProcessingResourceMetaData metadata)
throws ResourceInitializationException {
TypeSystemDescriptionFactory.forceTypeDescriptorsScan();
TypeSystemDescription tsDesc = TypeSystemDescriptionFactory.createTypeSystemDescription();
metadata.setTypeSystem(tsDesc);
}
/**
* Save descriptor XML to file system.
*/
private void toXML(ResourceSpecifier aDesc, String aFilename) throws SAXException, IOException {
OutputStream os = null;
try {
File out = new File(aFilename);
getLog().debug("Writing descriptor to: " + out);
os = new FileOutputStream(out);
aDesc.toXML(os);
} finally {
IOUtils.closeQuietly(os);
}
}
}