/**
* 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.aries.samples.goat.enhancer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.aries.samples.goat.api.ComponentInfo;
import org.apache.aries.samples.goat.api.ComponentInfoProvider;
import org.apache.aries.samples.goat.api.ModelInfoService;
import org.apache.aries.samples.goat.api.RelationshipInfo;
import org.apache.aries.samples.goat.api.RelationshipInfoProvider;
import org.apache.aries.samples.goat.info.ComponentInfoImpl;
import org.apache.aries.samples.goat.info.RelationshipInfoImpl;
public class ModelInfoEnhancerService implements ModelInfoService,
ComponentInfoProvider, RelationshipInfoProvider,
ComponentInfoProvider.ComponentInfoListener,
RelationshipInfoProvider.RelationshipInfoListener {
private static final String SERVICE_REGISTRATION = "Service registration";
private static final String SERVICE_USAGE = "Service usage";
// TODO where should we expose these shared strings?
private static final String SERVICE = "Service";
private ModelInfoService originalService;
private final Map<String, ComponentInfo> components = new HashMap<String, ComponentInfo>();
private final Map<String, RelationshipInfo> relationships = new HashMap<String, RelationshipInfo>();
private final List<ComponentInfoListener> clisteners;
private final List<RelationshipInfoListener> rlisteners;
public ModelInfoEnhancerService(ModelInfoService infoService) {
clisteners = Collections
.synchronizedList(new ArrayList<ComponentInfoListener>());
rlisteners = Collections
.synchronizedList(new ArrayList<RelationshipInfoListener>());
this.originalService = infoService;
Collection<ComponentInfo> originalComponents = originalService
.getComponentInfoProvider().getComponents();
// We keep all the original components
for (ComponentInfo info : originalComponents) {
components.put(info.getId(), info);
}
// We add a new component for each service
Collection<RelationshipInfo> originalRelationships = originalService
.getRelationshipInfoProvider().getRelationships();
// We keep all the original components
for (RelationshipInfo rel : originalRelationships) {
if (SERVICE.equals(rel.getType())) {
ComponentInfoImpl serviceComponent = new ComponentInfoImpl();
String id = constructServiceComponentId(rel);
serviceComponent.setId(id);
Map<String, String> componentProperties = new HashMap<String, String>();
componentProperties.put("Name", rel.getName());
serviceComponent.setComponentProperties(componentProperties);
components.put(id, serviceComponent);
// Make new relationships;
RelationshipInfoImpl registration = new RelationshipInfoImpl();
registration.setType(SERVICE_REGISTRATION);
registration.setName(rel.getName());
registration.setProvidedBy(rel.getProvidedBy());
registration.setRelationshipAspects(rel
.getRelationshipAspects());
ArrayList<ComponentInfo> arrayList = new ArrayList<ComponentInfo>();
arrayList.add(serviceComponent);
registration.setConsumedBy(arrayList);
relationships.put(constructId(registration), registration);
RelationshipInfoImpl consumption = new RelationshipInfoImpl();
consumption.setType(SERVICE_USAGE);
consumption.setName(rel.getName());
consumption.setProvidedBy(serviceComponent);
consumption.setConsumedBy(rel.getConsumedBy());
consumption
.setRelationshipAspects(rel.getRelationshipAspects());
relationships.put(constructId(consumption), consumption);
} else {
// Pass non-service relationships through
relationships.put(constructId(rel), rel);
}
originalService.getComponentInfoProvider()
.registerComponentInfoListener(this);
originalService.getRelationshipInfoProvider()
.registerRelationshipInfoListener(this);
}
}
@Override
public String getName() {
return "Model Enhancer Service";
}
@Override
public ComponentInfoProvider getComponentInfoProvider() {
return this;
}
@Override
public RelationshipInfoProvider getRelationshipInfoProvider() {
return this;
}
@Override
public Collection<RelationshipInfo> getRelationships() {
return relationships.values();
}
@Override
public Collection<ComponentInfo> getComponents() {
return components.values();
}
@Override
public ComponentInfo getComponentForId(String id) {
return components.get(id);
}
@Override
public void registerRelationshipInfoListener(
RelationshipInfoListener listener) {
rlisteners.add(listener);
}
@Override
public void registerComponentInfoListener(ComponentInfoListener listener) {
clisteners.add(listener);
}
@Override
public void updateRelationship(RelationshipInfo r) {
if (SERVICE.equals(r.getType())) {
updateSyntheticServiceArtefactsAndNotifyListeners(r);
} else {
// Update our copy
relationships.put(constructId(r), r);
// This shouldn't affect us, but pass it on to our listeners
for (RelationshipInfoListener listener : rlisteners) {
listener.updateRelationship(r);
}
}
}
@Override
public void removeRelationship(RelationshipInfo r) {
if (SERVICE.equals(r.getType())) {
removeSyntheticServiceArtefactsAndNotifyListeners(r);
} else {
// We don't want to track this relationship anymore
String id = constructId(r);
RelationshipInfo relationship = relationships.get(id);
relationships.remove(id);
if (relationship != null) {
// This shouldn't affect us, but pass it on to our listeners
for (RelationshipInfoListener listener : rlisteners) {
listener.removeRelationship(relationship);
}
}
}
}
@Override
public void updateComponent(ComponentInfo b) {
// Update our copy
components.put(b.getId(), b);
// This shouldn't affect us, but pass it on to our listeners
for (ComponentInfoListener listener : clisteners) {
listener.updateComponent(b);
}
}
@Override
public void removeComponent(ComponentInfo b) {
// This shouldn't affect us unless it has relationships pointing to it
// Cheerfully assume that gets handled upstream
// We don't want to know about this component anymore
ComponentInfo component = components.remove(b);
if (component != null) {// This shouldn't affect us, but pass it on to
// our listeners
for (ComponentInfoListener listener : clisteners) {
listener.removeComponent(component);
}
}
}
private String constructServiceComponentId(RelationshipInfo rel) {
return "/syntheticenhancedservices/" + rel.getName() + "/"
+ rel.getProvidedBy().getId();
}
private String constructId(RelationshipInfo b) {
return b.getType() + "/" + b.getName() + "/"
+ b.getProvidedBy().getId();
}
private void removeSyntheticServiceArtefactsAndNotifyListeners(
RelationshipInfo r) {
// We need to remove our two relationships and the synthetic
// component
String componentId = constructServiceComponentId(r);
// Do the relationships first
// The registration has type "service registration", and the
// original provider and name
String registrationRelationshipId = SERVICE_REGISTRATION + "/"
+ r.getName() + "/" + r.getProvidedBy().getId();
RelationshipInfo registrationRelationship = relationships
.get(registrationRelationshipId);
// The consumers have type "service usage", and the
// original name, and the new provided by
String usageRelationshipId = SERVICE_USAGE + "/" + r.getName() + "/"
+ componentId;
RelationshipInfo usageRelationship = relationships
.get(usageRelationshipId);
relationships.remove(usageRelationshipId);
relationships.remove(registrationRelationshipId);
// Tell our listeners about the relationships first
for (RelationshipInfoListener listener : rlisteners) {
if (usageRelationship != null) {
listener.removeRelationship(usageRelationship);
}
if (registrationRelationship != null) {
listener.removeRelationship(registrationRelationship);
}
}
ComponentInfo component = components.remove(componentId);
if (component != null) {
// Tell our listeners their service component went away
for (ComponentInfoListener listener : clisteners) {
listener.removeComponent(component);
}
}
}
private void updateSyntheticServiceArtefactsAndNotifyListeners(
RelationshipInfo r) {
// We need to update our two relationships and the synthetic
// component
// Hopefully the thing which changed won't prevent us
// from finding our relationship
String componentId = constructServiceComponentId(r);
// Do the relationships first
// The registration has type "service registration", and the
// original provider and name
String registrationRelationshipId = SERVICE_REGISTRATION + "/"
+ r.getName() + "/" + r.getProvidedBy().getId();
RelationshipInfoImpl registrationRelationship = (RelationshipInfoImpl) relationships
.get(registrationRelationshipId);
registrationRelationship.setName(r.getName());
registrationRelationship.setRelationshipAspects(r
.getRelationshipAspects());
// The consumers have type "service usage", and the
// original name, and the new provided by
String usageRelationshipId = SERVICE_USAGE + "/" + r.getName() + "/"
+ componentId;
RelationshipInfoImpl usageRelationship = (RelationshipInfoImpl) relationships
.get(usageRelationshipId);
// The consumers may have changed, so we update the usage relationship
usageRelationship.setConsumedBy(r.getConsumedBy());
usageRelationship.setName(r.getName());
usageRelationship.setRelationshipAspects(r.getRelationshipAspects());
// Tell our listeners about the relationships first
for (RelationshipInfoListener listener : rlisteners) {
if (usageRelationship != null) {
listener.updateRelationship(usageRelationship);
}
if (registrationRelationship != null) {
listener.updateRelationship(registrationRelationship);
}
}
ComponentInfo component = components.get(componentId);
if (component != null) {
// Tell our listeners their service component was updated
for (ComponentInfoListener listener : clisteners) {
listener.updateComponent(component);
}
}
}
}