/*
* 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;
}
}
}