001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.metaboss.componentproxy.profiling;
016:
017: import java.lang.reflect.InvocationHandler;
018: import java.lang.reflect.InvocationTargetException;
019: import java.lang.reflect.Method;
020: import java.util.Collections;
021: import java.util.HashSet;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.Set;
025: import java.util.TreeMap;
026:
027: import javax.management.JMException;
028: import javax.management.MBeanServer;
029: import javax.management.ObjectName;
030: import javax.naming.Context;
031: import javax.naming.InitialContext;
032: import javax.naming.NamingException;
033:
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036:
037: import com.jamonapi.Monitor;
038: import com.jamonapi.MonitorFactory;
039: import com.jamonapi.management.MonitorControllerMBean;
040:
041: /** This is the generic logging proxy, which just logs method invocations */
042: public class ProfilingProxyImpl implements InvocationHandler {
043: private static boolean sClassInitialised = false;
044: private static Object sClassInitialisationSemaphore = new Object();
045: private Object mUnderlyingObject = null;
046: private Class mUnderlyingObjectClass = null;
047: private String mUnderlyingObjectClassName = null;
048: private static Map sCreatedMonitorsMap = Collections
049: .synchronizedMap(new TreeMap());
050: private static Set sDisplayedInformationsSet = Collections
051: .synchronizedSet(new HashSet());
052: private static final String sInformationText = "Profiling is switched on for the {0} class.";
053: private static Log sProxyLogger = LogFactory
054: .getLog(ProfilingProxyImpl.class);
055:
056: // Pseudo class initialiser. Main feature that we can throw exception from here (unable to do that from native java class initialiser)
057: protected static void initialiseClassIfNecessary() {
058: if (!sClassInitialised) {
059: synchronized (sClassInitialisationSemaphore) {
060: if (!sClassInitialised) {
061: sClassInitialised = true;
062:
063: // Register the shutdown hook to print the statistics
064: Runtime.getRuntime().addShutdownHook(new Thread() {
065: public void run() {
066: if (sCreatedMonitorsMap.size() > 0) {
067: // First dump statistics in the more or less readable form to the screen
068: System.out.println();
069: System.out
070: .println("Dump of statistics accumulated by the ProfilingProxy");
071: System.out
072: .println("----------------------------------------------------");
073: // First calculate max label length
074: int lMaxLabelLength = 0;
075: for (Iterator lIter = sCreatedMonitorsMap
076: .keySet().iterator(); lIter
077: .hasNext();) {
078: String lName = (String) lIter
079: .next();
080: int lNameLength = lName.length();
081: if (lMaxLabelLength < lNameLength)
082: lMaxLabelLength = lNameLength;
083: }
084: // Now output statistics using a little formatting
085: for (Iterator lIter = sCreatedMonitorsMap
086: .entrySet().iterator(); lIter
087: .hasNext();) {
088: Map.Entry lEntry = (Map.Entry) lIter
089: .next();
090: String lName = (String) lEntry
091: .getKey();
092: int lNameLength = lName.length();
093: Monitor lMon = (Monitor) lEntry
094: .getValue();
095: System.out
096: .print("Performance data for "
097: + lName + ": ");
098: for (int i = (lMaxLabelLength - lNameLength); i > 0; i--)
099: System.out.print(" ");
100: System.out.println(lMon);
101: }
102: System.out
103: .println("----------------------------------------------------");
104:
105: }
106: }
107: });
108: // TODO: remove this registration to the bootstrap application code
109: // // Possibly register MBean, if there is an MBean server and MBean is not registered yet
110: // try
111: // {
112: // Context lContext = new InitialContext();
113: // MBeanServer lMBeanServer = (MBeanServer)lContext.lookup(com.metaboss.enterprise.management.MBeanServer.COMPONENT_URL);
114: // if (lMBeanServer != null)
115: // {
116: // try
117: // {
118: // ObjectName lProfilingProxyControllerObjectName = MonitorControllerMBean.getSingletonObjectName();
119: // // If this bean is already registered - there is no need to register another one
120: // if (!lMBeanServer.isRegistered(lProfilingProxyControllerObjectName))
121: // {
122: // sProxyLogger.info("Located JMX MBeanServer. Will register '" + lProfilingProxyControllerObjectName.getCanonicalName() + "' management bean. It can be used to manage profiling." );
123: //
124: // // Note that create bean may not work and throw ReflectionException
125: // // because jmx implementation might not see the classloader.
126: // // The registerMBean() appears to work better for us
127: // lMBeanServer.registerMBean(MonitorControllerMBean.getInstance(),lProfilingProxyControllerObjectName);
128: // }
129: // else
130: // sProxyLogger.info("Located JMX MBeanServer. The '" + lProfilingProxyControllerObjectName.getCanonicalName() + "' management bean is already registered. It can be used to manage profiling." );
131: // }
132: // catch(JMException e)
133: // {
134: // // This is an unexpected situation. Report it as an error
135: // sProxyLogger.error("Unable to register JMX MBean. (Caught an unexpected exception). The profiling management via managed beans is not available.",e);
136: // }
137: // }
138: // else
139: // sProxyLogger.warn("Unable to locate JMX MBeanServer (Lookup has not returned a valid server object). The profiling management via managed beans is not available.");
140: // }
141: // catch(NamingException e)
142: // {
143: // // This may happen when running context does not have a MBean server setup or confugured. Report it as a warning
144: // if (sProxyLogger.isDebugEnabled())
145: // sProxyLogger.debug("Unable to locate JMX MBeanServer (Caught an exception). The profiling management via managed beans is not available.",e);
146: // else
147: // sProxyLogger.warn("Unable to locate JMX MBeanServer (Caught an exception, which is logged with the debug category). The profiling management via managed beans is not available.");
148: // }
149: }
150: }
151: }
152: }
153:
154: /** The only available constructor. Sets the proxy up for the particular underlying object */
155: public ProfilingProxyImpl(Object pUnderlyingObject) {
156: // Run clas initialisation
157: initialiseClassIfNecessary();
158:
159: mUnderlyingObject = pUnderlyingObject;
160: mUnderlyingObjectClass = mUnderlyingObject.getClass();
161: mUnderlyingObjectClassName = mUnderlyingObjectClass.getName();
162: // Display the information message
163: if (sDisplayedInformationsSet
164: .contains(mUnderlyingObjectClassName) == false) {
165: // Usual pattern to guard against racing
166: synchronized (sDisplayedInformationsSet) {
167: if (sDisplayedInformationsSet
168: .contains(mUnderlyingObjectClassName) == false) {
169: String lFormattedInformationText = java.text.MessageFormat
170: .format(
171: sInformationText,
172: new Object[] { mUnderlyingObjectClassName });
173: sProxyLogger.info(lFormattedInformationText);
174: sDisplayedInformationsSet
175: .add(mUnderlyingObjectClassName);
176: }
177: }
178: }
179: }
180:
181: /** Generic handler method processed every invocation of every method */
182: public Object invoke(Object proxy, Method method, Object[] args)
183: throws Throwable {
184: String lMonitorName = mUnderlyingObjectClassName + "."
185: + method.getName() + "()";
186: Monitor lMonitor = MonitorFactory.start(lMonitorName);
187: sCreatedMonitorsMap.put(lMonitorName, lMonitor);
188: try {
189: return method.invoke(mUnderlyingObject, args);
190: } catch (InvocationTargetException e) {
191: // Make this proxy "invisible"
192: throw e.getCause();
193: } finally {
194: lMonitor.stop();
195: }
196: }
197: }
|