/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.stanbol.entityhub.core.impl;
import java.util.Collection;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.ReferenceStrategy;
import org.apache.stanbol.commons.namespaceprefix.NamespacePrefixService;
import org.apache.stanbol.entityhub.core.site.CacheImpl;
import org.apache.stanbol.entityhub.core.utils.OsgiUtils;
import org.apache.stanbol.entityhub.servicesapi.yard.Cache;
import org.apache.stanbol.entityhub.servicesapi.yard.Yard;
import org.apache.stanbol.entityhub.servicesapi.yard.YardException;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.component.ComponentContext;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* OSGI component for {@link CacheImpl}
* @since 0.12.0
* @author Rupert Westenthaler
*
*/
@Component(
//set the Class name of CacheImpl for backward compatibility
name="org.apache.stanbol.entityhub.core.site.CacheImpl",
configurationFactory = true,
policy = ConfigurationPolicy.REQUIRE, //the baseUri is required!
specVersion = "1.1",
metatype = true,
immediate = true)
@Properties(
value = {
@Property(name = Cache.CACHE_YARD),
@Property(name = Cache.ADDITIONAL_MAPPINGS, cardinality = Integer.MAX_VALUE)})
public class CacheComponent {
private final Logger log = LoggerFactory.getLogger(CacheComponent.class);
private ServiceTracker yardTracker;
private Yard yard;
private ComponentContext cc;
private ServiceRegistration cacheRegistration;
private Cache cache;
private String[] additionalMappings;
@Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY,
policy = ReferencePolicy.DYNAMIC,
bind = "bindNamespacePrefixService",
unbind = "unbindNamespacePrefixService",
strategy = ReferenceStrategy.EVENT)
private NamespacePrefixService nsPrefixService;
protected void bindNamespacePrefixService(NamespacePrefixService ps){
this.nsPrefixService = ps;
updateServiceRegistration(cc, yard, additionalMappings, nsPrefixService);
}
protected void unbindNamespacePrefixService(NamespacePrefixService ps){
if(ps.equals(this.nsPrefixService)){
this.nsPrefixService = null;
updateServiceRegistration(cc, yard, additionalMappings, nsPrefixService);
}
}
@Activate
protected void activate(final ComponentContext context) throws ConfigurationException, YardException, IllegalStateException, InvalidSyntaxException {
if (context == null || context.getProperties() == null) {
throw new IllegalStateException(String.format("Invalid ComponentContext parsed in activate (context=%s)", context));
}
this.cc = context;
Object value = context.getProperties().get(Cache.ADDITIONAL_MAPPINGS);
if(value instanceof String[]){
this.additionalMappings = (String[])value;
} else if(value instanceof String){
this.additionalMappings = new String[]{(String)value};
} else if(value instanceof Collection<?>){
try {
additionalMappings = ((Collection<?>)value).toArray(new String[((Collection<?>)value).size()]);
} catch (ArrayStoreException e) {
throw new ConfigurationException(Cache.ADDITIONAL_MAPPINGS,
"Additional Mappings MUST BE a String, String[] or Collection<String>!",e);
}
} else {
additionalMappings = null;
}
String yardId = OsgiUtils.checkProperty(context.getProperties(), Cache.CACHE_YARD).toString();
String cacheFilter = String.format("(&(%s=%s)(%s=%s))", Constants.OBJECTCLASS, Yard.class.getName(), Yard.ID, yardId);
yardTracker = new ServiceTracker(context.getBundleContext(), context.getBundleContext().createFilter(cacheFilter),
new ServiceTrackerCustomizer() {
//store the reference to the ComponentContext to avoid NPE if deactivate
//is called for the CacheComponent
final ComponentContext cc = context;
@Override
public void removedService(ServiceReference reference, Object service) {
if(service.equals(yard)){
yard = (Yard)yardTracker.getService();
updateServiceRegistration(cc, yard, additionalMappings, nsPrefixService);
}
cc.getBundleContext().ungetService(reference);
}
@Override
public void modifiedService(ServiceReference reference, Object service) {
//the service.ranking might have changed ... so check if the
//top ranked Cache is a different one
Yard newYard = (Yard)yardTracker.getService();
if(newYard == null || !newYard.equals(cache)){
yard = newYard; //set the new cahce
//and update the service registration
updateServiceRegistration(cc, yard, additionalMappings, nsPrefixService);
}
}
@Override
public Object addingService(ServiceReference reference) {
Object service = cc.getBundleContext().getService(reference);
if(service != null){
if(yardTracker.getServiceReference() == null || //the first added Service or
//the new service as higher ranking as the current
(reference.compareTo(yardTracker.getServiceReference()) > 0)){
yard = (Yard)service;
updateServiceRegistration(cc, yard, additionalMappings, nsPrefixService);
} // else the new service has lower ranking as the currently use one
} //else service == null -> ignore
return service;
}
});
yardTracker.open();
}
@Deactivate
protected void deactivate(ComponentContext context) {
this.yardTracker.close();
this.yardTracker = null;
this.cc = null;
this.yard = null;
this.cache = null;
if(this.cacheRegistration != null){
cacheRegistration.unregister();
cacheRegistration = null;
}
}
private synchronized void updateServiceRegistration(ComponentContext cc, Yard yard, String[] additionalMappings, NamespacePrefixService nsPrefixService) {
if(cacheRegistration != null){
cacheRegistration.unregister();
cacheRegistration = null;
cache = null;
}
if(cc != null && yard != null){
try {
cache = new CacheImpl(yard,additionalMappings, nsPrefixService);
} catch (YardException e) {
log.warn("Unable to init Cache for Yard '"+yard.getId()+"'!",e);
}
cacheRegistration = cc.getBundleContext().registerService(Cache.class.getName(), cache,
OsgiUtils.copyConfig(cc.getProperties()));
}
}
}