/*
* JBoss, Home of Professional Open Source
* Copyright 2008-13, Red Hat Middleware LLC, and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.overlord.rtgov.client;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.MessageFormat;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.overlord.rtgov.activity.collector.ActivityCollector;
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;
/**
* This class provides helper functions for creating proxies that record
* activity events based on Java invocations.
*
*/
public final class ActivityProxyHelper {
private static final Logger LOG=Logger.getLogger(ActivityProxyHelper.class.getName());
private static ActivityCollector _collector=null;
/**
* Default constructor.
*/
private ActivityProxyHelper() {
}
/**
* This method sets the activity collector.
*
* @param collector The activity collector
* @return The activity proxy helper
*/
public static ActivityProxyHelper setActivityCollector(ActivityCollector collector) {
if (LOG.isLoggable(Level.FINER)) {
LOG.finer("ActivityProxyHelper.setActivityCollector="+collector);
}
_collector = collector;
return (new ActivityProxyHelper());
}
/**
* This class creates a proxy for reporting activities based on the caller
* invoking methods on the callee, via the supplied interface.
*
* @param intfName The interface definition for the component being invoked
* @param caller The caller
* @param callee The service component
* @param <T> Client type
* @return The proxy
*/
@SuppressWarnings("unchecked")
public static <T> T createClientProxy(String intfName, Object caller, T callee) {
Class<?> intf=null;
for (Class<?> i : callee.getClass().getInterfaces()) {
if (i.getName().equals(intfName)) {
intf = i;
break;
}
}
if (intf != null) {
return (createClientProxy((Class<T>)intf, caller, callee));
} else {
LOG.severe("Interface '"+intfName+"' does not exist on class '"+callee.getClass()+"'");
return (null);
}
}
/**
* This class creates a proxy for reporting activities based on the caller
* invoking methods on the callee, via the supplied interface.
*
* @param intf The interface definition for the component being invoked
* @param caller The caller
* @param callee The service component
* @param <T> Client type
* @return The proxy
*/
@SuppressWarnings("unchecked")
public static <T> T createClientProxy(final Class<T> intf, final Object caller, final T callee) {
return ((T)Proxy.newProxyInstance(callee.getClass().getClassLoader(), new Class<?>[]{intf}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String content=null;
String mesgType=null;
String reqId=null;
String respId=null;
boolean scopeStarted=false;
boolean supportedMethod=!method.getName().equals("toString");
Throwable excResp=null;
if (supportedMethod) {
synchronized (_collector) {
// Check if initial activity in thread
if (!_collector.isScopeActive()) {
_collector.startScope();
scopeStarted = true;
}
}
// Create a unique identifier for the request and response
reqId = UUID.randomUUID().toString();
respId = UUID.randomUUID().toString();
if (caller != null) {
RequestSent rs=new RequestSent();
rs.setMessageId(reqId);
rs.setOperation(method.getName());
rs.setInterface(intf.getName());
rs.setServiceType(caller.getClass().getName());
if (args != null && args.length > 0) {
mesgType = "";
try {
for (int i=0; i < args.length; i++) {
if (i > 0) {
mesgType += ",";
}
mesgType += args[i].getClass().getName();
String data=_collector.processInformation(null, args[i].getClass().getName(),
args[i], null, rs);
if (content == null) {
content = data;
}
}
} catch (Exception e) {
LOG.log(Level.SEVERE, MessageFormat.format(
java.util.PropertyResourceBundle.getBundle(
"rtgov-client.Messages").getString("RTGOV-CLIENT-1"),
method.getName()), e);
}
}
rs.setContent(content);
rs.setMessageType(mesgType);
try {
_collector.validate(rs);
_collector.record(rs);
} catch (Throwable t) {
excResp = t;
}
}
}
// Invoke the target method
String respContent=null;
Object resp=null;
if (excResp == null) {
try {
resp = method.invoke(callee, args);
} catch (java.lang.reflect.InvocationTargetException e) {
excResp = e.getCause();
}
}
if (supportedMethod) {
if (caller != null) {
ResponseReceived rr=new ResponseReceived();
rr.setMessageId(respId);
rr.setReplyToId(reqId);
rr.setOperation(method.getName());
rr.setInterface(intf.getName());
rr.setServiceType(caller.getClass().getName());
rr.setContent(respContent);
if (resp != null) {
rr.setMessageType(resp.getClass().getName());
rr.setContent(_collector.processInformation(null,
resp.getClass().getName(), resp, null, rr));
} else if (excResp != null) {
Class<?> excType=getDefinedException(method, excResp);
if (excType != null) {
String faultName = excType.getSimpleName();
if (faultName != null && faultName.endsWith("Exception")
&& faultName.length() > 9) {
faultName = faultName.substring(0, faultName.length()-9);
}
rr.setFault(faultName);
rr.setContent(_collector.processInformation(null,
excResp.getClass().getName(), excResp, null, rr));
} else {
rr.setFault("ERROR");
rr.setContent(excResp.toString());
}
}
_collector.record(rr);
}
// Check if final activity in thread
if (scopeStarted) {
synchronized (_collector) {
_collector.endScope();
}
}
}
if (excResp != null) {
throw excResp;
}
return (resp);
}
}));
}
/**
* This method determines whether the method handles the supplied exception,
* and returns the appropriate type.
*
* @param method The method
* @param t The exception
* @return The handled exception type, or null if not handled
*/
protected static Class<?> getDefinedException(Method method, Throwable t) {
Class<?> ret=null;
for (int i=0; ret == null && i < method.getExceptionTypes().length; i++) {
if (method.getExceptionTypes()[i].isAssignableFrom(t.getClass())) {
ret = method.getExceptionTypes()[i];
}
}
return (ret);
}
/**
* This class creates a service proxy for reporting activities associated with the
* service being invoked.
*
* @param intfName The interface name for the component being invoked
* @param callee The service component
* @return The proxy
*/
public static Object createServiceProxy(final String intfName, final Object callee) {
Class<?> intf=null;
for (Class<?> i : callee.getClass().getInterfaces()) {
if (i.getName().equals(intfName)) {
intf = i;
break;
}
}
if (intf != null) {
return (createServiceProxy(intf, callee));
} else {
LOG.severe("Interface '"+intfName+"' does not exist on class '"+callee.getClass()+"'");
return (null);
}
}
/**
* This class creates a service proxy for reporting activities associated with the
* service being invoked.
*
* @param intf The interface for the component being invoked
* @param callee The service component
* @return The proxy
*/
public static Object createServiceProxy(final Class<?> intf, final Object callee) {
return (Proxy.newProxyInstance(callee.getClass().getClassLoader(), new Class<?>[]{intf}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String content=null;
String mesgType=null;
String reqId=null;
String respId=null;
boolean scopeStarted=false;
boolean supportedMethod=!method.getName().equals("toString");
Throwable excResp=null;
if (supportedMethod) {
// Check if initial activity in thread
synchronized (_collector) {
if (!_collector.isScopeActive()) {
_collector.startScope();
scopeStarted = true;
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Starting scope");
}
}
}
// Create a unique identifier for the request and response
reqId = UUID.randomUUID().toString();
respId = UUID.randomUUID().toString();
if (callee != null) {
RequestReceived rr=new RequestReceived();
rr.setMessageId(reqId);
rr.setOperation(method.getName());
rr.setInterface(intf.getName());
rr.setServiceType(callee.getClass().getName());
if (args != null && args.length > 0) {
mesgType = "";
try {
for (int i=0; i < args.length; i++) {
if (i > 0) {
mesgType += ",";
}
mesgType += args[i].getClass().getName();
String data=_collector.processInformation(null, args[i].getClass().getName(),
args[i], null, rr);
if (content == null) {
content = data;
}
}
} catch (Exception e) {
LOG.log(Level.SEVERE, MessageFormat.format(
java.util.PropertyResourceBundle.getBundle(
"rtgov-client.Messages").getString("RTGOV-CLIENT-1"),
method.getName()), e);
}
}
rr.setContent(content);
rr.setMessageType(mesgType);
try {
_collector.validate(rr);
_collector.record(rr);
} catch (Throwable t) {
excResp = t;
}
}
}
// Invoke the target method
String respContent=null;
Object resp=null;
if (excResp == null) {
try {
resp = method.invoke(callee, args);
} catch (java.lang.reflect.InvocationTargetException e) {
excResp = e.getCause();
}
}
if (supportedMethod) {
if (callee != null) {
ResponseSent rs=new ResponseSent();
rs.setMessageId(respId);
rs.setReplyToId(reqId);
rs.setOperation(method.getName());
rs.setInterface(intf.getName());
rs.setServiceType(callee.getClass().getName());
rs.setContent(respContent);
if (resp != null) {
rs.setMessageType(resp.getClass().getName());
rs.setContent(_collector.processInformation(null,
resp.getClass().getName(), resp, null, rs));
} else if (excResp != null) {
Class<?> excType=getDefinedException(method, excResp);
if (excType != null) {
String faultName = excType.getSimpleName();
if (faultName != null && faultName.endsWith("Exception")
&& faultName.length() > 9) {
faultName = faultName.substring(0, faultName.length()-9);
}
rs.setFault(faultName);
rs.setContent(_collector.processInformation(null,
excResp.getClass().getName(), excResp, null, rs));
} else {
rs.setFault("ERROR");
rs.setContent(excResp.toString());
}
}
_collector.record(rs);
}
// Check if final activity in thread
if (scopeStarted) {
synchronized (_collector) {
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest("Ending scope");
}
_collector.endScope();
}
}
}
if (excResp != null) {
throw excResp;
}
return (resp);
}
}));
}
}