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.net.URI;
19  import java.net.URISyntaxException;
20  import java.net.URL;
21  import java.text.DateFormat;
22  import java.util.ArrayList;
23  import java.util.Hashtable;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.swing.JOptionPane;
28  
29  import net.sourceforge.buildmonitor.BuildMonitor;
30  import net.sourceforge.buildmonitor.BuildReport;
31  import net.sourceforge.buildmonitor.MonitoringException;
32  import net.sourceforge.buildmonitor.BuildReport.Status;
33  import net.sourceforge.buildmonitor.utils.RssFeedDocument;
34  import net.sourceforge.buildmonitor.utils.RssFeedItem;
35  import net.sourceforge.buildmonitor.utils.RssFeedReader;
36  
37  /**
38   * The run method of this Runnable implementation monitor a Cruise Control build
39   * using Cruise Control RSS feeds.
40   * 
41   * @author sbrunot
42   * 
43   */
44  public class CruiseControlRssMonitor implements Monitor
45  {
46  	// ////////////////////////////////
47  	// Instance attributes
48  	// ////////////////////////////////
49  
50  	/**
51  	 * The RssFeedReader instance that we use for monitoring CC rss feed.
52  	 */
53  	private RssFeedReader rssFeedReader = null;
54  
55  	/**
56  	 * Delay between two update of the build status, in seconds
57  	 */
58  	private int updatePeriodInSeconds = 0;
59  
60  	/**
61  	 * A Map that contains the last builds status indexed by build project name
62  	 */
63  	private Map<String, RssFeedItem> lastBuildsStatus;
64  
65  	/**
66  	 * The instance of the build monitor application this thread is running for
67  	 */
68  	private BuildMonitor applicationInstance = null;
69  
70  	/**
71  	 * Should the thread stop its execution ?
72  	 */
73  	private boolean stop = false;
74  
75  	// ////////////////////////////////
76  	// Constructor
77  	// ////////////////////////////////
78  
79  	/**
80  	 * Create a new instance.
81  	 * 
82  	 * @param theApplicationInstance
83  	 *            the build monitor instance this thread is running for
84  	 * @param theUrlToTheCruiseControlRssFeed
85  	 *            URL to the cruise control rss feed to monitor
86  	 * @param theUpdatePeriodInSeconds
87  	 *            period in seconds between to checks of the rss feed to update
88  	 *            the build status
89  	 */
90  	public CruiseControlRssMonitor(BuildMonitor theApplicationInstance,
91  			URL theUrlToTheCruiseControlRssFeed,
92  			DateFormat theRssFeedDateFormat, int theUpdatePeriodInSeconds)
93  	{
94  		this.applicationInstance = theApplicationInstance;
95  		this.rssFeedReader = new RssFeedReader(theUrlToTheCruiseControlRssFeed,
96  				theRssFeedDateFormat);
97  		this.updatePeriodInSeconds = theUpdatePeriodInSeconds;
98  	}
99  
100 	// ////////////////////////////////
101 	// Monitor implementation
102 	// ////////////////////////////////
103 
104 	/**
105 	 * TODO: IMPLEMENTS AND DOCUMENTS ME !
106 	 */
107 	public void run()
108 	{
109 		while (!this.stop)
110 		{
111 			// update the build status
112 			try
113 			{
114 				updateStatus();
115 				updateBuildStatusGui();
116 			} catch (MonitoringException e)
117 			{
118 				this.applicationInstance.reportMonitoringException(e);
119 			}
120 
121 			// wait before updating the build status again
122 			try
123 			{
124 				Thread.sleep(this.updatePeriodInSeconds * 1000);
125 			} catch (InterruptedException e)
126 			{
127 			}
128 		}
129 	}
130 
131 	/**
132 	 * TODO: DO SOMETHING BETTER HERE.... Stop the thread execution
133 	 */
134 	public void stop()
135 	{
136 		this.stop = true;
137 	}
138 
139 	/**
140 	 * {@inheritDoc}
141 	 */
142 	public URI getMainPageURI()
143 	{
144 		URI returnedValue = null;
145 		try
146 		{
147 			returnedValue = new URI("http://cruisecontrol.sourceforge.net");
148 		}
149 		catch (URISyntaxException e)
150 		{
151 			throw new RuntimeException(e);
152 		}
153 		return returnedValue;
154 	}
155 
156 	/**
157 	 * {@inheritDoc}
158 	 */
159 	public URI getBuildURI(String theIdOfTheBuild)
160 	{
161 		// Not implemented yet: returns the main page
162 		return getMainPageURI();
163 	}
164 
165 	/**
166 	 * {@inheritDoc}
167 	 */
168 	public String getSystemTrayIconTooltipHeader()
169 	{
170 		return "Monitoring Cruise Control build";
171 	}
172 	
173 	/**
174 	 * {@inheritDoc}
175 	 */
176 	public void displayOptionsDialog()
177 	{
178 		// NOT IMPLEMENTED YET
179 		JOptionPane.showMessageDialog(null, "Sorry, no options available yet for the Cruise Control monitor.");
180 	}
181 
182 	/**
183 	 * {@inheritDoc}
184 	 */
185 	public String getMonitoredBuildSystemName()
186 	{
187 		return "Cruise Control";
188 	}
189 
190 	// ////////////////////////////////
191 	// Utilities methods
192 	// ////////////////////////////////
193 
194 	/**
195 	 * TODO: IMPLEMENTS ME AND DOCUMENT ME !
196 	 * 
197 	 */
198 	protected void updateStatus() throws MonitoringException
199 	{
200 		// parse the document to update the build status
201 		try
202 		{
203 			RssFeedDocument rssFeedDocument = this.rssFeedReader
204 					.getRssFeedDocument();
205 			if (rssFeedDocument.size() == 0)
206 			{
207 				throw new MonitoringException(
208 						"No build project(s) to monitor !", null);
209 			}
210 			this.lastBuildsStatus = new Hashtable<String, RssFeedItem>();
211 			for (int i = 0; i < rssFeedDocument.size(); i++)
212 			{
213 				RssFeedItem currentBuildStatus = rssFeedDocument.getItem(i);
214 				this.lastBuildsStatus.put(currentBuildStatus.getTitle()
215 						.substring(0,
216 								currentBuildStatus.getTitle().indexOf(' ')),
217 						currentBuildStatus);
218 			}
219 		} catch (Throwable t)
220 		{
221 			throw new MonitoringException(t, null);
222 		}
223 	}
224 
225 	/**
226 	 * TODO: WHAT IS THE LIMITATION ON TOOLTIPS LENGTH ?
227 	 * 
228 	 */
229 	protected void updateBuildStatusGui()
230 	{
231 		List<BuildReport> buildsReport = new ArrayList<BuildReport>();
232 
233 		if (this.lastBuildsStatus.keySet().isEmpty())
234 		{
235 			// This hould never happens (because a MonitoringException is raised
236 			// if there is no project to monitor)
237 			throw new RuntimeException(
238 					"They are no build project... what should the application do ???");
239 		}
240 
241 		for (String currentProject : this.lastBuildsStatus.keySet())
242 		{
243 			BuildReport currentReport = new BuildReport();
244 			currentReport.setId(currentProject);
245 			currentReport.setName(currentProject);
246 			RssFeedItem currentBuildStatus = (RssFeedItem) this.lastBuildsStatus
247 					.get(currentProject);
248 			currentReport.setDate(currentBuildStatus.getPubDate());
249 			if (currentBuildStatus.getDescription().contains("FAILED"))
250 			{
251 				currentReport.setStatus(Status.FAILED);
252 			} else
253 			{
254 				currentReport.setStatus(Status.OK);
255 			}
256 			buildsReport.add(currentReport);
257 		}
258 
259 		this.applicationInstance.updateBuildStatus(buildsReport);
260 	}
261 
262 	/**
263 	 * Exposes last builds status for unit testing...
264 	 * 
265 	 * @return the last builds status (see internal documentation)
266 	 */
267 	protected Map<String, RssFeedItem> getLastBuildsStatus()
268 	{
269 		return this.lastBuildsStatus;
270 	}
271 
272 }