/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author derek.adams@sapience360.com
*/
package org.jboss.internal.soa.esb.persistence.format.jcr;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URI;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.PathNotFoundException;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.message.urigen.JcrMessageURIGenerator;
import org.jboss.soa.esb.common.Configuration;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.message.MessagePayloadProxy;
import org.jboss.soa.esb.message.body.content.BytesBody;
import org.jboss.soa.esb.message.format.MessageFactory;
import org.jboss.soa.esb.message.properties.MessagePropertyFacade;
import org.jboss.soa.esb.message.urigen.MessageURIGenerator;
import org.jboss.soa.esb.message.urigen.URIGenerationException;
import org.jboss.soa.esb.services.persistence.MessageStore;
import org.jboss.soa.esb.services.persistence.MessageStoreException;
import org.jboss.soa.esb.Configurable;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.listeners.message.MessageDeliverException;
import org.jboss.soa.esb.helpers.ConfigTree;
/**
* Message store that persists messages to a JSR 170 content repository.
*
* //TODO add the handling of classficiations
*
* @author Derek Adams
*/
public class JCRMessageStoreImpl implements MessageStore, Configurable {
/** Static logger instance */
private static Logger LOGGER = Logger.getLogger(JCRMessageStoreImpl.class);
/** Generator for JCR-specific message URIs */
protected MessageURIGenerator uriGenerator = new JcrMessageURIGenerator();
/** Root node under which JCR messages are stored (lazy-loaded) */
protected Node messageStoreRootNode;
private MessagePayloadProxy payloadProxy;
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.services.persistence.MessageStore#addMessage(org.jboss.soa.esb.message.Message)
*/
public URI addMessage(Message message, String classification) throws MessageStoreException {
try {
Session session = JCRConnectionManager.getInstance().newRepositorySession();
Node root = getMessageStoreRootNode(session);
URI uid = saveMessage(root, message);
session.save();
return uid;
} catch (RepositoryException e) {
throw new MessageStoreException(e);
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.services.persistence.MessageStore#getMessage(java.net.URI)
*/
public Message getMessage(URI uri) throws MessageStoreException {
try {
Session session = JCRConnectionManager.getInstance().newRepositorySession();
Node root = getMessageStoreRootNode(session);
return loadMessage(root, uri);
} catch (RepositoryException e) {
throw new MessageStoreException(e);
}
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.services.persistence.MessageStore#getMessage(java.net.URI)
*/
public Message getMessage(URI uri, String classification) throws MessageStoreException {
try {
Session session = JCRConnectionManager.getInstance().newRepositorySession();
Node root = getMessageStoreRootNode(session);
return loadMessage(root, uri);
} catch (RepositoryException e) {
throw new MessageStoreException(e);
}
}
public void setConfiguration(ConfigTree config) throws ConfigurationException {
payloadProxy = new MessagePayloadProxy(config,
new String[] {BytesBody.BYTES_LOCATION},
new String[] {BytesBody.BYTES_LOCATION});
}
/**
* Save a message to the content repository.
*
* @param message
* @return
* @throws RepositoryException
*/
protected URI saveMessage(Node root, Message message) throws RepositoryException {
assertConfigured("saveMessage");
try {
URI messageURI = uriGenerator.generateMessageURI(message);
MessagePropertyFacade msgProps = new MessagePropertyFacade(message);
msgProps.setMessageId(messageURI.toString());
Node messageNode = root.addNode(messageURI.toString());
Node bodyNode = messageNode.addNode(JCRNodeNames.BODY_NODE_NAME);
Object messageBytes;
try {
messageBytes = payloadProxy.getPayload(message);
} catch (MessageDeliverException e) {
throw new RepositoryException(e);
}
if (messageBytes instanceof byte[]) {
ByteArrayInputStream stream = new ByteArrayInputStream((byte[]) messageBytes);
bodyNode.setProperty(JCRNodeNames.BODY_CONTENT_PROP_NAME, stream);
}
for (String propName : message.getProperties().getNames()) {
Object propObj = message.getProperties().getProperty(propName);
if (propObj instanceof String) {
messageNode.setProperty(propName, (String) propObj);
} else if (propObj instanceof Calendar) {
messageNode.setProperty(propName, (Calendar) propObj);
} else if (propObj instanceof Boolean) {
messageNode.setProperty(propName, (Boolean) propObj);
} else if (propObj instanceof Long) {
messageNode.setProperty(propName, (Long) propObj);
}
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Saved node to content repository:");
dumpNodeToLog(messageNode);
}
return messageURI;
} catch (URIGenerationException e) {
throw new RepositoryException(e);
}
}
/**
* Load a message from the content repository.
*
* @param root
* @param uri
* @return
* @throws RepositoryException
*/
protected Message loadMessage(Node root, URI uri) throws RepositoryException {
assertConfigured("loadMessage");
try {
Node messageNode = root.getNode(uri.toString());
Node bodyNode = messageNode.getNode(JCRNodeNames.BODY_NODE_NAME);
Property bodyContents = bodyNode.getProperty(JCRNodeNames.BODY_CONTENT_PROP_NAME);
Message message = MessageFactory.getInstance().getMessage();
if (bodyContents != null) {
byte[] contentBytes = IOUtils.toByteArray(bodyContents.getStream());
try {
payloadProxy.setPayload(message, contentBytes);
} catch (MessageDeliverException e) {
throw new RepositoryException(e);
}
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Loaded node from content repository:");
dumpNodeToLog(messageNode);
}
return message;
} catch (IOException e) {
throw new RepositoryException(e);
}
}
private void assertConfigured(String calledMethod) {
if(payloadProxy == null) {
throw new IllegalStateException("Invalid call to '" + calledMethod + "' on '" + getClass().getName() + "'. setConfiguration(ConfigTree) must be called before calling this method.");
}
}
/**
* Dumps the contents of a JCR node to the logger.
*
* @param node
* @throws RepositoryException
*/
protected void dumpNodeToLog(Node node) throws RepositoryException {
LOGGER.info(node.getPath());
if (node.getName().equals("jcr:system")) {
return;
}
// Display all node properties.
PropertyIterator properties = node.getProperties();
while (properties.hasNext()) {
Property property = properties.nextProperty();
if (property.getDefinition().isMultiple()) {
Value[] values = property.getValues();
for (int i = 0; i < values.length; i++) {
LOGGER.info(property.getPath() + " = " + values[i].getString());
}
} else {
LOGGER.info(property.getPath() + " = " + property.getString());
}
}
// Recurse into subnodes.
NodeIterator nodes = node.getNodes();
while (nodes.hasNext()) {
dumpNodeToLog(nodes.nextNode());
}
}
/**
* Get the root node under which messages are stored. Create the root path
* if needed.
*
* @param session
* @return
* @throws RepositoryException
*/
protected Node getMessageStoreRootNode(Session session) throws RepositoryException {
if (messageStoreRootNode == null) {
if (StringUtils.isEmpty(Configuration.getJcrStoreRootNodePath())) {
throw new RepositoryException("JCR root node path not specified in configuration.");
}
Node current = session.getRootNode();
String[] nodeNames = Configuration.getJcrStoreRootNodePath().split("/");
for (String nodeName : nodeNames) {
try {
current = current.getNode(nodeName);
} catch (PathNotFoundException e) {
current = current.addNode(nodeName);
}
}
session.save();
messageStoreRootNode = current;
}
return messageStoreRootNode;
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.services.persistence.MessageStore#getMessageURIGenerator()
*/
public MessageURIGenerator getMessageURIGenerator() {
return uriGenerator;
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.services.persistence.MessageStore#getUndeliveredMessages()
*/
public Map<URI, Message> getUndeliveredMessages(String classification) throws MessageStoreException {
return new HashMap<URI, Message>();
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.services.persistence.MessageStore#getUndeliveredMessages()
*/
public Map<URI, Message> getAllMessages(String classification) throws MessageStoreException {
return new HashMap<URI, Message>();
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.services.persistence.MessageStore#setDelivered(java.net.URI)
*/
public void setDelivered(URI uid) throws MessageStoreException {
}
/*
* (non-Javadoc)
*
* @see org.jboss.soa.esb.services.persistence.MessageStore#setUndelivered(java.net.URI)
*/
public void setUndelivered(URI uid) throws MessageStoreException {
}
/* (non-Javadoc)
* @see org.jboss.soa.esb.services.persistence.MessageStore#removeMessage(java.net.URI, java.lang.String)
*/
public int removeMessage(URI uid, String classification) throws MessageStoreException {
// TODO Auto-generated method stub
return 0;
}
}