/*
* JBoss, Home of Professional Open Source.
* Copyright 2007, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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.deployers.vfs.spi.deployer;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import org.jboss.deployers.spi.DeploymentException;
import org.jboss.deployers.spi.deployer.helpers.AbstractParsingDeployerWithOutput;
import org.jboss.deployers.spi.deployer.matchers.LazyPath;
import org.jboss.deployers.spi.deployer.matchers.NameIgnoreMechanism;
import org.jboss.deployers.spi.structure.MetaDataTypeFilter;
import org.jboss.deployers.structure.spi.DeploymentUnit;
import org.jboss.deployers.vfs.spi.structure.VFSDeploymentUnit;
import org.jboss.vfs.VirtualFile;
/**
* AbstractVFSParsingDeployer.
*
* @param <T> the type of output
* @author <a href="adrian@jboss.org">Adrian Brock</a>
* @author <a href="ales.justin@jboss.org">Ales Justin</a>
* @version $Revision: 1.1 $
*/
public abstract class AbstractVFSParsingDeployer<T> extends AbstractParsingDeployerWithOutput<T> implements FileMatcher
{
/** The alt mappings key */
private static final String ALT_MAPPINGS_MAP_KEY = "AltMappingsMap";
/** The allow multiple fiels flag */
private boolean allowMultipleFiles;
/** The metadata type filter */
private MetaDataTypeFilter filter = MetaDataTypeFilter.DEFAULT;
/**
* Create a new AbstractVFSParsingDeployer.
*
* @param output the type of output
* @throws IllegalArgumentException for null output
*/
public AbstractVFSParsingDeployer(Class<T> output)
{
super(output);
}
public boolean isDeployable(VirtualFile file)
{
String fileName = file.getName();
return matchFileName(fileName);
}
/**
* Match file name.
*
* @param fileName the file name.
*
* @return true if file name matches, false otherwise
*/
protected boolean matchFileName(String fileName)
{
String suffix = getSuffix();
if (suffix == null)
return getNames() != null && getNames().contains(fileName);
else
return fileName.endsWith(suffix);
}
/**
* Callback to do prechecking on the deployment
*
* @param unit the unit
* @return true by default
* @throws DeploymentException for any error
*/
protected boolean accepts(VFSDeploymentUnit unit) throws DeploymentException
{
return true;
}
@Override
protected boolean accepts(DeploymentUnit unit) throws DeploymentException
{
// Ignore non-vfs deployments
if (unit instanceof VFSDeploymentUnit == false)
{
if (log.isTraceEnabled())
log.trace("Not a vfs deployment: " + unit.getName());
return false;
}
return accepts((VFSDeploymentUnit) unit);
}
/**
* Get metadata type filter from unit - if deployer's filter is set to null.
*
* @param unit the deployment unit
* @return metadata type filter
*/
protected MetaDataTypeFilter getMetaDataTypeFilter(DeploymentUnit unit)
{
if (filter != null)
return filter;
MetaDataTypeFilter mdtf = unit.getAttachment(MetaDataTypeFilter.class);
return mdtf != null ? mdtf : MetaDataTypeFilter.DEFAULT;
}
/**
* Open stream and validate if not null.
*
* @param file the virtual file
* @return non-null input stream
* @throws Exception for any error or if file's stream is null
*/
protected InputStream openStreamAndValidate(VirtualFile file) throws Exception
{
if (file == null)
throw new IllegalArgumentException("Null file");
InputStream inputStream = SecurityActions.openStream(file);
if (inputStream == null)
throw new IOException("Null file stream: " + file);
return inputStream;
}
/**
* Get the alt mappings map.
*
* @param unit the deployment unit
* @return alt mappings map from attachments
*/
@SuppressWarnings("unchecked")
protected static Map<String, Class<?>> getAltMappings(DeploymentUnit unit)
{
if (unit == null)
throw new IllegalArgumentException("Null deployment unit");
return unit.getAttachment(ALT_MAPPINGS_MAP_KEY, Map.class);
}
/**
* Get metadata file.
* First try altDD, then fallback to original name.
*
* @param unit the vfs deployment unit
* @param altExpectedClass the expected class of altDD
* @param originalName the original file name
* @param checkMetaDataFile should we fall back to metadata file
* @return metadata file or null if it doesn't exist
*/
protected VirtualFile getMetadataFile(VFSDeploymentUnit unit, Class<?> altExpectedClass, String originalName, boolean checkMetaDataFile)
{
String altPrefix = (altExpectedClass != null ? altExpectedClass.getName() : originalName);
VirtualFile file = unit.getAttachment(altPrefix + ".altDD", VirtualFile.class);
if (file != null && altExpectedClass != null)
{
Map<String, Class<?>> altMappingsMap = getAltMappings(unit);
if (altMappingsMap == null)
{
altMappingsMap = new HashMap<String, Class<?>>();
unit.addAttachment(ALT_MAPPINGS_MAP_KEY, altMappingsMap, Map.class);
}
altMappingsMap.put(file.getName(), altExpectedClass);
}
if(checkMetaDataFile && file == null)
file = unit.getMetaDataFile(originalName, getMetaDataTypeFilter(unit));
return file;
}
/**
* Match file name to metadata class.
*
* @param unit the deployment unit
* @param fileName the file name
* @return matching metadata class
*/
protected Class<?> matchFileToClass(DeploymentUnit unit, String fileName)
{
Map<String, Class<?>> altMappingsMap = getAltMappings(unit);
return altMappingsMap != null ? altMappingsMap.get(fileName) : null;
}
/**
* Ignore file.
*
* @param unit the unit
* @param file the file
* @return true if we should ignore the file, false otherwise
*/
protected boolean ignoreFile(final VFSDeploymentUnit unit, final VirtualFile file)
{
NameIgnoreMechanism mechanism = getNameIgnoreMechanism();
if (mechanism != null)
{
LazyPath path = new LazyPath()
{
public String buildPath()
{
VirtualFile root = unit.getRoot();
return file.getPathNameRelativeTo(root);
}
};
return mechanism.ignorePath(unit, path);
}
return false;
}
@Override
protected T parse(DeploymentUnit unit, String name, T root) throws Exception
{
if (ignoreName(unit, name))
return null;
// Try to find the metadata
VFSDeploymentUnit vfsDeploymentUnit = (VFSDeploymentUnit) unit;
VirtualFile file = getMetadataFile(vfsDeploymentUnit, getOutput(), name, true);
return (file != null && ignoreFile(vfsDeploymentUnit, file) == false) ? parseAndInit(vfsDeploymentUnit, file, root) : null;
}
protected T parse(DeploymentUnit unit, Set<String> names, T root) throws Exception
{
if (names == null || names.isEmpty())
throw new IllegalArgumentException("Null or empty names.");
VFSDeploymentUnit vfsDeploymentUnit = (VFSDeploymentUnit)unit;
List<VirtualFile> files = new ArrayList<VirtualFile>();
Set<String> missingFiles = new HashSet<String>();
Set<String> ignoredFiles = new HashSet<String>();
for (String name : names)
{
if (ignoreName(unit, name))
{
ignoredFiles.add(name);
}
else
{
VirtualFile file = getMetadataFile(vfsDeploymentUnit, matchFileToClass(unit, name), name, true);
if (file != null)
{
if (ignoreFile(vfsDeploymentUnit, file))
ignoredFiles.add(file.getName());
else
files.add(file);
}
else
{
missingFiles.add(name);
}
}
}
if (missingFiles.size() + ignoredFiles.size() == names.size())
return null;
return mergeFiles(vfsDeploymentUnit, root, files, missingFiles);
}
@Override
protected T parse(DeploymentUnit unit, String name, String suffix, T root) throws Exception
{
// Should we include the deployment
// The infrastructure will only check leafs anyway so no need to check here
if (name == null && isIncludeDeploymentFile())
name = unit.getName();
// Try to find the metadata
VFSDeploymentUnit vfsDeploymentUnit = (VFSDeploymentUnit) unit;
// let's check altDD first
VirtualFile file = getMetadataFile(vfsDeploymentUnit, getOutput(), name, false);
if (file != null)
return parseAndInit(vfsDeploymentUnit, file, root, true);
// try all name+suffix matches
List<VirtualFile> files = vfsDeploymentUnit.getMetaDataFiles(name, suffix, getMetaDataTypeFilter(unit));
switch (files.size())
{
case 0 :
return null;
case 1 :
return parseAndInit(vfsDeploymentUnit, files.get(0), root, true);
default :
return handleMultipleFiles(vfsDeploymentUnit, root, files);
}
}
/**
* Parse the file, initialize the result if exists.
*
* @param unit the deployment unit
* @param file the file
* @param root the root
* @return parsed result
* @throws Exception for any error
*/
protected T parseAndInit(VFSDeploymentUnit unit, VirtualFile file, T root) throws Exception
{
return parseAndInit(unit, file, root, false);
}
/**
* Parse the file, initialize the result if exists.
*
* @param unit the deployment unit
* @param file the file
* @param root the root
* @param checkIgnore do we check for ignored names
* @return parsed result
* @throws Exception for any error
*/
protected T parseAndInit(VFSDeploymentUnit unit, VirtualFile file, T root, boolean checkIgnore) throws Exception
{
if (checkIgnore && ignoreFile(unit, file))
return null;
T result = parse(unit, file, root);
if (result != null)
init(unit, result, file);
return result;
}
protected T parse(DeploymentUnit unit, Set<String> names, String suffix, T root) throws Exception
{
if (names == null || names.isEmpty())
throw new IllegalArgumentException("Null or empty names.");
VFSDeploymentUnit vfsDeploymentUnit = (VFSDeploymentUnit)unit;
List<VirtualFile> files = new ArrayList<VirtualFile>();
Set<String> missingFiles = new HashSet<String>();
Set<String> ignoredFiles = new HashSet<String>();
for (String name : names)
{
if (ignoreName(unit, name))
{
ignoredFiles.add(name);
}
else
{
// try finding altDD file
VirtualFile file = getMetadataFile(vfsDeploymentUnit, matchFileToClass(unit, name), name, false);
if (file == null)
{
List<VirtualFile> matched = vfsDeploymentUnit.getMetaDataFiles(name, suffix, getMetaDataTypeFilter(unit));
if (matched != null && matched.isEmpty() == false)
{
for (VirtualFile m : matched)
{
if (ignoreFile(vfsDeploymentUnit, m))
ignoredFiles.add(m.getName());
else
files.add(m);
}
}
else
missingFiles.add(name);
}
else if (ignoreFile(vfsDeploymentUnit, file))
{
ignoredFiles.add(file.getName());
}
else
{
files.add(file);
}
}
}
if (missingFiles.size() + ignoredFiles.size() == names.size())
return null;
return mergeFiles(vfsDeploymentUnit, root, files, missingFiles);
}
/**
* Merge files into one piece of metatdata
*
* @param unit the unit
* @param root possibly null pre-existing root
* @param files matching meta files
* @param missingFiles file names that are missing matching file
* @return merged metadata
* @throws Exception for any error
*/
protected T mergeFiles(VFSDeploymentUnit unit, T root, List<VirtualFile> files, Set<String> missingFiles) throws Exception
{
return null;
}
/**
* Handle multiple files.
*
* @param unit the vfs deployment unit
* @param root possibly null pre-existing root
* @param files the matching files
* @return null or merged single result
* @throws Exception for any error
*/
protected T handleMultipleFiles(VFSDeploymentUnit unit, T root, List<VirtualFile> files) throws Exception
{
if (allowsMultipleFiles(files) == false)
throw new IllegalArgumentException("Multiple matching files not allowed: " + files);
for (VirtualFile file : files)
{
if (ignoreFile(unit, file) == false)
{
T result = parse(unit, file, root);
if (result != null)
{
init(unit, result, file);
unit.addAttachment(file.toURL().toString(), result, getOutput());
}
}
}
return null;
}
/**
* Check if we allow multiple files.
*
* Make sure you have deployers down
* the chain that will handle generated
* multiple attachments if this method returns true.
*
* @param files the matching files
* @return true if we allow, false otherwise
*/
protected boolean allowsMultipleFiles(List<VirtualFile> files)
{
return allowMultipleFiles;
}
/**
* Parse a deployment
*
* @param unit the deployment unit
* @param file the metadata file
* @param root - possibly null pre-existing root
* @return the metadata
* @throws Exception for any error
*/
protected abstract T parse(VFSDeploymentUnit unit, VirtualFile file, T root) throws Exception;
/**
* Initialise the metadata
*
* @param unit the unit
* @param metaData the metadata
* @param file the metadata file
* @throws Exception for any error
*/
protected void init(VFSDeploymentUnit unit, T metaData, VirtualFile file) throws Exception
{
}
/**
* Set allow multiple files.
*
* @param allowMultipleFiles the allow multiple files flag
*/
public void setAllowMultipleFiles(boolean allowMultipleFiles)
{
this.allowMultipleFiles = allowMultipleFiles;
}
/**
* Set metadata type filter.
*
* @param filter the metadata type filter
*/
public void setFilter(MetaDataTypeFilter filter)
{
this.filter = filter;
}
}