KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > distributed > BuildAgent


1 /****************************************************************************
2 * CruiseControl, a Continuous Integration Toolkit
3 * Copyright (c) 2001, ThoughtWorks, Inc.
4 * 651 W Washington Ave. Suite 600
5 * Chicago, IL 60661 USA
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * + Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * + Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
19 *
20 * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
21 * names of its contributors may be used to endorse or promote
22 * products derived from this software without specific prior
23 * written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 ****************************************************************************/

37
38 package net.sourceforge.cruisecontrol.distributed;
39
40 import java.io.IOException JavaDoc;
41 import java.net.URL JavaDoc;
42 import java.net.MalformedURLException JavaDoc;
43 import java.rmi.Remote JavaDoc;
44 import java.rmi.RemoteException JavaDoc;
45 import java.rmi.server.ExportException JavaDoc;
46 import java.util.Iterator JavaDoc;
47 import java.util.Properties JavaDoc;
48 import java.util.Arrays JavaDoc;
49
50 import net.jini.core.entry.Entry;
51 import net.jini.core.lookup.ServiceID;
52 import net.jini.core.lookup.ServiceRegistrar;
53 import net.jini.core.discovery.LookupLocator;
54 import net.jini.discovery.DiscoveryEvent;
55 import net.jini.discovery.DiscoveryListener;
56 import net.jini.discovery.LookupLocatorDiscovery;
57 import net.jini.lookup.ServiceIDListener;
58 import net.jini.lookup.JoinManager;
59 import net.jini.export.Exporter;
60 import net.jini.jeri.BasicILFactory;
61 import net.jini.jeri.BasicJeriExporter;
62 import net.jini.jeri.tcp.TcpServerEndpoint;
63 import net.sourceforge.cruisecontrol.distributed.util.PropertiesHelper;
64 import net.sourceforge.cruisecontrol.distributed.util.ReggieUtil;
65
66 import org.apache.log4j.Logger;
67
68
69
70 public class BuildAgent implements DiscoveryListener,
71             ServiceIDListener {
72
73     static final String JavaDoc MAIN_ARG_SKIP_UI = "-skipUI";
74
75     // package visible to allow BuildAgentUI console logger access to this Logger
76
static final Logger LOG = Logger.getLogger(BuildAgent.class);
77
78     public static final String JavaDoc JAVA_SECURITY_POLICY = "java.security.policy";
79     public static final String JavaDoc JINI_POLICY_FILE = "jini.policy.file";
80     
81     /** Optional unicast Lookup Registry URL.
82      * A Unicast Lookup Locater is useful if multicast isn't working. */

83     public static final String JavaDoc REGISTRY_URL = "registry.url";
84
85     private final BuildAgentServiceImpl serviceImpl;
86     private final Entry[] entries;
87     private final Exporter exporter;
88     private final JoinManager joinManager;
89     private ServiceID serviceID;
90     private final Remote JavaDoc proxy;
91
92     private Properties JavaDoc entryProperties;
93     private Properties JavaDoc configProperties;
94     
95     private final BuildAgentUI ui;
96
97     private int registrarCount = 0;
98     private synchronized void incrementRegCount() {
99         registrarCount++;
100     }
101     private synchronized void decrementRegCount() {
102         registrarCount--;
103     }
104     private synchronized int getRegCount() {
105         return registrarCount;
106     }
107
108     public BuildAgent(final boolean isSkipUI) {
109         this(BuildAgentServiceImpl.DEFAULT_AGENT_PROPERTIES_FILE,
110                 BuildAgentServiceImpl.DEFAULT_USER_DEFINED_PROPERTIES_FILE,
111                 isSkipUI);
112     }
113
114     public BuildAgent(final String JavaDoc propsFile, final String JavaDoc userDefinedPropertiesFilename,
115                       final boolean isSkipUI) {
116         loadProperties(propsFile, userDefinedPropertiesFilename);
117
118         serviceImpl = new BuildAgentServiceImpl();
119         serviceImpl.setAgentPropertiesFilename(propsFile);
120
121         entries = SearchablePropertyEntries.getPropertiesAsEntryArray(entryProperties);
122         if (!isSkipUI) {
123             LOG.info("Loading Build Agent UI (use param " + MAIN_ARG_SKIP_UI + " to bypass).");
124             ui = new BuildAgentUI(this);
125             //ui.updateAgentInfoUI(getService());
126
} else {
127             LOG.info("Bypassing Build Agent UI.");
128             ui = null;
129         }
130
131         exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(0),
132                 new BasicILFactory(), false, true);
133
134         try {
135             proxy = exporter.export(getService());
136         } catch (ExportException JavaDoc e) {
137             final String JavaDoc message = "Error exporting service";
138             LOG.error(message, e);
139             throw new RuntimeException JavaDoc(message, e);
140         }
141
142         // use a Unicast Lookup Locater if defined (useful if multicast isn't working)
143
// @todo Improvement: handle multiple comma separated urls in the property file.
144
// For now it handles only one ip address. It also could have a virtual url called
145
// "multicast", to make the property file more readable and understandable
146
final String JavaDoc registryURL = configProperties.getProperty(REGISTRY_URL);
147         final LookupLocatorDiscovery lld;
148         if (registryURL == null) {
149             lld = null;
150         } else {
151             final LookupLocator lookup;
152             try {
153                 lookup = new LookupLocator(registryURL);
154             } catch (MalformedURLException JavaDoc e) {
155                 final String JavaDoc message = "Error creating unicast lookup locator";
156                 LOG.error(message, e);
157                 throw new RuntimeException JavaDoc(message, e);
158             }
159             final LookupLocator[] lookups = new LookupLocator[] { lookup };
160             lld = new LookupLocatorDiscovery(lookups);
161         }
162
163         try {
164             joinManager = new JoinManager(getProxy(), entries, this, lld, null);
165         } catch (IOException JavaDoc e) {
166             final String JavaDoc message = "Error starting discovery";
167             LOG.error(message, e);
168             throw new RuntimeException JavaDoc(message, e);
169         }
170
171         getJoinManager().getDiscoveryManager().addDiscoveryListener(this);
172     }
173
174     /**
175      * @param propsFile
176      */

