Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/enhydra/servlet/debug/DebugManager.java


1   /*
2    * Enhydra Java Application Server Project
3    * 
4    * The contents of this file are subject to the Enhydra Public License
5    * Version 1.1 (the "License"); you may not use this file except in
6    * compliance with the License. You may obtain a copy of the License on
7    * the Enhydra web site ( http://www.enhydra.org/ ).
8    * 
9    * Software distributed under the License is distributed on an "AS IS"
10   * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
11   * the License for the specific terms governing rights and limitations
12   * under the License.
13   * 
14   * The Initial Developer of the Enhydra Application Server is Lutris
15   * Technologies, Inc. The Enhydra Application Server and portions created
16   * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17   * All Rights Reserved.
18   * 
19   * Contributor(s):
20   * 
21   * $Id: DebugManager.java,v 1.4.4.1 2000/10/19 17:59:10 jasona Exp $
22   */
23  
24  package org.enhydra.servlet.debug;
25  
26  import org.enhydra.servlet.connectionMethods.*;
27  import com.lutris.multiServer.*;
28  import org.enhydra.servlet.ServletContainer;
29  import org.enhydra.servlet.filter.*;
30  
31  
32  /**
33   * Servlet debug manager for monitoring servlet I/O.  An instance of this
34   * object monitors a single servlet instance.  A fixed-sized queue of servlet
35   * transaction records is maintained.  As a transaction is completed by the
36   * servlet, a record is added to the end of the queue.  If the queue has
37   * reached its maximum size, an element is removed from the head of the
38   * queue.  Applications wishing to monitor servlet transactions registers
39   * with this object and is called when objects are added or removed from
40   * the queue.  Calling on removal is necessary as one of the main goals of
41   * this class is to support presentations that display a list of transaction
42   * records. A unique id number is associated with each transaction record.
43   * The transaction record ids are sequencially allocated, one with a large
44   * number occured after one with a smaller number.
45   * 
46   * @version  $Revision: 1.4.4.1 $
47   * @author   Mark Diekhans
48   * @since    2.0
49   */
50  public class DebugManager {
51  
52      /**
53       * Servlet being managed.
54       */
55      private String servletId;
56  
57      /**
58       * Current monitoring state.
59       */
60      private boolean enabled = false;
61  
62      /**
63       * Queue of transaction records. This is protected so that subclasses
64       * may access the queue.
65       */
66      protected ServletRecordQueue recordQueue;
67  
68      /**
69       * List of objects to call on servlet debug events.  Kept as an array
70       * since addition/removal of callbacks is infrequence and the expected
71       * number is small.  The array may not be full.
72       */
73      private ServletRecordCallback[] callbacks =
74              new ServletRecordCallback[1];
75  
76      /**
77       * Number of valid entries in callbacks.
78       */
79      private int numCallbacks = 0;
80  
81      /**
82       * Filter that is used by this DebugManager.
83       */
84      private ServletRecorderFilter filter;
85      private String filterID; 
86  
87      /**
88       * Construct an new servlet debug manager and associate it with a servlet.
89       * Servlet monitoring is initially disabled. 
90       *
91       * @param servletId symbolic servlet identifier of the servlet to manage.
92       * @param queueSize maximum number of servlet transaction records that
93       *  can be queued at a given time.
94       * @param saveResponseData Should we save a copy of the data written
95       *  to the socket for each transaction? Could use up alot of memory.
96       */
97      public DebugManager(String servletId, 
98                          int queueSize,
99                          boolean saveResponseData) {
100         this.servletId = servletId;
101         recordQueue = new ServletRecordQueue(queueSize);
102         filter = new ServletRecorderFilter(this, saveResponseData, servletId);
103     }
104 
105     
106 
107     /**
108      * Enable recording servlet transactions. This adds a filter to the
109      * filter manager, and then adds the filter to all channels that
110      * map to the servlet.
111      *
112      *  @exception ConnectionMethodException If there is an error getting
113      *   the connection methods and channels, or adding the filter.
114      */
115     public synchronized void enable() throws ConnectionMethodException {
116   ServletContainer sc = (ServletContainer)
117     MultiServer.getService("ServletContainer");
118   ConnectionMethodManager cmm = sc.getConnectionMethodManager();
119   FilterManager fm = sc.getFilterManager();
120         String[] connectionIds = cmm.getIDs();
121         /*
122          *   Add to thr filter manager. We must delete it later when
123          *   debugging is disabled. Because the name starts with an
124          *   underscore, it will not be displayed in the admin app.
125          */
126         filterID = fm.getUniqueID("_debug_" + servletId);
127         fm.add(filterID, filter, "internal debugging");
128         for (int cmi = 0; cmi < connectionIds.length; cmi++) {
129             ConnectionMethod method = cmm.get(connectionIds[cmi]);
130             String[] channelIds = method.getChannelIDs();
131             for (int ch = 0; ch < channelIds.length; ch++) {
132                 String chanServletId = 
133                         method.getChannelStatus(channelIds[ch]).servletID;
134                 if (servletId.equals(chanServletId)) {
135                     method.addTransactionFilter(channelIds[ch], filterID);
136                 }
137             }
138         }
139   enabled = true;
140     }
141 
142     /**
143      * Disable recording servlet transactions. This removes the filter from
144      * all the channels, then removes the filter from the filter manager.
145      *
146      *  @exception ConnectionMethodException If there is an error getting
147      *   the connection methods and channels, or adding the filter.
148      */
149     public synchronized void disable() throws ConnectionMethodException {
150   ServletContainer sc = (ServletContainer)
151     MultiServer.getService("ServletContainer");
152   ConnectionMethodManager cmm = sc.getConnectionMethodManager();
153         String[] connectionIds = cmm.getIDs();
154 
155         for (int cmi = 0; cmi < connectionIds.length; cmi++) {
156             ConnectionMethod method = cmm.get(connectionIds[cmi]);
157             String[] channelIds = method.getChannelIDs();
158             for (int ch = 0; ch < channelIds.length; ch++) {
159                 String[] filterIds =
160                         method.getChannelStatus(channelIds[ch]).filterIDs;
161                 for (int fi = 0; fi < filterIds.length; fi++) {
162                     if (filterIds[fi].equals(filterID)) {
163                         method.removeTransactionFilter(channelIds[ch],
164                                                        filterID);
165                     }
166                 }
167             }
168         }
169         /*
170          *   Delete the filter from the filter manager.
171          */
172         sc.getFilterManager().delete(filterID);
173   enabled = false;
174     }
175 
176     /**
177      * Determine if servlets monitoring is enabled.
178      *
179      * @return <CODE>true</CODE> if enabled, <CODE>false</CODE> if disabled.
180      */
181     public boolean isEnabled() {
182         return enabled;
183     }
184 
185     /**
186      * Get the servlet associated with this object.
187      * 
188      * @return the servlet id.
189      */
190     public String getServlet() {
191         return servletId;
192     }
193 
194     /**
195      * Registered to be called when a servlet transaction is queued.
196      *
197      * @param callback The object to invoke when a transaction object
198      * is available.
199      * @param catchUp Pass in true if you want an initial series of
200      * <CODE>transactionRecordAddEvent()</CODE> calls, reflecting the
201      * current set of transactions stored in the queue. 
202      * Wanring: if this is set to true, and there are transactions in the
203      * queue, then <CODE>transactionRecordAddEvent()</CODE> will be
204      * called before this function returns. 
205      */
206     public synchronized void addTransactionCallback(
207             ServletRecordCallback callback, boolean catchUp) {
208         if (numCallbacks == callbacks.length) {
209             ServletRecordCallback[] newTbl =
210                 new ServletRecordCallback[callbacks.length * 2];
211             System.arraycopy(callbacks, 0, newTbl, 0, callbacks.length);
212             callbacks = newTbl;
213         }
214         callbacks[numCallbacks] = callback;
215         numCallbacks++;
216 
217         /*
218          * Should we inform the callee of the current set of transactions?
219          */
220         if (catchUp)
221             recordQueue.dumpContents(callback);
222     }
223 
224     /**
225      * Remove transaction callback that was created with
226      * <CODE>addTransactionCallback</CODE>.
227      *
228      * @param callback object to delete from callback list.
229      */
230     public synchronized void removeTranactionCallback(
231             ServletRecordCallback callback) {
232         int i;
233         for (i = 0; i < numCallbacks; i++) {
234             if (callbacks[i] == callback) {
235                 break;
236             }
237         }
238         // Compact.  Array bounds violation nails bad callback.
239         callbacks[i] = callbacks[numCallbacks - 1];
240         numCallbacks--;
241     }
242 
243     /**
244      * Find a servlet transaction record by id.
245      *
246      * @param transactionId unique number identifying the desired transaction
247      *  record.
248      * @return a transaction record or <CODE>null</CODE> if the transaction
249      *  is no longer in the queue.
250      */
251     public synchronized ServletTransactionRecord getTransactionRecord(
252             int transactionId) {
253         return recordQueue.findById(transactionId);
254     }
255 
256     /**
257      * Add a servlet transaction to the end of the queue.  Use by the
258      * servlet filters.
259      * 
260      * @param transactionRecord the record to add to the queue.
261      */
262     protected synchronized void addTransactionRecord(
263              ServletTransactionRecord transactionRecord) {
264         ServletTransactionRecord deletedRecord =
265             recordQueue.addRecord(transactionRecord);
266         
267         for (int i = 0; i < numCallbacks; i++) {
268             callbacks[i].transactionRecordAddEvent(transactionRecord,
269                                                    deletedRecord);
270         }
271     }
272 
273     /**
274      * Clear all records from the queue.
275      */
276     public synchronized void clearRecordQueue() {
277         recordQueue.clear();
278         for (int i = 0; i < numCallbacks; i++) {
279             callbacks[i].clearRecordsEvent();
280         }
281     }
282 }