package com.perforce.reviewer; import java.util.*; import java.io.*; import com.perforce.api.*; /** * Perforce Java Reviewer. This class utilizes the <a target="_top" href="http://public.perforce.com/public/perforce/api/java/p4package/index.html">Perforce Java API</a> to determine * what changes have taken place on the Perforce server and handle them * according to its configuration. * <p> * The default action that is taken on each changelist is to send * e-mail to those users that have have added a Reviews section to * their user specification that matches the current change. The e-mail sent out * will contain both a text-only and HTML version of the information. This has * been very useful for including links to additional information. * <p> * Requires <a target="_top" href="http://java.sun.com/products/javamail">JavaMail</a> and <a target="_top" href="http://java.sun.com/products/javabeans/glasgow/jaf.html">Activation</a> packages from Sun. When you have these, * make sure mail.jar, activation.jar, and p4.jar (from the Perforce Java API) are * in the same directory as the reviewer.jar. * <p> * As part of the reviwer.jar file, this application can be started on the * command line with java 1.2 or better using the format: * <pre> java -jar reviewer.jar /etc/reviewer.conf & </pre> * <p> * The <a href="doc-files/reviewer.conf">configuration file</a> is the only argument accepted on the command line. This * file specifies the Perforce environment and how each changelist is handled. * * @see ChangeListener * @author <a href="mailto:david@markley.cc">David Markley</a> * @version $Date: 2002/01/11 $ $Revision: #2 $ */ public class Reviewer extends Thread { private Env env; private Counter cntr; private Hashtable user_listeners = null; private Hashtable path_listeners = null; private Vector listeners = null; private ChangeListener default_listener = null; /** * Constructor. */ public Reviewer(Env env) { this(env, env.getProperty("p4.counter","review")); } public Reviewer(Env env, String counter) { super(); this.env = env; this.cntr = new Counter(env, counter); this.user_listeners = new Hashtable(); this.path_listeners = new Hashtable(); this.listeners = new Vector(); this.setDefaultUserListener(new MailListener()); ChangeListener l = null; Class cl; Enumeration en = null; String line; int pos; ClassLoader loader = null; String pfx, cls; System.out.println("Looking for more listeners..."); try { loader = ClassLoader.getSystemClassLoader(); } catch (Error err) { err.printStackTrace(System.out); } catch (Exception ex) { ex.printStackTrace(System.out); } Vector v = env.getPropertyList("p4.reviewer.listeners.user", ""); en = v.elements(); while (en.hasMoreElements()) { line = (String)en.nextElement(); if (-1 == (pos = line.indexOf(':'))) continue; pfx = line.substring(0,pos); cls = line.substring(pos+1); try { if (null != loader) { cl = loader.loadClass(cls); l = (ChangeListener)cl.newInstance(); addUserListener(pfx, l); } } catch (Exception ex) { System.out.println("WARNING: Could not add user listener: "+cls+" - "+pfx); } } en = env.getPropertyList("p4.reviewer.listeners.path", "").elements(); while (en.hasMoreElements()) { line = (String)en.nextElement(); if (-1 == (pos = line.indexOf(':'))) continue; pfx = line.substring(0,pos); cls = line.substring(pos+1); try { if (null != loader) { cl = loader.loadClass(cls); l = (ChangeListener)cl.newInstance(); addPathListener(pfx, l); } } catch (Exception ex) { System.out.println("WARNING: Could not add path listener: "+cls+" - "+pfx); } } } /** * Adds a change listner associated for a given path. When the path * matches any path within the current change, the ChangeListener's * handleChange() method is invoked with the current Env and Change. The * User array passed to handleChange() is set to the list of unspecified * users. */ public void addPathListener(String path, ChangeListener listener) { if (null == path || null == listener) return; listener.setEnv(env); Vector v = (Vector)path_listeners.get(path); if (null == v) { v = new Vector(); v.addElement(listener); path_listeners.put(path, v); } else { v.addElement(listener); } } /** * Adds a change listner associated with a particular user. When this * user appears on the reviews list for a change, the ChangeListener's * handleChange() method is invoked with the current Env and Change. The * User array passed to handleChange() is set to the list of * unspecified users. */ public void addUserListener(String user, ChangeListener listener) { if (null == user || null == listener) return; listener.setEnv(env); user_listeners.put(user, listener); } /** * Adds a change listner that will review every change. This ChangeListener's * handleChange() method will alway be invoked with the current Env, Change, * and list of reviewing users that that have not had a specific * ChangeListener added for them using the addUserListener method. * <p/> * There is a built-in listener that is set up as the default upon * instantiating this class. This listener will send e-mail to the list * of users. */ public void setDefaultUserListener(ChangeListener listener) { if (null == listener) return; System.out.println("Reviewer.setDefaultUserListener()"); listener.setEnv(env); this.default_listener = listener; } /** * Adds a change listner that will review every change. This ChangeListener's * handleChange() method will alway be invoked with the current Env, Change, * and list of reviewing users that have nod had a specific * ChangeListener added for them using the addUserListener method. */ public void addChangeListener(ChangeListener listener) { if (null == listener) return; listener.setEnv(env); listeners.addElement(listener); } /** * Check the counter every five seconds. */ public void run() { while (true) { checkCounter(); try { sleep(5000); } catch (InterruptedException iex) { /* don't care */ } } } /** * Notify listeners about the change. */ private void notifyListeners(Change chng, User[] usrs) { Vector v_users = new Vector(); ChangeListener l = null; Enumeration en; Hashtable matches = new Hashtable(); FileEntry fent; if (null == usrs) usrs = new User[0]; try { chng.sync(); } catch (Exception ex) { System.out.println("ERROR: Could not sync change: "+chng); ex.printStackTrace(System.out); } for (int i = 0; i < usrs.length; i++) { l = (ChangeListener)user_listeners.get(usrs[i].getId()); if (null == l) { v_users.addElement(usrs[i]); } else { matches.put(l, l); } } User[] a_users = new User[v_users.size()]; for (int i = 0; i < v_users.size(); i++) { a_users[i] = (User)v_users.elementAt(i); } en = path_listeners.keys(); Vector fents = chng.getFileEntries(); Vector lstnrs = null; String path = null; while (en.hasMoreElements()) { path = (String)en.nextElement(); lstnrs = (Vector)path_listeners.get(path); if (null == lstnrs) continue; for (int i = 0; i < fents.size(); i++) { fent = (FileEntry)fents.elementAt(i); /* System.out.println("wildPathMatch: "+path+" - "+fent.getDepotPath()); */ if (Utils.wildPathMatch(path, fent.getDepotPath())) { Enumeration en_lstnr = lstnrs.elements(); while (en_lstnr.hasMoreElements()) { l = (ChangeListener)en_lstnr.nextElement(); matches.put(l, l); } break; } } } en = listeners.elements(); while (en.hasMoreElements()) { l = (ChangeListener)en.nextElement(); matches.put(l, l); } matches.put(default_listener, default_listener); /* Loop through all the matches and call each handler in turn. */ en = matches.keys(); Object key; while (en.hasMoreElements()) { key = en.nextElement(); /* System.out.println("handlingChange: "+chng+" - "+key); */ l = (ChangeListener)matches.get(key); l.handleChange(chng, a_users); } } /** * Check to see if the counter has changed and who reviews the change. */ private void checkCounter() { Change[] chngs; User[] usrs; int i, j, max = 0; try { if (null != (chngs = cntr.review())) { for (i = 0; i < chngs.length; i++) { System.out.println("Reviewing change: "+chngs[i].getNumber()); if (max < chngs[i].getNumber()) max = chngs[i].getNumber(); usrs = chngs[i].reviews(); notifyListeners(chngs[i], usrs); } } } catch (Exception ex) { /* Print out the exception, but don't stop the reviewer! */ ex.printStackTrace(System.out); } if (0 < max) { cntr.setValue(max); try { cntr.commit(); } catch (PerforceException pex) { /* If the counter can not be set, die an ugly death. */ /* It's better to die, then to spam users with e-mail. */ System.out.println("ERROR: Reviewer could not set the counter!"); System.exit(-1); } } } /** * Command line execution method. The command line takes * a single argument, which is the path to the configuration file. * <p> * As part of the reviwer.jar file, this application can be started on the * command line with java 1.2 or better using the format: * <pre> java -jar reviewer.jar /etc/reviewer.conf & </pre> */ public static void main(String[] argv) { /* Debug.setDebugLevel(Debug.VERBOSE); Debug.setLogLevel(Debug.LOG_SPLIT); */ Env env = null; try { env = new Env(argv[0]); env.checkValidity(); } catch (PerforceException ex) { ex.printStackTrace(System.out); System.exit(-1); } System.out.println(env); Reviewer r = new Reviewer(env); r.start(); } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 4460 | Randall Gellens | branch review daemonsto my sandbox | ||
//guest/perforce_software/utils/reviewd/reviewer/com/perforce/reviewer/Reviewer.java | |||||
#2 | 2021 | David Markley | Integrated reviewer changes to the public depot. | ||
#1 | 1682 | rmg | Add David's Java Reviewer to //public. | ||
//guest/david_markley/reviewer/com/perforce/reviewer/Reviewer.java | |||||
#6 | 1670 | David Markley |
Completed the user documentation in preperation for the public depot. Includes example configuration for the Java Reviewer. |
||
#5 | 1642 | David Markley |
Corrected the Utils.wildPathMatch method so that it matches all wildcards properly. Added the extensible reviewer. |
||
#4 | 1633 | David Markley | Not sure why Java's Vector.toArray() method does not work properly. | ||
#3 | 1632 | David Markley | Added ChangeListener interface for more flexability. | ||
#2 | 1381 | David Markley | Corrected a serious defect in the FileEntry.parseFstat method. | ||
#1 | 1368 | David Markley |
Needed access to the Properties within the Env for the Reviewer. Moved things around to create the com.perforce.reviewer package. |