/* * JBoss, Home of Professional Open Source * Copyright 2006, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.deployers.vfs.structure.ear.support; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.jar.Attributes; import java.util.jar.Manifest; import org.jboss.deployers.spi.DeploymentException; import org.jboss.deployers.spi.annotations.AnnotationEnvironment; import org.jboss.deployers.spi.structure.ContextInfo; import org.jboss.deployers.vfs.plugins.structure.AbstractVFSStructureDeployer; import org.jboss.deployers.vfs.spi.structure.CandidateAnnotationsCallback; import org.jboss.deployers.vfs.spi.structure.StructureContext; import org.jboss.logging.Logger; import org.jboss.virtual.VFSUtils; import org.jboss.virtual.VirtualFile; import org.jboss.virtual.VirtualFileFilter; import org.jboss.virtual.plugins.vfs.helpers.SuffixMatchFilter; /** * A mock ear structure deployer that illustrates concepts involved with an ear * type of deployer. * * @author Scott.Stark@jboss.org * @author Ales.Justin@jboss.org * @version $Revision:$ */ public class MockEarStructureDeployer extends AbstractVFSStructureDeployer { /** * The default ear/lib filter */ public static final VirtualFileFilter DEFAULT_EAR_LIB_FILTER = new SuffixMatchFilter(".jar"); /** * The ear/lib filter */ private VirtualFileFilter earLibFilter = DEFAULT_EAR_LIB_FILTER; @Override public int getRelativeOrder() { return 1000; } /** * Get the earLibFilter. * * @return the earLibFilter. */ public VirtualFileFilter getEarLibFilter() { return earLibFilter; } /** * Set the earLibFilter. * * @param earLibFilter the filter * @throws IllegalArgumentException for a null filter */ public void setEarLibFilter(VirtualFileFilter earLibFilter) { if (earLibFilter == null) throw new IllegalArgumentException("Null filter"); this.earLibFilter = earLibFilter; } public boolean determineStructure(StructureContext structureContext) throws DeploymentException { ContextInfo context; boolean valid; VirtualFile file = structureContext.getFile(); try { if (file.isLeaf() == true || file.getName().endsWith(".ear") == false) return false; context = createContext(structureContext, "META-INF"); VirtualFile applicationProps = getMetaDataFile(file, "META-INF/application.properties"); VirtualFile jbossProps = getMetaDataFile(file, "META-INF/jboss-application.properties"); boolean scan = true; List<EarModule> modules = new ArrayList<EarModule>(); if (applicationProps != null) { scan = false; readAppXml(applicationProps, modules); } if (jbossProps != null) { readAppXml(jbossProps, modules); } // Add the ear lib contents to the classpath try { VirtualFile lib = file.getChild("lib"); if (lib != null) { List<VirtualFile> archives = lib.getChildren(earLibFilter); for (VirtualFile archive : archives) super.addClassPath(structureContext, archive, true, true, context); } } catch (IOException ignored) { // lib directory does not exist } // Add the ear manifest locations? addClassPath(structureContext, file, false, true, context); if (scan) scanEar(structureContext, file, modules); // Create subdeployments for the ear modules for (EarModule mod : modules) { String fileName = mod.getFileName(); if (fileName != null && (fileName = fileName.trim()).length() > 0) { try { VirtualFile module = file.getChild(fileName); if (module == null) { throw new RuntimeException(fileName + " module listed in application.xml does not exist within .ear " + file.getName()); } // Ask the deployers to analyze this if (structureContext.determineChildStructure(module) == false) { throw new RuntimeException(fileName + " module listed in application.xml is not a recognized deployment, .ear: " + file.getName()); } } catch (IOException e) { throw new RuntimeException(fileName + " module listed in application.xml does not exist within .ear " + file.getName(), e); } } } valid = true; } catch (Exception e) { throw new RuntimeException("Error determining structure: " + file.getName(), e); } return valid; } protected void readAppXml(VirtualFile file, List<EarModule> modules) throws IOException { InputStream in = file.openStream(); try { Properties props = new Properties(); props.load(in); for (Object key : props.keySet()) { String name = (String)key; String fileName = props.getProperty(name); EarModule module = new EarModule(name, fileName); modules.add(module); } } finally { in.close(); } } private void scanEar(StructureContext context, VirtualFile root, List<EarModule> modules) throws Exception { List<VirtualFile> archives = root.getChildren(); if (archives != null && archives.isEmpty() == false) { // enable candidate annotations context.setCandidateAnnotationScanning(true); EarCandidateAnnotationsCallback callback = new EarCandidateAnnotationsCallback(); context.addCallback(callback); String earPath = root.getPathName(); int counter = 0; for (VirtualFile vfArchive : archives) { String filename = earRelativePath(earPath, vfArchive.getPathName()); // Check if the module already exists, i.e. it is declared in jboss-app.xml EarModule moduleMetaData = getModule(modules, filename); if (moduleMetaData == null) { // reset callback result callback.reset(); int type = typeFromSuffix(context, filename, vfArchive); Integer callbackResult = null; if (type < 0) { callbackResult = callback.getResult(); if (callbackResult != null) type = callbackResult; } if (type >= 0) { String typeString = null; switch(type) { case J2eeModuleMetaData.EJB: typeString = "Ejb"; break; case J2eeModuleMetaData.CLIENT: typeString = "Java"; break; case J2eeModuleMetaData.CONNECTOR: typeString = "Connector"; break; case J2eeModuleMetaData.SERVICE: case J2eeModuleMetaData.HAR: typeString = "Service"; break; case J2eeModuleMetaData.WEB: typeString = "Web"; break; } // we didin't do full recognition yet if (callbackResult == null) { moduleMetaData = new EarModule(typeString + "Module" + counter, filename); modules.add(moduleMetaData); counter++; } } } } // reset candidate annotation scanning flag context.setCandidateAnnotationScanning(false); } } private EarModule getModule(List<EarModule> modules, String filename) { for(EarModule em : modules) if (filename.endsWith(em.getFileName())) return em; return null; } private int typeFromSuffix(StructureContext context, String path, VirtualFile archive) throws Exception { int type = -1; if (path.endsWith(".war")) type = J2eeModuleMetaData.WEB; else if (path.endsWith(".rar")) type = J2eeModuleMetaData.CONNECTOR; else if (path.endsWith(".har")) type = J2eeModuleMetaData.HAR; else if (path.endsWith(".sar")) type = J2eeModuleMetaData.SERVICE; else if (path.endsWith(".jar")) { // Look for a META-INF/application-client.xml VirtualFile mfFile = getMetaDataFile(archive, "META-INF/MANIFEST.MF"); VirtualFile clientXml = getMetaDataFile(archive, "META-INF/application-client.xml"); VirtualFile ejbXml = getMetaDataFile(archive, "META-INF/ejb-jar.xml"); VirtualFile jbossXml = getMetaDataFile(archive, "META-INF/jboss.xml"); if (clientXml != null) { type = J2eeModuleMetaData.CLIENT; } else if (mfFile != null) { Manifest mf = VFSUtils.readManifest(mfFile); Attributes attrs = mf.getMainAttributes(); if (attrs.containsKey(Attributes.Name.MAIN_CLASS)) { type = J2eeModuleMetaData.CLIENT; } else { determineType(context, archive); } } else if (ejbXml != null || jbossXml != null) { type = J2eeModuleMetaData.EJB; } else { determineType(context, archive); } } return type; } private void determineType(StructureContext context, VirtualFile archive) throws Exception { context.determineChildStructure(archive); } private String earRelativePath(String earPath, String pathName) { StringBuilder tmp = new StringBuilder(pathName); tmp.delete(0, earPath.length()); return tmp.toString(); } private VirtualFile getMetaDataFile(VirtualFile file, String path) { VirtualFile metaFile = null; try { metaFile = file.getChild(path); } catch (IOException ignored) { } return metaFile; } private static class EarCandidateAnnotationsCallback implements CandidateAnnotationsCallback { private static final Logger log = Logger.getLogger(EarCandidateAnnotationsCallback.class); private Integer result; private final static Map<Class<? extends Annotation>, Integer> map; static { map = new HashMap<Class<? extends Annotation>, Integer>(); map.put(Stateless.class, J2eeModuleMetaData.EJB); map.put(Service.class, J2eeModuleMetaData.SERVICE); map.put(AppClient.class, J2eeModuleMetaData.CLIENT); map.put(Servlet.class, J2eeModuleMetaData.WEB); } public void executeCallback(VirtualFile root, StructureContext currentContext, AnnotationEnvironment env, Class<? extends Annotation> annotationClass) { if (result == null) { result = map.get(annotationClass); } else { log.warn("Result already set: " + result); } } public void reset() { result = null; } public Integer getResult() { return result; } } }