/* * 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.analytics.util; import java.util.logging.Level; import java.util.logging.Logger; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.overlord.rtgov.activity.model.ActivityType; import org.overlord.rtgov.activity.model.ActivityTypeId; import org.overlord.rtgov.activity.model.ActivityUnit; import org.overlord.rtgov.activity.model.Context; import org.overlord.rtgov.activity.model.soa.RequestReceived; import org.overlord.rtgov.activity.model.soa.RequestSent; import org.overlord.rtgov.activity.model.soa.ResponseReceived; import org.overlord.rtgov.activity.model.soa.ResponseSent; import org.overlord.rtgov.activity.util.ActivityUtil; 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.MEPDefinition; import org.overlord.rtgov.analytics.service.OperationDefinition; import org.overlord.rtgov.analytics.service.RequestFaultDefinition; import org.overlord.rtgov.analytics.service.RequestResponseDefinition; import org.overlord.rtgov.analytics.service.ServiceDefinition; /** * This class provides utility functions related to the service * definition. * */ public final class ServiceDefinitionUtil { private static final String PRINCIPAL = "principal"; private static final Logger LOG=Logger.getLogger(ServiceDefinitionUtil.class.getName()); private static final ObjectMapper MAPPER=new ObjectMapper(); static { MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); MAPPER.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT); MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } /** * The default constructor. */ private ServiceDefinitionUtil() { } /** * This method serializes a Service Definition into a JSON representation. * * @param sdef The service definition * @return The JSON serialized representation * @throws Exception Failed to serialize */ public static byte[] serializeServiceDefinition(ServiceDefinition sdef) throws Exception { byte[] ret=null; java.io.ByteArrayOutputStream baos=new java.io.ByteArrayOutputStream(); MAPPER.writeValue(baos, sdef); ret = baos.toByteArray(); baos.close(); return (ret); } /** * This method deserializes a Service Definition from a JSON representation. * * @param sdef The JSON representation of the service definition * @return The service definition * @throws Exception Failed to deserialize */ public static ServiceDefinition deserializeServiceDefinition(byte[] sdef) throws Exception { ServiceDefinition ret=null; java.io.ByteArrayInputStream bais=new java.io.ByteArrayInputStream(sdef); ret = MAPPER.readValue(bais, ServiceDefinition.class); bais.close(); return (ret); } /** * This method processes the supplied activity unit to derive * service definition information. * * @param actUnit The activity unit * @return The service definitions */ public static java.util.Collection<ServiceDefinition> derive(ActivityUnit actUnit) { java.util.Map<String,ServiceDefinition> ret= new java.util.HashMap<String,ServiceDefinition>(); checkForServiceInvoked(ret, actUnit, 0, actUnit.getActivityTypes().size(), null); if (LOG.isLoggable(Level.FINEST)) { String au=null; try { au = new String(ActivityUtil.serializeActivityUnit(actUnit)); } catch (Exception e) { LOG.log(Level.FINEST, "Failed to deserialize activity unit: "+actUnit, e); } LOG.finest("Derive service definitions: ActivityUnit="+au+" ServiceDefinitions="+ret); } return (ret.values()); } /** * This method checks for the events associated with the service * being invoked. * * @param sdefs The service definitions * @param actUnit The activity unit * @param from The 'from' index * @param to The 'to' index * @param idef The invocation definition */ protected static void checkForServiceInvoked(java.util.Map<String,ServiceDefinition> sdefs, ActivityUnit actUnit, int from, int to, InvocationDefinition idef) { // Scan the activity types for a received request for (int i=from; i < to; i++) { ActivityType at1=actUnit.getActivityTypes().get(i); if (at1 instanceof RequestReceived) { RequestReceived rqr=(RequestReceived)at1; if (idef != null && idef.getServiceType() == null) { idef.setServiceType(rqr.getServiceType()); } if (rqr.getMessageId() != null) { // Locate the matching response sent activity for (int j=i+1; j < to; j++) { ActivityType at2=actUnit.getActivityTypes().get(j); if (at2 instanceof ResponseSent && ((ResponseSent)at2).getReplyToId() != null && ((ResponseSent)at2).getReplyToId().equals( rqr.getMessageId())) { ResponseSent rps=(ResponseSent)at2; // Process the activities related to this // matched interaction MEPDefinition resp=processServiceInvoked(sdefs, actUnit, rqr, rps); // Check if any invocations are performed in the // scope of this req/resp if (resp != null) { checkForExternalInvocations(sdefs, actUnit, resp, i+1, j); } // Advance 'i' so only checks after the sent // response i = j; // Escape from this loop, as the response has // been found break; } } } } } } /** * Check if there are any external service invocations during the scope of * the suppled MEP definition. * * @param sdefs The service definitions * @param actUnit The activity unit * @param mep The MEP definition * @param from The 'from' index * @param to The 'to' index */ protected static void checkForExternalInvocations(java.util.Map<String,ServiceDefinition> sdefs, ActivityUnit actUnit, MEPDefinition mep, int from, int to) { // Scan the activity types for a received request for (int i=from; i < to; i++) { ActivityType at1=actUnit.getActivityTypes().get(i); if (at1 instanceof RequestSent) { RequestSent rqs=(RequestSent)at1; if (rqs.getMessageId() != null) { // Locate the matching response received activity for (int j=i+1; j < to; j++) { ActivityType at2=actUnit.getActivityTypes().get(j); if (at2 instanceof ResponseReceived && ((ResponseReceived)at2).getReplyToId() != null && ((ResponseReceived)at2).getReplyToId().equals( rqs.getMessageId())) { ResponseReceived rpr=(ResponseReceived)at2; // Process the activities related to this // matched interaction InvocationDefinition idef=processExternalInvocation(sdefs, mep, rqs, rpr); // Check if any invocations are performed in the // scope of this req/resp checkForServiceInvoked(sdefs, actUnit, i+1, j, idef); // Advance 'i' so only checks after the received // response i = j; // Escape from this loop, as the response has // been found break; } } } } } } /** * This method processes the service invocation. * * @param sdefs The service definitions * @param actUnit The activity unit * @param rqr The request received event * @param rps The response sent event * @return The response definition associated with the req/resp */ protected static MEPDefinition processServiceInvoked(java.util.Map<String,ServiceDefinition> sdefs, ActivityUnit actUnit, RequestReceived rqr, ResponseSent rps) { MEPDefinition ret=null; if (rqr.getServiceType() == null) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("Can't process service invocation with missing service type and interface '" +rqr.getInterface()+"'"); } return null; } if (rqr.getInterface() == null) { if (LOG.isLoggable(Level.FINEST)) { LOG.finest("Can't process service invocation for service type '" +rqr.getServiceType()+"' due to missing interface"); } return null; } // Get service definition associated with the service type ServiceDefinition sd=sdefs.get(rqr.getServiceType()); // If not found, then create if (sd == null) { sd = new ServiceDefinition(); sd.setServiceType(rqr.getServiceType()); sd.setInternal(rqr.getInternal() || rps.getInternal()); // Mark service as internal if either request or response are sdefs.put(rqr.getServiceType(), sd); } InterfaceDefinition id=sd.getInterface(rqr.getInterface()); if (id == null) { id = new InterfaceDefinition(); id.setInterface(rqr.getInterface()); sd.getInterfaces().add(id); } OperationDefinition op=id.getOperation(rqr.getOperation()); if (op == null) { op = new OperationDefinition(); op.setName(rqr.getOperation()); id.getOperations().add(op); } // Check if normal or fault response InvocationMetric metrics=null; if (rps.getFault() == null || rps.getFault().trim().length() == 0) { RequestResponseDefinition nrd=op.getRequestResponse(); if (nrd == null) { nrd = new RequestResponseDefinition(); op.setRequestResponse(nrd); // Set the request and response ids nrd.setRequestId(ActivityTypeId.createId(rqr)); nrd.setResponseId(ActivityTypeId.createId(rps)); } metrics = nrd.getMetrics(); ret = nrd; } else { RequestFaultDefinition frd=op.getRequestFault(rps.getFault()); if (frd == null) { frd = new RequestFaultDefinition(); frd.setFault(rps.getFault()); // Set the request and response ids frd.setRequestId(ActivityTypeId.createId(rqr)); frd.setResponseId(ActivityTypeId.createId(rps)); op.getRequestFaults().add(frd); } metrics = frd.getMetrics(); metrics.setFaults(metrics.getFaults()+1); ret = frd; } // Copy the properties if (ret != null) { ret.getProperties().putAll(rqr.getProperties()); ret.getProperties().putAll(rps.getProperties()); // Check if principal defined for either activity if (!ret.getProperties().containsKey(PRINCIPAL)) { if (rqr.getPrincipal() != null) { ret.getProperties().put(PRINCIPAL, rqr.getPrincipal()); } else if (rps.getPrincipal() != null) { ret.getProperties().put(PRINCIPAL, rps.getPrincipal()); } } // Specify the origin information if (actUnit != null && actUnit.getOrigin() != null) { ret.getProperties().put("host", actUnit.getOrigin().getHost()); ret.getProperties().put("node", actUnit.getOrigin().getNode()); } } // Calculate stats long duration=rps.getTimestamp()-rqr.getTimestamp(); metrics.setAverage(((metrics.getAverage() * metrics.getCount())+duration) / (metrics.getCount()+1)); if (metrics.getMin() == 0 || duration < metrics.getMin()) { metrics.setMin(duration); } if (duration > metrics.getMax()) { metrics.setMax(duration); } metrics.setCount(metrics.getCount()+1); // Store context details sd.getContext().addAll(rqr.getContext()); java.util.Iterator<Context> iter=rps.getContext().iterator(); while (iter.hasNext()) { Context c=iter.next(); if (!sd.getContext().contains(c)) { sd.getContext().add(c); } } return (ret); } /** * This method processes the external invocation and associates the * details with the supplied MEP definition. * * @param sdefs The service definitions * @param call The MEP definition * @param rqs The request * @param rpr The response * @return The invocation definition */ protected static InvocationDefinition processExternalInvocation(java.util.Map<String,ServiceDefinition> sdefs, MEPDefinition call, RequestSent rqs, ResponseReceived rpr) { InvocationDefinition idef=call.getInvocation(rqs.getInterface(), rqs.getOperation(), rpr.getFault()); if (idef == null) { idef = new InvocationDefinition(); idef.setInterface(rqs.getInterface()); idef.setOperation(rqs.getOperation()); idef.setFault(rpr.getFault()); idef.setInternal(rqs.getInternal() || rpr.getInternal()); // Mark service as internal if either request or response are idef.getProperties().putAll(rqs.getProperties()); idef.getProperties().putAll(rpr.getProperties()); call.getInvocations().add(idef); } InvocationMetric metrics=idef.getMetrics(); long duration=rpr.getTimestamp()-rqs.getTimestamp(); metrics.setAverage(((metrics.getAverage() * metrics.getCount())+duration) / (metrics.getCount()+1)); if (metrics.getMin() == 0 || duration < metrics.getMin()) { metrics.setMin(duration); } if (duration > metrics.getMax()) { metrics.setMax(duration); } metrics.setCount(metrics.getCount()+1); if (idef.getFault() != null) { metrics.setFaults(metrics.getFaults()+1); } return (idef); } /** * This method merges the supplied service definition snapshots. * * @param snapshots The snapshots to merge * @return The merged service definitions */ public static java.util.Map<String,ServiceDefinition> mergeSnapshots( java.util.List<java.util.Map<String,ServiceDefinition>> snapshots) { return (mergeSnapshots(snapshots, false)); } /** * This method merges the supplied service definition snapshots. * * @param snapshots The snapshots to merge * @param retainContexts Whether to retain and merge context information * @return The merged service definitions */ public static java.util.Map<String,ServiceDefinition> mergeSnapshots( java.util.List<java.util.Map<String,ServiceDefinition>> snapshots, boolean retainContexts) { java.util.Map<String,ServiceDefinition> ret= new java.util.HashMap<String, ServiceDefinition>(); if (LOG.isLoggable(Level.FINER)) { LOG.finer("MERGE: "+snapshots); } java.util.Set<String> keys=new java.util.HashSet<String>(); // Build key set for (int i=0; i < snapshots.size(); i++) { java.util.Map<String,ServiceDefinition> sds=snapshots.get(i); keys.addAll(sds.keySet()); } for (String key : keys) { ServiceDefinition sd=new ServiceDefinition(); sd.setServiceType(key); for (int i=0; i < snapshots.size(); i++) { java.util.Map<String,ServiceDefinition> sds=snapshots.get(i); if (sds.containsKey(key)) { try { sd.merge(sds.get(key), retainContexts); } catch (Exception e) { LOG.log(Level.SEVERE, java.util.PropertyResourceBundle.getBundle( "analytics.Messages").getString("ANALYTICS-1"), e); } } } ret.put(key, sd); } if (LOG.isLoggable(Level.FINER)) { LOG.finer("MERGED: "+ret); } return (ret); } }