001: /*
002: * <copyright>
003: *
004: * Copyright 2001-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.core.plugin.freeze;
028:
029: import java.io.IOException;
030: import java.io.PrintWriter;
031: import java.util.Collection;
032: import java.util.HashSet;
033: import java.util.Iterator;
034: import java.util.Set;
035:
036: import javax.servlet.ServletException;
037: import javax.servlet.http.HttpServlet;
038: import javax.servlet.http.HttpServletRequest;
039: import javax.servlet.http.HttpServletResponse;
040:
041: import org.cougaar.core.component.ServiceBroker;
042: import org.cougaar.core.plugin.PluginBase;
043: import org.cougaar.core.service.ServletService;
044: import org.cougaar.core.service.ThreadControlService;
045: import org.cougaar.core.service.ThreadListenerService;
046: import org.cougaar.core.thread.Schedulable;
047: import org.cougaar.core.thread.ThreadListener;
048: import org.cougaar.util.UnaryPredicate;
049:
050: /**
051: * This component is a {@link javax.servlet.Servlet} that implements
052: * the plugin freeze.
053: * <p>
054: * NOTE: This servlet duplicates much code from the FreezeTargetPlugin. It
055: * could be refactored to extend that plugin, but since that capability may
056: * be obsoleted by this, I'll shoose to copy-paste rather than inherit.
057: * <p>
058: * This plugin implements the actual freezing of an agent. Freezing is
059: * accomplished by preventing the ThreadService from running certain
060: * classes of components. The relevant object is the so-called
061: * "consumer" of the ThreadService. For plugins, this is the plugin
062: * itself. For other uses of the ThreadService, the "consumer" may be
063: * different.
064: * <p>
065: * Generally, all plugins except those involved in the freeze process
066: * are prevented from running, but this can be modified by rules
067: * specified as plugin parameters. The rules are applied in this
068: * order:
069: * <pre>
070: * "allow " + FreezePlugin.class.getName()
071: * first plugin parameter
072: * second plugin parameter
073: * etc.
074: * "deny " + PluginBase.class.getName()
075: * </pre>
076: * <p>
077: * The form of the rule is one of the words, "deny" or "allow",
078: * followed by a space followed by the name of the class or interface
079: * that should be affected by the rule. The rule matches if it is
080: * legal to assign the consumer to a variable of the type named in the
081: * rule. This includes the class of the consumer itself, all
082: * interfaces implemented by the consumer or their superinterfaces,
083: * all superclasses of the consumer, and all interfaces implemented by
084: * any superclass or their superinterfaces.
085: * <p>
086: * The first rule is built-in and cannot be overridden. It allows all
087: * the freeze plugins to run while frozen. This is obviously necessary
088: * to handle thawing a frozen society. The last rule is always added
089: * and prevents all plugins that extend PluginBase from running except
090: * those allowed by preceding rules. While it is possible to write a
091: * component that behaves as a plugin but does not extend PluginBase,
092: * this does not happen in practice.
093: * <p>
094: * The effect of this final rule can be nullified by including rules
095: * (as plugin parameters) that specifically allow individual plugins.
096: * Indeed, the whole class of plugins extending PluginBase could be
097: * allowed. It is possible to prevent anything from being frozen in an
098: * agent by making the first plugin parameter be "allow
099: * java.lang.Object". Since every class extends java.lang.Object, this
100: * will allow every class to run.
101: */
102: public class FreezeServlet extends FreezePlugin implements
103: ThreadListener {
104: private static class BadGuy {
105: private Thread thread;
106: private Schedulable schedulable;
107: int hc;
108:
109: public BadGuy(Schedulable s, Thread t) {
110: thread = t;
111: schedulable = s;
112: hc = System.identityHashCode(t)
113: + System.identityHashCode(s);
114: }
115:
116: public int hashCode() {
117: return hc;
118: }
119:
120: public boolean equals(Object o) {
121: if (o == this )
122: return true;
123: if (o instanceof BadGuy) {
124: BadGuy that = (BadGuy) o;
125: return this .thread == that.thread
126: && this .schedulable == that.schedulable;
127: }
128: return false;
129: }
130:
131: public String toString() {
132: return schedulable.getState() + ": "
133: + schedulable.getConsumer().toString();
134: }
135: }
136:
137: // True if we have frozen this agent.
138: private boolean isFrozen = false;
139: private boolean isFreezing = false;
140: private ThreadListenerService threadListenerService;
141: private ThreadControlService threadControlService;
142: private ServletService servletService;
143: private Rules rules = new Rules();
144: private Set badGuys = new HashSet(); // Records the bad guys we have
145:
146: // seen enter the run state
147: // that have not left the run
148: // state
149:
150: public void unload() {
151: if (threadControlService != null) {
152: ServiceBroker sb = getServiceBroker();
153: sb.releaseService(this , ThreadListenerService.class,
154: threadListenerService);
155: sb.releaseService(this , ThreadControlService.class,
156: threadControlService);
157: }
158: super .unload();
159: }
160:
161: private UnaryPredicate myThreadQualifier = new UnaryPredicate() {
162: public boolean execute(Object o) {
163: Schedulable schedulable = (Schedulable) o;
164: Object consumer = schedulable.getConsumer();
165: return rules.allow(consumer);
166: }
167: };
168:
169: // Thread control logic. Threads are classified as good or bad. When
170: // frozen, we regulate the max running thread count to be no more
171: // than the number of goodguys that are on the runnable queue. The
172: // number of goodguys is the total of the known good guys (in the
173: // goodGuys set) and the anonymous ones. We have to assume that any
174: // thread we have never seen is a good guy. If an anonymous good guy
175: // steps off the stage we will recognize him and reduce the
176: // anonymousGoodGuys count.
177: public synchronized void threadQueued(Schedulable schedulable,
178: Object consumer) {
179: }
180:
181: public synchronized void threadDequeued(Schedulable schedulable,
182: Object consumer) {
183: }
184:
185: public synchronized void threadStarted(Schedulable schedulable,
186: Object consumer) {
187: if (logger.isDetailEnabled())
188: logger.detail("threadStarted: " + consumer);
189: if (!rules.allow(consumer)) {
190: badGuys
191: .add(new BadGuy(schedulable, Thread.currentThread()));
192: }
193: }
194:
195: public synchronized void threadStopped(Schedulable schedulable,
196: Object consumer) {
197: if (logger.isDetailEnabled())
198: logger.detail("threadStopped: " + consumer);
199: if (!rules.allow(consumer)) {
200: Thread currentThread = Thread.currentThread();
201: badGuys.remove(new BadGuy(schedulable, currentThread));
202: }
203: }
204:
205: public void rightGiven(String consumer) {
206: }
207:
208: public void rightReturned(String consumer) {
209: }
210:
211: private void setThreadLimit() {
212: threadControlService.setQualifier(myThreadQualifier);
213: }
214:
215: private void unsetThreadLimit() {
216: threadControlService.setQualifier(null);
217: }
218:
219: public void setupSubscriptions() {
220: super .setupSubscriptions();
221: rules.addAllowRule(FreezePlugin.class);
222: // Hope this is a List cause order is important.
223: Collection params = getParameters();
224: for (Iterator i = params.iterator(); i.hasNext();) {
225: String ruleSpec = (String) i.next();
226: try {
227: rules.addRule(ruleSpec);
228: } catch (Exception e) {
229: logger.error("Bad parameter: " + ruleSpec + " : "
230: + e.getMessage());
231: }
232: }
233: rules.addDenyRule(PluginBase.class);
234: if (logger.isInfoEnabled())
235: logger.info("rules=" + rules);
236: ServiceBroker sb = getServiceBroker();
237: threadControlService = (ThreadControlService) sb.getService(
238: this , ThreadControlService.class, null);
239: threadListenerService = (ThreadListenerService) sb.getService(
240: this , ThreadListenerService.class, null);
241: threadListenerService.addListener(this );
242: servletService = (ServletService) sb.getService(this ,
243: ServletService.class, null);
244: try {
245: servletService.register("/freezeControl", new MyServlet());
246: if (logger.isDebugEnabled())
247: logger.debug("Registered /freezeControl servlet");
248: } catch (Exception ex) {
249: if (logger.isErrorEnabled())
250: logger.error(
251: "Unable to register freezeControl servlet", ex);
252: }
253: }
254:
255: public void execute() {
256: if (timerExpired()) {
257: cancelTimer();
258: if (isFreezing)
259: checkStopped();
260: }
261: }
262:
263: private class MyServlet extends HttpServlet {
264: /**
265: * @see javax.servlet.http.HttpServlet#service(HttpServletRequest, HttpServletResponse)
266: */
267: protected void service(HttpServletRequest arg0,
268: HttpServletResponse arg1) throws ServletException,
269: IOException {
270: handleRequest(arg0, arg1);
271: }
272:
273: }
274:
275: private void handleRequest(HttpServletRequest req,
276: HttpServletResponse resp) {
277:
278: String response = "";
279: String action = req.getParameter("action");
280: if ("freeze".equals(action)) {
281: if (!isFrozen) {
282: if (logger.isDebugEnabled())
283: logger.debug("freezing");
284: setThreadLimit();
285: isFrozen = true;
286: isFreezing = true;
287: checkStopped();
288: }
289: response = "Freezing initiated";
290: } else if ("thaw".equals(action)) {
291: if (isFrozen) {
292: if (logger.isDebugEnabled())
293: logger.debug("thawed");
294: unsetThreadLimit(); // Unset thread limit
295: isFrozen = false;
296: isFreezing = false;
297: }
298: response = "Thawing initiated";
299: } else {
300: if (isFreezing)
301: response = "Agent is Freezing";
302: else if (isFrozen)
303: response = "Agent is Frozen";
304: else
305: response = "Agent is Thawed";
306: }
307: try {
308: PrintWriter out = resp.getWriter();
309: out.println("<html><head></head><body>");
310:
311: out.println(response);
312:
313: out.println("<table>");
314: out
315: .println("<tr><td><FORM METHOD=\"GET\" ACTION=\"freezeControl\">");
316: out
317: .println("<INPUT TYPE=\"hidden\" Name=\"action\" Value=\"freeze\">");
318: out.println("<INPUT TYPE=\"submit\" Value=\"Freeze\">");
319: out.println("</FORM>");
320: out.println("</td>");
321: out
322: .println("<td><FORM METHOD=\"GET\" ACTION=\"freezeControl\">");
323: out
324: .println("<INPUT TYPE=\"hidden\" Name=\"action\" Value=\"thaw\">");
325: out.println("<INPUT TYPE=\"submit\" Value=\"Thaw\">");
326: out.println("</FORM>");
327: out.println("</td></tr>");
328: out.println("</table>");
329: out
330: .println("<p><FORM METHOD=\"GET\" ACTION=\"freezeControl\">");
331: out.println("<INPUT TYPE=\"submit\" Value=\"Refresh\">");
332: out.println("</FORM>");
333: out.println("</body>");
334: } catch (IOException ioe) {
335: if (logger.isErrorEnabled())
336: logger.error("Error generating response", ioe);
337: }
338: }
339:
340: private synchronized void checkStopped() {
341: int stillRunning = badGuys.size();
342: if (stillRunning <= 0) {
343: if (logger.isDebugEnabled()) {
344: logger.debug("Frozen");
345: }
346: isFreezing = false;
347: } else {
348: if (logger.isDebugEnabled()) {
349: Set consumerSet = new HashSet();
350: for (Iterator i = badGuys.iterator(); i.hasNext();) {
351: BadGuy bg = (BadGuy) i.next();
352: consumerSet.add(bg.toString());
353: }
354: logger.debug("Still running: " + consumerSet);
355: }
356: resetTimer(5000);
357: }
358: }
359: }
|