/*******************************************************************************
* Copyright (c) 2007 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
*
* Contributors:
* Cambridge Semantics Incorporated
*******************************************************************************/
package org.openanzo.client;
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
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.NamedGraph;
import org.openanzo.rdf.AnzoGraph;
import org.openanzo.rdf.INamedGraph;
import org.openanzo.rdf.IQuadStore;
import org.openanzo.rdf.IStatementListener;
import org.openanzo.rdf.Statement;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.utils.SerializationConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
/**
* Implementation for graphs replicated to the client. AnzoClient.getReplicaGraph or AnzoClientGraph.getServerGraph should be called to construct a new
* ClientGraph.
*
* If the entire graph is tracked, replication will update the AnzoClient with all the statements in the server's graph, otherwise only the tracked statements
* will be in the local cache and non-tracked statements will not appear in find requests against the ClientGraph.
*
* @author Joe Betz <jpbetz@cambridgesemantics.com>
* @author Ben Szekely ( <a href="mailto:ben@cambridgesemantics.com">ben@cambridgesemantics.com </a>)
*/
public class ClientGraph extends AnzoGraph {
/**
*
*/
private static final long serialVersionUID = 3892574383524913306L;
private static final Logger log = LoggerFactory.getLogger(ClientGraph.class);
// The anzo client that manages this graph.
final AnzoClient anzoClient;
// The graph table to use to close this graph
final GraphTable graphTable;
protected boolean connected = false;
protected final INamedGraphInitializer[] namedGraphInitializers;
protected long revision = -1;
/**
* create a new AnzoGraph instance. This constructor should not be explicitly called. Instances of AnzoGraph are handed out by the anzoClient
*
* @param namedGraphUri
* The uri of the named graph
* @param store
* Optional store used to store the statements of this graph.
* @param metadataGraph
* The metadata graph that holds system information about this graph.
* @param anzoClient
* The anzo client that manages this graph.
*/
ClientGraph(URI namedGraphUri, IQuadStore store, INamedGraph metadataGraph, AnzoClient anzoClient, GraphTable graphTable, INamedGraphInitializer... graphInitializers) {
super(namedGraphUri, metadataGraph, store);
this.anzoClient = anzoClient;
this.graphTable = graphTable;
notifyAddRemove = false;
this.namedGraphInitializers = (graphInitializers != null) ? graphInitializers : new INamedGraphInitializer[0];
}
@Override
protected void checkClosed() {
super.checkClosed();
if (graphTable.type == GraphTable.Type.SERVER && !anzoClient.isConnected()) {
throw new AnzoRuntimeException(ExceptionConstants.CLIENT.CLIENT_NOT_CONNECTED);
}
}
@Override
public void add(Collection<Statement> statements) {
boolean inTransaction = anzoClient.inTransaction();
if (!inTransaction)
anzoClient.begin();
try {
super.add(statements);
} catch (RuntimeException e) {
if (!inTransaction)
anzoClient.abort();
throw new RuntimeException(e);
}
if (!inTransaction)
anzoClient.commit();
}
@Override
public void add(Statement... stmts) {
boolean inTransaction = anzoClient.inTransaction();
if (!inTransaction)
anzoClient.begin();
try {
super.add(stmts);
} catch (RuntimeException e) {
if (!inTransaction)
anzoClient.abort();
throw new RuntimeException(e);
}
if (!inTransaction)
anzoClient.commit();
}
@Override
public void remove(Collection<Statement> statements) {
boolean inTransaction = anzoClient.inTransaction();
if (!inTransaction)
anzoClient.begin();
try {
super.remove(statements);
} catch (RuntimeException e) {
if (!inTransaction)
anzoClient.abort();
throw new RuntimeException(e);
}
if (!inTransaction)
anzoClient.commit();
}
@Override
public void remove(Statement... stmts) {
boolean inTransaction = anzoClient.inTransaction();
if (!inTransaction)
anzoClient.begin();
try {
super.remove(stmts);
} catch (RuntimeException e) {
if (!inTransaction)
anzoClient.abort();
throw new RuntimeException(e);
}
if (!inTransaction)
anzoClient.commit();
}
/**
* Close the graph.
*/
@Override
public void close() {
anzoClient.clientLock.lock();
try {
GraphTable.ReleaseResult result = graphTable.release(getNamedGraphUri());
if (result.equals(GraphTable.ReleaseResult.CLOSE_ALL)) {
doClose();
} else if (result.equals(GraphTable.ReleaseResult.CLOSE_INSTANCE)) {
super.close();
}
} finally {
anzoClient.clientLock.unlock();
}
}
void doClose() {
try {
metadataGraph.close();
// if (anzoClient.isConnected()) {
if (!anzoClient.replicaGraphTable.contains(getNamedGraphUri()) && !anzoClient.serverGraphTable.contains(getNamedGraphUri())) {
Collection<Statement> stmts = anzoClient.quadStore.find(getNamedGraphUri(), NamedGraph.uuidProperty, null, getMetadataGraph().getNamedGraphUri());
if (!stmts.isEmpty()) {
Statement stmt = stmts.iterator().next();
anzoClient.namedGraphUpdateManager.removeNamedGraphUpdateTopic((URI) stmt.getObject());
}
// we have to clear the quad store for both replica and server
// graphs because server graphs store the uuid in the quad store.
//if (graphTable.type.equals(Type.REPLICA)) {
anzoClient.quadStore.remove(null, null, null, getNamedGraphUri());
anzoClient.quadStore.remove(null, null, null, metadataGraph.getNamedGraphUri());
//}
}
// }
} catch (AnzoException ae) {
throw new AnzoRuntimeException(ae);
} finally {
super.close();
}
}
/**
* @return the revision
*/
public long getRevision() {
return revision;
}
/**
* @param revision
* the revision to set
*/
public void setRevision(long revision) {
this.revision = revision;
}
@Override
public void registerListener(IStatementListener<INamedGraph> listener) {
Set<IStatementListener<INamedGraph>> listeners = graphTable.listeners.get(getNamedGraphUri());
if (listeners == null) {
listeners = new CopyOnWriteArraySet<IStatementListener<INamedGraph>>();
graphTable.listeners.put(getNamedGraphUri(), listeners);
}
listeners.add(listener);
}
@Override
public void unregisterListener(IStatementListener<INamedGraph> listener) {
Set<IStatementListener<INamedGraph>> listeners = graphTable.listeners.get(getNamedGraphUri());
if (listeners != null) {
listeners.remove(listener);
}
}
@Override
public void notifyAddStatements(Statement... statements) {
if (anzoClient.getUserDescription() != null) {
MDC.put(SerializationConstants.userDescription, anzoClient.getUserDescription());
}
try {
Set<IStatementListener<INamedGraph>> listeners = graphTable.listeners.get(getNamedGraphUri());
if (listeners != null) {
for (IStatementListener<INamedGraph> listener : listeners) {
try {
listener.statementsAdded(this, statements);
} catch (Throwable t) {
if (log.isWarnEnabled()) {
log.warn(LogUtils.INTERNAL_MARKER, Messages.formatString(ExceptionConstants.CLIENT.FAILED_NOTIFYING_ADD_STATEMENTS, listener.getClass().getName()), t);
}
}
}
}
} finally {
if (anzoClient.getUserDescription() != null) {
MDC.remove(SerializationConstants.userDescription);
}
}
}
@Override
public void notifyRemoveStatements(Statement... statements) {
if (anzoClient.getUserDescription() != null) {
MDC.put(SerializationConstants.userDescription, anzoClient.getUserDescription());
}
try {
Set<IStatementListener<INamedGraph>> listeners = graphTable.listeners.get(getNamedGraphUri());
if (listeners != null) {
for (IStatementListener<INamedGraph> listener : listeners) {
try {
listener.statementsRemoved(this, statements);
} catch (Throwable t) {
if (log.isWarnEnabled()) {
log.warn(LogUtils.INTERNAL_MARKER, Messages.formatString(ExceptionConstants.CLIENT.FAILED_NOTIFYING_REMOVE_STATEMENTS, listener.getClass().getName()), t);
}
}
}
}
} finally {
if (anzoClient.getUserDescription() != null) {
MDC.remove(SerializationConstants.userDescription);
}
}
}
}