/*
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors.
*
* 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.switchyard.tools.maven.plugins.switchyard;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
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.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.StringUtils;
import org.switchyard.common.type.Classes;
import org.switchyard.config.OutputKey;
import org.switchyard.config.model.MergeScanner;
import org.switchyard.config.model.Model;
import org.switchyard.config.model.ModelPullerScanner;
import org.switchyard.config.model.Scanner;
import org.switchyard.config.model.ScannerInput;
import org.switchyard.config.model.ScannerOutput;
import org.switchyard.config.model.switchyard.SwitchYardModel;
import org.switchyard.config.model.switchyard.SwitchYardNamespace;
/**
* Maven mojo for configuring SwitchYard.
*
* @param <M> the Model type being configured
*
* @author David Ward <<a href="mailto:dward@jboss.org">dward@jboss.org</a>> © 2011 Red Hat Inc.
*/
@Mojo(name="configure", defaultPhase=LifecyclePhase.PROCESS_CLASSES, requiresDependencyResolution=ResolutionScope.COMPILE)
public class ConfigureMojo<M extends Model> extends AbstractMojo {
private static final String SWITCHARD_XML_FILE_NAME = "switchyard.xml";
private static final String SWITCHYARD_XML_DEFAULT_FOLDER = "META-INF";
private static final String DEFAULT_SWITCHYARD_XML_FILE_PATH = SWITCHYARD_XML_DEFAULT_FOLDER + File.separator + SWITCHARD_XML_FILE_NAME;
@Parameter(property="project.artifactId")
private String _project_artifactId;
@Parameter(property="project.build.outputDirectory", alias="outputDirectory")
private File _project_build_outputDirectory;
@Parameter(property="project.compileClasspathElements")
private List<String> _project_compileClasspathElements;
@Parameter(property="project.resources")
private List<Resource> _project_resources;
@Parameter(property="validate", alias="validate")
private boolean _validate = true;
@Parameter(alias="outputFile")
private File _outputFile;
@Parameter(alias="scanDirectories")
private File[] _scanDirectories = new File[] {};
@Parameter(alias="scannerClassNames")
private String[] _scannerClassNames = new String[] {};
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
final List<URL> mojoURLs = new ArrayList<URL>();
try {
for (String compileClasspaths : _project_compileClasspathElements) {
mojoURLs.add(new File(compileClasspaths).toURI().toURL());
}
for (Resource resource : _project_resources) {
String path = resource.getTargetPath();
if (path != null) {
File file = new File(path);
if (file.exists()) {
mojoURLs.add(file.toURI().toURL());
}
}
}
if (_project_build_outputDirectory.getAbsoluteFile().exists()) {
mojoURLs.add(_project_build_outputDirectory.getAbsoluteFile().toURI().toURL());
}
for (File scanDir : _scanDirectories) {
if (scanDir != null) {
scanDir = scanDir.getAbsoluteFile();
if (scanDir.exists()) {
mojoURLs.add(scanDir.toURI().toURL());
}
}
}
} catch (MalformedURLException mue) {
throw new MojoExecutionException(mue.getMessage(), mue);
}
ClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() {
public URLClassLoader run() {
return new URLClassLoader(mojoURLs.toArray(new URL[mojoURLs.size()]), ConfigureMojo.class.getClassLoader());
}
});
ClassLoader previous = Classes.setTCCL(loader);
Writer writer = null;
try {
List<Scanner<M>> scanners = new ArrayList<Scanner<M>>();
addModelPullerScanners(scanners);
for (String scannerClassName : _scannerClassNames) {
@SuppressWarnings("unchecked")
Class<Scanner<M>> scannerClass = (Class<Scanner<M>>)Classes.forName(scannerClassName, loader);
if (scannerClass != null) {
Scanner<M> scanner = scannerClass.newInstance();
scanners.add(scanner);
}
}
//getLog().info("SwitchYard configure plugin using scanners: " + scanners);
MergeScanner<M> merge_scanner = new MergeScanner<M>(true, scanners);
List<URL> scannerURLs = new ArrayList<URL>();
if (_scanDirectories.length == 0) {
scannerURLs.add(_project_build_outputDirectory.toURI().toURL());
} else {
for (File scanDir : _scanDirectories) {
if (scanDir != null) {
scannerURLs.add(scanDir.toURI().toURL());
}
}
}
getLog().info("SwitchYard configure plugin scanner URLs: " + scannerURLs);
ScannerInput<M> scanner_input = new ScannerInput<M>().setCompositeName(_project_artifactId).setURLs(scannerURLs);
M model = merge_scanner.scan(scanner_input).getModel();
if (_outputFile == null) {
File od = new File(_project_build_outputDirectory, SWITCHYARD_XML_DEFAULT_FOLDER);
if (!od.exists()) {
if (!od.mkdirs()) {
throw new Exception("mkdirs() on " + od + " failed.");
}
}
_outputFile = new File(od, SWITCHARD_XML_FILE_NAME);
} else {
// make sure output directory exists
File od = _outputFile.getParentFile();
if (!od.exists()) {
if (!od.mkdirs()) {
throw new Exception("mkdirs() on " + od + " failed.");
}
}
}
getLog().info("Outputting SwitchYard configuration model to " + _outputFile.getAbsolutePath());
writer = new BufferedWriter(new FileWriter(_outputFile));
model.write(writer, OutputKey.PRETTY_PRINT);
writer.flush();
// we write the output before we assert validity so that the user can compare the written XML to the validation error
if (_validate) {
getLog().info("Validating SwitchYard configuration model...");
model.assertModelValid();
} else {
getLog().warn("Skipping validation of SwitchYard configuration model. (Enable with <validate>true</validate>.)");
}
} catch (Throwable t) {
throw new MojoExecutionException(t.getMessage(), t);
} finally {
Classes.setTCCL(previous);
if (writer != null) {
try {
writer.close();
} catch (IOException ioe) {
throw new MojoExecutionException(ioe.getMessage(), ioe);
}
}
}
}
/**
* Add a ModelPullerScanner for each instance of META-INF/switchyard.xml
* that is found in a resource or scan folder. Any includes or excludes
* specified for the resource folder are respected (i.e. if a switchyard.xml
* file is in the resource folder, but excluded, a scanner will not be added
* for that file). No filtering is applied to scan folders.
*
* @param scanners the scanners list
* @throws IOException if an error occurs scanning the resource folder(s) for files.
*/
private void addModelPullerScanners(final List<Scanner<M>> scanners) throws IOException {
for (Resource resource : _project_resources) {
addModelPullerScanner(new File(resource.getDirectory()).getCanonicalFile(),
convertInExcludes(resource.getIncludes()), convertInExcludes(resource.getExcludes()), scanners);
}
for (File file : _scanDirectories) {
addModelPullerScanner(file.getCanonicalFile(), null, null, scanners);
}
}
/**
* Adds a scanner if a "META-INF/switchyard.xml" file is found in the folder specified by baseDir.
*
* @param baseDir the directory to search.
* @param includes file includes.
* @param excludes file excludes.
* @param scanners the scanners list
* @throws IOException if an error occurs.
*/
private void addModelPullerScanner(final File baseDir, final String includes, final String excludes, final List<Scanner<M>> scanners) throws IOException {
final File switchYardFile = new File(baseDir, DEFAULT_SWITCHYARD_XML_FILE_PATH).getCanonicalFile();
for (File file : (List<File>)FileUtils.getFiles(baseDir, includes, excludes)) {
file = file.getCanonicalFile();
if (switchYardFile.equals(file)) {
// found a match, add a scanner
scanners.add(new ModelPullerScanner<M>(file) {
@Override
public ScannerOutput<M> scan(ScannerInput<M> input) throws IOException {
ScannerOutput<M> output = super.scan(input);
M model = output.getModel();
if (model != null) {
Model root = model.getModelRoot();
if (root instanceof SwitchYardModel) {
if (!input.isSwitchyardNamespaceSet()) {
String uri = root.getModelRootNamespace();
SwitchYardNamespace ns = SwitchYardNamespace.fromUri(uri);
input.setSwitchyardNamespace(ns);
}
}
}
return output;
}
});
// one file per folder
return;
}
}
}
private String convertInExcludes(List<String> cludes) {
if (cludes == null) {
return null;
}
final String converted = StringUtils.join(cludes.iterator(), ",");
if (converted.length() == 0) {
return null;
}
return converted;
}
}