KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > taskblocks > bugzilla > BugzillaSubmitter


1 /*
2  * Copyright (C) Jakub Neubauer, 2007
3  *
4  * This file is part of TaskBlocks
5  *
6  * TaskBlocks is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * TaskBlocks is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */

19
20 package taskblocks.bugzilla;
21
22 import java.io.BufferedReader JavaDoc;
23 import java.io.ByteArrayInputStream JavaDoc;
24 import java.io.CharArrayWriter JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.InputStreamReader JavaDoc;
28 import java.io.OutputStream JavaDoc;
29 import java.io.PrintWriter JavaDoc;
30 import java.io.UnsupportedEncodingException JavaDoc;
31 import java.net.HttpURLConnection JavaDoc;
32 import java.net.MalformedURLException JavaDoc;
33 import java.net.URL JavaDoc;
34 import java.net.URLEncoder JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.HashMap JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Map JavaDoc;
39 import java.util.regex.Matcher JavaDoc;
40 import java.util.regex.Pattern JavaDoc;
41
42 import javax.xml.parsers.DocumentBuilder JavaDoc;
43 import javax.xml.parsers.DocumentBuilderFactory JavaDoc;
44 import javax.xml.parsers.ParserConfigurationException JavaDoc;
45
46 import org.w3c.dom.Document JavaDoc;
47 import org.w3c.dom.Element JavaDoc;
48 import org.xml.sax.SAXException JavaDoc;
49
50 import taskblocks.Utils;
51
52 /**
53  * Instances of this class can submit new bugs to Bugzilla.
54  * It uses it's bug_submit cgi script to submit new bug. The output html page is parsed
55  * to recognize the status of the operation.
56  *
57  * @author j.neubauer
58  */

