/******************************************************************************* * Copyright (c) 2007-2010 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: Nov 1, 2007 * Revision: $Id$ * * Contributors: * Cambridge Semantics Incorporated - initial API and implementation *******************************************************************************/ package org.openanzo.datasource.services; import java.io.Writer; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import org.apache.commons.collections15.MultiMap; import org.openanzo.datasource.IUpdateService; import org.openanzo.exceptions.AnzoException; import org.openanzo.exceptions.ExceptionConstants; import org.openanzo.exceptions.LogUtils; import org.openanzo.rdf.Statement; import org.openanzo.rdf.URI; import org.openanzo.rdf.utils.AnzoMultiMap; import org.openanzo.rdf.utils.ReadWriteUtils; import org.openanzo.services.AnzoPrincipal; import org.openanzo.services.DynamicServiceStats; import org.openanzo.services.IOperationContext; import org.openanzo.services.IUpdateResultListener; import org.openanzo.services.IUpdates; import org.openanzo.services.serialization.CommonSerializationUtils; import org.openanzo.services.serialization.IUpdatesHandler; import org.openanzo.services.serialization.IUpdatesReader; import org.openanzo.services.serialization.UpdatesCollector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base implementation of the IUpdateService * * @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>) * */ public abstract class BaseUpdateService extends BaseDatasourceComponent implements IUpdateService { private static final Logger log = LoggerFactory.getLogger(BaseUpdateService.class); /** {@link IUpdateResultListener}s for updates that are fired to datasource internal update listeners */ protected final Collection<IUpdateResultListener> datasourceUpdateResultListeners = new HashSet<IUpdateResultListener>(); /** {@link IUpdateResultListener}s for updates to external update listeners */ protected final Collection<IUpdateResultListener> globalUpdateResultListeners = new HashSet<IUpdateResultListener>(); private final DynamicServiceStats stats = new DynamicServiceStats("update", "importStatements"); private static final String FIRE_GLOBAL_UDPATE_EVENTS = "fireGlobalUpdateEvents"; public String getName() { return getDatasource().getName() + ",Service=UpdateService"; } public String getDescription() { return "Update Service for " + getDatasource().getName(); } /** * @return the stats */ public DynamicServiceStats getStatistics() { return stats; } /** * Add a global update result listener * * @param listener * global update result listener */ public void addGlobalUpdateResultListener(IUpdateResultListener listener) { if (listener != null) globalUpdateResultListeners.add(listener); } /** * Remove a global update result listener * * @param listener * global update result listener */ public void removeGlobalUpdateResultListener(IUpdateResultListener listener) { if (listener != null) globalUpdateResultListeners.remove(listener); } /** * Add a datasource specific update result listener * * @param listener * global update result listener */ public void addDatasourceUpdateResultListener(IUpdateResultListener listener) { if (listener != null) datasourceUpdateResultListeners.add(listener); } /** * Remove a datasource specific update result listener * * @param listener * global update result listener */ public void removeDatasourceUpdateResultListener(IUpdateResultListener listener) { if (listener != null) datasourceUpdateResultListeners.remove(listener); } public void start() throws AnzoException { stats.setEnabled(true); } public void reset() throws AnzoException { stats.reset(); } /** * Checks if the user calling the operation is an anonymous user. If it is an anonymous user, then an exception is thrown. * * @param context * @throws AnzoException */ private void throwExceptionIfAnonymousUser(IOperationContext context) throws AnzoException { AnzoPrincipal principal = context.getOperationPrincipal(); if (principal != null && principal.isAnonymous()) { throw new AnzoException(ExceptionConstants.SERVER.ANONYMOUS_UPDATE_NOT_ALLOWED_ERROR); } } public IUpdates update(IOperationContext context, boolean returnResults, IUpdates transactions) throws AnzoException { long start = 0; if (stats.isEnabled() || log.isDebugEnabled()) { start = System.currentTimeMillis(); } throwExceptionIfAnonymousUser(context); if (getLockProvider() != null) getLockProvider().readLock().lock(); logEntry(); try { IUpdates updateResults = updateInternal(context, returnResults, transactions); for (IUpdateResultListener listener : datasourceUpdateResultListeners) { try { listener.updateComplete(context, updateResults); } catch (AnzoException ae) { log.error(LogUtils.DATASOURCE_MARKER, "Error in listener:" + listener.getClass().getName()); } } Boolean fireGlobal = (Boolean) context.getAttribute(FIRE_GLOBAL_UDPATE_EVENTS); if (fireGlobal == null || fireGlobal.booleanValue()) { for (IUpdateResultListener listener : globalUpdateResultListeners) { try { listener.updateComplete(context, updateResults); } catch (AnzoException ae) { log.error(LogUtils.DATASOURCE_MARKER, "Error in listener:" + listener.getClass().getName()); } } } return updateResults; } finally { if (log.isDebugEnabled()) { log.debug(LogUtils.TIMING_MARKER, "Base Update Total,{}", (System.currentTimeMillis() - start)); } if (stats.isEnabled()) { stats.use("update", (System.currentTimeMillis() - start)); } if (getLockProvider() != null) getLockProvider().readLock().unlock(); logExit(); } } public void update(IOperationContext context, boolean returnResults, String input, String inputFormat, Writer output, String resultFormat) throws AnzoException { long start = 0; if (stats.isEnabled() || log.isDebugEnabled()) { start = System.currentTimeMillis(); } throwExceptionIfAnonymousUser(context); if (getLockProvider() != null) getLockProvider().readLock().lock(); logEntry(); try { IUpdatesReader formatReader = CommonSerializationUtils.getUpdatesReader(input, inputFormat); UpdatesCollector collector = new UpdatesCollector(); // TODO: should we stream update results? IUpdates updateResults = updateInternal(context, returnResults, formatReader, collector); for (IUpdateResultListener listener : datasourceUpdateResultListeners) { try { listener.updateComplete(context, updateResults); } catch (AnzoException ae) { log.error(LogUtils.DATASOURCE_MARKER, "Error in listener:" + listener.getClass().getName()); } } Boolean fireGlobal = (Boolean) context.getAttribute(FIRE_GLOBAL_UDPATE_EVENTS); if (fireGlobal == null || fireGlobal.booleanValue()) { for (IUpdateResultListener listener : globalUpdateResultListeners) { try { listener.updateComplete(context, updateResults); } catch (AnzoException ae) { log.error(LogUtils.DATASOURCE_MARKER, "Error in listener:" + listener.getClass().getName()); } } } CommonSerializationUtils.writeUpdates(returnResults, collector.getUpdates(), output, resultFormat); } finally { if (log.isDebugEnabled()) { log.debug(LogUtils.TIMING_MARKER, "Base Update Time,{}", (System.currentTimeMillis() - start)); } if (stats.isEnabled()) { stats.use("update", (System.currentTimeMillis() - start)); } if (getLockProvider() != null) getLockProvider().readLock().unlock(); logExit(); } } public IUpdates importStatements(IOperationContext context, Collection<Statement> statements, Collection<Statement> graphTemplate) throws AnzoException { long start = 0; if (stats.isEnabled()) { start = System.currentTimeMillis(); } throwExceptionIfAnonymousUser(context); if (getLockProvider() != null) getLockProvider().readLock().lock(); logEntry(); try { MultiMap<URI, Statement> map = new AnzoMultiMap<URI, Statement>(); for (Statement stmt : statements) { URI ngUri = stmt.getNamedGraphUri(); if (ngUri == null) { throw new AnzoException(ExceptionConstants.DATASOURCE.STATEMENT_NO_GRAPH); } map.put(ngUri, stmt); } if (graphTemplate == null) { graphTemplate = Collections.<Statement> emptySet(); } IUpdates updateResults = importStatementsInternal(context, map, graphTemplate); for (IUpdateResultListener listener : datasourceUpdateResultListeners) { try { listener.updateComplete(context, updateResults); } catch (AnzoException ae) { log.error(LogUtils.DATASOURCE_MARKER, "Error in listener:" + listener.getClass().getName()); } } Boolean fireGlobal = (Boolean) context.getAttribute(FIRE_GLOBAL_UDPATE_EVENTS); if (fireGlobal == null || fireGlobal.booleanValue()) { for (IUpdateResultListener listener : globalUpdateResultListeners) { try { listener.updateComplete(context, updateResults); } catch (AnzoException ae) { log.error(LogUtils.DATASOURCE_MARKER, "Error in listener:" + listener.getClass().getName()); } } } if (log.isDebugEnabled()) { log.debug(LogUtils.TIMING_MARKER, "Base Import,{},{}", (System.currentTimeMillis() - start), map.values().size()); } return updateResults; } finally { if (stats.isEnabled()) { stats.use("importStatements", (System.currentTimeMillis() - start)); } if (getLockProvider() != null) getLockProvider().readLock().unlock(); logExit(); } } public void importStatements(IOperationContext context, String reader, String readerFormat, Collection<Statement> graphTemplate, Writer output, String resultFormat) throws AnzoException { long start = 0; if (stats.isEnabled()) { start = System.currentTimeMillis(); } throwExceptionIfAnonymousUser(context); if (getLockProvider() != null) getLockProvider().readLock().lock(); logEntry(); try { MultiMap<URI, Statement> statements = ReadWriteUtils.readStatementSets(reader, readerFormat); if (statements.containsKey(null)) { throw new AnzoException(ExceptionConstants.DATASOURCE.STATEMENT_NO_GRAPH); } if (graphTemplate == null) { graphTemplate = Collections.<Statement> emptySet(); } IUpdates updateResults = importStatementsInternal(context, statements, graphTemplate); for (IUpdateResultListener listener : datasourceUpdateResultListeners) { try { listener.updateComplete(context, updateResults); } catch (AnzoException ae) { log.error(LogUtils.DATASOURCE_MARKER, "Error in listener:" + listener.getClass().getName()); } } Boolean fireGlobal = (Boolean) context.getAttribute(FIRE_GLOBAL_UDPATE_EVENTS); if (fireGlobal == null || fireGlobal.booleanValue()) { for (IUpdateResultListener listener : globalUpdateResultListeners) { try { listener.updateComplete(context, updateResults); } catch (AnzoException ae) { log.error(LogUtils.DATASOURCE_MARKER, "Error in listener:" + listener.getClass().getName()); } } } CommonSerializationUtils.writeUpdates(false, updateResults, output, resultFormat); if (log.isDebugEnabled()) { log.debug(LogUtils.TIMING_MARKER, "Base Import,{},{}", (System.currentTimeMillis() - start), statements.values().size()); } } finally { if (stats.isEnabled()) { stats.use("importStatements", (System.currentTimeMillis() - start)); } if (getLockProvider() != null) getLockProvider().readLock().unlock(); logExit(); } } /** * Update the server * * @param context * {@link IOperationContext} context for this operation * @param returnResults * return update results and not just errors * @param transactions * The set of transactions to commit to the server * @return the results of updating the server * @throws AnzoException */ protected abstract IUpdates updateInternal(IOperationContext context, boolean returnResults, IUpdates updates) throws AnzoException; /** * Update the server * * @param context * {@link IOperationContext} context for this operation * @param returnResults * return update results and not just errors * @param reader * reader providing the transactions that need to be processed * @param writer * writer that transaction results should be written to if returnResults is true * @return the results of updating the server * @throws AnzoException */ protected abstract IUpdates updateInternal(IOperationContext context, boolean returnResults, IUpdatesReader reader, IUpdatesHandler writer) throws AnzoException; /** * Update the server * * @param context * {@link IOperationContext} context for this operation * @param returnResults * return update results and not just errors * @param statements * The set of statements to import to the server * @param graphTemplate * The set of statements to use as new graph template * @return the results of updating the server * @throws AnzoException */ protected abstract IUpdates importStatementsInternal(IOperationContext context, MultiMap<URI, Statement> statements, Collection<Statement> graphTemplate) throws AnzoException; }