View Javadoc

1   /*
2    * Copyright 2007 Sebastien Brunot (sbrunot@gmail.com)
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *   
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sourceforge.buildmonitor.monitors;
17  
18  import java.io.BufferedReader;
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileNotFoundException;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStreamReader;
25  import java.io.StringReader;
26  import java.net.ConnectException;
27  import java.net.HttpURLConnection;
28  import java.net.SocketException;
29  import java.net.URI;
30  import java.net.URISyntaxException;
31  import java.net.URL;
32  import java.net.URLEncoder;
33  import java.net.UnknownHostException;
34  import java.text.ParseException;
35  import java.text.SimpleDateFormat;
36  import java.util.ArrayList;
37  import java.util.Date;
38  import java.util.Hashtable;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Properties;
42  
43  import javax.xml.xpath.XPath;
44  import javax.xml.xpath.XPathExpressionException;
45  import javax.xml.xpath.XPathFactory;
46  
47  import net.sourceforge.buildmonitor.BuildMonitor;
48  import net.sourceforge.buildmonitor.BuildReport;
49  import net.sourceforge.buildmonitor.MonitoringException;
50  import net.sourceforge.buildmonitor.BuildReport.Status;
51  import net.sourceforge.buildmonitor.dialogs.BambooPropertiesDialog;
52  
53  import org.xml.sax.InputSource;
54  
55  /**
56   * The run method of this Runnable implementation monitor a Bamboo build
57   * 
58   * @author sbrunot
59   * 
60   */
61  public class BambooMonitor implements Monitor
62  {
63  	//////////////////////////////
64  	// Nested classes
65  	//////////////////////////////
66  
67  	/**
68  	 * Set of properties needed to monitor a bamboo server.
69  	 */
70  	private class BambooProperties
71  	{
72  		//////////////////////////////
73  		// Constants
74  		//////////////////////////////
75  
76  		private static final String BAMBOO_PASSWORD_PROPERTY_KEY = "bamboo.password";
77  
78  		private static final String BAMBOO_USERNAME_PROPERTY_KEY = "bamboo.username";
79  
80  		private static final String UPDATE_PERIOD_IN_SECONDS_PROPERTY_KEY = "update.period.in.seconds";
81  
82  		private static final String BAMBOO_SERVER_BASE_URL_PROPERTY_KEY = "bamboo.server.base.url";
83  
84  		private static final String USER_PROPERTIES_FILE = "bamboo-monitor.properties";
85  
86  		//////////////////////////////
87  		// Instance attributes
88  		//////////////////////////////
89  
90  		private String serverBaseUrl = null;
91  		
92  		private String username = null;
93  		
94  		private String password = null;
95  		
96  		private Integer updatePeriodInSeconds = null;
97  		
98  		//////////////////////////////
99  		// Getters and Setters
100 		//////////////////////////////
101 
102 		/**
103 		 * Get URL to the bamboo server
104 		 * @return the URL to the bamboo server
105 		 */
106 		public String getServerBaseUrl()
107 		{
108 			return this.serverBaseUrl;
109 		}
110 
111 		/**
112 		 * Set URL to the bamboo server
113 		 * @param theServerBaseUrl the URL to the bamboo server
114 		 */
115 		public void setServerBaseUrl(String theServerBaseUrl)
116 		{
117 			this.serverBaseUrl = theServerBaseUrl;
118 			// trim the remaining / in server base url if it exists
119 			if (this.serverBaseUrl != null && this.serverBaseUrl.endsWith("/"))
120 			{
121 				this.serverBaseUrl = this.serverBaseUrl.substring(0, this.serverBaseUrl.length() - 1);
122 			}
123 		}
124 
125 		/**
126 		 * Get the period (in seconds) of build status update
127 		 * @return the period (in seconds) of build status update
128 		 */
129 		public Integer getUpdatePeriodInSeconds()
130 		{
131 			return this.updatePeriodInSeconds;
132 		}
133 
134 		/**
135 		 * Set the period (in seconds) of build status update
136 		 * @param theUpdatePeriodInSeconds the period (in seconds) of build status update
137 		 */
138 		public void setUpdatePeriodInSeconds(Integer theUpdatePeriodInSeconds)
139 		{
140 			this.updatePeriodInSeconds = theUpdatePeriodInSeconds;
141 		}
142 
143 		/**
144 		 * Get the bamboo user name
145 		 * @return the bamboo user name
146 		 */
147 		public String getUsername()
148 		{
149 			return this.username;
150 		}
151 
152 		/**
153 		 * Set the bamboo user name
154 		 * @param theUsername the bamboo user name
155 		 */
156 		public void setUsername(String theUsername)
157 		{
158 			this.username = theUsername;
159 		}
160 		
161 		/**
162 		 * Get the bamboo user password
163 		 */
164 		public String getPassword()
165 		{
166 			return this.password;
167 		}
168 
169 		/**
170 		 * Set the bamboo user password
171 		 * @param thePassword the bamboo user password
172 		 */
173 		public void setPassword(String thePassword)
174 		{
175 			this.password = thePassword;
176 		}
177 
178 		//////////////////////////////
179 		// File persistence
180 		//////////////////////////////
181 		
182 		/**
183 		 * Load the properties from the {@link #USER_PROPERTIES_FILE} file in the
184 		 * user home directory.
185 		 */
186 		public void loadFromFile() throws FileNotFoundException, IOException
187 		{
188 			// Load the content of the properties file into a Properties object
189 			Properties bambooMonitorProperties = new Properties();
190 			File bambooMonitorPropertiesFile = new File(System.getProperty("user.home"), USER_PROPERTIES_FILE);
191 			if (bambooMonitorPropertiesFile.exists())
192 			{
193 				FileInputStream bambooMonitorPropertiesFileIS = new FileInputStream(bambooMonitorPropertiesFile);
194 				bambooMonitorProperties.load(bambooMonitorPropertiesFileIS);
195 				bambooMonitorPropertiesFileIS.close();
196 			}
197 			
198 			// Update the attributes using the values defined in the properties file
199 			synchronized (this)
200 			{
201 				setServerBaseUrl(bambooMonitorProperties.getProperty(BAMBOO_SERVER_BASE_URL_PROPERTY_KEY));
202 
203 				String updatePeriodInSecondsAsString = bambooMonitorProperties.getProperty(UPDATE_PERIOD_IN_SECONDS_PROPERTY_KEY);
204 				if (updatePeriodInSecondsAsString != null)
205 				{
206 					try
207 					{
208 						setUpdatePeriodInSeconds(Integer.parseInt(updatePeriodInSecondsAsString));
209 					}
210 					catch(NumberFormatException e)
211 					{
212 						// Use a default value
213 						setUpdatePeriodInSeconds(300);
214 					}
215 				}
216 				setUsername(bambooMonitorProperties.getProperty(BAMBOO_USERNAME_PROPERTY_KEY));
217 				setPassword(bambooMonitorProperties.getProperty(BAMBOO_PASSWORD_PROPERTY_KEY));
218 			}
219 		}
220 		
221 		/**
222 		 * Save the properties from the {@link #USER_PROPERTIES_FILE} file in the
223 		 * user home directory.
224 		 */
225 		public void saveToFile() throws FileNotFoundException, IOException
226 		{
227 			// Build a Properties object that contains the values of the bamboo properties
228 			Properties bambooMonitorProperties = new Properties();
229 			synchronized (this)
230 			{
231 				bambooMonitorProperties.setProperty(BAMBOO_SERVER_BASE_URL_PROPERTY_KEY, this.serverBaseUrl);
232 				bambooMonitorProperties.setProperty(BAMBOO_USERNAME_PROPERTY_KEY, this.username);
233 				bambooMonitorProperties.setProperty(BAMBOO_PASSWORD_PROPERTY_KEY, this.password);
234 				bambooMonitorProperties.setProperty(UPDATE_PERIOD_IN_SECONDS_PROPERTY_KEY, "" + this.getUpdatePeriodInSeconds());
235 			}
236 			
237 			// Store the Properties object in the file
238 			File bambooMonitorPropertiesFile = new File(System.getProperty("user.home"), USER_PROPERTIES_FILE);
239 			FileOutputStream buildMonitorPropertiesOutputStream = new FileOutputStream(bambooMonitorPropertiesFile);
240 			bambooMonitorProperties.store(buildMonitorPropertiesOutputStream, "File last updated on " + new Date());
241 			buildMonitorPropertiesOutputStream.close();
242 		}
243 	}
244 
245 	//////////////////////////////
246 	// Constants
247 	//////////////////////////////
248 
249 	private static final String REST_LOGIN_URL = "/api/rest/login.action";
250 
251 	private static final String REST_LIST_BUILD_NAMES_URL = "/api/rest/listBuildNames.action";
252 
253 	private static final String REST_GET_LATEST_BUILD_RESULTS_URL = "/api/rest/getLatestBuildResults.action";
254 
255 	private static final String URL_ENCODING = "UTF-8";
256 
257 	//////////////////////////////
258 	// Instance attributes
259 	//////////////////////////////
260 
261 	private BuildMonitor buildMonitorInstance = null;
262 	
263 	private boolean stop = false;
264 
265 	private BambooProperties bambooProperties = new BambooProperties();
266 
267 	private BambooPropertiesDialog optionsDialog = null;
268 	
269 	//////////////////////////////
270 	// Constructors
271 	//////////////////////////////
272 
273 	public BambooMonitor(BuildMonitor theBuildMonitorInstance) throws FileNotFoundException, IOException
274 	{
275 		this.buildMonitorInstance = theBuildMonitorInstance;
276 
277 		// build the options dialog
278 		this.optionsDialog = new BambooPropertiesDialog(null, true);
279 		this.optionsDialog.setIconImage(this.buildMonitorInstance.getDialogsDefaultIcon());
280 		this.optionsDialog.setTitle("Bamboo server monitoring parameters");
281 		this.optionsDialog.pack();
282 
283 		// load the monitor properties
284 		this.bambooProperties.loadFromFile();
285 
286 		// if at least one of the properties is not defined, open a window to ask for their definition
287 		if ((this.bambooProperties.getServerBaseUrl() == null) || (this.bambooProperties.getUpdatePeriodInSeconds() == null) || (this.bambooProperties.getUsername() == null) || (this.bambooProperties.getPassword() == null))
288 		{
289 			displayOptionsDialog(true);
290 			if (this.optionsDialog.getLastClickedButton() != BambooPropertiesDialog.BUTTON_OK)
291 			{
292 				System.exit(0);
293 			}
294 		}
295 	}
296 
297 	//////////////////////////////
298 	// Monitor implementation
299 	//////////////////////////////
300 	
301 	/**
302 	 * {@inheritDoc}
303 	 */
304 	public void run()
305 	{
306 		String authenticationIdentifier = getNewBambooTicket();
307 		while (!this.stop)
308 		{
309 			// TODO: TRY / CATCH FOR AUTHENTICATION EXCEPTION IN CASE THE AUTHENTICATION IDENTIFIER IS NOT VALID ANYMORE AND SHOULD BE RENEWED...
310 			try
311 			{
312 				String bambooServerBaseUrl = null;
313 				Integer updatePeriodInSeconds = null;
314 				synchronized (this.bambooProperties)
315 				{
316 					bambooServerBaseUrl = this.bambooProperties.getServerBaseUrl();
317 					updatePeriodInSeconds = this.bambooProperties.getUpdatePeriodInSeconds();
318 				}
319 				Map<String, String> builds = listBuildNames(bambooServerBaseUrl, authenticationIdentifier);
320 				List<BuildReport> lastBuildStatus = new ArrayList<BuildReport>();
321 				for (String key : builds.keySet())
322 				{
323 					BuildReport lastBuildReport = getLatestBuildResults(bambooServerBaseUrl, authenticationIdentifier, key);
324 					lastBuildReport.setName(builds.get(key));
325 					lastBuildStatus.add(lastBuildReport);
326 				}
327 				this.buildMonitorInstance.updateBuildStatus(lastBuildStatus);
328 				
329 				// wait before updating the build status again
330 				try
331 				{
332 					Thread.sleep(updatePeriodInSeconds * 1000);
333 				} catch (InterruptedException e)
334 				{
335 					// Nothing to do: continue !
336 				}
337 			}
338 			catch (MonitoringException e)
339 			{
340 				if (e.getCause() != null && e.getCause() instanceof BambooTicketNeedToBeRenewedError)
341 				{
342 					// Renew the authentication ticket
343 					authenticationIdentifier = getNewBambooTicket();
344 				}
345 				else
346 				{
347 					this.buildMonitorInstance.reportMonitoringException(e);
348 					// wait one second before trying again
349 					try
350 					{
351 						Thread.sleep(1000);
352 					} catch (InterruptedException e2)
353 					{
354 						// Nothing to do: continue !
355 					}
356 				}
357 			}
358 		}
359 	}
360 	
361 	/**
362 	 * Returns a new Bamboo authentication identifier (bamboo ticket)
363 	 * @return
364 	 */
365 	private String getNewBambooTicket()
366 	{
367 		while (true)
368 		{
369 			try
370 			{
371 				String bambooUsername = null;
372 				String bambooPassword = null;
373 				String bambooServerBaseUrl = null;
374 				synchronized (this.bambooProperties)
375 				{
376 					bambooUsername = this.bambooProperties.getUsername();
377 					bambooPassword = this.bambooProperties.getPassword();
378 					bambooServerBaseUrl = this.bambooProperties.getServerBaseUrl();
379 				}
380 				return login(bambooServerBaseUrl, bambooUsername, bambooPassword);
381 			}
382 			catch (MonitoringException e)
383 			{
384 				this.buildMonitorInstance.reportMonitoringException(e);
385 				try
386 				{
387 					Thread.sleep(15000);
388 				} catch (InterruptedException ie)
389 				{
390 				}
391 			}			
392 		}
393 	}
394 
395 	/**
396 	 * {@inheritDoc}
397 	 */
398 	public void stop()
399 	{
400 		this.stop = true;
401 	}
402 
403 	/**
404 	 * {@inheritDoc}
405 	 */
406 	public URI getMainPageURI()
407 	{
408 		URI returnedValue = null;
409 		try
410 		{
411 			synchronized (this.bambooProperties)
412 			{
413 				returnedValue = new URI(this.bambooProperties.getServerBaseUrl());
414 			}
415 		}
416 		catch (URISyntaxException e)
417 		{
418 			throw new RuntimeException(e);
419 		}
420 		return returnedValue;
421 	}
422 	
423 	/**
424 	 * {@inheritDoc}
425 	 */
426 	public URI getBuildURI(String theIdOfTheBuild)
427 	{
428 		URI returnedValue = null;
429 		try
430 		{
431 			synchronized (this.bambooProperties)
432 			{
433 				returnedValue = new URI(this.bambooProperties.getServerBaseUrl() + "/browse/" + theIdOfTheBuild);
434 			}
435 		}
436 		catch (URISyntaxException e)
437 		{
438 			throw new RuntimeException(e);
439 		}
440 		return returnedValue;
441 	}
442 	
443 	/**
444 	 * {@inheritDoc}
445 	 */
446 	public String getSystemTrayIconTooltipHeader()
447 	{
448 		synchronized (this.bambooProperties)
449 		{
450 			return "Monitoring Bamboo server at " + this.bambooProperties.getServerBaseUrl();
451 		}
452 	}
453 
454 	/**
455 	 * {@inheritDoc}
456 	 */
457 	public void displayOptionsDialog()
458 	{
459 		displayOptionsDialog(false);
460 	}
461 
462 	/**
463 	 * {@inheritDoc}
464 	 */
465 	public String getMonitoredBuildSystemName()
466 	{
467 		return "Bamboo server";
468 	}
469 
470 	//////////////////////////////
471 	// Private methods
472 	//////////////////////////////
473 
474 	private void displayOptionsDialog(boolean isDialogOpenedForPropertiesCreation)
475 	{
476 		if (!this.optionsDialog.isVisible())
477 		{
478 			// Init server base URL field
479 			if (this.bambooProperties.getServerBaseUrl() != null)
480 			{
481 				this.optionsDialog.baseURLField.setText(this.bambooProperties.getServerBaseUrl());
482 			}
483 			else
484 			{
485 				this.optionsDialog.baseURLField.setText("http://localhost:8085");
486 			}
487 			
488 			// Init username field
489 			if (this.bambooProperties.getUsername() != null)
490 			{
491 				this.optionsDialog.usernameField.setText(this.bambooProperties.getUsername());
492 			}
493 			else
494 			{
495 				this.optionsDialog.usernameField.setText("");
496 			}
497 			
498 			// Init password field
499 			if (this.bambooProperties.getPassword() != null)
500 			{
501 				this.optionsDialog.passwordField.setText(this.bambooProperties.getPassword());
502 			}
503 			else
504 			{
505 				this.optionsDialog.passwordField.setText("");
506 			}
507 			
508 			// Init update period (in minutes) field
509 			if (this.bambooProperties.getUpdatePeriodInSeconds() != null)
510 			{
511 				this.optionsDialog.updatePeriodField.setValue(this.bambooProperties.getUpdatePeriodInSeconds() / 60);
512 			}
513 			else
514 			{
515 				this.optionsDialog.updatePeriodField.setValue(5);			
516 			}
517 
518 			// If the dialog is opened for properties edition (not creation), update fields status (ok / error)
519 			if (!isDialogOpenedForPropertiesCreation)
520 			{
521 				this.optionsDialog.updateBaseURLFieldStatus();
522 				this.optionsDialog.updateUsernameFieldStatus();
523 				this.optionsDialog.updatePasswordFieldStatus();
524 			}
525 
526 			// Show the options dialog
527 			if (!this.optionsDialog.isDisplayable())
528 			{
529 				this.optionsDialog.pack();
530 			}
531 			this.optionsDialog.setVisible(true);
532 			this.optionsDialog.toFront();
533 
534 			if (this.optionsDialog.getLastClickedButton() == BambooPropertiesDialog.BUTTON_OK)
535 			{
536 				// Update the properties and save them
537 				synchronized (this.bambooProperties)
538 				{
539 					this.bambooProperties.setServerBaseUrl(this.optionsDialog.baseURLField.getText());
540 					this.bambooProperties.setUsername(this.optionsDialog.usernameField.getText());
541 					this.bambooProperties.setPassword(new String(this.optionsDialog.passwordField.getPassword()));
542 					this.bambooProperties.setUpdatePeriodInSeconds((Integer) (this.optionsDialog.updatePeriodField.getValue()) * 60);
543 				}
544 				try
545 				{
546 					this.bambooProperties.saveToFile();
547 				}
548 				catch (FileNotFoundException e)
549 				{
550 					throw new RuntimeException(e);
551 				}
552 				catch (IOException e)
553 				{
554 					throw new RuntimeException(e);
555 				}
556 				
557 				// make sure that the new properties are taken into account immediately ?
558 				this.buildMonitorInstance.reportConfigurationUpdatedToBeTakenIntoAccountImmediately();
559 			}			
560 		}
561 		else
562 		{
563 			// Give focus to the options windows if it masked by another window
564 			this.optionsDialog.setVisible(true);
565 			this.optionsDialog.toFront();
566 		}
567 	}
568 	
569 	/**
570 	 * Login and create an authentication token.
571 	 * Returns the token if login was successful, or returns an error (MonitoringException) otherwise.
572 	 * @param theBambooServerBaseURL base URL to the bamboo server
573 	 */
574 	private String login(String theBambooServerBaseURL, String theUsername, String thePassword) throws MonitoringException
575 	{
576 		String returnedValue = null;
577 		try
578 		{
579 			// Call the login URL an retrieve the server response
580 			URL methodURL = new URL(theBambooServerBaseURL + REST_LOGIN_URL + "?username=" + URLEncoder.encode(theUsername, URL_ENCODING) + "&password=" + URLEncoder.encode(thePassword, URL_ENCODING));
581 			String serverResponse = returnedValue = callBambooApi(methodURL);
582 			InputSource serverResponseIS = new InputSource(new StringReader(serverResponse));
583 			returnedValue = XPathFactory.newInstance().newXPath().evaluate("/response/auth", serverResponseIS);
584 		}
585 		catch(MonitoringException e)
586 		{
587 			throw e;
588 		}
589 		catch (Throwable e)
590 		{
591 			throw new MonitoringException(e, null);
592 		}
593 		return returnedValue;
594 	}
595 	
596 	/**
597 	 * Provides a list of all the builds on this Bamboo server.
598 	 * @param theBambooServerBaseURL
599 	 * @param theAuthenticationIdentifier
600 	 * @return
601 	 */
602 	private Map<String, String> listBuildNames(String theBambooServerBaseURL, String theAuthenticationIdentifier) throws MonitoringException
603 	{
604 		Map<String, String> returnedValue = new Hashtable<String, String>();
605 		try
606 		{
607 			// Call the list build names URL an retrieve the server response
608 			URL methodURL = new URL(theBambooServerBaseURL + REST_LIST_BUILD_NAMES_URL + "?auth=" + URLEncoder.encode(theAuthenticationIdentifier, URL_ENCODING));
609 			String serverResponse = callBambooApi(methodURL);
610 			int currentBuildNameIndex = 1;
611 			boolean moreBuildNames = true;
612 			while (moreBuildNames)
613 			{
614 				InputSource serverResponseIS = new InputSource(new StringReader(serverResponse));
615 				String currentBuildName = XPathFactory.newInstance().newXPath().evaluate("/response/build[" + currentBuildNameIndex + "]/name", serverResponseIS);
616 				serverResponseIS = new InputSource(new StringReader(serverResponse));
617 				String currentBuildKey = XPathFactory.newInstance().newXPath().evaluate("/response/build[" + currentBuildNameIndex + "]/key", serverResponseIS);
618 				if ("".equals(currentBuildKey))
619 				{
620 					moreBuildNames = false;
621 				}
622 				else
623 				{
624 					returnedValue.put(currentBuildKey, currentBuildName);
625 					currentBuildNameIndex++;
626 				}
627 			}
628 		}
629 		catch(MonitoringException e)
630 		{
631 			throw e;
632 		}
633 		catch (Throwable e)
634 		{
635 			throw new MonitoringException(e, null);
636 		}
637 		return returnedValue;
638 	}
639 	
640 	/**
641 	 * Provides the latest build results for the given buildName.
642 	 * @param theAuthenticationIdentifier
643 	 * @param theBuildKey
644 	 * @return
645 	 */
646 	private BuildReport getLatestBuildResults(String theBambooServerBaseURL, String theAuthenticationIdentifier, String theBuildKey) throws MonitoringException
647 	{
648 		BuildReport returnedValue = null;
649 		try
650 		{
651 			URL methodURL = new URL(theBambooServerBaseURL + REST_GET_LATEST_BUILD_RESULTS_URL + "?auth=" + URLEncoder.encode(theAuthenticationIdentifier, URL_ENCODING) + "&buildKey=" + URLEncoder.encode(theBuildKey, URL_ENCODING));
652 			String serverResponse = callBambooApi(methodURL);
653 			returnedValue = new BuildReport();
654 			returnedValue.setId(theBuildKey);
655 			InputSource serverResponseIS = new InputSource(new StringReader(serverResponse));
656 			String buildState = XPathFactory.newInstance().newXPath().evaluate("/response/buildState", serverResponseIS);
657 			if ("Successful".equals(buildState))
658 			{
659 				returnedValue.setStatus(Status.OK);
660 			}
661 			else if ("Failed".equals(buildState))
662 			{
663 				returnedValue.setStatus(Status.FAILED);
664 			}
665 			else
666 			{
667 				throw new MonitoringException("Unknown build state " + buildState + " returned for build " + theBuildKey, null);
668 			}
669 			serverResponseIS = new InputSource(new StringReader(serverResponse));
670 			String buildTime = XPathFactory.newInstance().newXPath().evaluate("/response/buildTime", serverResponseIS);
671 			// TODO: IS IT A CONSTANT OR A SERVER PARAMETER ?
672 			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
673 			try
674 			{
675 				returnedValue.setDate(dateFormat.parse(buildTime));
676 			}
677 			catch (ParseException e)
678 			{
679 				// TODO: display a message to the end user to asl for defining another value for the date parser ????
680 			}
681 			serverResponseIS = new InputSource(new StringReader(serverResponse));
682 		}
683 		catch(MonitoringException e)
684 		{
685 			throw e;
686 		}
687 		catch (Throwable t)
688 		{
689 			throw new MonitoringException(t, null);
690 		}
691 		return returnedValue;
692 	}
693 	
694 	/**
695 	 * Call a bamboo REST api method and return the result (or throw a MonitoringException)
696 	 * @param theURL
697 	 * @return
698 	 * @throws BambooTicketNeedToBeRenewedError error thrown is the current bamboo authentication
699 	 * ticket is not valid (anymore) and needs to be renewed.
700 	 */
701 	private String callBambooApi(URL theURL) throws MonitoringException, BambooTicketNeedToBeRenewedError
702 	{
703 		String returnedValue = null;
704 		HttpURLConnection urlConnection = null;
705 		BufferedReader urlConnectionReader = null;
706 		try
707 		{
708 			// Call the Bamboo api and retrieve the server response
709 			urlConnection = (HttpURLConnection) theURL.openConnection();
710 			urlConnection.connect();
711 			urlConnectionReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
712 			String line = null;
713 			StringBuffer serverResponse = new StringBuffer();
714 			while ((line = urlConnectionReader.readLine()) != null)
715 			{
716 				serverResponse.append(line);
717 			}
718 			// parse the server response
719 			returnedValue = serverResponse.toString();
720 			// TODO: IF THE SERVER INSTALLATION IS NOT FINISHED, AN HTML PAGE IS RETURNED BY THE URL... WE SHOULD TRY TO DETECT IF IT IS NOT THE CASE HERE !!!
721 			if (returnedValue.contains("<title>Bamboo Setup Wizard - Atlassian Bamboo</title>"))
722 			{
723 				// TODO: OUVRIR BROWSER VERS L'INSTANCE BAMBOO ?
724 				throw new MonitoringException("Your Bamboo server installation is not finished ! Double click here to complete the Bamboo Setup Wizard !", getMainPageURI());
725 			}
726 			InputSource is = new InputSource(new StringReader(serverResponse.toString()));
727 			XPath xpath = XPathFactory.newInstance().newXPath();
728 			String error = xpath.evaluate("/errors/error", is);
729 			if (!"".equals(error))
730 			{
731 				if ("User not authenticated yet, or session timed out.".equals(error))
732 				{
733 					// A new authentication ticket should be requested !
734 					throw new BambooTicketNeedToBeRenewedError();
735 				}
736 				else
737 				{
738 					boolean isErrorOptionsRelated = false;
739 					URI uriForNonOptionsRelatedErrors = getMainPageURI();
740 					if ("Invalid username or password.".equals(error))
741 					{
742 						isErrorOptionsRelated = true;
743 					}
744 					if ("The remote API has been disabled.".equals(error))
745 					{
746 						error += " Double click here to enable it.";
747 						// Build the URI to the Bamboo general configuration setting page (BASE_SERVER_URL/admin/configure!default.action)
748 						try
749 						{
750 							synchronized (this.bambooProperties)
751 							{
752 								uriForNonOptionsRelatedErrors = new URI(this.bambooProperties.getServerBaseUrl() + "/admin/configure!default.action");
753 							}
754 						}
755 						catch (URISyntaxException e)
756 						{
757 							throw new RuntimeException(e);
758 						}
759 					}
760 
761 					throw new MonitoringException("Error reported by the Bamboo server: " + error, isErrorOptionsRelated, uriForNonOptionsRelatedErrors);
762 				}
763 			}
764 		}
765 		catch (ClassCastException e)
766 		{
767 			// This error should only occurs if the user has modified the properties file "by hand"
768 			throw new MonitoringException("Problem: the base URL defined for the Bamboo server in Options is not an http URL.", true, null);
769 		}
770 		catch (UnknownHostException e)
771 		{
772 			throw new MonitoringException("Problem: cannot find host " + theURL.getHost() + " on the network.", true, null);
773 		}
774 		catch (ConnectException e)
775 		{
776 			throw new MonitoringException("Problem: cannot connect to port " + theURL.getPort() + " on host " + theURL.getHost() + ".", true, null);
777 		}
778 		catch (FileNotFoundException e)
779 		{
780 			throw new MonitoringException("Problem: cannot find the Bamboo server REST api using the base URL defined for the Bamboo server in Options. Seems that this URL is not the one to your Bamboo server home page...", true, null);
781 		}
782 		catch(SocketException e)
783 		{
784 			throw new MonitoringException("Problem: network error, connection lost.", null);
785 		}
786 		catch (XPathExpressionException e)
787 		{
788 			throw new MonitoringException("Problem: the Bamboo Server returned an unexpected content for attribute <error>: " + returnedValue, null);
789 		}
790 		catch (MonitoringException e)
791 		{
792 			throw e;
793 		}
794 		catch (Throwable t)
795 		{
796 			throw new MonitoringException(t, null);
797 		}
798 		finally
799 		{
800 			if (urlConnectionReader != null)
801 			{
802 				try
803 				{
804 					urlConnectionReader.close();
805 				}
806 				catch (IOException e)
807 				{
808 					// Nothing to be done here
809 				}
810 			}
811 			if (urlConnection != null)
812 			{
813 				urlConnection.disconnect();
814 			}
815 		}
816 		return returnedValue;
817 	}
818 
819 }