0001: package com.quadcap.app.bugdb;
0002:
0003: /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
0004: *
0005: * This software is distributed under the Quadcap Free Software License.
0006: * This software may be used or modified for any purpose, personal or
0007: * commercial. Open Source redistributions are permitted. Commercial
0008: * redistribution of larger works derived from, or works which bundle
0009: * this software requires a "Commercial Redistribution License"; see
0010: * http://www.quadcap.com/purchase.
0011: *
0012: * Redistributions qualify as "Open Source" under one of the following terms:
0013: *
0014: * Redistributions are made at no charge beyond the reasonable cost of
0015: * materials and delivery.
0016: *
0017: * Redistributions are accompanied by a copy of the Source Code or by an
0018: * irrevocable offer to provide a copy of the Source Code for up to three
0019: * years at the cost of materials and delivery. Such redistributions
0020: * must allow further use, modification, and redistribution of the Source
0021: * Code under substantially the same terms as this license.
0022: *
0023: * Redistributions of source code must retain the copyright notices as they
0024: * appear in each source code file, these license terms, and the
0025: * disclaimer/limitation of liability set forth as paragraph 6 below.
0026: *
0027: * Redistributions in binary form must reproduce this Copyright Notice,
0028: * these license terms, and the disclaimer/limitation of liability set
0029: * forth as paragraph 6 below, in the documentation and/or other materials
0030: * provided with the distribution.
0031: *
0032: * The Software is provided on an "AS IS" basis. No warranty is
0033: * provided that the Software is free of defects, or fit for a
0034: * particular purpose.
0035: *
0036: * Limitation of Liability. Quadcap Software shall not be liable
0037: * for any damages suffered by the Licensee or any third party resulting
0038: * from use of the Software.
0039: */
0040:
0041: import java.io.ByteArrayInputStream;
0042: import java.io.ByteArrayOutputStream;
0043: import java.io.InputStream;
0044: import java.io.PrintWriter;
0045:
0046: import java.util.Enumeration;
0047: import java.util.Hashtable;
0048: import java.util.Properties;
0049: import java.util.Vector;
0050:
0051: import java.sql.DriverManager;
0052: import java.sql.SQLException;
0053:
0054: import java.net.URLDecoder;
0055:
0056: import java.sql.Connection;
0057: import java.sql.PreparedStatement;
0058: import java.sql.ResultSet;
0059: import java.sql.ResultSetMetaData;
0060: import java.sql.Statement;
0061: import java.sql.SQLException;
0062:
0063: import javax.servlet.ServletException;
0064: import javax.servlet.http.HttpServletRequest;
0065: import javax.servlet.http.HttpSession;
0066: import javax.servlet.http.HttpSessionBindingEvent;
0067: import javax.servlet.http.HttpSessionBindingListener;
0068: import javax.servlet.http.HttpSessionContext;
0069:
0070: /**
0071: * A session bean encapsulating access to the Quadcap Bug Database.
0072: * JSP pages use methods of this bean to perform the various functions
0073: * of the bug database application.
0074: *
0075: * @author Stan Bailes
0076: */
0077: public class BugSession implements HttpSessionBindingListener {
0078: Connection conn;
0079:
0080: /** My user name. 'nobody' until logged in. */
0081: String me = "nobody";
0082:
0083: String my_person_type = null;
0084: boolean isAdmin = false;
0085: boolean isManager = false;
0086: boolean isDeveloper = false;
0087: boolean isUser = false;
0088:
0089: static Object lock = new Object();
0090:
0091: Vector debug_history = new Vector();
0092:
0093: PreparedStatement getComponentOwner;
0094: PreparedStatement insertBugHist;
0095: PreparedStatement insertPerson;
0096: PreparedStatement getBugHist;
0097: PreparedStatement getBug;
0098: PreparedStatement getQuery;
0099: PreparedStatement saveQuery;
0100: PreparedStatement getUser;
0101: PreparedStatement updateUser;
0102: PreparedStatement deleteUser;
0103: PreparedStatement getProject;
0104: PreparedStatement newProject;
0105: PreparedStatement deleteProject;
0106: PreparedStatement updateProject;
0107:
0108: final String driverName = "com.quadcap.jdbc.JdbcDriver";
0109: final String dbUrl = "jdbc:qed:bugdb;create=true";
0110: final String dbUser = "bugdb";
0111: final String dbPass = "bugdb";
0112:
0113: /**
0114: * Create a new session.
0115: */
0116: public BugSession() {
0117: // Make sure we're connected to the database. If we're absolutely
0118: // the first user, load the schema.
0119: try {
0120: Class.forName(driverName);
0121: getConnection();
0122: prepareStatements();
0123: checkSchema();
0124: } catch (Exception e) {
0125: e.printStackTrace(System.out);
0126: throw new RuntimeException(e.toString());
0127: }
0128: }
0129:
0130: /**
0131: * Return the JDBC connection for this session.
0132: *
0133: * @return this session's connection.
0134: */
0135: public Connection getConnection() throws SQLException {
0136: if (conn == null) {
0137: Properties props = new Properties();
0138: props.put("user", dbUser);
0139: props.put("password", dbPass);
0140: props.put("create", "true");
0141: this .conn = DriverManager.getConnection(dbUrl, props);
0142: //conn.setAutoCommit(false);
0143: }
0144: return conn;
0145: }
0146:
0147: /**
0148: * This application object has just been bound into an HttpSession
0149: *
0150: * @param event identifies the session and the name in the
0151: * session of this object.
0152: */
0153: public void valueBound(HttpSessionBindingEvent event) {
0154: HttpSession session = event.getSession();
0155: session.setMaxInactiveInterval(6 * 3600); // six hours
0156: }
0157:
0158: /**
0159: * This application object has just been removed from an HttpSession
0160: *
0161: * @param event identifies the session and the name in the
0162: * session of this object.
0163: */
0164: public void valueUnbound(HttpSessionBindingEvent event) {
0165: logout(null);
0166: try {
0167: conn.close();
0168: } catch (Exception e) {
0169: }
0170: }
0171:
0172: /**
0173: * Prepare the various SQL statements we'll be using.
0174: *
0175: * @exception SQLException may be thrown
0176: */
0177: private final void prepareStatements() throws SQLException {
0178: getComponentOwner = conn
0179: .prepareStatement("select owner from component where component = ?");
0180:
0181: insertBugHist = conn
0182: .prepareStatement("insert into bug_history (bug_id, hist_pos, submitter,"
0183: + " abstract, description, comments, state, priority, type,"
0184: + " owner, component, modtime) values(?,?,?,?,?,?,?,?,?,?,?,?)");
0185:
0186: insertPerson = conn
0187: .prepareStatement("insert into person(name,password,email,person_type) "
0188: + "values(?,?,?,?,?)");
0189:
0190: getBugHist = conn.prepareStatement(
0191: "select hist_size from bug where"
0192: + " bug_id = ? for update",
0193: ResultSet.TYPE_SCROLL_SENSITIVE,
0194: ResultSet.CONCUR_UPDATABLE);
0195: getBug = conn.prepareStatement("select * from bug_history "
0196: + "where bug_id = ? " + "order by hist_pos desc");
0197: getQuery = conn.prepareStatement("select query from queries "
0198: + "where owner = ? and name = ?");
0199: saveQuery = conn
0200: .prepareStatement("insert into queries values(?,?,?)");
0201: getUser = conn
0202: .prepareStatement("select * from person where name=?");
0203: updateUser = conn.prepareStatement(
0204: "select * from person where name=? for update",
0205: ResultSet.TYPE_SCROLL_SENSITIVE,
0206: ResultSet.CONCUR_UPDATABLE);
0207: deleteUser = conn
0208: .prepareStatement("delete from person where name=?");
0209: getProject = conn
0210: .prepareStatement("select * from component where component=?");
0211: newProject = conn
0212: .prepareStatement("insert into component values(?,?)");
0213: updateProject = conn.prepareStatement(
0214: "select * from component where component=? for update",
0215: ResultSet.TYPE_SCROLL_SENSITIVE,
0216: ResultSet.CONCUR_UPDATABLE);
0217: deleteProject = conn
0218: .prepareStatement("delete from component where component=?");
0219: }
0220:
0221: /**
0222: * Check to make sure the database schema exists. If not, load it
0223: * from the <code>bugdb.sql</code> file.
0224: *
0225: * @exception SQLException may be thrown.
0226: */
0227: private final void checkSchema() throws SQLException {
0228: synchronized (lock) {
0229: Statement s = conn.createStatement();
0230: ResultSet rs = null;
0231: try {
0232: rs = s.executeQuery("select * from singleton");
0233: } catch (SQLException e) {
0234: Loader loader = new Loader();
0235: loader.setConnection(conn);
0236: ClassLoader c = this .getClass().getClassLoader();
0237: InputStream is = c
0238: .getResourceAsStream("com/quadcap/app/bugdb/bugdb.sql");
0239: loader.loadStream(is);
0240: } finally {
0241: if (rs != null)
0242: rs.close();
0243: s.close();
0244: }
0245: }
0246: }
0247:
0248: /**
0249: * Is this session associated with a valid user?
0250: *
0251: * @return true if the user is valid.
0252: */
0253: public boolean isUser() {
0254: return isUser;
0255: }
0256:
0257: /**
0258: * Is this session associated with a user with 'developer' privileges?
0259: *
0260: * @return true if the user is valid and has developer privileges.
0261: */
0262: public boolean isDeveloper() {
0263: return isDeveloper;
0264: }
0265:
0266: /**
0267: * Is this session associated with a user with 'manager' privileges?
0268: *
0269: * @return true if the user is valid and has manager privileges.
0270: */
0271: public boolean isManager() {
0272: return isManager;
0273: }
0274:
0275: /**
0276: * Is this session associated with a user with 'administrator' privileges?
0277: *
0278: * @return true if the user is valid and has administrator privileges.
0279: */
0280: public boolean isAdmin() {
0281: return isAdmin;
0282: }
0283:
0284: /**
0285: * Return the saved bug search query with the specified name
0286: *
0287: * @param name the name of the query
0288: * @return the query string
0289: * @exception ServletException may be thrown
0290: */
0291: public String getQuery(String name) throws ServletException {
0292: try {
0293: getQuery.clearParameters();
0294: getQuery.setString(1, me);
0295: getQuery.setString(2, name);
0296: ResultSet rs = getQuery.executeQuery();
0297: try {
0298: if (rs.next()) {
0299: return rs.getString(1);
0300: } else {
0301: return "";
0302: }
0303: } finally {
0304: rs.close();
0305: }
0306: } catch (SQLException ex) {
0307: throw new ServletException(ex.toString());
0308: }
0309: }
0310:
0311: /**
0312: * Save a bug search query.
0313: *
0314: * @param name the query name
0315: * @param query the query string
0316: * @exception ServletException may be thrown
0317: */
0318: public void saveQuery(String name, String query)
0319: throws ServletException {
0320: try {
0321: saveQuery.clearParameters();
0322: saveQuery.setString(1, name);
0323: saveQuery.setString(2, query);
0324: saveQuery.setString(3, me);
0325: saveQuery.executeUpdate();
0326: } catch (SQLException ex) {
0327: throw new ServletException(ex.toString());
0328: }
0329: }
0330:
0331: /**
0332: * Execute the 'logout' action.
0333: *
0334: * @param props the request properties for this action. Ignored.
0335: *
0336: */
0337: public boolean logout(Properties props) {
0338: me = "nobody";
0339: isAdmin = isManager = isDeveloper = isUser = false;
0340: if (conn != null) {
0341: try {
0342: conn.close();
0343: } catch (Throwable t) {
0344: }
0345: conn = null;
0346: }
0347: return true;
0348: }
0349:
0350: /**
0351: * Execute the 'login' action.
0352: *
0353: * @param props the request properties for this action.
0354: * <dl>
0355: * <dt>username</dt><dd>The user's login name</dd>
0356: * <dt>password</dt><dd>The user's password</dd>
0357: * </dl>
0358: *
0359: * @exception SQLException may be thrown
0360: */
0361: public boolean login(Properties props) throws SQLException {
0362: String user = getString(props, "username");
0363: String pass = getString(props, "password");
0364: getConnection();
0365: Statement s = conn.createStatement();
0366: try {
0367: ResultSet rs = s
0368: .executeQuery("select * from person where name = '"
0369: + user + "' and password = '" + pass + "'");
0370: try {
0371: if (rs.next()) {
0372: me = user;
0373: my_person_type = rs.getString("person_type");
0374: isAdmin = my_person_type.equals("Administrator");
0375: isManager = isAdmin
0376: || my_person_type.equals("Manager");
0377: isDeveloper = isManager
0378: || my_person_type.equals("Developer");
0379: isUser = isDeveloper
0380: || my_person_type.equals("User");
0381: return true;
0382: }
0383: } finally {
0384: rs.close();
0385: }
0386: } finally {
0387: s.close();
0388: }
0389: return false;
0390: }
0391:
0392: /**
0393: * Return the next integer in the specified sequence.
0394: *
0395: * @return the next integer in this sequence.
0396: * @exception SQLException may be thrown
0397: */
0398: int getNextId(String type) throws SQLException {
0399: Statement s = conn.createStatement(
0400: ResultSet.TYPE_SCROLL_SENSITIVE,
0401: ResultSet.CONCUR_UPDATABLE);
0402: try {
0403: int ret = 0;
0404: ResultSet rs = s
0405: .executeQuery("select ivalue from singleton where name = '"
0406: + type + "'");
0407: try {
0408: if (rs.next()) {
0409: ret = rs.getInt(1);
0410: rs.updateInt(1, ret + 1);
0411: rs.updateRow();
0412: }
0413: } finally {
0414: rs.close();
0415: }
0416: return ret;
0417: } finally {
0418: s.close();
0419: }
0420: }
0421:
0422: /**
0423: * Return the number of records in the bug history for the specified
0424: * bug.
0425: *
0426: * @param bug_id the bug number
0427: * @return the number of history records for this bug.
0428: * @exception SQLException may be thrown
0429: */
0430: int getBugHistSize(int bug_id) throws SQLException {
0431: getBugHist.clearParameters();
0432: getBugHist.setInt(1, bug_id);
0433: ResultSet rs = getBugHist.executeQuery();
0434: try {
0435: if (rs.next()) {
0436: int ret = rs.getInt(1) + 1;
0437: rs.updateInt(1, ret);
0438: rs.updateRow();
0439: return ret;
0440: } else {
0441: throw new SQLException("no bug: " + bug_id);
0442: }
0443: } finally {
0444: rs.close();
0445: }
0446: }
0447:
0448: /**
0449: * Execute the 'addUser' action.
0450: *
0451: * @param props the request properties for this action:<p>
0452: * <dl>
0453: * <dt>name</dt><dd>The user's login name</dd>
0454: * <dt>password</dt><dd>The user's password</dd>
0455: * <dt>password2</dt><dd>The user's password confirmation</dd>
0456: * <dt>email</dt><dd>The user's email address</dd>
0457: * <dt>person_type</dt><dd>The user's 'class', one of
0458: * <ul><li>User</li>
0459: * <li>Developer</li>
0460: * <li>Manager</li>
0461: * <li>Administrator</li>
0462: * </ul></dt>
0463: * </dl>
0464: *
0465: * @exception SQLException may be thrown
0466: */
0467: public boolean addUser(Properties props) throws SQLException,
0468: ServletException {
0469: String p1 = getString(props, "password");
0470: String p2 = getString(props, "password2");
0471: if (!p1.equals(p2)) {
0472: throw new ServletException(
0473: "Password doesn't match confirm password");
0474: }
0475: insertPerson.clearParameters();
0476: insertPerson.setString(1, getString(props, "name"));
0477: insertPerson.setString(2, getString(props, "password"));
0478: insertPerson.setString(3, getString(props, "email"));
0479:
0480: String person_type = "User";
0481: if (isAdmin) {
0482: String t = getString(props, "person_type");
0483: if (t != null && t.length() > 0)
0484: person_type = t;
0485: }
0486: insertPerson.setString(4, person_type);
0487:
0488: return 1 == insertPerson.executeUpdate();
0489: }
0490:
0491: /**
0492: * Execute the 'updateUser' action.
0493: * @param props the request properties for this action:<p>
0494: * <dl>
0495: * <dt>name</dt><dd>The user's login name</dd>
0496: * <dt>password</dt><dd>The user's password</dd>
0497: * <dt>email</dt><dd>The user's email address</dd>
0498: * <dt>person_type</dt><dd>The user's 'class', one of
0499: * <ul><li>User</li>
0500: * <li>Developer</li>
0501: * <li>Manager</li>
0502: * <li>Administrator</li>
0503: * </ul></dt>
0504: * </dl>
0505: *
0506: * @exception SQLException may be thrown
0507: */
0508: public boolean updateUser(Properties p) throws SQLException {
0509: updateUser.clearParameters();
0510: updateUser.setString(1, p.getProperty("name"));
0511: ResultSet rs = updateUser.executeQuery();
0512: try {
0513: if (rs.next()) {
0514: if (!p.getProperty("password").equals("password")) {
0515: rs.updateString("password", p
0516: .getProperty("password"));
0517: }
0518: rs.updateString("email", p.getProperty("email"));
0519: rs.updateString("person_type", p
0520: .getProperty("person_type"));
0521: rs.updateRow();
0522: return true;
0523: } else {
0524: return false;
0525: }
0526: } finally {
0527: rs.close();
0528: }
0529: }
0530:
0531: /**
0532: * Execute the 'deleteUser' action
0533: *
0534: * @exception SQLException may be thrown
0535: */
0536: public boolean deleteUser(Properties p) throws SQLException {
0537: deleteUser.clearParameters();
0538: deleteUser.setString(1, p.getProperty("name"));
0539: return deleteUser.executeUpdate() == 1;
0540: }
0541:
0542: /**
0543: * Generate an HTML <code><SELECT></code> element listing all
0544: * elements in the specified table.
0545: *
0546: * @param type the table (e.g., priority, state, etc.)
0547: * @return the HTML string.
0548: */
0549: public String listOptions(String type) throws ServletException {
0550: return listOptions(type, null);
0551: }
0552:
0553: /**
0554: * Generate an HTML <code><SELECT></code> element listing all
0555: * elements in the specified table.
0556: *
0557: * @param type the table (e.g., priority, state, etc.) Also the name
0558: * of the SELECT element, and the name of the column.
0559: * @param sel if non null, the value to be initially selected
0560: * @return the HTML string.
0561: */
0562: public String listOptions(String type, String sel)
0563: throws ServletException {
0564: return listOptions(type, type, sel);
0565: }
0566:
0567: /**
0568: * Generate an HTML <code><SELECT></code> element listing all
0569: * elements in the specified table.
0570: *
0571: * @param table the table (e.g., priority, state, etc.) Also the name
0572: * of the SELECT element.
0573: * @param type the name of the column containing the enumerated values.
0574: * @param sel if non null, the value to be initially selected
0575: * @return the HTML string.
0576: */
0577: public String listOptions(String table, String type, String sel)
0578: throws ServletException {
0579: return listOptions(table, table, type, sel);
0580: }
0581:
0582: /**
0583: * Generate an HTML <code><SELECT></code> element listing all
0584: * elements in the specified table.
0585: *
0586: * @param selType the name of the SELECT widget.
0587: * @param table the table (e.g., priority, state, etc.)
0588: * @param type the name of the column containing the enumerated values.
0589: * @param sel if non null, the value to be initially selected
0590: * @return the HTML string.
0591: */
0592: public String listOptions(String seltype, String table,
0593: String type, String sel) throws ServletException {
0594: try {
0595: Statement s = conn.createStatement();
0596: StringBuffer sb = new StringBuffer("\n<select name=\"");
0597: sb.append(seltype);
0598: sb.append("\">");
0599: try {
0600: ResultSet rs = s.executeQuery("select " + type
0601: + " from " + table);
0602: try {
0603: while (rs.next()) {
0604: String t = rs.getString(1);
0605: sb.append("\n <option value=\"");
0606: sb.append(t);
0607: sb.append('"');
0608: if (sel != null && t.equals(sel)) {
0609: sb.append(" selected");
0610: }
0611: sb.append(">");
0612: sb.append(t);
0613: sb.append("</option>");
0614: }
0615: } finally {
0616: rs.close();
0617: }
0618: sb.append("</select>");
0619: return sb.toString();
0620: } finally {
0621: s.close();
0622: }
0623: } catch (SQLException e) {
0624: throw new ServletException(e.toString());
0625: }
0626: }
0627:
0628: /**
0629: * Generate an HTML <code><SELECT></code> element listing all
0630: * users.
0631: *
0632: * @param type the name of the SELECT element.
0633: * @param sel the value (if any) to be initially selected.
0634: */
0635: public String listUsers(String type, String sel)
0636: throws ServletException {
0637: return listOptions(type, "person", "name", sel);
0638: }
0639:
0640: /**
0641: * Return the name of the user who owns the specified component.
0642: *
0643: * @param component the component name
0644: * @return the component's owner
0645: * @exception SQLException may be thrown
0646: * @exception ServletException may be thrown
0647: */
0648: public String getComponentOwner(String component)
0649: throws SQLException, ServletException {
0650: synchronized (getComponentOwner) {
0651: getComponentOwner.clearParameters();
0652: getComponentOwner.setString(1, component);
0653: ResultSet rs = getComponentOwner.executeQuery();
0654: try {
0655: if (rs.next()) {
0656: return rs.getString(1);
0657: } else {
0658: return "nobody";
0659: }
0660: } finally {
0661: rs.close();
0662: }
0663: }
0664: }
0665:
0666: /**
0667: * Return an integer valued property
0668: *
0669: * @param props a properties object
0670: * @param name the name of the property to retrieve.
0671: * @return the property value
0672: * @exception ServletException is thrown if the property isn't a valid
0673: * integer.
0674: */
0675: static int getInt(Properties props, String name)
0676: throws ServletException {
0677: try {
0678: return Integer.parseInt(props.getProperty(name));
0679: } catch (Throwable e) {
0680: throw new ServletException(
0681: "Missing or malformed parameter: " + name);
0682: }
0683: }
0684:
0685: /**
0686: * Return a string-valued property.
0687: *
0688: * @param props a properties object
0689: * @param name the name of the property to retrieve.
0690: * @return the property value, or the empty string if the property
0691: * isn't defined.
0692: */
0693: static String getString(Properties props, String name) {
0694: String s = props.getProperty(name);
0695: if (s == null)
0696: return "";
0697: return s;
0698: }
0699:
0700: /**
0701: * Execute the 'addBug' action.
0702: *
0703: * @param props the request properties, contains values for the various
0704: * fields in the 'bug' and 'bug_history' tables.
0705: *
0706: * @return true if this action succeeded.
0707: * @exception ServletException may be thrown.
0708: */
0709: public boolean addBug(Properties props) throws ServletException {
0710: if (!isUser())
0711: throw new ServletException("not logged in");
0712: try {
0713: Statement s = conn.createStatement();
0714: try {
0715: int bug_id = getNextId("bug");
0716: int ret = s
0717: .executeUpdate("insert into bug(bug_id, hist_size) values("
0718: + bug_id + ",0)");
0719: if (ret != 1) {
0720: throw new ServletException("add bug failed");
0721: }
0722:
0723: String component = getString(props, "component");
0724: String owner = getComponentOwner(component);
0725:
0726: insertBugHist.clearParameters();
0727: insertBugHist.setInt(1, bug_id); // bug_id
0728: insertBugHist.setInt(2, 0); // hist_pos
0729: insertBugHist.setString(3, me); // submitter
0730: insertBugHist
0731: .setString(4, getString(props, "abstract"));
0732: insertBugHist.setString(5, getString(props,
0733: "description"));
0734: insertBugHist
0735: .setString(6, getString(props, "comments"));
0736: insertBugHist.setString(7, "Active"); // state
0737: insertBugHist
0738: .setString(8, getString(props, "priority"));
0739: insertBugHist.setString(9, getString(props, "type"));
0740: insertBugHist.setString(10, owner); // owner
0741: insertBugHist.setString(11, component); // component
0742: insertBugHist.setTimestamp(12, now()); // modtime
0743: ret = insertBugHist.executeUpdate();
0744: if (ret != 1) {
0745: throw new ServletException(
0746: "add bug history failed, ret = " + ret);
0747: }
0748: return true;
0749: } finally {
0750: s.close();
0751: }
0752: } catch (SQLException e) {
0753: throw new ServletException(e.toString());
0754: }
0755: }
0756:
0757: /**
0758: * Execute the 'updateBug' action.
0759: *
0760: * @param props the request properties, contains values for the various
0761: * fields in the 'bug' and 'bug_history' tables.
0762: *
0763: * @return true if this action succeeded.
0764: * @exception ServletException may be thrown.
0765: */
0766: public boolean updateBug(Properties props) throws ServletException {
0767: if (!isUser())
0768: throw new ServletException("not logged in");
0769: try {
0770: int bug_id = getInt(props, "bug_id");
0771: int hist_pos = getBugHistSize(bug_id);
0772:
0773: String component = getString(props, "component");
0774: String owner = getComponentOwner(component);
0775:
0776: insertBugHist.clearParameters();
0777: insertBugHist.setInt(1, bug_id); // bug_id
0778: insertBugHist.setInt(2, hist_pos); // hist_pos
0779: insertBugHist.setString(3, me); // submitter
0780: insertBugHist.setString(4, getString(props, "abstract"));
0781: insertBugHist.setString(5, getString(props, "description"));
0782: insertBugHist.setString(6, getString(props, "comments"));
0783: insertBugHist.setString(7, getString(props, "state"));
0784: insertBugHist.setString(8, getString(props, "priority"));
0785: insertBugHist.setString(9, getString(props, "type"));
0786: insertBugHist.setString(10, getString(props, "owner"));
0787: insertBugHist.setString(11, getString(props, "component"));
0788: insertBugHist.setTimestamp(12, now()); // modtime
0789: if (insertBugHist.executeUpdate() != 1) {
0790: throw new ServletException("update bug history failed");
0791: }
0792: return true;
0793: } catch (SQLException e) {
0794: throw new ServletException(e.toString());
0795: }
0796: }
0797:
0798: /**
0799: * Make a sql timestamp value representing the current time.
0800: *
0801: * @return a timestamp value.
0802: */
0803: static final java.sql.Timestamp now() {
0804: return new java.sql.Timestamp(System.currentTimeMillis());
0805: }
0806:
0807: /**
0808: * Utility to do URL decoding without any exception handling.
0809: * Lazy bastard.
0810: * @param s the url to decode
0811: * @return the decoded url
0812: */
0813: public final String urlDecode(String s) {
0814: try {
0815: if (s == null)
0816: return "";
0817: return java.net.URLDecoder.decode(s);
0818: } catch (Throwable e) {
0819: return s;
0820: }
0821: }
0822:
0823: /**
0824: * Perform the bug search operation.
0825: *
0826: * @param query the query string, a boolean predicate selecting bugs
0827: * to be returned.
0828: * @param ob an optional column name to use with an 'order by' clause,
0829: * to cause the list of bugs to be sorted based on that column.
0830: * @return a <code>Vector</code> of <code>Properties</code> objects,
0831: * where each Properties element contains entries for each column
0832: * in the search result.
0833: * @exception ServletException may be thrown
0834: */
0835: public Vector searchBugs(String query, String ob)
0836: throws ServletException {
0837: Vector v = new Vector();
0838: try {
0839: Statement s = conn.createStatement();
0840: if (query == null)
0841: query = "";
0842: query = query.trim();
0843: if (query.length() > 0) {
0844: query = "and " + query;
0845: }
0846: if (ob != null) {
0847: query = query + " order by " + ob;
0848: }
0849: StringBuffer sb = new StringBuffer();
0850: sb
0851: .append("select * from bug natural join bug_history where ");
0852: sb.append("(hist_size = hist_pos) " + query);
0853: ResultSet rs = s.executeQuery(sb.toString());
0854: ResultSetMetaData rm = rs.getMetaData();
0855: try {
0856: while (rs.next()) {
0857: Properties p = new Properties();
0858: for (int i = 1; i <= rm.getColumnCount(); i++) {
0859: String name = rm.getColumnLabel(i);
0860: Object obj = rs.getObject(i);
0861: String val = (obj == null) ? "" : obj
0862: .toString();
0863: p.put(name.toLowerCase(), val);
0864: }
0865: v.addElement(p);
0866: }
0867: } finally {
0868: rs.close();
0869: }
0870: } catch (SQLException e) {
0871: throw new ServletException(e.toString());
0872: } catch (Throwable t) {
0873: t.printStackTrace(System.out);
0874: }
0875: return v;
0876: }
0877:
0878: /**
0879: * Turn the current row in the result set into a Hashtable.
0880: *
0881: * @param rs a result set
0882: * @return a Hashtable where the keys are the string-valued column labels
0883: * of the result set and the values are the string-valued column values
0884: * from the current row of the result set.
0885: *
0886: * @exception SQLException may be thrown
0887: */
0888: final Hashtable getProps(ResultSet rs) throws SQLException {
0889: Hashtable p = new Hashtable();
0890: ResultSetMetaData rm = rs.getMetaData();
0891: for (int i = 1; i <= rm.getColumnCount(); i++) {
0892: String name = rm.getColumnLabel(i);
0893: String val = String.valueOf(rs.getObject(i));
0894: p.put(name.toLowerCase(), val);
0895: }
0896: return p;
0897: }
0898:
0899: /**
0900: * Return a list of valid users, as a Vector of Properties objects,
0901: * where each Hashtable object contains all of the user's properties
0902: * (except the password!)
0903: *
0904: * @return a list of valid users
0905: * @exception SQLException may be thrown
0906: */
0907: public Vector getUsers() throws SQLException {
0908: Vector v = new Vector();
0909: Statement s = conn.createStatement();
0910: try {
0911: ResultSet rs = s.executeQuery("select * from person");
0912: try {
0913: while (rs.next()) {
0914: Hashtable t = getProps(rs);
0915: t.remove("password");
0916: v.addElement(t);
0917: }
0918: } finally {
0919: rs.close();
0920: }
0921: } finally {
0922: s.close();
0923: }
0924: return v;
0925: }
0926:
0927: /**
0928: * Return a list of valid project/components, as a Vector of Hashtable
0929: * objects,
0930: * where each Hashtable object contains all of the user's properties
0931: * (except the password!)
0932: *
0933: * @return a list of valid components
0934: * @exception SQLException may be thrown
0935: */
0936: public Vector getProjects() throws SQLException {
0937: Vector v = new Vector();
0938: Statement s = conn.createStatement();
0939: try {
0940: ResultSet rs = s.executeQuery("select * from component");
0941: try {
0942: while (rs.next()) {
0943: Hashtable t = getProps(rs);
0944: v.addElement(t);
0945: }
0946: } finally {
0947: rs.close();
0948: }
0949: } finally {
0950: s.close();
0951: }
0952: return v;
0953: }
0954:
0955: /**
0956: * Return the properties for a single user.
0957: *
0958: * @param user the user name
0959: * @return the user's properties (except the password)
0960: * @exception SQLException may be thrown
0961: */
0962: public Hashtable getUser(String user) throws SQLException {
0963: getUser.clearParameters();
0964: getUser.setString(1, user);
0965: ResultSet rs = getUser.executeQuery();
0966: try {
0967: if (rs.next()) {
0968: Hashtable t = getProps(rs);
0969: t.remove("password");
0970: return t;
0971: } else {
0972: return null;
0973: }
0974: } finally {
0975: rs.close();
0976: }
0977: }
0978:
0979: /**
0980: * Return the properties for a single component.
0981: *
0982: * @param name the component name
0983: * @return the component's properties
0984: * @exception SQLException may be thrown
0985: */
0986: public Hashtable getProject(String user) throws SQLException {
0987: getProject.clearParameters();
0988: getProject.setString(1, user);
0989: ResultSet rs = getProject.executeQuery();
0990: try {
0991: if (rs.next()) {
0992: Hashtable t = getProps(rs);
0993: return t;
0994: } else {
0995: return null;
0996: }
0997: } finally {
0998: rs.close();
0999: }
1000: }
1001:
1002: /**
1003: * Execute the 'newProject' action.
1004: *
1005: * @param props the request properties for this action:<p>
1006: * <dl>
1007: * <dt>component</dt><dd>The name of the project</dd>
1008: * <dt>owner</dt><dd>The owner of the project.</dd>
1009: * </dl>
1010: *
1011: * @exception SQLException may be thrown
1012: */
1013: public boolean newProject(Properties p) throws SQLException {
1014: newProject.clearParameters();
1015: newProject.setString(1, p.getProperty("component"));
1016: newProject.setString(2, p.getProperty("owner"));
1017: return 1 == newProject.executeUpdate();
1018: }
1019:
1020: /**
1021: * Execute the 'updateProject' action.
1022: * @param props the request properties for this action:<p>
1023: * <dl>
1024: * <dt>component</dt><dd>The name of the project</dd>
1025: * <dt>owner</dt><dd>The owner of the project.</dd>
1026: * </dl>
1027: *
1028: * @exception SQLException may be thrown
1029: */
1030: public boolean updateProject(Properties p) throws SQLException {
1031: updateProject.clearParameters();
1032: updateProject.setString(1, p.getProperty("component"));
1033: ResultSet rs = updateProject.executeQuery();
1034: try {
1035: if (rs.next()) {
1036: rs.updateString("owner", p.getProperty("owner"));
1037: rs.updateRow();
1038: return true;
1039: } else {
1040: return false;
1041: }
1042: } finally {
1043: rs.close();
1044: }
1045: }
1046:
1047: /**
1048: * Execute the 'deleteProject' action
1049: *
1050: * @exception SQLException may be thrown
1051: */
1052: public boolean deleteProject(Properties p) throws SQLException {
1053: deleteProject.clearParameters();
1054: deleteProject.setString(1, p.getProperty("component"));
1055: return deleteProject.executeUpdate() == 1;
1056: }
1057:
1058: /**
1059: * The properties that we ignore when computing deltas between
1060: * successive bug history records.
1061: */
1062: static Hashtable nonDeltas = new Hashtable();
1063: static {
1064: nonDeltas.put("modtime", "");
1065: nonDeltas.put("comments", "");
1066: }
1067:
1068: /**
1069: * Given two bug history records, return a hashtable containing entries
1070: * for each field which changed between the two records.
1071: *
1072: * @param a the newer history record
1073: * @param b the older history record
1074: * @return a Hashtable where the keys are the names of the changed fields
1075: * and the values are strings of the form:<p>
1076: * <pre>Changed from <i>bval</i> to <i>aval</i></pre>
1077: */
1078: Hashtable diffProps(Hashtable a, Hashtable b) {
1079: Hashtable t = new Hashtable();
1080: Enumeration e = a.keys();
1081: while (e.hasMoreElements()) {
1082: String key = (String) e.nextElement();
1083: if (key.equals("history"))
1084: continue;
1085: if (key.equals("hist_pos"))
1086: continue;
1087: String aval = (String) a.get(key);
1088: String bval = (String) b.get(key);
1089: if (!aval.equals(bval)) {
1090: if (nonDeltas.get(key) != null) {
1091: t.put(key, bval);
1092: } else {
1093: t.put(key, "Changed from " + bval + " to " + aval);
1094: }
1095: }
1096: }
1097: return t;
1098: }
1099:
1100: /**
1101: * Return the current information and history for the specified bug.
1102: *
1103: * @param bug_id
1104: * @return a Hashtable where the keys are the names of the bug
1105: * properties and the values are the current (i.e., most recent)
1106: * values for those properties. The special key <i>"history"</i>
1107: * has a Vector of Hashtables value containing the reverse-chronological
1108: * history deltas for the bug.
1109: *
1110: * @exception ServletException may be thrown
1111: */
1112: public Hashtable getBug(String bug_id) throws ServletException {
1113: try {
1114: getBug.clearParameters();
1115: getBug.setInt(1, Integer.parseInt(bug_id));
1116: ResultSet rs = getBug.executeQuery();
1117: try {
1118: Hashtable ret = null;
1119: Vector hist = new Vector();
1120: Hashtable last = null;
1121: String description = "";
1122: while (rs.next()) {
1123: Hashtable props = getProps(rs);
1124: description = rs.getString("Description");
1125: if (ret == null) {
1126: ret = props;
1127: ret.put("history", hist);
1128: } else {
1129: hist.addElement(diffProps(last, props));
1130: }
1131: last = props;
1132: }
1133: // take the 'last' description. Cause the results are returned
1134: // newest first, this gives us the original bug description.
1135: if (ret == null)
1136: ret = new Hashtable();
1137: ret.put("description", description);
1138: return ret;
1139: } finally {
1140: rs.close();
1141: }
1142: } catch (SQLException e) {
1143: throw new ServletException(e.toString());
1144: }
1145: }
1146:
1147: /**
1148: * Replace occurrences of <, >, and & with their HTML
1149: * entity-representation equivalents.
1150: *
1151: * @param the the string to encode
1152: * @return the encoded string
1153: */
1154: public String htmlEncode(String s) {
1155: try {
1156: ByteArrayOutputStream bos = new ByteArrayOutputStream();
1157: PrintWriter pw = new PrintWriter(bos);
1158: HtmlWriter w = new HtmlWriter(pw);
1159: for (int i = 0; i < s.length(); i++) {
1160: w.write(s.charAt(i));
1161: }
1162: w.flush();
1163: return bos.toString();
1164: } catch (Exception e) {
1165: return s;
1166: }
1167: }
1168:
1169: /**
1170: * The main request handler. Determine which action is to be invoked
1171: * and dispatch the appropriate routine.
1172: *
1173: * @param req the http request
1174: * @exception ServletException may be thrown
1175: * @exception SQLException may be thrown
1176: */
1177: public void handleRequest(HttpServletRequest req)
1178: throws ServletException, SQLException {
1179: String action = req.getParameter("action");
1180: boolean commit = false;
1181: try {
1182: if (action != null) {
1183: Properties p = getRequestProperties(req);
1184: if (action.equals("login")) {
1185: commit = login(p);
1186: } else if (action.equals("logout")) {
1187: commit = logout(p);
1188: } else if (!isUser()) {
1189: throw new ServletException("not logged in");
1190: } else if (action.equals("newbug")) {
1191: commit = addBug(p);
1192: } else if (action.equals("modify")) {
1193: commit = updateBug(p);
1194: } else if (action.equals("register")) {
1195: commit = addUser(p);
1196: } else if (action.equals("updateUser")) {
1197: commit = updateUser(p);
1198: } else if (action.equals("deleteUser")) {
1199: commit = deleteUser(p);
1200: } else if (action.equals("newUser")) {
1201: commit = addUser(p);
1202: } else if (action.equals("newProject")) {
1203: commit = newProject(p);
1204: } else if (action.equals("updateProject")) {
1205: commit = updateProject(p);
1206: } else if (action.equals("deleteProject")) {
1207: commit = deleteProject(p);
1208: } else if (action.equals("noop")) {
1209: } else {
1210: throw new ServletException("Bad action: " + action);
1211: }
1212: } else if (!isUser()) {
1213: throw new ServletException("not logged in");
1214: }
1215: } finally {
1216: // if (commit && conn != null) {
1217: // conn.commit();
1218: // } else {
1219: // conn.rollback();
1220: // }
1221: }
1222: }
1223:
1224: /**
1225: * Build a Properties object containing all of the request parameters.
1226: *
1227: * @param req the http request
1228: * @return the request parameters
1229: */
1230: public Properties getRequestProperties(HttpServletRequest req) {
1231: Properties p = new Properties();
1232: Enumeration e = req.getParameterNames();
1233: while (e.hasMoreElements()) {
1234: String name = (String) e.nextElement();
1235: String val = req.getParameter(name);
1236: try {
1237: val = URLDecoder.decode(val);
1238: } catch (Throwable ex) {
1239: }
1240: p.put(name, val);
1241: }
1242: return p;
1243: }
1244: }
|