1 37 38 package net.sourceforge.cruisecontrol.distributed; 39 40 import java.io.File ; 41 import java.io.IOException ; 42 import java.io.Serializable ; 43 import java.net.InetAddress ; 44 import java.net.UnknownHostException ; 45 import java.net.URL ; 46 import java.net.MalformedURLException ; 47 import java.rmi.RemoteException ; 48 import java.util.Map ; 49 import java.util.Properties ; 50 import java.util.Date ; 51 import java.util.List ; 52 import java.util.ArrayList ; 53 import java.util.HashMap ; 54 55 import net.sourceforge.cruisecontrol.Builder; 56 import net.sourceforge.cruisecontrol.CruiseControlException; 57 import net.sourceforge.cruisecontrol.PluginRegistry; 58 import net.sourceforge.cruisecontrol.PluginXMLHelper; 59 import net.sourceforge.cruisecontrol.distributed.util.PropertiesHelper; 60 import net.sourceforge.cruisecontrol.distributed.util.ZipUtil; 61 import net.sourceforge.cruisecontrol.util.FileUtil; 62 import net.sourceforge.cruisecontrol.util.Util; 63 64 import org.apache.log4j.Logger; 65 import org.jdom.Element; 66 67 import javax.jnlp.ServiceManager; 68 import javax.jnlp.BasicService; 69 import javax.jnlp.UnavailableServiceException; 70 71 74 public class BuildAgentServiceImpl implements BuildAgentService, Serializable { 75 76 private static final Logger LOG = Logger.getLogger(BuildAgentServiceImpl.class); 77 78 private static final String CRUISE_BUILD_DIR = "cruise.build.dir"; 79 80 static final String DEFAULT_AGENT_PROPERTIES_FILE = "agent.properties"; 81 private String agentPropertiesFilename = DEFAULT_AGENT_PROPERTIES_FILE; 82 83 static final String DEFAULT_USER_DEFINED_PROPERTIES_FILE = "user-defined.properties"; 84 85 86 private final String machineName; 87 88 private final Date dateStarted; 89 private boolean isBusy; 90 private Date dateClaimed; 91 private boolean isPendingKill; 92 private Date pendingKillSince; 93 private boolean isPendingRestart; 94 private Date pendingRestartSince; 95 96 private Properties configProperties; 97 private final Map distributedAgentProps = new HashMap (); 98 private String logDir; 99 private String outputDir; 100 private String buildRootDir; 101 private String logsFilePath; 102 private String outputFilePath; 103 104 private final List agentStatusListeners = new ArrayList (); 105 106 private final String logMsgPrefix; 107 130 private final void logPrefixDebug(final Object message) { 131 LOG.debug(logMsgPrefix + message); 132 } 133 private final void logPrefixInfo(final Object message) { 134 LOG.info(logMsgPrefix + message); 135 } 136 private final void logPrefixError(final Object message) { 137 LOG.error(logMsgPrefix + message); 138 } 139 private final void logPrefixError(final Object message, final Throwable throwable) { 140 LOG.error(logMsgPrefix + message, throwable); 141 } 142 143 144 145 public BuildAgentServiceImpl() { 146 dateStarted = new Date (); 147 148 try { 149 machineName = InetAddress.getLocalHost().getHostName(); 150 } catch (UnknownHostException e) { 151 final String message = "Failed to get hostname"; 152 LOG.error(message, e); 154 System.err.println(message + " - " + e.getMessage()); 155 throw new RuntimeException (message, e); 156 } 157 logMsgPrefix = "Agent Host: " + machineName + "; "; 158 } 159 160 161 public Date getDateStarted() { 162 return dateStarted; 163 } 164 165 166 public synchronized String getModule() { 167 return (String ) distributedAgentProps.get(PropertiesHelper.DISTRIBUTED_MODULE); 168 } 169 170 void setAgentPropertiesFilename(final String filename) { 171 agentPropertiesFilename = filename; 172 } 173 private String getAgentPropertiesFilename() { 174 return agentPropertiesFilename; 175 } 176 177 private final String busyLock = new String ("busyLock"); 178 void setBusy(final boolean newIsBusy) { 179 if (!newIsBusy) { if (isPendingRestart()) { 181 doRestart(); 183 } else if (isPendingKill()) { 184 doKill(); 186 } 187 188 distributedAgentProps.clear(); 190 191 dateClaimed = null; 192 } else { 193 dateClaimed = new Date (); 194 } 195 196 synchronized (busyLock) { 197 isBusy = newIsBusy; 198 } 199 200 fireAgentStatusChanged(); 201 202 logPrefixInfo("agent busy status changed to: " + newIsBusy); 203 } 204 205 public Element doBuild(final Element nestedBuilderElement, final Map projectPropertiesMap, 206 final Map distributedAgentProperties) throws RemoteException { 207 synchronized (busyLock) { 208 if (!isBusy()) { setBusy(true); } 211 } 212 try { 213 logPrefixDebug("Build Agent Props: " + distributedAgentProperties.toString()); 214 distributedAgentProps.putAll(distributedAgentProperties); 215 216 final String infoMessage = "Building module: " + getModule() 217 + "\n\tAgentLogDir: " + distributedAgentProps.get( 218 PropertiesHelper.DISTRIBUTED_AGENT_LOGDIR) 219 + "\n\tAgentOutputDir: " + distributedAgentProps.get( 220 PropertiesHelper.DISTRIBUTED_AGENT_OUTPUTDIR); 221 222 System.out.println(); 223 System.out.println(infoMessage); 224 logPrefixInfo(infoMessage); 225 226 logPrefixDebug("Build Agent Project Props: " + projectPropertiesMap.toString()); 227 228 fireAgentStatusChanged(); 231 232 final Element buildResults; 233 final Builder nestedBuilder; 234 try { 235 nestedBuilder = createBuilder(nestedBuilderElement); 236 nestedBuilder.validate(); 237 } catch (CruiseControlException e) { 238 final String message = "Failed to configure nested Builder on agent"; 239 logPrefixError(message, e); 240 System.err.println(message + " - " + e.getMessage()); 241 throw new RemoteException (message, e); 242 } 243 244 try { 245 buildResults = nestedBuilder.build(projectPropertiesMap); 246 } catch (CruiseControlException e) { 247 final String message = "Failed to complete build on agent"; 248 logPrefixError(message, e); 249 System.err.println(message + " - " + e.getMessage()); 250 throw new RemoteException (message, e); 251 } 252 prepareLogsAndArtifacts(); 253 return buildResults; 254 } catch (RemoteException e) { 255 logPrefixError("doBuild threw exception, setting busy to false."); 256 setBusy(false); 257 throw e; } 259 } 260 261 private Builder createBuilder(final Element builderElement) throws CruiseControlException { 262 263 configProperties = (Properties ) PropertiesHelper.loadRequiredProperties( 264 getAgentPropertiesFilename()); 265 266 final String overrideTarget 267 = (String ) distributedAgentProps.get(PropertiesHelper.DISTRIBUTED_OVERRIDE_TARGET); 268 final PluginXMLHelper pluginXMLHelper = PropertiesHelper.createPluginXMLHelper(overrideTarget); 269 270 final PluginRegistry plugins = PluginRegistry.createRegistry(); 271 final Class pluginClass = plugins.getPluginClass(builderElement.getName()); 272 final Builder builder = (Builder) pluginXMLHelper.configure(builderElement, pluginClass, false); 273 274 return builder; 275 } 276 277 280 private void prepareLogsAndArtifacts() { 281 final String buildDirProperty = configProperties.getProperty(CRUISE_BUILD_DIR); 282 try { 283 buildRootDir = new File (buildDirProperty).getCanonicalPath(); 284 } catch (IOException e) { 285 final String message = "Couldn't create " + buildDirProperty; 286 logPrefixError(message, e); 287 System.err.println(message + " - " + e.getMessage()); 288 throw new RuntimeException (message); 289 } 290 291 logDir = getAgentResultDir(PropertiesHelper.RESULT_TYPE_LOGS, 292 PropertiesHelper.DISTRIBUTED_AGENT_LOGDIR); 293 294 outputDir = getAgentResultDir(PropertiesHelper.RESULT_TYPE_OUTPUT, 295 PropertiesHelper.DISTRIBUTED_AGENT_OUTPUTDIR); 296 297 298 logsFilePath = buildRootDir + File.separator + PropertiesHelper.RESULT_TYPE_LOGS + ".zip"; 299 ZipUtil.zipFolderContents(logsFilePath, logDir); 300 outputFilePath = buildRootDir + File.separator + PropertiesHelper.RESULT_TYPE_OUTPUT + ".zip"; 301 ZipUtil.zipFolderContents(outputFilePath, outputDir); 302 } 303 304 private String getAgentResultDir(final String resultType, final String resultProperty) { 305 String resultDir; 306 resultDir = (String ) distributedAgentProps.get(resultProperty); 307 logPrefixDebug("Result: " + resultType + "Prop value: " + resultDir); 308 309 if (resultDir == null || "".equals(resultDir)) { 310 resultDir = buildRootDir + File.separator + resultType + File.separator + getModule(); 312 } 313 new File (resultDir).mkdirs(); 314 return resultDir; 315 } 316 317 public String getMachineName() { 318 return machineName; 319 } 320 321 public void claim() { 322 synchronized (busyLock) { 325 if (isBusy()) { 326 throw new IllegalStateException ("Cannot claim agent on " + getMachineName() 327 + " that is busy building module: " 328 + getModule()); 329 } 330 setBusy(true); 331 } 332 } 333 334 public boolean isBusy() { 335 synchronized (busyLock) { 336 logPrefixDebug("Is busy called. value: " + isBusy); 337 return isBusy; 338 } 339 } 340 341 public Date getDateClaimed() { 342 return dateClaimed; 343 } 344 345 private void setPendingKill(final boolean isPendingKill) { 346 synchronized (busyLock) { 347 this.isPendingKill = isPendingKill; 348 pendingKillSince = new Date (); 349 } 350 } 351 public boolean isPendingKill() { 352 synchronized (busyLock) { 353 return isPendingKill; 354 } 355 } 356 public Date getPendingKillSince() { 357 return pendingKillSince; 358 } 359 360 361 private void setPendingRestart(final boolean isPendingRestart) { 362 synchronized (busyLock) { 363 this.isPendingRestart = isPendingRestart; 364 pendingRestartSince = new Date (); 365 } 366 } 367 public boolean isPendingRestart() { 368 synchronized (busyLock) { 369 return isPendingRestart; 370 } 371 } 372 public Date getPendingRestartSince() { 373 return pendingRestartSince; 374 } 375 376 377 public boolean resultsExist(final String resultsType) throws RemoteException { 378 if (resultsType.equals(PropertiesHelper.RESULT_TYPE_LOGS)) { 379 return !(new File (logDir).list().length == 0); 380 } else if (resultsType.equals(PropertiesHelper.RESULT_TYPE_OUTPUT)) { 381 return !(new File (outputDir).list().length == 0); 382 } else { 383 return false; 384 } 385 } 386 387 public byte[] retrieveResultsAsZip(final String resultsType) throws RemoteException { 388 389 final String zipFilePath = buildRootDir + File.separator + resultsType + ".zip"; 390 391 final byte[] response; 392 try { 393 response = FileUtil.getFileAsBytes(new File (zipFilePath)); 394 } catch (IOException e) { 395 final String message = "Unable to get file " + zipFilePath; 396 logPrefixError(message, e); 397 System.err.println(message + " - " + e.getMessage()); 398 throw new RuntimeException (message, e); 399 } 400 return response; 401 } 402 403 public void clearOutputFiles() { 404 try { 405 if (logDir != null) { 406 logPrefixDebug("Deleting contents of " + logDir); 407 Util.deleteFile(new File (logDir)); 408 } else { 409 logPrefixDebug("Skip delete agent logDir: " + logDir); 410 } 411 if (logsFilePath != null) { 412 logPrefixDebug("Deleting log zip " + logsFilePath); 413 Util.deleteFile(new File (logsFilePath)); 414 } else { 415 logPrefixError("Skipping delete of log zip, file path is null."); 416 } 417 418 if (outputDir != null) { 419 logPrefixDebug("Deleting contents of " + outputDir); 420 Util.deleteFile(new File (outputDir)); 421 } else { 422 logPrefixDebug("Skip delete agent outputDir: " + outputDir); 423 } 424 if (outputFilePath != null) { 425 logPrefixDebug("Deleting output zip " + outputFilePath); 426 Util.deleteFile(new File (outputFilePath)); 427 } else { 428 logPrefixError("Skipping delete of output zip, file path is null."); 429 } 430 431 setBusy(false); 432 433 } catch (RuntimeException e) { 434 logPrefixError("Error cleaning agent build files.", e); 435 throw e; 436 } 437 } 438 439 440 private void doRestart() { 441 logPrefixInfo("Attempting agent restart."); 442 443 synchronized (busyLock) { 444 if (!isBusy()) { 445 claim(); 447 } 448 } 449 450 final BasicService basicService; 451 try { 452 basicService = (BasicService) ServiceManager.lookup(BasicService.class.getName()); 453 } catch (UnavailableServiceException e) { 454 final String errMsg = "Couldn't find webstart Basic Service. Is Agent running outside of webstart?"; 455 logPrefixError(errMsg, e); 456 throw new RuntimeException (errMsg, e); 457 } 458 final URL codeBaseURL = basicService.getCodeBase(); 459 logPrefixInfo("basicService.getCodeBase()=" + codeBaseURL.toString()); 460 461 final URL relaunchURL; 464 try { 465 relaunchURL = new URL (codeBaseURL, "agent.jnlp"); 466 } catch (MalformedURLException e) { 467 final String errMsg = "Error building webstart relaunch URL from " + codeBaseURL.toString(); 468 logPrefixError(errMsg, e); 469 throw new RuntimeException (errMsg, e); 470 } 471 if (basicService.showDocument(relaunchURL)) { 472 logPrefixInfo("Relaunched agent via URL: " + relaunchURL.toString() + ". Will kill current agent now."); 473 doKill(); } else { 475 final String errMsg = "Failed to relaunch agent via URL: " + relaunchURL.toString(); 476 logPrefixError(errMsg); 477 throw new RuntimeException (errMsg); 478 } 479 } 480 481 private void doKill() { 482 logPrefixInfo("Attempting agent kill."); 483 synchronized (busyLock) { 484 if (!isBusy()) { 485 claim(); 487 } 488 } 489 BuildAgent.kill(); 490 } 491 492 public void kill(final boolean afterBuildFinished) throws RemoteException { 493 setPendingKill(true); 494 495 if (!afterBuildFinished || !isBusy()) { 498 doKill(); } else if (isBusy()) { 500 ; } 502 fireAgentStatusChanged(); 503 } 504 505 public void restart(final boolean afterBuildFinished) throws RemoteException { 506 setPendingRestart(true); 507 508 if (!afterBuildFinished || !isBusy()) { 511 doRestart(); 512 } else if (isBusy()) { 513 ; } 515 fireAgentStatusChanged(); 516 } 517 518 public String asString() { 519 final StringBuffer sb = new StringBuffer (); 520 sb.append("Machine Name: "); 521 sb.append(getMachineName()); 522 sb.append(";\t"); 523 sb.append("Started: "); 524 sb.append(getDateStarted()); 525 526 sb.append("\n\tBusy: "); 527 sb.append(isBusy()); 528 sb.append(";\tSince: "); 529 sb.append(getDateClaimed()); 530 sb.append(";\tModule: "); 531 sb.append(getModule()); 532 533 sb.append("\n\tPending Restart: "); 534 sb.append(isPendingRestart()); 535 sb.append(";\tPending Restart Since: "); 536 sb.append(getPendingRestartSince()); 537 538 sb.append("\n\tPending Kill: "); 539 sb.append(isPendingKill()); 540 sb.append(";\tPending Kill Since: "); 541 sb.append(getPendingKillSince()); 542 543 return sb.toString(); 544 } 545 546 public void addAgentStatusListener(final BuildAgent.AgentStatusListener listener) { 547 agentStatusListeners.add(listener); 548 } 549 public void removeAgentStatusListener(final BuildAgent.AgentStatusListener listener) { 550 agentStatusListeners.remove(listener); 551 } 552 private void fireAgentStatusChanged() { 553 for (int i = 0; i < agentStatusListeners.size(); i++) { 554 ((BuildAgent.AgentStatusListener) agentStatusListeners.get(i)).statusChanged(this); 555 } 556 } 557 } 558 | Popular Tags |