59 public class BugzillaSubmitter {
60
61     /** Bug property name */
62     public static final String JavaDoc KEYWORDS = "keywords";
63
64     /** Bug property name */
65     public static final String JavaDoc PRODUCT = "product";
66
67     /** Bug property name */
68     public static final String JavaDoc VERSION = "version";
69
70     /** Bug property name */
71     public static final String JavaDoc COMPONENT = "component";
72
73     /** Bug property name */
74     public static final String JavaDoc HARDWARE = "rep_platform";
75
76     /** Bug property name */
77     public static final String JavaDoc OS = "op_sys";
78
79     /** Bug property name */
80     public static final String JavaDoc PRIORITY = "priority";
81
82     /** Bug property name */
83     public static final String JavaDoc SEVERITY = "bug_severity";
84
85     /**
86      * Bug property name Probably supported from bugzilla version 3.0, bug only
87      * NEW and ASSIGNED values
88      */

89     public static final String JavaDoc STATUS = "bug_status";
90
91     /** Bug property name */
92     public static final String JavaDoc ASSIGNED_TO = "assigned_to";
93
94     /** Bug property name */
95     public static final String JavaDoc SUMMARY = "short_desc";
96
97     /** Bug property name */
98     public static final String JavaDoc DESCRIPTION = "comment";
99
100     /** Bug property name */
101     public static final String JavaDoc ESTIMATED_TIME = "estimated_time";
102
103     /** Bug property name */
104     public static final String JavaDoc BLOCKS = "blocked";
105
106     /** Must be enabled on bugzilla server */
107     public static final String JavaDoc STATUS_WHITEBOARD = "status_whiteboard";
108
109     /**
110      * Regular expression used to parse output from bugzilla and to find the submitted bug id.
111      * if not found, it is supposed that error occured.
112      */

113     public String JavaDoc _successRegexp = "Bug ([0-9]+) Submitted";
114     
115     /**
116      * Regular expression used to find title of the error if submission doesn't
117      * success. By default, it is the title of the web page
118      */

119     public String JavaDoc _errTitleRegexp = "<title>(.*)</title>";
120
121     /**
122      * Regular expression used to find description of error if submission doesn't
123      * success. This one retrieves the main body of the page.
124      */

125     public String JavaDoc _errDetailRegexp = "<div id=\"bugzilla-body\">(.*)</div>.*?<div id=\"footer\">";
126
127     /** Regular expressions used to clean the detail error message. */
128     public String JavaDoc[] _errDetailRemovalRegexps = new String JavaDoc[] {
129             "(?s)<script.*?</script>",
130             "(?s)<div id=\"docslinks\">.*?</div>"
131     };
132
133     /**
134      * encodes a form data from the given key-value pairs.
135      *
136      * @param formData
137      * @return
138      * @throws UnsupportedEncodingException
139      */

140     private static String JavaDoc buildFormBody(Map JavaDoc<String JavaDoc, String JavaDoc> formData)
141             throws UnsupportedEncodingException JavaDoc {
142         StringBuilder JavaDoc body = new StringBuilder JavaDoc();
143         int count = 0;
144         for (Map.Entry JavaDoc<String JavaDoc, String JavaDoc> e : formData.entrySet()) {
145             if (count > 0) {
146                 body.append("&");
147             }
148             body.append(URLEncoder.encode(e.getKey(), "UTF-8"));
149             body.append("=");
150             body.append(URLEncoder.encode(e.getValue(), "UTF-8"));
151             count++;
152         }
153         return body.toString();
154     }
155
156     /**
157      * Submits the given body with POST method to specified url
158      *
159      * @param url must be http protocol
160      * @param body
161      * @return http reply data
162      * @throws IOException
163      */

164     private String JavaDoc submit(URL JavaDoc url, String JavaDoc body) throws IOException JavaDoc {
165
166         // URL must use the http protocol!
167
HttpURLConnection JavaDoc conn = (HttpURLConnection JavaDoc) url.openConnection();
168         conn.setRequestMethod("POST");
169         conn.setAllowUserInteraction(false); // you may not ask the user
170
conn.setDoOutput(true); // we want to send things
171
// the Content-type should be default, but we set it anyway
172
conn.setRequestProperty("Content-type",
173                 "application/x-www-form-urlencoded; charset=utf-8");
174         // the content-length should not be necessary, but we're cautious
175
conn.setRequestProperty("Content-length", Integer.toString(body.length()));
176
177         // get the output stream to POST our form data
178
OutputStream JavaDoc rawOutStream = conn.getOutputStream();
179         PrintWriter JavaDoc pw = new PrintWriter JavaDoc(rawOutStream);
180
181         pw.print(body); // here we "send" our body!
182
pw.flush();
183         pw.close();
184
185         // get the input stream for reading the reply
186
// IMPORTANT! Your body will not get transmitted if you get the
187
// InputStream before completely writing out your output first!
188
InputStream JavaDoc rawInStream = conn.getInputStream();
189
190         // Get response.
191
// We hope, that bugzilla results are utf-8 encoded
192
BufferedReader JavaDoc rdr = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(rawInStream, "UTF-8"));
193         CharArrayWriter JavaDoc result = new CharArrayWriter JavaDoc();
194         char[] buf = new char[1024];
195         int count = rdr.read(buf);
196         while(count > 0) {
197             result.write(buf, 0, count);
198             count = rdr.read(buf);
199         }
200         
201         conn.disconnect();
202         return result.toString();
203     }
204
205     private void ensureDefault(Map JavaDoc<String JavaDoc, String JavaDoc> map, String JavaDoc key,
206             String JavaDoc defaultValue) {
207         if (!map.containsKey(key)) {
208             map.put(key, defaultValue);
209         }
210     }
211
212     /**
213      * Submits new bug to bugzilla server running at specified url.
214      * If bugzilla returns error page, and exception is thrown with error message
215      * extracted by parsing the result html page with regular expressions
216      * {@link #_errTitleRegexp}, {@link #_errDetailRegexp} and {@link #_errDetailRemovalRegexps}.
217      * Bug submission success is recognized by parsing output and finding bug id with
218      * regular expressiont {@link #_successRegexp}.
219      *
220      *
221      * @param baseUrl
222      * base url of bugzilla server
223      * @param user
224      * user name for authentication
225      * @param password
226      * password for authentication
227      * @param properties
228      * properties of new bug. Use constants in this class as keys.
229      * @return submitted bug id.
230      *
231      * @throws IOException if connection error occures
232      * @throws Exception in other cases. If connection was successfull, error messages are
233      * extracted from the html page.
234      */

