001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.junit;
043:
044: import java.io.ByteArrayOutputStream;
045: import java.io.IOException;
046: import java.io.InputStream;
047: import java.net.URL;
048: import java.text.MessageFormat;
049: import java.util.HashMap;
050: import java.util.LinkedList;
051: import java.util.Map;
052: import java.util.logging.Handler;
053: import java.util.logging.Level;
054: import java.util.logging.LogRecord;
055: import java.util.logging.Logger;
056: import java.util.regex.Pattern;
057: import junit.framework.Assert;
058:
059: /** Basic skeleton for logging test case.
060: *
061: * @author Jaroslav Tulach
062: */
063: final class ControlFlow extends Object {
064: /** Registers hints for controlling thread switching in multithreaded
065: * applications.
066: * @param url the url to read the file from
067: * @exception IOException thrown when there is problem reading the url
068: */
069: static void registerSwitches(Logger listenTo, Logger reportTo,
070: URL url, int timeout) throws IOException {
071: ByteArrayOutputStream os = new ByteArrayOutputStream();
072: InputStream is = url.openStream();
073: for (;;) {
074: int ch = is.read();
075: if (ch == -1)
076: break;
077: os.write(ch);
078: }
079: os.close();
080: is.close();
081:
082: registerSwitches(listenTo, reportTo, new String(os
083: .toByteArray(), "utf-8"), timeout);
084: }
085:
086: /** Registers hints for controlling thread switching in multithreaded
087: * applications.
088:
089: */
090: static void registerSwitches(Logger listenTo, Logger reportTo,
091: String order, int timeout) {
092: LinkedList<Switch> switches = new LinkedList<Switch>();
093:
094: Map<String, Pattern> exprs = new HashMap<String, Pattern>();
095:
096: int pos = 0;
097: for (;;) {
098: int thr = order.indexOf("THREAD:", pos);
099: if (thr == -1) {
100: break;
101: }
102: int msg = order.indexOf("MSG:", thr);
103: if (msg == -1) {
104: Assert.fail("After THREAD: there must be MSG: "
105: + order.substring(thr));
106: }
107: int end = order.indexOf("THREAD:", msg);
108: if (end == -1) {
109: end = order.length();
110: }
111:
112: String thrName = order.substring(pos + 7, msg).trim();
113: String msgText = order.substring(msg + 4, end).trim();
114:
115: Pattern p = exprs.get(msgText);
116: if (p == null) {
117: p = Pattern.compile(msgText);
118: exprs.put(msgText, p);
119: }
120:
121: Switch s = new Switch(thrName, p);
122: switches.add(s);
123:
124: pos = end;
125: }
126:
127: ErrManager h = new ErrManager(switches, listenTo, reportTo,
128: timeout);
129: listenTo.addHandler(h);
130: }
131:
132: //
133: // Logging support
134: //
135: private static final class ErrManager extends Handler {
136: private LinkedList<Switch> switches;
137: private int timeout;
138: /** maps names of threads to their instances*/
139: private Map<String, Thread> threads = new HashMap<String, Thread>();
140:
141: /** the logger to send internal messages to, if any */
142: private Logger msg;
143:
144: /** assigned to */
145: private Logger assigned;
146:
147: public ErrManager(LinkedList<Switch> switches, Logger assigned,
148: Logger msg, int t) {
149: this .switches = switches;
150: this .msg = msg;
151: this .timeout = t;
152: this .assigned = assigned;
153: setLevel(Level.FINEST);
154: }
155:
156: public void publish(LogRecord record) {
157: if (switches == null) {
158: assigned.removeHandler(this );
159: return;
160: }
161:
162: String s = record.getMessage();
163: if (s != null && record.getParameters() != null) {
164: s = MessageFormat.format(s, record.getParameters());
165: }
166:
167: boolean log = msg != null;
168: boolean expectingMsg = false;
169: for (;;) {
170: synchronized (switches) {
171: if (switches.isEmpty()) {
172: return;
173: }
174:
175: Switch w = switches.getFirst();
176: String threadName = Thread.currentThread()
177: .getName();
178: boolean foundMatch = false;
179:
180: if (w.matchesThread()) {
181: if (!w.matchesMessage(s)) {
182: // same thread but wrong message => go on
183: return;
184: }
185: // the correct message from the right thread found
186: switches.removeFirst();
187: if (switches.isEmpty()) {
188: // end of sample, make all run
189: switches.notifyAll();
190: return;
191: }
192: w = switches.getFirst();
193: if (w.matchesThread()) {
194: // next message is also from this thread, go on
195: return;
196: }
197: expectingMsg = true;
198: foundMatch = true;
199: } else {
200: // compute whether we shall wait or not
201: for (Switch check : switches) {
202: if (check.matchesMessage(s)) {
203: expectingMsg = true;
204: break;
205: }
206: }
207: }
208:
209: // make it other thread run
210: Thread t = threads.get(w.name);
211: if (t != null) {
212: if (log) {
213: loginternal("t: " + threadName
214: + " interrupts: " + t.getName());
215: }
216: t.interrupt();
217: }
218: threads.put(threadName, Thread.currentThread());
219:
220: //
221: // if (log) {
222: // loginternal("t: " + Thread.currentThread().getName() + " log: " + s + " result: " + m + " for: " + w + "\n");
223: // }
224: if (!expectingMsg) {
225: return;
226: }
227:
228: // clear any interrupt that happend before
229: Thread.interrupted();
230: try {
231: if (log) {
232: loginternal("t: " + threadName + " log: "
233: + s + " waiting");
234: }
235: switches.wait(timeout);
236: if (log) {
237: loginternal("t: " + threadName + " log: "
238: + s + " timeout");
239: }
240: return;
241: } catch (InterruptedException ex) {
242: // ok, we love to be interrupted => go on
243: if (log) {
244: loginternal("t: " + threadName + " log: "
245: + s + " interrupted");
246: }
247: if (foundMatch) {
248: return;
249: }
250: }
251: }
252: }
253: }
254:
255: public void flush() {
256: }
257:
258: public void close() throws SecurityException {
259: }
260:
261: private void loginternal(String string) {
262: msg.info(string);
263: }
264:
265: } // end of ErrManager
266:
267: private static final class Switch {
268: private Pattern msg;
269: private String name;
270:
271: public Switch(String n, Pattern m) {
272: this .name = n;
273: this .msg = m;
274: }
275:
276: /** @return true if the thread name of the caller matches this switch
277: */
278: public boolean matchesThread() {
279: String thr = Thread.currentThread().getName();
280: return name.equals(thr);
281: }
282:
283: /** @return true if the message matches the one provided by this switch
284: */
285: public boolean matchesMessage(String logMsg) {
286: return msg.matcher(logMsg).matches();
287: }
288:
289: public String toString() {
290: return "Switch[" + name + "]: " + msg;
291: }
292: }
293: }
|