/**
* 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.tuscany.sca.osgi.service.discovery.impl;
import static org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper.getConfiguration;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_FRAMEWORK_UUID;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_ID;
import static org.osgi.service.remoteserviceadmin.RemoteConstants.ENDPOINT_SERVICE_ID;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;
import java.util.logging.Level;
import javax.xml.namespace.QName;
import org.apache.tuscany.sca.assembly.Binding;
import org.apache.tuscany.sca.core.UtilityExtensionPoint;
import org.apache.tuscany.sca.deployment.Deployer;
import org.apache.tuscany.sca.implementation.osgi.SCAConfig;
import org.apache.tuscany.sca.implementation.osgi.ServiceDescription;
import org.apache.tuscany.sca.implementation.osgi.ServiceDescriptions;
import org.apache.tuscany.sca.osgi.remoteserviceadmin.impl.OSGiHelper;
import org.apache.tuscany.sca.policy.Intent;
import org.apache.tuscany.sca.policy.PolicySet;
import org.oasisopen.sca.ServiceRuntimeException;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.remoteserviceadmin.EndpointDescription;
import org.osgi.util.tracker.BundleTracker;
import org.osgi.util.tracker.BundleTrackerCustomizer;
import org.osgi.util.tracker.ServiceTracker;
public class LocalDiscoveryService extends AbstractDiscoveryService implements BundleTrackerCustomizer {
private Deployer deployer;
private BundleTracker bundleTracker;
private Collection<ExtenderConfiguration> extenders = new ArrayList<ExtenderConfiguration>();
public LocalDiscoveryService(BundleContext context) {
super(context);
}
public void start() {
super.start();
UtilityExtensionPoint utilities = this.registry.getExtensionPoint(UtilityExtensionPoint.class);
this.deployer = utilities.getUtility(Deployer.class);
bundleTracker = new BundleTracker(context, Bundle.ACTIVE | Bundle.STARTING, this);
bundleTracker.open();
}
public static ServiceTracker getTracker(BundleContext context) {
Filter filter = null;
try {
filter =
context.createFilter("(& (" + Discovery.SUPPORTED_PROTOCOLS
+ "=local) ("
+ Constants.OBJECTCLASS
+ "="
+ Discovery.class.getName()
+ "))");
} catch (InvalidSyntaxException e) {
throw new IllegalArgumentException(e);
}
return new ServiceTracker(context, filter, null);
}
private EndpointDescription createEndpointDescription(ServiceDescription sd) {
Map<String, Object> props = new HashMap<String, Object>(sd.getProperties());
props.put(Constants.OBJECTCLASS, sd.getInterfaces().toArray(new String[sd.getInterfaces().size()]));
if (!props.containsKey(ENDPOINT_SERVICE_ID)) {
props.put(ENDPOINT_SERVICE_ID, Long.valueOf(System.currentTimeMillis()));
}
if (!props.containsKey(ENDPOINT_FRAMEWORK_UUID)) {
props.put(ENDPOINT_FRAMEWORK_UUID, OSGiHelper.getFrameworkUUID(context));
}
if (!props.containsKey(ENDPOINT_ID)) {
props.put(ENDPOINT_ID, UUID.randomUUID().toString());
}
EndpointDescription sed = new EndpointDescription(props);
return sed;
}
private void removeServicesDeclaredInBundle(Bundle bundle) {
for (Iterator<Map.Entry<EndpointDescription, Bundle>> i = endpointDescriptions.entrySet().iterator(); i.hasNext();) {
Entry<EndpointDescription, Bundle> entry = i.next();
if (entry.getValue().equals(bundle)) {
serviceDescriptionRemoved(entry.getKey());
i.remove();
}
}
}
private void serviceDescriptionAdded(EndpointDescription endpointDescription) {
endpointChanged(endpointDescription, ADDED);
}
private void serviceDescriptionRemoved(EndpointDescription endpointDescription) {
endpointChanged(endpointDescription, REMOVED);
}
public void stop() {
if (bundleTracker != null) {
bundleTracker.close();
}
super.stop();
}
public Object addingBundle(Bundle bundle, BundleEvent event) {
if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null || bundle.getBundleId() == 0) {
// Ignore fragments
return null;
}
Collection<URL> scaConfigs = getConfiguration(bundle, "SCA-Configuration", "OSGI-INF/sca-config/*.xml");
Collection<URL> descriptions = getConfiguration(bundle, "Remote-Service", null);
if (scaConfigs.isEmpty() && descriptions.isEmpty()) {
return null;
}
ExtenderConfiguration extender = new ExtenderConfiguration();
for (URL url : scaConfigs) {
try {
SCAConfig scaConfig = deployer.loadXMLDocument(url, deployer.createMonitor());
extender.scaConfigs.add(scaConfig);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
throw new ServiceRuntimeException(e);
}
}
for (URL url : descriptions) {
try {
ServiceDescriptions sds = deployer.loadXMLDocument(url, deployer.createMonitor());
extender.remoteServiceDescriptions.add(sds);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
// throw new ServiceRuntimeException(e);
}
}
// Add to the extenders before notifying the listeners (the endpoints may references to the config)
ExtenderConfiguration.validate(extenders, extender);
this.extenders.add(extender);
// Notify
for (ServiceDescriptions sds : extender.getRemoteServiceDescriptions()) {
for (ServiceDescription sd : sds) {
EndpointDescription sed = createEndpointDescription(sd);
endpointDescriptions.put(sed, bundle);
serviceDescriptionAdded(sed);
}
}
return extender;
}
public void modifiedBundle(Bundle bundle, BundleEvent event, Object object) {
// STARTING --> ACTIVE
}
public void removedBundle(Bundle bundle, BundleEvent event, Object object) {
if (object instanceof ExtenderConfiguration) {
extenders.remove((ExtenderConfiguration)object);
removeServicesDeclaredInBundle(bundle);
}
}
public Collection<ExtenderConfiguration> getConfigurations() {
return extenders;
}
public static class ExtenderConfiguration {
private Collection<SCAConfig> scaConfigs = new ArrayList<SCAConfig>();
private Collection<ServiceDescriptions> remoteServiceDescriptions = new ArrayList<ServiceDescriptions>();
public Collection<ServiceDescriptions> getRemoteServiceDescriptions() {
return remoteServiceDescriptions;
}
public Collection<SCAConfig> getSCAConfigs() {
return scaConfigs;
}
public static void validate(Collection<ExtenderConfiguration> configs, ExtenderConfiguration newConfig) {
Map<QName, Binding> bindings = new HashMap<QName, Binding>();
Map<QName, Intent> intents = new HashMap<QName, Intent>();
Map<QName, PolicySet> policySets = new HashMap<QName, PolicySet>();
for (ExtenderConfiguration config : configs) {
for (SCAConfig c : config.getSCAConfigs()) {
addBindings(bindings, c);
addIntents(intents, c);
addPolicySets(policySets, c);
}
}
for (SCAConfig c : newConfig.getSCAConfigs()) {
addBindings(bindings, c);
addIntents(intents, c);
addPolicySets(policySets, c);
}
}
private static void addIntents(Map<QName, Intent> intents, SCAConfig c) {
for (Intent i: c.getIntents()) {
if (intents.put(i.getName(), i) != null) {
throw new ServiceRuntimeException("Duplicate intent: " + i.getName());
}
}
}
private static void addPolicySets(Map<QName, PolicySet> policySets, SCAConfig c) {
for (PolicySet ps: c.getPolicySets()) {
if (policySets.put(ps.getName(), ps) != null) {
throw new ServiceRuntimeException("Duplicate policySet: " + ps.getName());
}
}
}
private static void addBindings(Map<QName, Binding> bindings, SCAConfig c) {
for (Binding b : c.getBindings()) {
QName name = new QName(c.getTargetNamespace(), b.getName());
if (bindings.put(name, b) != null) {
throw new ServiceRuntimeException("Duplicate binding: " + name);
}
}
}
}
@Override
protected Dictionary<String, Object> getProperties() {
Dictionary<String, Object> props = super.getProperties();
props.put(SUPPORTED_PROTOCOLS, new String[] {"local"});
return props;
}
}