/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, 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.system.server.profileservice.repository;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
import java.util.Map;
import org.jboss.deployers.client.plugins.deployment.AbstractDeployment;
import org.jboss.deployers.client.spi.Deployment;
import org.jboss.deployers.spi.attachments.AttachmentsFactory;
import org.jboss.deployers.spi.attachments.MutableAttachments;
import org.jboss.deployers.structure.spi.DeploymentContext;
import org.jboss.deployers.structure.spi.main.MainDeployerStructure;
import org.jboss.deployers.vfs.spi.client.VFSDeploymentFactory;
import org.jboss.deployers.vfs.spi.structure.VFSDeploymentContext;
import org.jboss.logging.Logger;
import org.jboss.managed.api.ManagedCommon;
import org.jboss.managed.api.ManagedComponent;
import org.jboss.managed.api.ManagedObject;
import org.jboss.profileservice.spi.ProfileDeployment;
import org.jboss.system.server.profileservice.attachments.AttachmentMetaData;
import org.jboss.system.server.profileservice.attachments.AttachmentStore;
import org.jboss.system.server.profileservice.attachments.RepositoryAttachmentMetaData;
import org.jboss.system.server.profileservice.attachments.RepositoryAttachmentMetaDataFactory;
import org.jboss.system.server.profileservice.persistence.PersistenceFactory;
import org.jboss.system.server.profileservice.persistence.xml.PersistenceRoot;
import org.jboss.virtual.VirtualFile;
/**
* The AbstractAttachmentStore.
*
* @author <a href="mailto:emuckenh@redhat.com">Emanuel Muckenhuber</a>
* @version $Revision: 106340 $
*/
public class AbstractAttachmentStore implements AttachmentStore
{
/** The metadata name */
public static final String METADATA_NAME = "metadata";
/** The logger. */
private static final Logger log = Logger.getLogger(AbstractAttachmentStore.class);
/** The vfs deployment factory. */
private static final VFSDeploymentFactory deploymentFactory = VFSDeploymentFactory.getInstance();
/** The attachment store root. */
private final URI attachmentStoreRoot;
/** The attachment serializer. */
private AbstractFileAttachmentsSerializer serializer;
/** The main deployer. */
private MainDeployerStructure mainDeployer;
/** The persistence factory. */
private PersistenceFactory persistenceFactory;
protected static URI getURI(File root)
{
if(root == null)
throw new IllegalArgumentException("Null attachment root.");
if(root.exists() && root.isDirectory() == false)
throw new IllegalArgumentException("Attachment root is not a directory.");
return root.toURI();
}
public AbstractAttachmentStore(File root)
{
this(getURI(root));
}
public AbstractAttachmentStore(URI uri)
{
if(uri == null)
throw new IllegalArgumentException("Null uri.");
this.attachmentStoreRoot = uri;
}
public URI getAttachmentStoreRoot()
{
return this.attachmentStoreRoot;
}
public MainDeployerStructure getMainDeployer()
{
return mainDeployer;
}
public void setMainDeployer(MainDeployerStructure mainDeployer)
{
this.mainDeployer = mainDeployer;
}
public AbstractFileAttachmentsSerializer getSerializer()
{
return this.serializer;
}
public void setSerializer(AbstractFileAttachmentsSerializer serializer)
{
this.serializer = serializer;
}
public PersistenceFactory getPersistenceFactory()
{
return persistenceFactory;
}
public void setPersistenceFactory(PersistenceFactory persistenceFactory)
{
this.persistenceFactory = persistenceFactory;
}
public Deployment createDeployment(ProfileDeployment deployment) throws Exception
{
Deployment mcDeployment = null;
if(deployment.getRoot() == null)
mcDeployment = new AbstractDeployment(deployment.getName());
else
mcDeployment = deploymentFactory.createVFSDeployment(deployment.getRoot());
// Add the deployment attachments as PredeterminedManagedObjects
Map<String, Object> attachments = deployment.getAttachments();
if(attachments != null && attachments.isEmpty() == false)
{
MutableAttachments predetermined = AttachmentsFactory.createMutableAttachments();
for(String name : attachments.keySet())
predetermined.addAttachment(name, attachments.get(name));
mcDeployment.setPredeterminedManagedObjects(predetermined);
}
return mcDeployment;
}
/**
* Load the attachment metadata for a deployment.
*
* @param relativeDeploymentPath the relative path
* @return the attachment metadata
*/
protected RepositoryAttachmentMetaData loadAttachmentMetaData(String relativeDeploymentPath)
{
// attachments/simpleName - hash/metadata.xml
String fixedMetadataPath = getMetaDataPathName(relativeDeploymentPath);
try
{ // Try to load the repository attachment metadata
return getSerializer().loadAttachment(fixedMetadataPath, RepositoryAttachmentMetaData.class);
}
catch(Exception e)
{
log.error("Failed to load attachment metadata from relative path: "+ relativeDeploymentPath, e);
}
return null;
}
public RepositoryAttachmentMetaData loadMetaData(VirtualFile deploymentRoot) throws Exception
{
if(deploymentRoot == null)
throw new IllegalArgumentException("Null deployment root.");
String deploymentPath = createRelativeDeploymentPath(deploymentRoot);
return loadAttachmentMetaData(deploymentPath);
}
public void removeComponent(String ctx, ManagedComponent comp) throws Exception
{
if(ctx == null)
throw new IllegalArgumentException("null deployment ctx name");
if(comp == null)
throw new IllegalArgumentException("null managed component");
saveAttachment(ctx, comp, true);
}
public void updateDeployment(String ctx, ManagedComponent comp) throws Exception
{
if(ctx == null)
throw new IllegalArgumentException("null deployment ctx name");
if(comp == null)
throw new IllegalArgumentException("null managed component");
saveAttachment(ctx, comp, false);
}
public void saveAttachment(String deploymentName, ManagedComponent component, boolean remove) throws Exception
{
VFSDeploymentContext ctx = getDeploymentContext(deploymentName);
if(ctx == null)
throw new IllegalStateException("Cannot persist attachment, failed to find deployment: " + deploymentName);
// Get the root
VirtualFile root = ctx.getRoot();
String deploymentPath = createRelativeDeploymentPath(root);
// Load previous saved information
RepositoryAttachmentMetaData repositoryMetaData = loadAttachmentMetaData(deploymentPath);
if(repositoryMetaData == null)
{
repositoryMetaData = RepositoryAttachmentMetaDataFactory.createInstance();
repositoryMetaData.setDeploymentName(root.getName());
}
// Get the parent MO
ManagedCommon parent = component;
while(parent.getParent() != null)
parent = parent.getParent();
// Get the managed object, as a component can also be a child of a managedObject
ManagedObject managedObject = component.getDeployment().getManagedObject(parent.getName());
if(managedObject == null && parent instanceof ManagedObject)
managedObject = (ManagedObject) parent;
// Get the current attachment
String attachmentName = managedObject.getAttachmentName();
List<AttachmentMetaData> attachments = repositoryMetaData.getAttachments();
if(attachments == null)
{
attachments = new ArrayList<AttachmentMetaData>();
repositoryMetaData.setAttachments(attachments);
}
// Extract the attachment
AttachmentMetaData attachment = null;
for(AttachmentMetaData a : attachments)
{
if(attachmentName.equals(a.getName()))
attachment = a;
}
// Create a new one
if(attachment == null)
{
// Create attachment meta data
attachment = new AttachmentMetaData();
// Add attachment meta data
attachments.add(attachment);
}
// Is attachmentName the same as the className ?
attachment.setName(attachmentName);
attachment.setClassName(managedObject.getAttachment().getClass().getName());
// Save the attachment
String attachmentPath = deploymentPath + HashGenerator.createHash(attachment.getName());
// Create the persistence information
PersistenceRoot persistenceRoot = getSerializer().loadAttachment(attachmentPath, PersistenceRoot.class);
//
persistenceRoot = createPersistedMetaData(persistenceRoot, managedObject, component, remove);
// Serialize the attachment
getSerializer().saveAttachment(attachmentPath, persistenceRoot);
// Update the last modified.
long lastModified = System.currentTimeMillis();
attachment.setLastModified(lastModified);
repositoryMetaData.setLastModified(lastModified);
// Save the updated repository meta data
getSerializer().saveAttachment(getMetaDataPathName(deploymentPath), repositoryMetaData);
}
/**
* create the xml meta data for persisting the managed object.
*
* @param parent the parent managed object.
* @param the managed object
* @param handler the persistence handler
* @return the xml metadata.
*/
protected PersistenceRoot createPersistedMetaData(PersistenceRoot root, ManagedObject managedObject, ManagedComponent component, boolean remove)
{
if(root == null)
root = new PersistenceRoot();
if(remove)
{
root = this.persistenceFactory.removeComponent(root, managedObject, component);
}
else
{
root = this.persistenceFactory.updateComponent(root, managedObject, component);
}
if(root.getName() == null)
root.setName(managedObject.getAttachmentName());
if(root.getClassName() == null)
root.setClassName(managedObject.getAttachment().getClass().getName());
return root;
}
public PersistenceRoot loadAttachment(VirtualFile deploymentCtx, AttachmentMetaData attachment) throws Exception
{
if(deploymentCtx == null)
throw new IllegalArgumentException("Null deployment root.");
if(attachment == null)
throw new IllegalArgumentException("Null attachment");
String deploymentPath = createRelativeDeploymentPath(deploymentCtx);
// Load
String attachmentPath = deploymentPath + HashGenerator.createHash(attachment.getName());
PersistenceRoot root = getSerializer().loadAttachment(attachmentPath, PersistenceRoot.class);
if(root == null)
{
attachmentPath = deploymentPath + attachment.getName();
root = getSerializer().loadAttachment(attachmentPath, PersistenceRoot.class);
}
return root;
}
/**
* Get the metadata path, based on a relative path.
*
* @param deploymentPath the relative path to the deployment
* @return
*/
protected String getMetaDataPathName(String deploymentPath)
{
return deploymentPath.endsWith(File.separator) ? deploymentPath + METADATA_NAME : deploymentPath + File.separator + METADATA_NAME;
}
/**
* Create the relative path to the persisted deployment attachment meta data.
* The string is simpleName + "-" + hash (based on the URI of the deployment)
*
* @param deployment the deployment
* @return the relative name
* @throws Exception
*/
protected String createRelativeDeploymentPath(VirtualFile vf) throws Exception
{
if(vf == null)
throw new IllegalStateException("Null deployment.");
// deployment URI
String pathName = vf.toURI().toString();
String fileName = vf.getName();
// Generate hash
String hash = HashGenerator.createHash(pathName);
// simple name + "-" + hash
return fileName + "-" + hash + File.separator;
}
/**
* Get deployment context.
*
* @param name the deployment context name
* @return vfs deployment context or null if doesn't exist or not vfs based
*/
@SuppressWarnings("deprecation")
protected VFSDeploymentContext getDeploymentContext(String name)
{
if (mainDeployer == null)
throw new IllegalStateException("Null main deployer.");
DeploymentContext deploymentContext = mainDeployer.getDeploymentContext(name);
if (deploymentContext == null || deploymentContext instanceof VFSDeploymentContext == false)
return null;
return (VFSDeploymentContext)deploymentContext;
}
private static class HashGenerator
{
/** The digest. */
private static MessageDigest digest;
/**
* Create a hash based on a deployment vfs path name.
*
* @param deployment the deployment
* @return a hash
* @throws NoSuchAlgorithmException
* @throws MalformedURLException
* @throws URISyntaxException
*/
public static String createHash(String pathName)
throws NoSuchAlgorithmException, MalformedURLException, URISyntaxException
{
// buffer
StringBuffer buffer = new StringBuffer();
// formatter
Formatter f = new Formatter(buffer);
// get the bytez
byte[] bytez = internalCreateHash(pathName);
for(byte b : bytez)
{
// format the byte
f.format("%02x", b);
}
// toString
return f.toString();
}
protected static byte[] internalCreateHash(String pathName) throws NoSuchAlgorithmException
{
MessageDigest digest = getDigest();
try
{
// update
digest.update(pathName.getBytes());
// return
return digest.digest();
}
finally
{
// reset
digest.reset();
}
}
public static MessageDigest getDigest() throws NoSuchAlgorithmException
{
if(digest == null)
digest = MessageDigest.getInstance("MD5");
return digest;
}
}
}