/*******************************************************************************
* Copyright (c) 2008 Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source$
* Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
* Created on: Mar 12, 2008
* Revision: $Id$
*
* Contributors:
* Cambridge Semantics Incorporated - initial API and implementation
*******************************************************************************/
package org.openanzo.client;
import java.io.StringWriter;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import org.openanzo.combus.CombusConnection;
import org.openanzo.exceptions.AnzoException;
import org.openanzo.exceptions.AnzoRuntimeException;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.exceptions.LogUtils;
import org.openanzo.exceptions.Messages;
import org.openanzo.ontologies.openanzo.AnzoFactory;
import org.openanzo.ontologies.openanzo.NamedGraph;
import org.openanzo.rdf.AnzoGraph;
import org.openanzo.rdf.Constants;
import org.openanzo.rdf.RDFFormat;
import org.openanzo.rdf.Statement;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.Constants.NAMESPACES;
import org.openanzo.rdf.utils.SerializationConstants;
import org.openanzo.rdf.utils.UriGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
*
*/
public class StatementChannel implements IStatementChannel {
private final static Logger log = LoggerFactory.getLogger(StatementChannel.class);
private final CopyOnWriteArraySet<IStatementChannelListener> listeners = new CopyOnWriteArraySet<IStatementChannelListener>();
private final CombusConnection combusConnection;
private final URI uri;
private String topic;
private final AnzoGraph namedGraph;
private final ChannelMessageListener messageListener;
private boolean closed = false;
private final AnzoClient anzoClient;
/**
* Create a new StatementChannel with the given URI and AnzoGraph
*/
StatementChannel(CombusConnection combusConnection, URI uri, AnzoGraph namedGraph, AnzoClient client) {
this.anzoClient = client;
this.combusConnection = combusConnection;
if (!this.combusConnection.isConnected()) {
throw new AnzoRuntimeException(ExceptionConstants.COMBUS.JMS_NOT_CONNECTED);
}
this.uri = uri;
this.namedGraph = namedGraph;
messageListener = new ChannelMessageListener();
}
public void close() throws AnzoException {
closed = true;
this.anzoClient.closeChannel(this);
combusConnection.unregisterTopicListener(topic);
this.namedGraph.close();
for (IStatementChannelListener listener : listeners) {
try {
listener.channelClosed();
} catch (Throwable t) {
if (log.isWarnEnabled()) {
log.warn(LogUtils.COMBUS_MARKER, "Error closing statement channel", t);
}
}
}
}
public AnzoGraph getNamedGraph() {
return namedGraph;
}
public URI getURI() {
return uri;
}
private void notifyListeners(Map<String, Object> messageProperties, Collection<Statement> statements) {
for (IStatementChannelListener listener : listeners) {
try {
listener.statementsReceived(messageProperties, statements);
} catch (Throwable t) {
if (log.isWarnEnabled()) {
log.warn(LogUtils.INTERNAL_MARKER, Messages.formatString(ExceptionConstants.COMBUS.ERROR_CHANNEL_LISTENER, listener.getClass().getName()), t);
}
}
}
}
protected void connect() throws AnzoException {
combusConnection.registerTopicListener(getTopic(), messageListener);
}
public void registerListener(IStatementChannelListener listener) throws AnzoException {
if (closed) {
throw new AnzoException(ExceptionConstants.COMBUS.JMS_NOT_CONNECTED);
}
listeners.add(listener);
if (listeners.size() == 1) {
connect();
}
}
private String getTopic() throws AnzoException {
if (topic == null) {
NamedGraph ng = AnzoFactory.getNamedGraph(uri, namedGraph.getMetadataGraph());
URI uuid = ng.getUuid();
if (uuid != null) {
topic = UriGenerator.generateEncapsulatedString(NAMESPACES.STREAM_TOPIC_PREFIX, uuid.toString());
}
}
return topic;
}
public void sendMessage(Map<String, Object> messageProperties, Collection<Statement> statements) throws AnzoException {
if (closed) {
throw new AnzoException(ExceptionConstants.COMBUS.JMS_NOT_CONNECTED);
}
if (statements.size() > 0) {
try {
TextMessage message = combusConnection.createMessage();
message.setIntProperty(SerializationConstants.protocolVersion, Constants.VERSION);
if (messageProperties != null) {
for (Map.Entry<String, Object> entry : messageProperties.entrySet()) {
if (!entry.getKey().equals(SerializationConstants.userUri)) {
message.setObjectProperty(entry.getKey(), entry.getValue());
}
}
}
StringWriter stmtsWriter = new StringWriter();
org.openanzo.rdf.utils.ReadWriteUtils.writeStatements(statements, stmtsWriter, RDFFormat.JSON, null, false);
message.setText(stmtsWriter.toString());
combusConnection.publishMessage(getTopic(), message);
} catch (JMSException jmsex) {
throw new AnzoException(ExceptionConstants.COMBUS.COULD_NOT_PUBLISH, jmsex);
}
}
}
public void unregisterListener(IStatementChannelListener listener) throws AnzoException {
if (closed) {
throw new AnzoException(ExceptionConstants.COMBUS.JMS_NOT_CONNECTED);
}
listeners.remove(listener);
if (listeners.size() == 0) {
combusConnection.unregisterTopicListener(topic);
}
}
class ChannelMessageListener implements MessageListener {
@SuppressWarnings("unchecked")
public void onMessage(Message message) {
try {
String stmts = ((TextMessage) message).getText();
Collection<Statement> statements = org.openanzo.rdf.utils.ReadWriteUtils.readStatements(stmts, RDFFormat.JSON);
Map<String, Object> properties = new HashMap<String, Object>();
for (Enumeration<String> keys = message.getPropertyNames(); keys.hasMoreElements();) {
String key = keys.nextElement();
properties.put(key, message.getObjectProperty(key));
}
notifyListeners(properties, statements);
} catch (JMSException jmsex) {
log.warn(LogUtils.COMBUS_MARKER, Messages.formatString(ExceptionConstants.COMBUS.ERROR_PROCESSING_MESSGE, "statement channel"), jmsex);
throw new AnzoRuntimeException(ExceptionConstants.COMBUS.JMS_MESSAGE_PARSING, jmsex);
} catch (AnzoException jmsex) {
log.warn(LogUtils.COMBUS_MARKER, Messages.formatString(ExceptionConstants.COMBUS.ERROR_PROCESSING_MESSGE, "statement channel"), jmsex);
throw new AnzoRuntimeException(ExceptionConstants.COMBUS.JMS_MESSAGE_PARSING, jmsex);
}
}
}
}