177     private void loadProperties(final String JavaDoc propsFile, final String JavaDoc userDefinedPropertiesFilename) {
178         configProperties = (Properties JavaDoc) PropertiesHelper.loadRequiredProperties(propsFile);
179         entryProperties = new SearchablePropertyEntries(userDefinedPropertiesFilename).getProperties();
180
181         final String JavaDoc policyFileValue = configProperties.getProperty(JINI_POLICY_FILE);
182         LOG.info("policyFileValue: " + policyFileValue);
183
184         // resource loading technique below dies in webstart
185
//URL policyFile = ClassLoader.getSystemClassLoader().getResource(policyFileValue);
186
final URL JavaDoc policyFile = BuildAgent.class.getClassLoader().getResource(policyFileValue);
187         LOG.info("policyFile: " + policyFile);
188         System.setProperty(JAVA_SECURITY_POLICY, policyFile.toExternalForm());
189         ReggieUtil.setupRMISecurityManager();
190     }
191
192     private Exporter getExporter() {
193         return exporter;
194     }
195
196     private JoinManager getJoinManager() {
197         return joinManager;
198     }
199
200     Entry[] getEntries() {
201         return entries;
202     }
203
204     void addAgentStatusListener(final BuildAgent.AgentStatusListener listener) {
205         serviceImpl.addAgentStatusListener(listener);
206     }
207     void removeAgentStatusListener(final BuildAgent.AgentStatusListener listener) {
208         serviceImpl.removeAgentStatusListener(listener);
209     }
210
211     public void terminate() {
212         LOG.info("Terminating build agent.");
213         getExporter().unexport(true);
214         getJoinManager().terminate();
215         // allow some time for cleanup
216
try {
217             Thread.sleep(2000);
218         } catch (InterruptedException JavaDoc e) {
219             LOG.warn("Sleep interrupted during terminate", e);
220         }
221
222         if (ui != null) {
223             ui.dispose();
224             LOG.info("UI disposed");
225         }
226     }
227
228
229     private Remote JavaDoc getProxy() {
230         return proxy;
231     }
232
233
234     public synchronized BuildAgentService getService() {
235         return serviceImpl;
236     }
237
238
239     public synchronized void serviceIDNotify(final ServiceID serviceID) {
240         // @todo technically, should serviceID be stored permanently and reused?....
241
this.serviceID = serviceID;
242         LOG.info("ServiceID assigned: " + this.serviceID);
243         if (ui != null) {
244             ui.updateAgentInfoUI(getService());
245         }
246     }
247     synchronized ServiceID getServiceID() {
248         return serviceID;
249     }
250
251
252     private void logRegistration(final ServiceRegistrar registrar) {
253         String JavaDoc host = null;
254         try {
255             host = registrar.getLocator().getHost();
256         } catch (RemoteException JavaDoc e) {
257             LOG.warn("Failed to get registrar's hostname");
258         }
259         LOG.info("Registering BuildAgentService with Registrar: " + host);
260
261         final String JavaDoc machineName = (String JavaDoc) entryProperties.get("hostname");
262         LOG.debug("Registered machineName: " + machineName);
263
264         LOG.debug("Entries: ");
265         for (Iterator JavaDoc iter = entryProperties.keySet().iterator(); iter.hasNext();) {
266             final String JavaDoc key = (String JavaDoc) iter.next();
267             LOG.debug(" " + key + " = " + entryProperties.get(key));
268         }
269     }
270
271     private boolean isNotFirstDiscovery;
272
273     public void discovered(final DiscoveryEvent evt) {
274         final ServiceRegistrar[] registrarsArray = evt.getRegistrars();
275         ServiceRegistrar registrar;
276         for (int n = 0; n < registrarsArray.length; n++) {
277             incrementRegCount();
278             registrar = registrarsArray[n];
279             logRegistration(registrar);
280             LOG.debug("Registered with registrar: " + registrar.getServiceID());
281         }
282         if (!isNotFirstDiscovery) {
283             LOG.info("BuildAgentService open for business...");
284             isNotFirstDiscovery = true;
285         }
286     }
287
288     public void discarded(final DiscoveryEvent evt) {
289         final ServiceRegistrar[] registrarsArray = evt.getRegistrars();
290         ServiceRegistrar registrar;
291         for (int n = 0; n < registrarsArray.length; n++) {
292             decrementRegCount();
293             registrar = registrarsArray[n];
294             LOG.debug("Discarded registrar: " + registrar.getServiceID());
295         }
296     }
297
298
299     private static final Object JavaDoc KEEP_ALIVE = new Object JavaDoc();
300     private static Thread JavaDoc mainThread;
301
302     private static synchronized void setMainThread(final Thread JavaDoc newMainThread) {
303         mainThread = newMainThread;
304     }
305     static synchronized Thread JavaDoc getMainThread() {
306         return mainThread;
307     }
308
309     public static void main(final String JavaDoc[] args) {
310
311         LOG.info("Starting agent...args: " + Arrays.asList(args).toString());
312
313         // @todo cmd line arg processing cleanup
314

315         final boolean isSkipUI;
316         if (args.length < 3 || !args[2].equalsIgnoreCase(MAIN_ARG_SKIP_UI)) {
317             isSkipUI = false;
318         } else {
319             isSkipUI = true;
320     }
321
322         final BuildAgent buildAgent;
323         if (args.length > 0) {
324             if (args.length > 1) {
325                 buildAgent = new BuildAgent(args[0], args[1], isSkipUI);
326             } else {
327                 buildAgent = new BuildAgent(args[0], BuildAgentServiceImpl.DEFAULT_USER_DEFINED_PROPERTIES_FILE,
328                         isSkipUI);
329             }
330         } else {
331             buildAgent = new BuildAgent(isSkipUI);
332         }
333
334
335         setMainThread(Thread.currentThread());
336
337         // stay around forever
338
synchronized (KEEP_ALIVE) {
339            try {
340                KEEP_ALIVE.wait();
341            } catch (InterruptedException JavaDoc e) {
342                LOG.error("Keep Alive wait interrupted", e);
343             } finally {
344                 buildAgent.terminate();
345            }
346         }
347     }
348
349     public static void kill() {
350         final Thread JavaDoc main = getMainThread();
351         if (main != null) {
352             main.interrupt();
353             LOG.info("Waiting for main thread to finish.");
354             try {
355                 main.join(30 * 1000);
356                 //main.join();
357
} catch (InterruptedException JavaDoc e) {
358                 LOG.error("Error during waiting from Agent to die.", e);
359             }
360             if (main.isAlive()) {
361                 main.interrupt(); // how can this happen?
362
LOG.error("Main thread should have died.");
363             }
364             setMainThread(null);
365         } else {
366             LOG.info("WARNING: Kill was called, but MainThread is null. Doing nothing.");
367         }
368     }
369
370     static interface AgentStatusListener {
371         public void statusChanged(BuildAgentService buildAgentServiceImpl);
372     }
373
374 }
375
Popular Tags