blob: 6f8f1cfb7e96ce4e1d566552079f6e82fbf796bb [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2006, 2010 VMware Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html and the Apache License v2.0
* is available at http://www.opensource.org/licenses/apache2.0.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* VMware Inc.
*****************************************************************************/
package org.eclipse.gemini.blueprint.service.exporter.support.internal.support;
import org.eclipse.gemini.blueprint.util.OsgiServiceReferenceUtils;
import org.springframework.beans.factory.BeanFactory;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import static java.util.Optional.empty;
import static java.util.Optional.of;
/**
* Class encapsulating the lazy dependency lookup of the target bean. Handles caching as well as the lazy listener
* notifications.
*
* @author Costin Leau
*/
public class LazyTargetResolver implements UnregistrationNotifier {
private final BeanFactory beanFactory;
private final String beanName;
private final boolean cacheService;
private volatile Object target;
private final Object lock = new Object();
private final AtomicBoolean activated;
private final ListenerNotifier notifier;
private volatile ServiceRegistrationDecorator decorator;
public LazyTargetResolver(Object target, BeanFactory beanFactory, String beanName, boolean cacheService,
ListenerNotifier notifier, boolean lazyListeners) {
this.target = target;
this.beanFactory = beanFactory;
this.beanName = beanName;
this.cacheService = cacheService;
this.notifier = notifier;
this.activated = new AtomicBoolean(!lazyListeners);
}
public void activate() {
if (activated.compareAndSet(false, true) && notifier != null) {
// no service registered
if (decorator == null) {
notifier.callUnregister(null, null);
} else {
Object target = getBeanIfAlreadyInstantiatedOrSingletonScoped().orElse(null);
Map properties = (Map) OsgiServiceReferenceUtils.getServicePropertiesSnapshot(decorator.getReference());
notifier.callRegister(target, properties);
}
}
}
public Object getBean() {
if (target != null) {
return target;
}
if (beanFactory.isSingleton(beanName) || !cacheService) {
return beanFactory.getBean(beanName);
}
// Per Blueprint spec, 121.6.8 Scope, a service must be represented by a single component instance,
// regardless of the component's scope. We must thus obtain the non-singleton component instance and treat
// it like a singleton.
//
// However, multiple bean instances may exist and may be requested concurrently in this scenario. Following, we will explicitly allow multiple
// bean instances to be created during concurrent access to avoid interlocking (independent locking here and in the bean factory) which would
// always mean potential deadlocks.
//
// The bean may be created multiple times, however the guarantees of the OSGi spec are met: Exactly one instance
// is shared as the service instance, superfluous instances are simply ignored.
Object targetCandidate = beanFactory.getBean(beanName);
if (target == null) {
synchronized (lock) {
if (target == null) {
target = targetCandidate;
}
}
}
return target;
}
public Class<?> getType() {
if (target != null) {
return target.getClass();
}
if (beanFactory.isSingleton(beanName)) {
return beanFactory.getBean(beanName).getClass();
}
return beanFactory.getType(beanName);
}
public void unregister(Map properties) {
if (activated.get() && notifier != null) {
Object target = getBeanIfAlreadyInstantiatedOrSingletonScoped().orElse(null);
notifier.callUnregister(target, properties);
}
}
public void setDecorator(ServiceRegistrationDecorator decorator) {
this.decorator = decorator;
if (decorator != null) {
decorator.setNotifier(this);
}
}
public void notifyIfPossible() {
if (activated.get() && notifier != null) {
Object target = getBeanIfAlreadyInstantiatedOrSingletonScoped().orElse(null);
Map properties = (Map) OsgiServiceReferenceUtils.getServicePropertiesSnapshot(decorator.getReference());
notifier.callRegister(target, properties);
}
}
// called when the exporter is activated but no service is published
public void startupUnregisterIfPossible() {
if (activated.get() && notifier != null) {
notifier.callUnregister(null, null);
}
}
private Optional<Object> getBeanIfAlreadyInstantiatedOrSingletonScoped() {
if (target != null) {
return of(target);
}
if (cacheService || beanFactory.isSingleton(beanName)) {
return of(getBean());
}
return empty();
}
}