Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.lambdacube.aspecio.internal.service.ServiceWeavingManager Maven / Gradle / Ivy
package io.lambdacube.aspecio.internal.service;
import static io.lambdacube.aspecio.AspecioConstants.SERVICE_ASPECT_WEAVE;
import static io.lambdacube.aspecio.AspecioConstants.SERVICE_ASPECT_WEAVE_OPTIONAL;
import static io.lambdacube.aspecio.AspecioConstants._SERVICE_ASPECT_WOVEN;
import static io.lambdacube.aspecio.internal.AspecioUtils.asStringProperties;
import static io.lambdacube.aspecio.internal.AspecioUtils.getIntValue;
import static io.lambdacube.aspecio.internal.AspecioUtils.getLongValue;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Stream;
import com.github.gfx.util.WeakIdentityHashMap;
import org.osgi.framework.AllServiceListener;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWiring;
import io.lambdacube.aspecio.AspecioConstants;
import io.lambdacube.aspecio.internal.AspecioUtils;
import io.lambdacube.aspecio.internal.logging.AspecioLogger;
import io.lambdacube.aspecio.internal.logging.AspecioLoggerFactory;
import io.lambdacube.aspecio.internal.weaving.AspectWeaver;
import io.lambdacube.aspecio.internal.weaving.BridgingClassLoader;
import io.lambdacube.aspecio.internal.weaving.DynamicClassLoader;
import io.lambdacube.aspecio.internal.weaving.WovenClassHolder;
import io.lambdacube.aspecio.internal.weaving.shared.Woven;
public final class ServiceWeavingManager implements AllServiceListener {
private static final AspecioLogger LOGGER = AspecioLoggerFactory.getLogger(ServiceWeavingManager.class);
private static final String SERVICE_FILTER = MessageFormat.format("(&(|({0}=*)({1}=*))(!({2}=*)))", SERVICE_ASPECT_WEAVE,
SERVICE_ASPECT_WEAVE_OPTIONAL, _SERVICE_ASPECT_WOVEN);
private final Map, WovenService> wovenServiceByServiceRef = Collections.synchronizedSortedMap(new TreeMap<>());
private final Map> wovenServicesByAspect = new ConcurrentHashMap<>();
private final List wovenServiceListeners = new CopyOnWriteArrayList<>();
// Everything in here is weak, using identity equality, so it nicely cleans-up by itself as bundles are cleaned-up,
// if there are no stale-references on our bundles or services of course...
private final Map> classLoaders = Collections.synchronizedMap(new WeakIdentityHashMap<>());
private final BundleContext bundleContext;
private volatile boolean closed = false;
public ServiceWeavingManager(BundleContext bundleContext) {
this.bundleContext = bundleContext;
}
public void open() {
try {
this.bundleContext.addServiceListener(this, SERVICE_FILTER);
ServiceReference>[] serviceReferences = this.bundleContext.getAllServiceReferences((String) null, SERVICE_FILTER);
if (serviceReferences != null) {
synchronized (this) {
for (ServiceReference> sr : serviceReferences) {
onServiceRegistration(sr);
}
}
}
} catch (InvalidSyntaxException e) {
throw new AssertionError("Could not create filter?!", e);
}
}
public void close() {
this.closed = true;
this.bundleContext.removeServiceListener(this);
synchronized (this) {
for (ServiceReference> sr : wovenServiceByServiceRef.keySet()) {
this.bundleContext.ungetService(sr);
}
wovenServiceByServiceRef.clear();
}
}
@Override
public void serviceChanged(ServiceEvent event) {
if (closed) {
return;
}
ServiceReference> sr = event.getServiceReference();
switch (event.getType()) {
case ServiceEvent.REGISTERED:
onServiceRegistration(sr);
break;
case ServiceEvent.MODIFIED:
onServiceUpdate(sr);
break;
case ServiceEvent.MODIFIED_ENDMATCH:
case ServiceEvent.UNREGISTERING:
onServiceDeparture(sr);
break;
}
}
private synchronized void onServiceRegistration(ServiceReference> reference) {
if (wovenServiceByServiceRef.containsKey(reference)) {
// This might happen if a service arrives between the listener registration and the initial getAllServiceReferences call
return;
}
long originalServiceId = getLongValue(reference.getProperty(Constants.SERVICE_ID));
LOGGER.debug("Preparing the weaving service id {} provided by {}", originalServiceId, reference.getBundle().getSymbolicName());
List requiredAspectsToWeave = new ArrayList<>(
Arrays.asList(asStringProperties(reference.getProperty(AspecioConstants.SERVICE_ASPECT_WEAVE))));
List optionalAspectsToWeave = new ArrayList<>(
Arrays.asList(asStringProperties(reference.getProperty(AspecioConstants.SERVICE_ASPECT_WEAVE_OPTIONAL))));
List objectClass = new ArrayList<>(Arrays.asList(asStringProperties(reference.getProperty(Constants.OBJECTCLASS))));
int serviceRanking = getIntValue(reference.getProperty(Constants.SERVICE_RANKING), 0);
ServiceScope serviceScope = ServiceScope.fromString(AspecioUtils.asStringProperty(reference.getProperty(Constants.SERVICE_SCOPE)));
// Keep original properties, except for managed ones.
Hashtable serviceProperties = new Hashtable<>();
for (String key : reference.getPropertyKeys()) {
Object val = reference.getProperty(key);
switch (key) {
case Constants.SERVICE_ID:
case Constants.SERVICE_PID:
case Constants.SERVICE_BUNDLEID:
case Constants.SERVICE_RANKING:
case Constants.OBJECTCLASS:
case Constants.SERVICE_SCOPE:
case AspecioConstants.SERVICE_ASPECT_WEAVE:
case AspecioConstants.SERVICE_ASPECT_WEAVE_OPTIONAL:
continue;
default:
serviceProperties.put(key, val);
}
}
serviceRanking++;
// Check if we can weave it
List> interfaces = new ArrayList<>();
for (String intf : objectClass) {
try {
Class> cls = reference.getBundle().loadClass(intf);
if (!cls.isInterface()) {
// Cannot weave!
LOGGER.warn("Cannot weave service id {} because it provides service that are not interfaces, such as {}",
originalServiceId, cls.getName());
bundleContext.ungetService(reference);
return;
}
interfaces.add(cls);
} catch (ClassNotFoundException e) {
// Should not happen
LOGGER.error("Could not find class, not weaving service id {}", originalServiceId, e);
bundleContext.ungetService(reference);
return;
}
}
serviceProperties.put(Constants.SERVICE_RANKING, serviceRanking);
AspecioServiceObject aspecioServiceObject = new AspecioServiceObject(serviceScope, reference,
originalService -> weave(interfaces, originalService));
WovenService wovenService = new WovenService(originalServiceId, requiredAspectsToWeave, optionalAspectsToWeave, reference,
objectClass, serviceProperties, aspecioServiceObject);
wovenServiceByServiceRef.put(reference, wovenService);
Iterator aspectIt = Stream.concat(requiredAspectsToWeave.stream(), optionalAspectsToWeave.stream()).distinct().iterator();
while (aspectIt.hasNext()) {
String aspect = aspectIt.next();
List wovenServices = wovenServicesByAspect.computeIfAbsent(aspect, k -> new ArrayList<>());
wovenServices.add(wovenService);
}
fireEvent(WovenServiceEvent.SERVICE_REGISTRATION, wovenService);
}
private synchronized void onServiceUpdate(ServiceReference> reference) {
WovenService wovenService = wovenServiceByServiceRef.get(reference);
if (wovenService == null) {
return;
}
List requiredAspectsToWeave = new ArrayList<>(
Arrays.asList(asStringProperties(reference.getProperty(AspecioConstants.SERVICE_ASPECT_WEAVE))));
List optionalAspectsToWeave = new ArrayList<>(
Arrays.asList(asStringProperties(reference.getProperty(AspecioConstants.SERVICE_ASPECT_WEAVE_OPTIONAL))));
int serviceRanking = getIntValue(reference.getProperty(Constants.SERVICE_RANKING), 0);
// Keep original properties, except for managed ones.
Hashtable serviceProperties = new Hashtable<>();
for (String key : reference.getPropertyKeys()) {
Object val = reference.getProperty(key);
switch (key) {
case Constants.SERVICE_ID:
case Constants.SERVICE_PID:
case Constants.SERVICE_BUNDLEID:
case Constants.SERVICE_RANKING:
case Constants.OBJECTCLASS:
case Constants.SERVICE_SCOPE:
case AspecioConstants.SERVICE_ASPECT_WEAVE:
case AspecioConstants.SERVICE_ASPECT_WEAVE_OPTIONAL:
continue;
default:
serviceProperties.put(key, val);
}
}
serviceRanking++;
serviceProperties.put(Constants.SERVICE_RANKING, serviceRanking);
boolean requiredAspectsChanged = !Objects.equals(requiredAspectsToWeave, wovenService.requiredAspects);
boolean optionalAspectsChanged = !Objects.equals(optionalAspectsToWeave, wovenService.optionalAspects);
boolean servicePropertiesChanged = !Objects.equals(serviceProperties, wovenService.serviceProperties);
WovenService updatedWovenService = wovenService.update(requiredAspectsToWeave, optionalAspectsToWeave, serviceProperties);
int mask = requiredAspectsChanged ? WovenServiceEvent.REQUIRED_ASPECT_CHANGE : 0;
mask |= optionalAspectsChanged ? WovenServiceEvent.OPTIONAL_ASPECT_CHANGE : 0;
mask |= servicePropertiesChanged ? WovenServiceEvent.SERVICE_PROPERTIES_CHANGE : 0;
if (mask != 0) {
fireEvent(new WovenServiceEvent(WovenServiceEvent.EventKind.SERVICE_UPDATE, mask), updatedWovenService);
}
}
private synchronized void onServiceDeparture(ServiceReference> reference) {
WovenService wovenService = wovenServiceByServiceRef.get(reference);
if (wovenService == null) {
return;
}
fireEvent(WovenServiceEvent.SERVICE_DEPARTURE, wovenService);
}
private Woven weave(List> interfaces, Object delegateToWeave) {
DynamicClassLoader dynamicClassLoader = getDynamicClassLoader(delegateToWeave);
WovenClassHolder wovenClassHolder = AspectWeaver.weave(dynamicClassLoader, delegateToWeave.getClass(),
interfaces.toArray(new Class>[0]));
return wovenClassHolder.weavingFactory.apply(delegateToWeave);
}
private DynamicClassLoader getDynamicClassLoader(Object delegateToWeave) {
Bundle bundle = FrameworkUtil.getBundle(delegateToWeave.getClass());
BundleRevision bundleRev = bundle.adapt(BundleRevision.class);
Map clByRev = classLoaders.computeIfAbsent(bundle,
k -> Collections.synchronizedMap(new WeakIdentityHashMap<>()));
DynamicClassLoader dynamicClassLoader = clByRev.computeIfAbsent(bundleRev,
k -> {
ClassLoader classLoader = k.getBundle().adapt(BundleWiring.class).getClassLoader();
return new DynamicClassLoader(new BridgingClassLoader(classLoader,
AspectWeaver.class.getClassLoader()));
});
return dynamicClassLoader;
}
private void fireEvent(WovenServiceEvent event, WovenService wovenService) {
wovenServiceListeners.forEach(l -> l.onWovenServiceEvent(event, wovenService));
}
public void addListener(WovenServiceListener wovenServiceListener) {
wovenServiceListeners.add(wovenServiceListener);
}
public void removeListener(WovenServiceListener wovenServiceListener) {
wovenServiceListeners.remove(wovenServiceListener);
}
public List getWovenServicesForAspect(String aspectName) {
return wovenServicesByAspect.get(aspectName);
}
}