235     public String JavaDoc submit(String JavaDoc baseUrl, String JavaDoc user, String JavaDoc password,
236             Map JavaDoc<String JavaDoc, String JavaDoc> properties) throws Exception JavaDoc {
237
238         // fill in default values
239
ensureDefault(properties, STATUS, "NEW");
240         ensureDefault(properties, SEVERITY, "normal");
241         ensureDefault(properties, PRIORITY, "P2");
242         ensureDefault(properties, "bug_file_loc", "http://");
243
244         // authentication
245
properties.put("form_name", "enter_bug");
246         properties.put("Bugzilla_login", user);
247         properties.put("Bugzilla_password", password);
248         properties.put("GoAheadAndLogIn", "1");
249
250         String JavaDoc formBody = buildFormBody(properties);
251         String JavaDoc result = submit(new URL JavaDoc(baseUrl + "/post_bug.cgi"), formBody);
252         // System.out.println(result);
253

254         Matcher JavaDoc m = Pattern.compile(_successRegexp).matcher(result);
255         if (m.find()) {
256             String JavaDoc bugId = m.group(1);
257             return bugId;
258         } else {
259
260             String JavaDoc errText = "";
261             m = Pattern.compile(_errTitleRegexp).matcher(result);
262             if (m.find()) {
263                 errText = m.group(1);
264             }
265
266             String JavaDoc errText2 = "";
267             m = Pattern.compile(_errDetailRegexp, Pattern.DOTALL).matcher(result);
268             if (m.find()) {
269                 errText2 = m.group(1);
270             }
271             if (errText2.length() > 0) {
272                 for (String JavaDoc removeRegexp : _errDetailRemovalRegexps) {
273                     errText2 = errText2.replaceAll(removeRegexp, "");
274                 }
275                 errText2 = errText2.replaceAll("<[^>]*>", "");
276                 errText2 = errText2.replaceAll("\r?\n", " ");
277                 errText2 = errText2.replaceAll(" +", " ");
278             }
279             throw new Exception JavaDoc(errText + ": " + errText2);
280         }
281     }
282     
283     public String JavaDoc query(String JavaDoc baseUrl, String JavaDoc user, String JavaDoc password, String JavaDoc[] bugs) throws MalformedURLException JavaDoc, IOException JavaDoc, SAXException JavaDoc, ParserConfigurationException JavaDoc {
284         Map JavaDoc<String JavaDoc, String JavaDoc> formData = new HashMap JavaDoc<String JavaDoc, String JavaDoc>();
285         formData.put("ctype", "xml");
286         formData.put("excludefield", "attachmentdata");
287         String JavaDoc body = buildFormBody(formData);
288
289         for(String JavaDoc bugId: bugs) {
290             body += "&";
291             body += URLEncoder.encode("id", "UTF-8");
292             body += "=";
293             body += URLEncoder.encode(bugId, "UTF-8");
294         }
295         
296         String JavaDoc result = submit(new URL JavaDoc(baseUrl + "/show_record.cgi"), body);
297         
298         // parse the resulting xml
299
Document JavaDoc doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream JavaDoc(result.getBytes("UTF-8")));
300         Element JavaDoc rootE = doc.getDocumentElement();
301         if(!rootE.getNodeName().equals("bugzilla")) {
302             throw new IOException JavaDoc("Wrong xml answer, doesn't looks like bugzilla");
303         }
304         
305         List JavaDoc<Map JavaDoc<String JavaDoc, String JavaDoc>> bugsData = new ArrayList JavaDoc<Map JavaDoc<String JavaDoc,String JavaDoc>>();
306         for(Element JavaDoc bugE: Utils.getChilds(rootE, "bug")) {
307             Map JavaDoc<String JavaDoc, String JavaDoc> bugData = new HashMap JavaDoc<String JavaDoc, String JavaDoc>();
308             fillBugData(bugE, bugData);
309         }
310         return result;
311     }
312
313     private void fillBugData(Element JavaDoc bugE, Map JavaDoc<String JavaDoc, String JavaDoc> bugData) {
314     }
315 }
316
Popular Tags