package org.jboss.services.deployment;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.jboss.logging.Logger;
import org.jboss.services.deployment.metadata.ConfigInfo;
import org.jboss.services.deployment.metadata.ConfigInfoBinding;
import org.jboss.services.deployment.metadata.PropertyInfo;
import org.jboss.services.deployment.metadata.TemplateInfo;
import org.jboss.system.server.ServerConfigLocator;
import org.jboss.util.file.Files;
import org.jboss.xml.binding.ObjectModelFactory;
import org.jboss.xml.binding.Unmarshaller;
public class DeploymentManager
{
public static final String TEMPLATE_CONFIG_FILE = "template-config.xml";
public static final String TEMPLATE_ERROR_PARAM = "template-error";
public static final String CONTEXT_HELPER = "helper";
private Logger log;
private File templateDir;
private File undeployDir;
private File deployDir;
private Map configMap;
VelocityEngine ve;
public DeploymentManager(String templateDir, String undeployDir, String deployDir, Logger log)
throws Exception
{
this.log = log;
initialize(templateDir, undeployDir, deployDir);
}
public Set listModuleTemplates()
{
Set keys = configMap.keySet();
synchronized(configMap)
{
return new TreeSet(keys);
}
}
public List getTemplatePropertyInfo(String template)
throws Exception
{
ConfigInfo ci = (ConfigInfo)configMap.get(template);
if (ci == null)
{
throw new Exception("template does not exist: " + template);
}
else
{ List propertyList = ci.getPropertyInfoList();
List newList = new ArrayList(propertyList.size());
for (Iterator i = propertyList.iterator(); i.hasNext();)
{
newList.add(new PropertyInfo((PropertyInfo)i.next()));
}
return newList;
}
}
public String createModule(String module, String template, HashMap properties)
throws Exception
{
if (module == null || template == null || properties == null)
throw new Exception("Null argument: module=" + module +
", template=" + template + ", properties=" + properties);
if (!module.equals(Files.encodeFileName(module)))
throw new Exception("not a filesystem friendly module name: " + module);
ConfigInfo ci = (ConfigInfo)configMap.get(template);
if (ci == null)
throw new Exception("template does not exist: " + template);
File outputModule;
String extension = ci.getExtension();
if (extension == null || module.endsWith(extension))
outputModule = new File(this.undeployDir, module);
else
outputModule = new File(this.undeployDir, module + extension);
if (outputModule.exists())
throw new Exception("module already exist: " + outputModule);
String vmTemplate = ci.getTemplate();
try
{
if (vmTemplate != null )
{
VelocityContext ctx = createTemplateContext(ci, properties);
BufferedWriter out = new BufferedWriter(new FileWriter(outputModule));
try {
boolean success = ve.mergeTemplate(template + '/' + vmTemplate, ctx, out);
if (success == true)
{
String errorMsg = (String)ctx.get(TEMPLATE_ERROR_PARAM);
if (errorMsg.length() > 0)
throw new Exception("Template error: " + errorMsg);
else
log.debug("created module '" + outputModule.getName() + "' based on template '" + template + "'");
}
else
throw new Exception("Failed to create module '" + outputModule.getName());
}
finally
{
out.close();
}
}
else
{
VelocityContext ctx = createTemplateContext(ci, properties);
String copydir = ci.getCopydir();
File sourceDir = new File(this.templateDir, template + '/' + copydir);
deepCopy(sourceDir, outputModule);
List templateList = ci.getTemplateInfoList();
for (Iterator i = templateList.iterator(); i.hasNext(); )
{
TemplateInfo ti = (TemplateInfo)i.next();
File outputFile = new File(outputModule, ti.getOutput());
File outputPath = outputFile.getParentFile();
if (!outputPath.exists())
if (!outputPath.mkdirs())
throw new IOException("cannot create directory: " + outputPath);
BufferedWriter out = new BufferedWriter(new FileWriter(outputFile));
try {
boolean success = ve.mergeTemplate(template + '/' + ti.getInput(), ctx, out);
if (success == true)
{
String errorMsg = (String)ctx.get(TEMPLATE_ERROR_PARAM);
if (errorMsg.length() > 0)
throw new Exception("Template error: " + errorMsg);
else
log.debug("created module '" + outputModule.getName() + "' based on template '" + template + "'");
}
else
throw new Exception("Failed to create package '" + outputModule.getName());
}
finally
{
out.close();
}
}
}
}
catch (Exception e)
{
if (outputModule.exists())
{
boolean deleted = Files.delete(outputModule);
if (!deleted)
log.warn("Failed to clean-up erroneous module: " + outputModule);
}
throw e;
}
return outputModule.getName();
}
public boolean removeModule(String module)
{
File target = new File(this.undeployDir, module);
return Files.delete(target);
}
public void moveToDeployDir(String module)
throws Exception
{
File source = new File(this.undeployDir, module);
File target = new File(this.deployDir, module);
if (source.exists())
{
boolean moved = source.renameTo(target);
if (!moved)
throw new Exception("cannot move module: " + module);
}
else
throw new Exception("module does not exist: " + module);
}
public void moveToModuleDir(String module)
throws Exception
{
File source = new File(this.deployDir, module);
File target = new File(this.undeployDir, module);
if (source.exists())
{
boolean moved = source.renameTo(target);
if (!moved)
throw new Exception("cannot move module: " + module);
}
else
throw new Exception("module does not exist: " + module);
}
public URL getDeployedURL(String module)
throws Exception
{
File target = new File(this.deployDir, module);
if (!target.exists())
throw new Exception("module does not exist: " + target);
return target.toURL();
}
public URL getUndeployedURL(String module)
throws Exception
{
File target = new File(this.undeployDir, module);
if (!target.exists())
throw new Exception("module does not exist: " + target);
return target.toURL();
}
private void initialize(String templateDir, String undeployDir, String deployDir)
throws Exception
{
boolean debug = log.isDebugEnabled();
boolean trace = log.isTraceEnabled();
this.templateDir = initDir(templateDir, false);
if (debug)
log.debug("template dir=" + this.templateDir);
this.undeployDir = initDir(undeployDir, true);
if (debug)
log.debug("undeployDir dir=" + this.undeployDir);
this.deployDir = initDir(deployDir, false);
if (debug)
log.debug("deploy dir=" + this.deployDir);
List configFiles = findTemplateConfigFiles(this.templateDir);
if (debug)
log.debug("template config files=" + configFiles);
Map map = Collections.synchronizedMap(new TreeMap());
for (Iterator i = configFiles.iterator(); i.hasNext(); ) {
File file = (File)i.next();
ConfigInfo ci = parseXMLconfig(file);
ci.setName(file.getParentFile().getName());
if (trace)
log.trace("file: " + file + " ConfigInfo: " + ci);
Object existingValue = map.put(ci.getName(), ci);
if (existingValue != null)
throw new Exception("Duplicate template configuration entry: " + ci.getName());
}
this.configMap = map;
this.ve = new VelocityEngine();
this.ve.setProperty("runtime.log.logsystem.class",
"org.apache.velocity.runtime.log.SimpleLog4JLogSystem");
this.ve.setProperty("runtime.log.logsystem.log4j.category",
log.getName() + ".VelocityEngine");
this.ve.setProperty("file.resource.loader.path", this.templateDir.getCanonicalPath());
this.ve.init();
}
private File initDir(String targetDir, boolean create)
throws Exception
{
File dir = null;
try {
URL fileURL = new URL(targetDir);
File file = new File(fileURL.getFile());
if(file.isDirectory() && file.canRead() && file.canWrite()) {
dir = file;
}
}
catch(Exception e) {
File homeDir = ServerConfigLocator.locate().getServerHomeDir();
dir = new File(homeDir, targetDir);
if (create == true)
dir.mkdirs();
if (!dir.isDirectory())
throw new Exception("The target directory is not valid: "
+ dir.getCanonicalPath());
}
return dir;
}
private List findTemplateConfigFiles(File basedir)
{
List files = new ArrayList();
FileFilter dirFilter = new FileFilter()
{
public boolean accept(File file)
{
return file.isDirectory() && !file.getName().startsWith(".");
}
};
File[] dirs = basedir.listFiles(dirFilter);
for (int i = 0; i < dirs.length; i++) {
File file = new File(dirs[i], TEMPLATE_CONFIG_FILE);
if (file.isFile() && file.canRead())
files.add(file);
}
return files;
}
private ConfigInfo parseXMLconfig(File file)
throws Exception
{
InputStream is = new FileInputStream(file);
Unmarshaller unmarshaller = new Unmarshaller();
ObjectModelFactory factory = new ConfigInfoBinding();
ConfigInfo ci = (ConfigInfo)unmarshaller.unmarshal(is, factory, null);
is.close();
return ci;
}
private VelocityContext createTemplateContext(ConfigInfo ci, HashMap map)
throws Exception
{
VelocityContext vc;
List propertyList = ci.getPropertyInfoList();
if (propertyList.size() > 0)
{
vc = new VelocityContext();
for (Iterator i = propertyList.iterator(); i.hasNext(); ) {
PropertyInfo pi = (PropertyInfo)i.next();
String name = pi.getName();
String type = pi.getType();
boolean optional = pi.isOptional();
Object defaultValue = pi.getDefaultValue();
if (name == null || name.length() == 0 || type == null || type.length() == 0)
throw new Exception("Null or empty name/type property metadata for template: " + ci.getName());
Object sentValue = map.get(name);
if (sentValue != null)
{
if (!type.equals(sentValue.getClass().getName()))
throw new Exception("Expected type '" + type + "' for property '" + name +
"', got '" + sentValue.getClass().getName());
vc.put(name, sentValue);
}
else if (optional == false) {
if (defaultValue != null) {
vc.put(name, defaultValue);
}
else {
throw new Exception("Required property missing: '" + name + "' of type '" + type + "'");
}
}
}
}
else
{
vc = new VelocityContext(map);
}
vc.put(TEMPLATE_ERROR_PARAM, "");
vc.put(CONTEXT_HELPER, new ContextHelper());
return vc;
}
private void deepCopy(File sourceDir, File targetDir)
throws IOException
{
if (!sourceDir.isDirectory())
throw new IOException("sourceDir not a directory: " + sourceDir);
if (!targetDir.mkdir())
throw new IOException("could not create directory: " + targetDir);
File[] files = sourceDir.listFiles();
for (int i = 0; i < files.length; i++)
{
File source = files[i];
if (!source.canRead())
throw new IOException("cannot read: " + source);
if (source.isFile())
Files.copy(source, new File(targetDir, source.getName()));
else
deepCopy(source, new File(targetDir, source.getName()));
}
}
}