/*******************************************************************************
* 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
*
* File: $Source$
* Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>)
* Created on: Nov 23, 2007
* Revision: $Id$
*
* Contributors:
* Cambridge Semantics Incorporated - initial API and implementation
*******************************************************************************/
package org.openanzo.datasource.services;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.openanzo.analysis.RequestAnalysis;
import org.openanzo.cache.ICache;
import org.openanzo.cache.ICacheProvider;
import org.openanzo.datasource.IAuthorizationService;
import org.openanzo.datasource.IDatasource;
import org.openanzo.exceptions.AnzoException;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.ontologies.openanzo.NamedGraph;
import org.openanzo.rdf.Constants;
import org.openanzo.rdf.Statement;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.Constants.NAMESPACES;
import org.openanzo.services.DynamicServiceStats;
import org.openanzo.services.IAuthorizationEventListener;
import org.openanzo.services.IOperationContext;
import org.openanzo.services.Privilege;
import org.openanzo.services.serialization.WriterStringValueSetHandler;
import org.slf4j.MDC;
/**
* A cached authorization service caches the acls and results from requests, and updates caches based on updates.
*
* @author Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com</a>)
*
*/
public class CachedAuthorizationService implements IAuthorizationService, IAuthorizationEventListener {
protected CachedAuthorizationServiceStats stats = null;
/** Service's Name in {@link String} form */
public static final String CACHED_SERVICE_NAME = NAMESPACES.SERVICE_PREFIX + "CachedAuthorizationService";
/** Service's Name in {@link String} form */
public static final URI CACHED_SERVICE_URI = Constants.valueFactory.createURI(CACHED_SERVICE_NAME);
protected final IAuthorizationService parentService;
private static String CACHED = "CachedOperation";
private ICache<URI, Map<Privilege, Set<URI>>> cache;
/**
* Create a new CacheAuthorizationService
*
* @param parentService
* the parent authorization service
* @param cacheProvider
* cache provider the the authorization service
*/
public CachedAuthorizationService(IAuthorizationService parentService, ICacheProvider cacheProvider) {
stats = new CachedAuthorizationServiceStats("getRolesForGraph");
this.parentService = parentService;
this.cache = (cacheProvider != null) ? cacheProvider.<URI, Map<Privilege, Set<URI>>> openCache(parentService.getDatasource().getInstanceURI() + "_" + "AuthorizationService", 10000, true) : null;
}
public String getName() {
return parentService.getName() + "Cached";
}
public String getDescription() {
return "Cached Authorization Service for " + parentService.getName();
}
public IDatasource getDatasource() {
return parentService.getDatasource();
}
public void start() throws AnzoException {
parentService.start();
stats.setEnabled(true);
}
public void close() throws AnzoException {
parentService.close();
}
public DynamicServiceStats getStatistics() {
return stats;
}
private ThreadLocal<ClassLoader> oldLoader = new ThreadLocal<ClassLoader>();
private void entry() {
oldLoader.set(Thread.currentThread().getContextClassLoader());
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
}
private void exit() {
Thread.currentThread().setContextClassLoader(oldLoader.get());
oldLoader.remove();
}
public Set<URI> getRolesForGraph(IOperationContext context, URI namedGraphUri, Privilege privilage) throws AnzoException {
long start = 0;
if (stats.isEnabled()) {
start = System.currentTimeMillis();
}
entry();
try {
Map<Privilege, Set<URI>> ufg = (cache != null) ? (Map<Privilege, Set<URI>>) cache.get(namedGraphUri) : null;
Set<URI> roles = null;
if (ufg == null || !ufg.containsKey(privilage)) {
if (stats.isEnabled())
stats.getGetRolesForGraphCacheMiss().increment();
roles = parentService.getRolesForGraph(context, namedGraphUri, privilage);
if (ufg == null) {
ufg = new HashMap<Privilege, Set<URI>>();
}
ufg.put(privilage, roles);
if (cache != null)
cache.put(namedGraphUri, ufg);
if (RequestAnalysis.isAnalysisEnabled()) {
RequestAnalysis.addAnalysisProperty(RequestAnalysis.ANS_PROP_CACHE_HIT, Boolean.FALSE);
}
} else {
roles = ufg.get(privilage);
if (stats.isEnabled())
stats.getGetRolesForGraphCacheHit().increment();
if (RequestAnalysis.isAnalysisEnabled()) {
RequestAnalysis.addAnalysisProperty(RequestAnalysis.ANS_PROP_CACHE_HIT, Boolean.TRUE);
}
}
return roles;
} finally {
exit();
if (stats.isEnabled()) {
stats.use("getRolesForGraph", (System.currentTimeMillis() - start));
}
}
}
public void getRolesForGraph(IOperationContext context, URI namedGraphUri, Privilege privilage, Writer output, String format) throws AnzoException {
Set<URI> rfg = getRolesForGraph(context, namedGraphUri, privilage);
WriterStringValueSetHandler vsh = new WriterStringValueSetHandler(output, format);
try {
vsh.start();
for (URI role : rfg) {
vsh.handleValue(role.toString());
}
vsh.end();
} catch (IOException ioe) {
throw new AnzoException(ExceptionConstants.IO.WRITE_ERROR, ioe);
}
}
public void handleAuthorizationUpdates(Set<Statement> aclAdditions, Set<Statement> aclRemovals) throws AnzoException {
entry();
try {
for (Statement acl : aclRemovals) {
URI namedGraphUri = (URI) acl.getSubject();
URI privilege = acl.getPredicate();
URI role = (URI) acl.getObject();
Privilege priv = null;
if (privilege.equals(NamedGraph.canBeAddedToByProperty)) {
priv = Privilege.ADD;
} else if (privilege.equals(NamedGraph.canBeRemovedFromByProperty)) {
priv = Privilege.REMOVE;
} else if (privilege.equals(NamedGraph.canBeReadByProperty)) {
priv = Privilege.READ;
}
Map<Privilege, Set<URI>> roleMap = (cache != null) ? (Map<Privilege, Set<URI>>) cache.get(namedGraphUri) : null;
if (roleMap != null) {
if (roleMap.containsKey(priv)) {
Set<URI> roles = roleMap.get(priv);
//log.debug("Removing role from cache");
roles.remove(role);
if (cache != null)
cache.put(namedGraphUri, roleMap);
}
}
}
for (Statement acl : aclAdditions) {
URI namedGraphUri = (URI) acl.getSubject();
URI privilege = acl.getPredicate();
URI role = (URI) acl.getObject();
Privilege priv = null;
if (privilege.equals(NamedGraph.canBeAddedToByProperty)) {
priv = Privilege.ADD;
} else if (privilege.equals(NamedGraph.canBeRemovedFromByProperty)) {
priv = Privilege.REMOVE;
} else if (privilege.equals(NamedGraph.canBeReadByProperty)) {
priv = Privilege.READ;
}
Map<Privilege, Set<URI>> roleMap = (cache != null) ? (Map<Privilege, Set<URI>>) cache.get(namedGraphUri) : null;
if (roleMap != null) {
if (roleMap.containsKey(priv)) {
Set<URI> roles = roleMap.get(priv);
// log.debug("Adding role to cache");
roles.add(role);
if (cache != null)
cache.put(namedGraphUri, roleMap);
}
}
}
} finally {
exit();
}
}
public void reset() throws AnzoException {
parentService.reset();
if (cache != null)
cache.clear();
}
public void logEntry() {
parentService.logEntry();
MDC.put(CACHED, "true");
}
public void logExit() {
parentService.logExit();
MDC.remove(CACHED);
}
}