/*
* 2012-3 Red Hat Inc. and/or its affiliates and other contributors.
*
* Licensed 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.overlord.rtgov.service.dependency;
import java.util.logging.Logger;
import org.overlord.rtgov.analytics.service.InterfaceDefinition;
import org.overlord.rtgov.analytics.service.InvocationDefinition;
import org.overlord.rtgov.analytics.service.InvocationMetric;
import org.overlord.rtgov.analytics.service.OperationDefinition;
import org.overlord.rtgov.analytics.service.RequestFaultDefinition;
import org.overlord.rtgov.analytics.service.ServiceDefinition;
import org.overlord.rtgov.analytics.situation.Situation;
/**
* This class builds a service view representing the
* dependency between a list of service definitions.
*
*/
public final class ServiceDependencyBuilder {
private static final Logger LOG=Logger.getLogger(ServiceDependencyBuilder.class.getName());
/**
* The default constructor.
*/
private ServiceDependencyBuilder() {
}
/**
* This method returns the set of service definitions that
* initiate business activity (i.e. have no client services).
*
* @param sds The service definitions
* @return The set of initial services
*/
public static java.util.Set<ServiceDefinition> getInitialServices(java.util.Collection<ServiceDefinition> sds) {
java.util.Set<ServiceDefinition> ret=
new java.util.HashSet<ServiceDefinition>();
for (ServiceDefinition sd : sds) {
java.util.Set<ServiceDefinition> clients=
getServiceClients(sd, sds);
if (clients.size() == 0) {
ret.add(sd);
}
}
return (ret);
}
/**
* This method returns the set of services that are clients to the
* supplied service interface.
*
* @param mainsd The service definition
* @param sds The other service definitions to check
* @return The set of service definitions that are clients of
* the service definition
*/
public static java.util.Set<ServiceDefinition> getServiceClients(ServiceDefinition mainsd,
java.util.Collection<ServiceDefinition> sds) {
java.util.Set<ServiceDefinition> ret=
new java.util.HashSet<ServiceDefinition>();
for (ServiceDefinition sd : sds) {
if (!sd.getServiceType().equals(mainsd.getServiceType())) {
for (InterfaceDefinition iDef : sd.getInterfaces()) {
for (OperationDefinition opDef : iDef.getOperations()) {
if (opDef.getRequestResponse() != null) {
for (InvocationDefinition invDef
: opDef.getRequestResponse().getInvocations()) {
if (mainsd.getInterface(invDef.getInterface()) != null) {
if (!ret.contains(sd)) {
ret.add(sd);
}
}
}
}
for (RequestFaultDefinition rfd : opDef.getRequestFaults()) {
for (InvocationDefinition invDef
: rfd.getInvocations()) {
if (mainsd.getInterface(invDef.getInterface()) != null) {
if (!ret.contains(sd)) {
ret.add(sd);
}
}
}
}
}
}
}
}
return (ret);
}
/**
* This method builds a service graph from a collection of service
* definitions. If specified, the result will focus on a nominated service,
* only showing its direct client services, and the services it directly
* or indirectly calls.
*
* @param sds The service definitions
* @param sits The situations
* @param focusServiceType The optional service type to focus on
* @return The service graph
*/
public static ServiceGraph buildGraph(java.util.Set<ServiceDefinition> sds,
java.util.List<Situation> sits, String focusServiceType) {
ServiceGraph ret=buildGraph(sds, sits);
if (focusServiceType != null) {
filter(ret, focusServiceType);
}
return (ret);
}
/**
* This method filters out any service node that is not a client, or
* direct/indirect service provider, to the supplied service type.
*
* @param sg
* @param focusServiceType
*/
protected static void filter(ServiceGraph sg, String focusServiceType) {
ServiceNode focus=sg.getServiceNode(focusServiceType);
// If focus service node is not found, then ignore and return the
// complete graph
if (focus != null) {
// For each service, that is not the focus service, determine if either
// a client or direct/indirect service.
java.util.Set<ServiceNode> serviceProviders=new java.util.HashSet<ServiceNode>();
java.util.Iterator<ServiceNode> serviceNodeIter=sg.getServiceNodes().iterator();
while (serviceNodeIter.hasNext()) {
ServiceNode node=serviceNodeIter.next();
// If node is the focus, or it has already been identified as a service
// provider, then just continue
if (node == focus || serviceProviders.contains(node)) {
continue;
}
// Check if service node is a direct client of the focus service
if (sg.getUsageLink(node, focus) != null) {
continue;
}
java.util.Set<ServiceNode> visited=new java.util.HashSet<ServiceNode>();
if (!isServiceProvider(sg, node, focus, visited)) {
// Remove invocation links associated with the node
java.util.Iterator<InvocationLink> ilIter=sg.getInvocationLinks().iterator();
while (ilIter.hasNext()) {
InvocationLink il=ilIter.next();
if (node.getOperations().contains(il.getSource())
|| node.getOperations().contains(il.getTarget())) {
ilIter.remove();
}
}
// Remove usage links associated with the node
java.util.Iterator<UsageLink> ulIter=sg.getUsageLinks().iterator();
while (ulIter.hasNext()) {
UsageLink ul=ulIter.next();
if (ul.getSource() == node
|| ul.getTarget() == node) {
ulIter.remove();
}
}
// Remove node from graph
serviceNodeIter.remove();
} else {
serviceProviders.addAll(visited);
}
}
}
}
/**
* This method determines whether the supplied
*
* @param sg The service graph
* @param node The node of interest
* @param focus The focus service
* @param visited The list of nodes already visited
* @return Whether the node is a direct or indirect service provider of the focus service
*/
protected static boolean isServiceProvider(ServiceGraph sg, ServiceNode node, ServiceNode focus,
java.util.Set<ServiceNode> visited) {
if (visited.contains(node)) {
return (false);
}
visited.add(node);
if (sg.getUsageLink(focus, node) != null) {
return (true);
}
for (UsageLink ul : sg.getUsageLinks()) {
if (ul.getTarget() == node) {
if (isServiceProvider(sg, ul.getSource(), focus, visited)) {
return (true);
}
}
}
return (false);
}
/**
* This method builds a service graph from a collection of service
* definitions.
*
* @param sds The service definitions
* @param sits The situations
* @return The service graph
*/
public static ServiceGraph buildGraph(java.util.Set<ServiceDefinition> sds,
java.util.List<Situation> sits) {
ServiceGraph ret=new ServiceGraph();
// Get set of initial services
java.util.Set<ServiceDefinition> initialNodes=
getInitialServices(sds);
// Initialize the service nodes
for (ServiceDefinition sd : sds) {
ServiceNode sn=new ServiceNode();
sn.setService(sd);
for (InterfaceDefinition idef : sd.getInterfaces()) {
for (OperationDefinition op : idef.getOperations()) {
OperationNode opn=new OperationNode();
opn.setService(sd);
opn.setOperation(op);
if (sits != null) {
for (Situation s : sits) {
String[] parts=s.subjectAsParts();
if (parts.length > 1 && parts[0].equals(
sn.getService().getServiceType())
&& parts[1].equals(op.getName())) {
opn.getSituations().add(s);
}
}
}
sn.getOperations().add(opn);
}
}
sn.getProperties().put(ServiceNode.INITIAL_NODE, initialNodes.contains(sd));
if (sits != null) {
// Associate situations specific to the interface just with the service node
for (Situation s : sits) {
String[] parts=s.subjectAsParts();
if (parts.length == 1 && parts[0].equals(
sn.getService().getServiceType())) {
sn.getSituations().add(s);
}
}
}
ret.getServiceNodes().add(sn);
}
// Initialize invocation links between operations
for (ServiceDefinition sd : sds) {
ServiceNode sn=ret.getServiceNode(sd.getServiceType());
for (InterfaceDefinition idef : sd.getInterfaces()) {
for (OperationDefinition op : idef.getOperations()) {
OperationNode opn=sn.getOperation(op.getName());
if (op.getRequestResponse() != null) {
linkOperationNodes(ret, sn, opn,
op.getRequestResponse().getInvocations());
}
for (RequestFaultDefinition rfd : op.getRequestFaults()) {
linkOperationNodes(ret, sn, opn,
rfd.getInvocations());
}
}
}
}
return (ret);
}
/**
* This method links the source operation node with the invoked
* operation nodes.
*
* @param sg The service graph
* @param sn The source service node
* @param opn The source operation node
* @param ids The list of invocations
*/
protected static void linkOperationNodes(ServiceGraph sg, ServiceNode sn,
OperationNode opn, java.util.List<InvocationDefinition> ids) {
for (InvocationDefinition id : ids) {
ServiceNode tsn=null;
// Workaround to support FSW6.0 with RTGov 1, which did not have
// the getServiceType() method.
try {
if (id.getServiceType() != null) {
tsn = sg.getServiceNode(id.getServiceType());
}
} catch (Throwable t) {
// Ignore
if (LOG.isLoggable(java.util.logging.Level.FINEST)) {
LOG.log(java.util.logging.Level.FINEST, "Failed to get service type (assume running in FSW6.0)", t);
}
}
if (tsn == null && id.getInterface() != null) {
tsn = sg.getServiceNodeForInterface(id.getInterface());
}
if (tsn != null) {
UsageLink existingul=sg.getUsageLink(sn, tsn);
if (existingul == null) {
UsageLink ul=new UsageLink();
ul.setSource(sn);
ul.setTarget(tsn);
ul.getInvocations().add(id);
sg.getUsageLinks().add(ul);
} else {
LOG.fine("Usage link between source '"+sn
+"' and target '"+tsn
+"' has already been defined: "+existingul);
// Copy invocation definitions to existing link
existingul.getInvocations().add(id);
}
// Find target for invocation link
OperationNode topn=tsn.getOperation(id.getOperation());
if (topn != null) {
InvocationLink existingil=sg.getInvocationLink(opn, topn);
if (existingil == null) {
InvocationLink il=new InvocationLink();
il.setSource(opn);
il.setTarget(topn);
il.getInvocations().add(id);
sg.getInvocationLinks().add(il);
} else {
LOG.fine("Link between source '"+opn
+"' and target '"+topn
+"' has already been defined: "+existingil);
// Copy invocation definitions to existing link
existingil.getInvocations().add(id);
}
}
}
}
}
/**
* This method returns a merged invocation metric value associated with the supplied
* invocation definitions.
*
* @param invocations The invocation details
* @return The merged metrics
*/
public static InvocationMetric getMergedMetrics(java.util.List<InvocationDefinition> invocations) {
InvocationMetric ret=new InvocationMetric();
for (InvocationDefinition id : invocations) {
ret.merge(id.getMetrics());
}
return (ret);
}
}