Mercurial > dive4elements > framework
view artifact-database/src/main/java/de/intevation/artifactdatabase/ArtifactDatabaseImpl.java @ 91:730ff077a58c
More javadoc, some code formatting.
artifacts/trunk@847 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Sun, 28 Mar 2010 10:01:03 +0000 |
parents | b2e0cb83631c |
children | 73d0ebae81d7 |
line wrap: on
line source
package de.intevation.artifactdatabase; import de.intevation.artifactdatabase.Backend.PersistentArtifact; import de.intevation.artifacts.Artifact; import de.intevation.artifacts.ArtifactDatabase; import de.intevation.artifacts.ArtifactDatabaseException; import de.intevation.artifacts.ArtifactFactory; import de.intevation.artifacts.ArtifactNamespaceContext; import de.intevation.artifacts.ArtifactSerializer; import de.intevation.artifacts.CallContext; import de.intevation.artifacts.CallMeta; import de.intevation.artifacts.Service; import de.intevation.artifacts.ServiceFactory; import java.io.IOException; import java.io.OutputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> */ public class ArtifactDatabaseImpl implements ArtifactDatabase, Id.Filter, Backend.FactoryLookup { private static Logger logger = Logger.getLogger(ArtifactDatabaseImpl.class); public static final String NO_SUCH_FACTORY = "No such factory"; public static final String NO_SUCH_ARTIFACT = "No such artifact"; public static final String NOT_IN_BACKGROUND = "Not in background"; public static final String INVALID_CALL_STATE = "Invalid after call state"; public static final String CREATION_FAILED = "Creation of artifact failed"; public static final String INTERNAL_ERROR = "Creation of artifact failed"; public static final String NO_SUCH_SERVICE = "No such service"; public static final String DIGEST_ALGORITHM = "SHA-1"; public static final String XPATH_IMPORT_CHECKSUM = "/art:action/art:data/@checksum"; public static final String XPATH_IMPORT_FACTORY = "/art:action/art:data/@factory"; public static final String XPATH_IMPORT_DATA = "/art:action/art:data/text()"; public static final String INVALID_CHECKSUM = "Invalid checksum"; public static final String CHECKSUM_MISMATCH = "Mismatching checksum"; public static final String NO_DATA = "No data"; public static final String INVALID_ARTIFACT = "Invalid artifact"; public class CallContextImpl implements CallContext { protected PersistentArtifact artifact; protected int action; protected CallMeta callMeta; protected HashMap customValues; public CallContextImpl( PersistentArtifact artifact, int action, CallMeta callMeta ) { this.artifact = artifact; this.action = action; this.callMeta = callMeta; } public void afterCall(int action) { this.action = action; if (action == BACKGROUND) { addIdToBackground(artifact.getId()); } } public void afterBackground(int action) { if (this.action != BACKGROUND) { throw new IllegalStateException(NOT_IN_BACKGROUND); } fromBackground(artifact, action); } public Object globalContext() { return context; } public ArtifactDatabase getDatabase() { return ArtifactDatabaseImpl.this; } public CallMeta getMeta() { return callMeta; } public Long getTimeToLive() { return artifact.getTTL(); } public void postCall() { switch (action) { case NOTHING: break; case TOUCH: artifact.touch(); break; case STORE: artifact.store(); break; case BACKGROUND: logger.warn("BACKGROUND processing is not fully implemented, yet!"); artifact.store(); break; default: logger.error(INVALID_CALL_STATE + ": " + action); throw new IllegalStateException(INVALID_CALL_STATE); } } public Object getContextValue(Object key) { return customValues != null ? customValues.get(key) : null; } public Object putContextValue(Object key, Object value) { if (customValues == null) { customValues = new HashMap(); } return customValues.put(key, value); } } // class CallContextImpl public class DeferredOutputImpl implements DeferredOutput { protected PersistentArtifact artifact; protected Document format; protected CallMeta callMeta; public DeferredOutputImpl() { } public DeferredOutputImpl( PersistentArtifact artifact, Document format, CallMeta callMeta ) { this.artifact = artifact; this.format = format; this.callMeta = callMeta; } public void write(OutputStream output) throws IOException { CallContextImpl cc = new CallContextImpl( artifact, CallContext.TOUCH, callMeta); try { artifact.getArtifact().out(format, output, cc); } finally { cc.postCall(); } } } // class DeferredOutputImpl protected String [][] factoryNamesAndDescription; protected HashMap name2factory; protected String [][] serviceNamesAndDescription; protected HashMap name2service; protected Backend backend; protected Object context; protected byte [] exportSecret; protected HashSet backgroundIds; public ArtifactDatabaseImpl() { } public ArtifactDatabaseImpl(FactoryBootstrap bootstrap) { this(bootstrap, null); } public ArtifactDatabaseImpl(FactoryBootstrap bootstrap, Backend backend) { backgroundIds = new HashSet(); setupArtifactFactories(bootstrap); setupServices(bootstrap); context = bootstrap.getContext(); exportSecret = bootstrap.getExportSecret(); wireWithBackend(backend); } protected void setupArtifactFactories(FactoryBootstrap bootstrap) { name2factory = new HashMap(); ArtifactFactory [] factories = bootstrap.getArtifactFactories(); factoryNamesAndDescription = new String[factories.length][]; for (int i = 0; i < factories.length; ++i) { ArtifactFactory factory = factories[i]; String name = factory.getName(); String description = factory.getDescription(); factoryNamesAndDescription[i] = new String [] { name, description }; name2factory.put(name, factory); } } protected void setupServices(FactoryBootstrap bootstrap) { name2service = new HashMap(); ServiceFactory [] serviceFactories = bootstrap.getServiceFactories(); serviceNamesAndDescription = new String[serviceFactories.length][]; for (int i = 0; i < serviceFactories.length; ++i) { ServiceFactory factory = serviceFactories[i]; String name = factory.getName(); String description = factory.getDescription(); serviceNamesAndDescription[i] = new String [] { name, description }; name2service.put( name, factory.createService(bootstrap.getContext())); } } public void wireWithBackend(Backend backend) { if (backend != null) { this.backend = backend; backend.setFactoryLookup(this); } } protected void fromBackground(PersistentArtifact artifact, int action) { logger.warn("BACKGROUND processing is not fully implemented, yet!"); switch (action) { case CallContext.NOTHING: break; case CallContext.TOUCH: artifact.touch(); break; case CallContext.STORE: artifact.store(); break; default: logger.warn("operation not allowed in fromBackground"); } removeIdFromBackground(artifact.getId()); } protected void removeIdFromBackground(int id) { synchronized (backgroundIds) { backgroundIds.remove(Integer.valueOf(id)); } } protected void addIdToBackground(int id) { synchronized (backgroundIds) { backgroundIds.add(Integer.valueOf(id)); } } public List filterIds(List ids) { int N = ids.size(); ArrayList out = new ArrayList(N); synchronized (backgroundIds) { for (int i = 0; i < N; ++i) { Id id = (Id)ids.get(i); if (!backgroundIds.contains(Integer.valueOf(id.getId()))) { out.add(id); } } } return out; } public String [][] artifactFactoryNamesAndDescriptions() { return factoryNamesAndDescription; } public ArtifactFactory getInternalArtifactFactory(String factoryName) { return getArtifactFactory(factoryName); } public ArtifactFactory getArtifactFactory(String factoryName) { return (ArtifactFactory)name2factory.get(factoryName); } public Document createArtifactWithFactory( String factoryName, CallMeta callMeta, Document data ) throws ArtifactDatabaseException { ArtifactFactory factory = getArtifactFactory(factoryName); if (factory == null) { throw new ArtifactDatabaseException(NO_SUCH_FACTORY); } Artifact artifact = factory.createArtifact( backend.newIdentifier(), context, data); if (artifact == null) { throw new ArtifactDatabaseException(CREATION_FAILED); } PersistentArtifact persistentArtifact; try { persistentArtifact = backend.storeInitially( artifact, factory, factory.timeToLiveUntouched(artifact, context)); } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); throw new ArtifactDatabaseException(CREATION_FAILED); } CallContextImpl cc = new CallContextImpl( persistentArtifact, CallContext.NOTHING, callMeta); try { return artifact.describe(null, cc); } finally { cc.postCall(); } } public Document describe(String identifier, Document data, CallMeta callMeta) throws ArtifactDatabaseException { // TODO: Handle background tasks PersistentArtifact artifact = backend.getArtifact(identifier); if (artifact == null) { throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); } CallContextImpl cc = new CallContextImpl( artifact, CallContext.TOUCH, callMeta); try { return artifact.getArtifact().describe(data, cc); } finally { cc.postCall(); } } public Document advance(String identifier, Document target, CallMeta callMeta) throws ArtifactDatabaseException { // TODO: Handle background tasks PersistentArtifact artifact = backend.getArtifact(identifier); if (artifact == null) { throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); } CallContextImpl cc = new CallContextImpl( artifact, CallContext.STORE, callMeta); try { return artifact.getArtifact().advance(target, cc); } finally { cc.postCall(); } } public Document feed(String identifier, Document data, CallMeta callMeta) throws ArtifactDatabaseException { // TODO: Handle background tasks PersistentArtifact artifact = backend.getArtifact(identifier); if (artifact == null) { throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); } CallContextImpl cc = new CallContextImpl( artifact, CallContext.STORE, callMeta); try { return artifact.getArtifact().feed(data, cc); } finally { cc.postCall(); } } public DeferredOutput out(String identifier, Document format, CallMeta callMeta) throws ArtifactDatabaseException { // TODO: Handle background tasks PersistentArtifact artifact = backend.getArtifact(identifier); if (artifact == null) { throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); } return new DeferredOutputImpl(artifact, format, callMeta); } public Document exportArtifact(String artifact, CallMeta callMeta) throws ArtifactDatabaseException { final String [] factoryName = new String[1]; byte [] bytes = (byte [])backend.loadArtifact( artifact, new Backend.ArtifactLoader() { public Object load( ArtifactFactory factory, Long ttl, byte [] bytes, int id ) { factoryName[0] = factory.getName(); ArtifactSerializer serializer = factory.getSerializer(); Artifact artifact = serializer.fromBytes(bytes); artifact.cleanup(context); return serializer.toBytes(artifact); } }); if (bytes == null) { throw new ArtifactDatabaseException(NO_SUCH_ARTIFACT); } return createExportDocument( factoryName[0], bytes, exportSecret); } protected static Document createExportDocument( String factoryName, byte [] artifact, byte [] secret ) { Document document = XMLUtils.newDocument(); MessageDigest md; try { md = MessageDigest.getInstance(DIGEST_ALGORITHM); } catch (NoSuchAlgorithmException nsae) { logger.error(nsae.getLocalizedMessage(), nsae); return document; } md.update(artifact); md.update(secret); String checksum = Hex.encodeHexString(md.digest()); XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator( document, ArtifactNamespaceContext.NAMESPACE_URI, ArtifactNamespaceContext.NAMESPACE_PREFIX); Element root = ec.create("action"); document.appendChild(root); Element type = ec.create("type"); ec.addAttr(type, "name", "export"); root.appendChild(type); Element data = ec.create("data"); ec.addAttr(data, "checksum", checksum); ec.addAttr(data, "factory", factoryName); data.setTextContent(Base64.encodeBase64String(artifact)); root.appendChild(data); return document; } public Document importArtifact(Document input, CallMeta callMeta) throws ArtifactDatabaseException { String factoryName = XMLUtils.xpathString( input, XPATH_IMPORT_FACTORY, ArtifactNamespaceContext.INSTANCE); ArtifactFactory factory; if (factoryName == null || (factoryName = factoryName.trim()).length() == 0 || (factory = getArtifactFactory(factoryName)) == null) { throw new ArtifactDatabaseException(NO_SUCH_FACTORY); } String checksumString = XMLUtils.xpathString( input, XPATH_IMPORT_CHECKSUM, ArtifactNamespaceContext.INSTANCE); byte [] checksum; if (checksumString == null || (checksumString = checksumString.trim()).length() == 0 || (checksum = StringUtils.decodeHex(checksumString)) == null ) { throw new ArtifactDatabaseException(INVALID_CHECKSUM); } checksumString = null; String dataString = XMLUtils.xpathString( input, XPATH_IMPORT_DATA, ArtifactNamespaceContext.INSTANCE); if (dataString == null || (dataString = dataString.trim()).length() == 0) { throw new ArtifactDatabaseException(NO_DATA); } byte [] data = Base64.decodeBase64(dataString); dataString = null; MessageDigest md; try { md = MessageDigest.getInstance(DIGEST_ALGORITHM); } catch (NoSuchAlgorithmException nsae) { logger.error(nsae.getLocalizedMessage(), nsae); return XMLUtils.newDocument(); } md.update(data); md.update(exportSecret); byte [] digest = md.digest(); if (!Arrays.equals(checksum, digest)) { throw new ArtifactDatabaseException(CHECKSUM_MISMATCH); } ArtifactSerializer serializer = factory.getSerializer(); Artifact artifact = serializer.fromBytes(data); data = null; if (artifact == null) { throw new ArtifactDatabaseException(INVALID_ARTIFACT); } artifact.setIdentifier(backend.newIdentifier()); PersistentArtifact persistentArtifact; try { persistentArtifact = backend.storeOrReplace( artifact, factory, factory.timeToLiveUntouched(artifact, context)); } catch (Exception e) { logger.error(e.getLocalizedMessage(), e); throw new ArtifactDatabaseException(CREATION_FAILED); } CallContextImpl cc = new CallContextImpl( persistentArtifact, CallContext.NOTHING, callMeta); try { return artifact.describe(input, cc); } finally { cc.postCall(); } } public String [][] serviceNamesAndDescriptions() { return serviceNamesAndDescription; } public Document process( String serviceName, Document input, CallMeta callMeta ) throws ArtifactDatabaseException { Service service = (Service)name2service.get(serviceName); if (service == null) { throw new ArtifactDatabaseException(NO_SUCH_SERVICE); } return service.process(input, context, callMeta); } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :