/**
* 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.jboss.loom.utils.as7;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.commons.lang.StringUtils;
import org.jboss.loom.actions.ModuleCreationAction.ModuleXmlInfo;
import org.jboss.loom.conf.AS7Config;
import org.jboss.loom.ex.MigrationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Util class for generation of module XML.
*
* @author Roman Jakubco
*/
public class AS7ModuleUtils {
private static final String MODULE_NS = "urn:jboss:module:1.1";
/**
* Creates module.xml.
*/
public static void createModuleXML_FreeMarker( ModuleXmlInfo moduleInfo, File modFile ) throws MigrationException {
//Writer out = null;
try {
Configuration cfg = new Configuration();
cfg.setClassForTemplateLoading( AS7ModuleUtils.class, "/org/jboss/loom/utils/as7/" );
cfg.setObjectWrapper( new DefaultObjectWrapper() );
cfg.setSharedVariable("modInfo", moduleInfo);
Template temp = cfg.getTemplate("module.xml.freemarker");
try( Writer out = new FileWriter( modFile ) ){
temp.process( null, out );
}
} catch( TemplateException | IOException ex ) {
throw new MigrationException("Failed creating " + modFile.getPath() + ": " + ex.getMessage(), ex);
}
}
/**
* Example of module xml,
* <module xmlns="urn:jboss:module:1.1" name="com.h2database.h2">
* <resources>
* <resource-root path="h2-1.3.168.jar"/>
* </resources>
* <dependencies>
* <module name="javax.api"/>
* <module name="javax.transaction.api"/>
* <module name="javax.servlet.api" optional="true"/>
* </dependencies>
* </module>
*
* @Deprecated Screws up namespaces. I haven't found a way to fix it in JAXP. Switched to FreeMarker.
*/
public static Document createModuleXML(String moduleName, String jarFile, String[] deps) throws ParserConfigurationException {
Document doc = createDoc();
//Document doc = createDoc(MODULE_NS, "module");
Element root = doc.createElement("module");
//Element root = doc.createElementNS("module", MODULE_NS);
doc.appendChild(root);
//Element root = doc.getDocumentElement();
root.setAttribute("xmlns", MODULE_NS);
root.setAttribute("name", moduleName);
Element resources = doc.createElementNS(MODULE_NS, "resources");
root.appendChild(resources);
Element resource = doc.createElementNS(MODULE_NS, "resource-root");
resource.setAttribute("path", jarFile);
resources.appendChild(resource);
// Dependencies
Element dependencies = doc.createElementNS(MODULE_NS, "dependencies");
boolean optional = false;
for( String modName : deps ) {
if( modName == null ){
optional = true;
continue;
}
Element module = doc.createElementNS(MODULE_NS, "module");
module.setAttribute("name", modName);
if( optional )
module.setAttribute("optional", "true");
dependencies.appendChild(module);
optional = false;
}
root.appendChild(dependencies);
return doc;
}
/**
* Parses syntax "foo ?bar baz" into String[]{"foo", null, "bar", "baz"}.
* (? and null means that the following dep is optional.)
*/
private static String[] parseDeps( String str ) {
List<String> deps = new LinkedList();
for( String name : StringUtils.split(str) ){
if( name.charAt(0) == '?' ){
deps.add( null );
name = name.substring(1);
}
deps.add( name );
}
return deps.toArray( new String[deps.size()] );
}
public static File transformDocToFile(Document doc, File file) throws TransformerException {
final TransformerFactory tf = TransformerFactory.newInstance();
final Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform( new DOMSource(doc), new StreamResult(file));
return file;
}
private static Document createDoc() throws ParserConfigurationException {
return createDoc( null, null );
}
private static Document createDoc( String namespace, String rootElmName ) throws ParserConfigurationException
{
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
// Need specifically Xerces as it treats namespaces better way.
/*DocumentBuilderFactory domFactory;
try {
domFactory = (DocumentBuilderFactory) Class.forName("com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl").newInstance();
} catch( ClassNotFoundException | InstantiationException | IllegalAccessException ex ) {
throw new IllegalStateException("JDK's DocumentBuilderFactoryImpl not found:\n " + ex.getMessage(), ex );
}*/
//DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance("com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", ClassLoader.getSystemClassLoader());
domFactory.setIgnoringComments(true);
domFactory.setNamespaceAware( false );
domFactory.setValidating( false );
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.getDOMImplementation().createDocument( namespace, rootElmName, null );// rootElmName
return doc;
}
/**
* Returns the name of the module which uses given .jar.
* For example, file at modules/system/layers/base/com/h2database/h2/main/h2-1.3.168.jar
* should return "com.h2database.h2".
*
* The current implementation is naive, assuming that the .jar file is in the module's root dir, where module.xml is.
*
* This method behavior is likely to change with various versions of EAP.
*/
public static String identifyModuleContainingJar( AS7Config as7Config, File jar ) {
String modAbsPath = as7Config.getModulesDir().getPath();
String jarAbsPath = jar.getParentFile().getParentFile().getPath();
String commonPrefix = StringUtils.getCommonPrefix( new String[]{ modAbsPath, jarAbsPath } );
String diff = jarAbsPath.substring( commonPrefix.length() );
String modName = StringUtils.removeStart( diff, "/" );
return modName.replace('/', '.');
}
}// class