Source Code Cross Referenced for BuildAgentServiceImpl.java in  » Build » cruisecontrol » net » sourceforge » cruisecontrol » distributed » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Build » cruisecontrol » net.sourceforge.cruisecontrol.distributed 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /****************************************************************************
0002:         * CruiseControl, a Continuous Integration Toolkit
0003:         * Copyright (c) 2001, ThoughtWorks, Inc.
0004:         * 200 E. Randolph, 25th Floor
0005:         * Chicago, IL 60601 USA
0006:         * All rights reserved.
0007:         *
0008:         * Redistribution and use in source and binary forms, with or without
0009:         * modification, are permitted provided that the following conditions
0010:         * are met:
0011:         *
0012:         *     + Redistributions of source code must retain the above copyright
0013:         *       notice, this list of conditions and the following disclaimer.
0014:         *
0015:         *     + Redistributions in binary form must reproduce the above
0016:         *       copyright notice, this list of conditions and the following
0017:         *       disclaimer in the documentation and/or other materials provided
0018:         *       with the distribution.
0019:         *
0020:         *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
0021:         *       names of its contributors may be used to endorse or promote
0022:         *       products derived from this software without specific prior
0023:         *       written permission.
0024:         *
0025:         * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0026:         * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0027:         * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0028:         * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
0029:         * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0030:         * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0031:         * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
0032:         * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0033:         * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0034:         * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
0035:         * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0036:         ****************************************************************************/package net.sourceforge.cruisecontrol.distributed;
0037:
0038:        import java.io.File;
0039:        import java.io.IOException;
0040:        import java.net.InetAddress;
0041:        import java.net.UnknownHostException;
0042:        import java.net.URL;
0043:        import java.net.MalformedURLException;
0044:        import java.rmi.RemoteException;
0045:        import java.util.Map;
0046:        import java.util.Properties;
0047:        import java.util.Date;
0048:        import java.util.List;
0049:        import java.util.ArrayList;
0050:        import java.util.HashMap;
0051:
0052:        import net.sourceforge.cruisecontrol.Builder;
0053:        import net.sourceforge.cruisecontrol.CruiseControlException;
0054:        import net.sourceforge.cruisecontrol.Progress;
0055:        import net.sourceforge.cruisecontrol.builders.AntBuilder;
0056:        import net.sourceforge.cruisecontrol.builders.AntScript;
0057:        import net.sourceforge.cruisecontrol.builders.CompositeBuilder;
0058:        import net.sourceforge.cruisecontrol.distributed.core.PropertiesHelper;
0059:        import net.sourceforge.cruisecontrol.distributed.core.ZipUtil;
0060:        import net.sourceforge.cruisecontrol.distributed.core.FileUtil;
0061:        import net.sourceforge.cruisecontrol.distributed.core.CCDistVersion;
0062:        import net.sourceforge.cruisecontrol.distributed.core.ProgressRemote;
0063:        import net.sourceforge.cruisecontrol.distributed.core.RemoteResult;
0064:        import net.sourceforge.cruisecontrol.distributed.core.jnlputil.AntProgressLoggerInstaller;
0065:        import net.sourceforge.cruisecontrol.util.IO;
0066:        import net.sourceforge.cruisecontrol.util.Util;
0067:
0068:        import org.apache.log4j.Logger;
0069:        import org.apache.log4j.Level;
0070:        import org.jdom.Element;
0071:
0072:        import javax.jnlp.ServiceManager;
0073:        import javax.jnlp.BasicService;
0074:        import javax.jnlp.UnavailableServiceException;
0075:
0076:        /**
0077:         * Build Agent implementation.
0078:         */
0079:        public class BuildAgentServiceImpl implements  BuildAgentService {
0080:
0081:            private static final Logger LOG = Logger
0082:                    .getLogger(BuildAgentServiceImpl.class);
0083:
0084:            private static final String CRUISE_BUILD_DIR = "cruise.build.dir";
0085:
0086:            static final String DEFAULT_AGENT_PROPERTIES_FILE = "agent.properties";
0087:            private String agentPropertiesFilename = DEFAULT_AGENT_PROPERTIES_FILE;
0088:
0089:            static final String DEFAULT_USER_DEFINED_PROPERTIES_FILE = "user-defined.properties";
0090:
0091:            /**
0092:             * The default number of milliseconds a restart() or kill() should delay before executing
0093:             * in order to allow remote calls to complete, and thereby allow successful builds
0094:             * to complete on the Distributed Master.
0095:             */
0096:            private static final int DEFAULT_DELAY_MS_KILLRESTART = 5 * 1000;
0097:            /** Name of system property who's value, if defined, will override the default delay. */
0098:            static final String SYSPROP_CCDIST_DELAY_MS_KILLRESTART = "cc.dist.delayMSKillRestart";
0099:
0100:            /** Cache host name. */
0101:            private final String machineName;
0102:
0103:            private final Date dateStarted;
0104:            private boolean isBusy;
0105:            private Date dateClaimed;
0106:            private boolean isPendingKill;
0107:            private Date pendingKillSince;
0108:            private boolean isPendingRestart;
0109:            private Date pendingRestartSince;
0110:
0111:            private Properties configProperties;
0112:
0113:            private String projectName;
0114:            private ProgressRemote buildProgressRemote;
0115:            private final Map distributedAgentProps = new HashMap();
0116:
0117:            private File logDir;
0118:            private File outputDir;
0119:            private RemoteResult[] remoteResults;
0120:            private File buildRootDir;
0121:            private File zippedLogs;
0122:            private File zippedOutput;
0123:
0124:            private final List agentStatusListeners = new ArrayList();
0125:
0126:            private final String logMsgPrefix;
0127:
0128:            /**
0129:             * Prepends Agent machine name to error message. This is especially
0130:             * useful when combined with an "email logger" config for Log4j using a modified
0131:             * log4j.properties on build agents. For example:
0132:             * <pre>
0133:             * log4j.rootCategory=INFO,A1,FILE,Mail
0134:             *
0135:             * ...
0136:             *
0137:             * # Mail is set to be a SMTPAppender
0138:             * log4j.appender.Mail=org.apache.log4j.net.SMTPAppender
0139:             * log4j.appender.Mail.BufferSize=100
0140:             * log4j.appender.Mail.From=ccbuild@yourdomain.com
0141:             * log4j.appender.Mail.SMTPHost=yoursmtp.mailhost.com
0142:             * log4j.appender.Mail.Subject=CC has had an error!!!
0143:             * log4j.appender.Mail.To=youremail@yourdomain.com
0144:             * log4j.appender.Mail.layout=org.apache.log4j.PatternLayout
0145:             * log4j.appender.Mail.layout.ConversionPattern=%d{dd.MM.yyyy HH:mm:ss} %-5p [%x] [%c{3}] %m%n
0146:             *
0147:             * </pre>
0148:             *
0149:             * @param message  the message to log (will be prefixed with machineName).
0150:             */
0151:            private void logPrefixDebug(final Object message) {
0152:                LOG.debug(logMsgPrefix + message);
0153:            }
0154:
0155:            private void logPrefixInfo(final Object message) {
0156:                LOG.info(logMsgPrefix + message);
0157:            }
0158:
0159:            private void logPrefixError(final Object message) {
0160:                LOG.error(logMsgPrefix + message);
0161:            }
0162:
0163:            private void logPrefixError(final Object message,
0164:                    final Throwable throwable) {
0165:                LOG.error(logMsgPrefix + message, throwable);
0166:            }
0167:
0168:            private final BuildAgent serviceContainer;
0169:
0170:            /**
0171:             * Constructor. 
0172:             * @param serviceContainer the BuildAgent instance in charge of publishing this service.
0173:             */
0174:            BuildAgentServiceImpl(final BuildAgent serviceContainer) {
0175:                dateStarted = new Date();
0176:
0177:                this .serviceContainer = serviceContainer;
0178:
0179:                try {
0180:                    machineName = InetAddress.getLocalHost().getHostName();
0181:                } catch (UnknownHostException e) {
0182:                    final String message = "Failed to get hostname";
0183:                    // Don't call Log helper method here since hostname is not yet set
0184:                    LOG.error(message, e);
0185:                    System.err.println(message + " - " + e.getMessage());
0186:                    throw new RuntimeException(message, e);
0187:                }
0188:                logMsgPrefix = "Agent Host: " + machineName + "; ";
0189:            }
0190:
0191:            /** @return the date this Build Agent started running (not when a specific build started). */
0192:            public Date getDateStarted() {
0193:                return dateStarted;
0194:            }
0195:
0196:            /**
0197:             * @return the project being built now, or null if no project is being built.
0198:             */
0199:            public String getProjectName() {
0200:                return projectName;
0201:            }
0202:
0203:            void setAgentPropertiesFilename(final String filename) {
0204:                agentPropertiesFilename = filename;
0205:            }
0206:
0207:            private String getAgentPropertiesFilename() {
0208:                return agentPropertiesFilename;
0209:            }
0210:
0211:            private DelayedAction lastDelayedAction;
0212:
0213:            DelayedAction getLastDelayedAction() {
0214:                return lastDelayedAction;
0215:            }
0216:
0217:            private void setLastDelayedAction(DelayedAction lastDelayedAction) {
0218:                this .lastDelayedAction = lastDelayedAction;
0219:            }
0220:
0221:            /**
0222:             * Executes the {@link #execAction()} method after a fixed delay has expired.
0223:             */
0224:            abstract static class DelayedAction extends Thread {
0225:                /** Allow unit test to be notified when delayed action completes. */
0226:                static interface FinishedListener {
0227:                    public void finished(final DelayedAction delayedAction);
0228:                }
0229:
0230:                static final class Type {
0231:                    public static final Type RESTART = new Type("restart");
0232:                    public static final Type KILL = new Type("kill");
0233:
0234:                    private final String name;
0235:
0236:                    private Type(final String name) {
0237:                        this .name = name;
0238:                    }
0239:
0240:                    public String toString() {
0241:                        return name;
0242:                    }
0243:                }
0244:
0245:                private Throwable thrown;
0246:                private final int delay;
0247:                private final Type type;
0248:                private boolean isFinished;
0249:
0250:                /** Allow unit test to be notified when delayed action completes. */
0251:                private FinishedListener finishedListener;
0252:
0253:                DelayedAction(final Type type) {
0254:                    delay = Integer.getInteger(
0255:                            SYSPROP_CCDIST_DELAY_MS_KILLRESTART,
0256:                            DEFAULT_DELAY_MS_KILLRESTART).intValue();
0257:                    this .type = type;
0258:                    start();
0259:                }
0260:
0261:                public final void run() {
0262:                    try {
0263:                        LOG.info("Executing Agent " + type + " in " + delay
0264:                                + " milliseconds...");
0265:                        Thread.sleep(delay);
0266:                    } catch (InterruptedException e) {
0267:                        // ignore
0268:                    }
0269:
0270:                    try {
0271:                        execAction();
0272:                    } catch (Throwable t) {
0273:                        thrown = t;
0274:                        LOG.error("Error executing delayed action.", t);
0275:                    } finally {
0276:                        isFinished = true;
0277:                        if (finishedListener != null) {
0278:                            finishedListener.finished(this );
0279:                        }
0280:                    }
0281:                }
0282:
0283:                void setFinishedListener(final FinishedListener finishedListener) {
0284:                    this .finishedListener = finishedListener;
0285:                }
0286:
0287:                public Throwable getThrown() {
0288:                    return thrown;
0289:                }
0290:
0291:                public Type getType() {
0292:                    return type;
0293:                }
0294:
0295:                public boolean isFinished() {
0296:                    return isFinished;
0297:                }
0298:
0299:                /**
0300:                 * Implement in order to run the desired Action
0301:                 */
0302:                public abstract void execAction();
0303:            }
0304:
0305:            private final String busyLock = "busyLock";
0306:
0307:            void setBusy(final boolean newIsBusy) {
0308:                if (!newIsBusy) { // means the claim is being released
0309:
0310:                    if (isPendingRestart()) {
0311:                        // restart after delay to allow in progress remote calls to finish
0312:                        setLastDelayedAction(new DelayedAction(
0313:                                DelayedAction.Type.RESTART) {
0314:                            public void execAction() {
0315:                                doRestart();
0316:                            }
0317:                        });
0318:                        // do NOT reset busy state, since action is pending
0319:                        return;
0320:                    } else if (isPendingKill()) {
0321:                        // kill now after delay to allow in progress remote calls to finish
0322:                        setLastDelayedAction(new DelayedAction(
0323:                                DelayedAction.Type.KILL) {
0324:                            public void execAction() {
0325:                                doKill();
0326:                            }
0327:                        });
0328:                        // do NOT reset busy state, since action is pending
0329:                        return;
0330:                    }
0331:
0332:                    // clear out distributed build agent props
0333:                    distributedAgentProps.clear();
0334:
0335:                    // clear project name
0336:                    projectName = null;
0337:
0338:                    dateClaimed = null;
0339:                } else {
0340:                    dateClaimed = new Date();
0341:                }
0342:
0343:                synchronized (busyLock) {
0344:                    isBusy = newIsBusy;
0345:                }
0346:
0347:                fireAgentStatusChanged();
0348:
0349:                logPrefixInfo("agent busy status changed to: " + newIsBusy);
0350:            }
0351:
0352:            public Element doBuild(final Builder nestedBuilder,
0353:                    final Map projectPropertiesMap,
0354:                    final Map distributedAgentProperties)
0355:                    throws RemoteException {
0356:
0357:                return doBuild(nestedBuilder, projectPropertiesMap,
0358:                        distributedAgentProperties, null);
0359:            }
0360:
0361:            public Element doBuild(final Builder nestedBuilder,
0362:                    final Map projectPropertiesMap,
0363:                    final Map distributedAgentProperties,
0364:                    final ProgressRemote progressRemote) throws RemoteException {
0365:
0366:                return doBuild(nestedBuilder, projectPropertiesMap,
0367:                        distributedAgentProperties, progressRemote, null);
0368:            }
0369:
0370:            public Element doBuild(final Builder nestedBuilder,
0371:                    final Map projectPropertiesMap,
0372:                    final Map distributedAgentProperties,
0373:                    final ProgressRemote progressRemote,
0374:                    final RemoteResult[] remoteResults) throws RemoteException {
0375:
0376:                synchronized (busyLock) {
0377:                    if (!isBusy()) { // only reclaim if needed, since it resets the dateClaimed.
0378:                        setBusy(true); // we could remove this, since claim() is called during lookup...
0379:                    }
0380:                }
0381:
0382:                projectName = (String) projectPropertiesMap
0383:                        .get(PropertiesHelper.PROJECT_NAME);
0384:                if (null == projectName) {
0385:                    throw new RemoteException("Missing required property: "
0386:                            + PropertiesHelper.PROJECT_NAME
0387:                            + " in projectProperties");
0388:                }
0389:
0390:                final Level origLogLevel = Logger.getRootLogger().getLevel();
0391:                final boolean isDebugBuild = Boolean.valueOf(
0392:                        (String) distributedAgentProperties
0393:                                .get(PropertiesHelper.DISTRIBUTED_AGENT_DEBUG))
0394:                        .booleanValue();
0395:                boolean isDebugOverriden = false;
0396:                try {
0397:                    // Override log level if needed
0398:                    if (isDebugBuild && !LOG.isDebugEnabled()) {
0399:                        LOG
0400:                                .info("Switching Agent log level to Debug for build.");
0401:                        Logger.getRootLogger().setLevel(Level.DEBUG);
0402:                        isDebugOverriden = true;
0403:                    }
0404:
0405:                    buildProgressRemote = progressRemote;
0406:
0407:                    logPrefixDebug("Build Agent Props: "
0408:                            + distributedAgentProperties.toString());
0409:                    distributedAgentProps.putAll(distributedAgentProperties);
0410:
0411:                    this .remoteResults = remoteResults;
0412:
0413:                    String remoreResultsMsg = "";
0414:                    if (remoteResults != null) {
0415:                        for (int i = 0; i < remoteResults.length; i++) {
0416:                            remoreResultsMsg += "\n\tRemoteResult: "
0417:                                    + remoteResults[i].getAgentDir()
0418:                                            .getAbsolutePath();
0419:                        }
0420:                    }
0421:                    final String infoMessage = "Building project: "
0422:                            + projectName
0423:                            + "\n\tAgentLogDir: "
0424:                            + distributedAgentProps
0425:                                    .get(PropertiesHelper.DISTRIBUTED_AGENT_LOGDIR)
0426:                            + "\n\tAgentOutputDir: "
0427:                            + distributedAgentProps
0428:                                    .get(PropertiesHelper.DISTRIBUTED_AGENT_OUTPUTDIR)
0429:                            + remoreResultsMsg;
0430:
0431:                    logPrefixInfo(infoMessage);
0432:
0433:                    logPrefixDebug("Build Agent Project Props: "
0434:                            + projectPropertiesMap.toString());
0435:
0436:                    // this is done only to update agent UI info regarding ProjectName - which isn't available
0437:                    // until projectPropertiesMap has been set.
0438:                    fireAgentStatusChanged();
0439:
0440:                    configProperties = (Properties) PropertiesHelper
0441:                            .loadRequiredProperties(getAgentPropertiesFilename());
0442:
0443:                    if (progressRemote != null) {
0444:                        progressRemote
0445:                                .setValueRemote("validating remote builder");
0446:                        fireAgentStatusChanged(); // update UI
0447:                    }
0448:
0449:                    // @todo Test under webstart 4, 5 and 6.0
0450:                    // must do Ant Logger Lib injection before validation
0451:                    injectAntProgressLoggerLibIfNeeded(nestedBuilder);
0452:
0453:                    try {
0454:                        nestedBuilder.validate();
0455:                    } catch (CruiseControlException e) {
0456:                        final String message = "Failed to validate nested Builder on agent";
0457:                        logPrefixError(message, e);
0458:                        System.err.println(message + " - " + e.getMessage());
0459:                        throw new RemoteException(message, e);
0460:                    }
0461:
0462:                    final String overrideTarget = (String) distributedAgentProps
0463:                            .get(PropertiesHelper.DISTRIBUTED_OVERRIDE_TARGET);
0464:
0465:                    // wrap progressRemote with a local progress
0466:                    final Progress progressLocal;
0467:                    if (progressRemote != null) {
0468:                        progressLocal = new Progress() {
0469:                            public void setValue(String value) {
0470:                                try {
0471:                                    progressRemote.setValueRemote(value);
0472:                                    fireAgentStatusChanged(); // update UI
0473:                                } catch (RemoteException e) {
0474:                                    throw new RuntimeException(
0475:                                            "Error setting progress", e);
0476:                                }
0477:                            }
0478:
0479:                            public String getValue() {
0480:                                try {
0481:                                    return progressRemote.getValueRemote();
0482:                                } catch (RemoteException e) {
0483:                                    throw new RuntimeException(
0484:                                            "Error getting progress", e);
0485:                                }
0486:                            }
0487:                        };
0488:                    } else {
0489:                        progressLocal = null;
0490:                    }
0491:
0492:                    if (progressRemote != null) {
0493:                        progressRemote.setValueRemote("running remote builder");
0494:                        fireAgentStatusChanged(); // update UI
0495:                    }
0496:                    final Element buildResults;
0497:                    try {
0498:                        if (overrideTarget == null) {
0499:                            buildResults = nestedBuilder.build(
0500:                                    projectPropertiesMap, progressLocal);
0501:                        } else {
0502:                            buildResults = nestedBuilder.buildWithTarget(
0503:                                    projectPropertiesMap, overrideTarget,
0504:                                    progressLocal);
0505:                        }
0506:                    } catch (CruiseControlException e) {
0507:                        final String message = "Failed to complete build on agent";
0508:                        logPrefixError(message, e);
0509:                        System.err.println(message + " - " + e.getMessage());
0510:                        throw new RemoteException(message, e);
0511:                    }
0512:
0513:                    if (progressRemote != null) {
0514:                        progressRemote.setValueRemote("preparing results");
0515:                        fireAgentStatusChanged(); // update UI
0516:                    }
0517:                    prepareLogsAndArtifacts();
0518:                    return buildResults;
0519:                } catch (RemoteException e) {
0520:                    logPrefixError("doBuild threw exception, setting busy to false.");
0521:                    setBusy(false);
0522:                    throw e; // rethrow original exception
0523:                } finally {
0524:                    buildProgressRemote = null;
0525:
0526:                    // restore original log level if overriden
0527:                    if (isDebugOverriden) {
0528:                        Logger.getRootLogger().setLevel(origLogLevel);
0529:                        LOG
0530:                                .info("Restored Agent log level to: "
0531:                                        + origLogLevel);
0532:                    }
0533:                }
0534:            }
0535:
0536:            static void injectAntProgressLoggerLibIfNeeded(final Builder builder) {
0537:                if (builder instanceof  AntBuilder) {
0538:                    doInjectAntProgressLoggerLibIfNeeded((AntBuilder) builder);
0539:                } else if (builder instanceof  CompositeBuilder) {
0540:                    final Builder[] builders = ((CompositeBuilder) builder)
0541:                            .getBuilders();
0542:                    for (int i = 0; i < builders.length; i++) {
0543:                        injectAntProgressLoggerLibIfNeeded(builders[i]);
0544:                    }
0545:                }
0546:            }
0547:
0548:            private static void doInjectAntProgressLoggerLibIfNeeded(
0549:                    final AntBuilder antBuilder) {
0550:
0551:                if (antBuilder.getProgressLoggerLib() != null) {
0552:                    // path already set, so don't interfere
0553:                    LOG
0554:                            .debug("Agent skipping AntProgressLogger injection, already set to: "
0555:                                    + antBuilder.getProgressLoggerLib());
0556:                    return; // no kludges required
0557:                }
0558:
0559:                final String defaultAntProgressLoggerLib;
0560:                try {
0561:                    defaultAntProgressLoggerLib = AntScript
0562:                            .findDefaultProgressLoggerLib();
0563:
0564:                    if (defaultAntProgressLoggerLib != null) {
0565:                        // AntScript will be able to find the jar on it's own, so again, don't interfere
0566:                        LOG
0567:                                .debug("Agent skipping AntProgressLogger injection, AntScript will set to: "
0568:                                        + defaultAntProgressLoggerLib);
0569:                        return; // no kludges required
0570:                    }
0571:                } catch (AntScript.ProgressLibLocatorException e) {
0572:                    // This exception is expected under Webstart, so ignore and continue
0573:                    LOG
0574:                            .debug("Couldn't find default AntProgressLogger, will attempt injection.");
0575:                }
0576:
0577:                // This assumes JNLP Extension for Ant Progress Logger has been installed.
0578:                final String jnlpMuffinAntProgressLoggerPath = AntProgressLoggerInstaller
0579:                        .getJNLPMuffinAntProgressLoggerPath();
0580:                LOG.debug("jnlpMuffinAntProgressLoggerPath: "
0581:                        + jnlpMuffinAntProgressLoggerPath);
0582:
0583:                final File progressLoggerJar = new File(
0584:                        jnlpMuffinAntProgressLoggerPath);
0585:                if (!progressLoggerJar.exists()) {
0586:                    throw new IllegalStateException(
0587:                            "JNLP Build Agent couldn't find progress logger lib jar in expected location: "
0588:                                    + progressLoggerJar.getAbsolutePath());
0589:                }
0590:
0591:                antBuilder.setProgressLoggerLib(progressLoggerJar
0592:                        .getAbsolutePath());
0593:                LOG.debug("Injected AntProgressLogger lib: "
0594:                        + progressLoggerJar.getAbsolutePath()
0595:                        + " into AntBuilder.");
0596:            }
0597:
0598:            /**
0599:             * Zip any build artifacts found in the logDir and/or outputDir.
0600:             */
0601:            void prepareLogsAndArtifacts() {
0602:                final String buildDirProperty = configProperties
0603:                        .getProperty(CRUISE_BUILD_DIR);
0604:                try {
0605:                    buildRootDir = new File(buildDirProperty)
0606:                            .getCanonicalFile();
0607:                } catch (IOException e) {
0608:                    final String message = "Couldn't create "
0609:                            + buildDirProperty;
0610:                    logPrefixError(message, e);
0611:                    System.err.println(message + " - " + e.getMessage());
0612:                    throw new RuntimeException(message);
0613:                }
0614:
0615:                logDir = getAgentResultDir(PropertiesHelper.RESULT_TYPE_LOGS,
0616:                        PropertiesHelper.DISTRIBUTED_AGENT_LOGDIR);
0617:
0618:                outputDir = getAgentResultDir(
0619:                        PropertiesHelper.RESULT_TYPE_OUTPUT,
0620:                        PropertiesHelper.DISTRIBUTED_AGENT_OUTPUTDIR);
0621:
0622:                zippedLogs = ZipUtil.getTempResultsZipFile(buildRootDir,
0623:                        projectName, PropertiesHelper.RESULT_TYPE_LOGS);
0624:                ZipUtil.zipFolderContents(zippedLogs.getAbsolutePath(), logDir
0625:                        .getAbsolutePath());
0626:
0627:                zippedOutput = ZipUtil.getTempResultsZipFile(buildRootDir,
0628:                        projectName, PropertiesHelper.RESULT_TYPE_OUTPUT);
0629:                ZipUtil.zipFolderContents(zippedOutput.getAbsolutePath(),
0630:                        outputDir.getAbsolutePath());
0631:
0632:                if (remoteResults != null) {
0633:                    for (int i = 0; i < remoteResults.length; i++) {
0634:
0635:                        final File agentResultDir = remoteResults[i]
0636:                                .getAgentDir();
0637:                        ensureDirExists(agentResultDir);
0638:
0639:                        remoteResults[i].storeTempZippedFile(ZipUtil
0640:                                .getTempResultsZipFile(buildRootDir,
0641:                                        projectName, "remoteResult" + i));
0642:
0643:                        ZipUtil.zipFolderContents(remoteResults[i]
0644:                                .fetchTempZippedFile().getAbsolutePath(),
0645:                                agentResultDir.getAbsolutePath());
0646:                    }
0647:                }
0648:
0649:            }
0650:
0651:            private File getAgentResultDir(final String resultType,
0652:                    final String resultProperty) {
0653:                String resultDir;
0654:                resultDir = (String) distributedAgentProps.get(resultProperty);
0655:                logPrefixDebug("Result: " + resultType + "Prop value: "
0656:                        + resultDir);
0657:
0658:                if (resultDir == null || "".equals(resultDir)) {
0659:                    // use canonical behavior if attribute is not set
0660:                    resultDir = buildRootDir + File.separator + resultType
0661:                            + File.separator + projectName;
0662:                }
0663:
0664:                // ensure dir exists
0665:                final File fileResultDir = new File(resultDir);
0666:                ensureDirExists(fileResultDir);
0667:
0668:                return fileResultDir;
0669:            }
0670:
0671:            private static void ensureDirExists(final File fileResultDir) {
0672:                if (!fileResultDir.exists()) {
0673:                    if (!Util.doMkDirs(fileResultDir)) {
0674:                        final String msg = "Error creating Agent result dir: "
0675:                                + fileResultDir.getAbsolutePath();
0676:                        LOG.error(msg);
0677:                        throw new RuntimeException(msg);
0678:                    }
0679:                }
0680:            }
0681:
0682:            public String getMachineName() {
0683:                return machineName;
0684:            }
0685:
0686:            public void claim() {
0687:                // flag this agent as busy for now. Intended to prevent mulitple builds on same agent,
0688:                // when multiple master threads find the same agent, before any build thread has started.
0689:                synchronized (busyLock) {
0690:                    if (isBusy()) {
0691:                        throw new IllegalStateException(
0692:                                "Cannot claim agent on " + getMachineName()
0693:                                        + " that is busy building project: "
0694:                                        + projectName);
0695:                    }
0696:                    setBusy(true);
0697:                }
0698:            }
0699:
0700:            public boolean isBusy() {
0701:                synchronized (busyLock) {
0702:                    logPrefixDebug("Is busy called. value: " + isBusy);
0703:                    return isBusy;
0704:                }
0705:            }
0706:
0707:            public Date getDateClaimed() {
0708:                return dateClaimed;
0709:            }
0710:
0711:            private void setPendingKill() {
0712:                synchronized (busyLock) {
0713:                    this .isPendingKill = true;
0714:                    pendingKillSince = new Date();
0715:                }
0716:            }
0717:
0718:            public boolean isPendingKill() {
0719:                synchronized (busyLock) {
0720:                    return isPendingKill;
0721:                }
0722:            }
0723:
0724:            public Date getPendingKillSince() {
0725:                return pendingKillSince;
0726:            }
0727:
0728:            private void setPendingRestart() {
0729:                synchronized (busyLock) {
0730:                    this .isPendingRestart = true;
0731:                    pendingRestartSince = new Date();
0732:                }
0733:            }
0734:
0735:            public boolean isPendingRestart() {
0736:                synchronized (busyLock) {
0737:                    return isPendingRestart;
0738:                }
0739:            }
0740:
0741:            public Date getPendingRestartSince() {
0742:                return pendingRestartSince;
0743:            }
0744:
0745:            public boolean resultsExist(final String resultsType)
0746:                    throws RemoteException {
0747:                if (resultsType.equals(PropertiesHelper.RESULT_TYPE_LOGS)) {
0748:                    return recursiveFilesExist(logDir);
0749:                } else if (resultsType
0750:                        .equals(PropertiesHelper.RESULT_TYPE_OUTPUT)) {
0751:                    return recursiveFilesExist(outputDir);
0752:                } else {
0753:                    throw new RemoteException("Unrecognized result type: "
0754:                            + resultsType);
0755:                }
0756:            }
0757:
0758:            public boolean remoteResultExists(final int idx)
0759:                    throws RemoteException {
0760:                if (remoteResults != null) {
0761:                    final boolean resultsExist = recursiveFilesExist(remoteResults[idx]
0762:                            .getAgentDir());
0763:                    if (resultsExist) {
0764:                        return true;
0765:                    }
0766:                }
0767:                return false;
0768:            }
0769:
0770:            static boolean recursiveFilesExist(final File fileToCheck) {
0771:
0772:                if (!fileToCheck.exists()) { // save time, if it's doesn't exist at all
0773:                    return false;
0774:                } else if (fileToCheck.isFile()) { // save time, if it's a file
0775:                    return true;
0776:                }
0777:
0778:                final File[] dirs = fileToCheck.listFiles();
0779:                for (int i = 0; i < dirs.length; i++) {
0780:                    if (recursiveFilesExist(dirs[i])) {
0781:                        return true; // we found a file so return now, no need to keep looking.
0782:                    }
0783:                }
0784:                return false;
0785:            }
0786:
0787:            /**
0788:             * Return the file containing the given type of results. Package visible for unit testing.
0789:             * @param resultsType the type of results file
0790:             * @return the file containing the given type of results. Package visible for unit testing.
0791:             * @throws RemoteException if an invalid result type is given.
0792:             */
0793:            File getResultsZip(final String resultsType) throws RemoteException {
0794:                final File zipFile;
0795:                if (PropertiesHelper.RESULT_TYPE_LOGS.equals(resultsType)) {
0796:                    zipFile = zippedLogs;
0797:                } else if (PropertiesHelper.RESULT_TYPE_OUTPUT
0798:                        .equals(resultsType)) {
0799:                    zipFile = zippedOutput;
0800:                } else {
0801:                    throw new RemoteException("Unrecognized result type: "
0802:                            + resultsType);
0803:                }
0804:                return zipFile;
0805:            }
0806:
0807:            public byte[] retrieveResultsAsZip(final String resultsType)
0808:                    throws RemoteException {
0809:
0810:                final File zipFile = getResultsZip(resultsType);
0811:
0812:                final byte[] response;
0813:                try {
0814:                    response = FileUtil.getFileAsBytes(zipFile);
0815:                } catch (IOException e) {
0816:                    final String message = "Unable to get file "
0817:                            + zipFile.getAbsolutePath();
0818:                    logPrefixError(message, e);
0819:                    System.err.println(message + " - " + e.getMessage());
0820:                    throw new RuntimeException(message, e);
0821:                }
0822:
0823:                return response;
0824:            }
0825:
0826:            public byte[] retrieveRemoteResult(final int resultIdx)
0827:                    throws RemoteException {
0828:
0829:                RemoteResult remoteResult = null;
0830:                if (remoteResults != null) {
0831:                    for (int i = 0; i < remoteResults.length; i++) {
0832:                        if (resultIdx == remoteResults[i].getIdx()) {
0833:                            remoteResult = remoteResults[i];
0834:                            break;
0835:                        }
0836:                    }
0837:                }
0838:
0839:                if (remoteResult == null) {
0840:                    final String message = "Invalid remote result index: "
0841:                            + resultIdx;
0842:                    logPrefixError(message);
0843:                    System.err.println(message);
0844:                    throw new RuntimeException(message);
0845:                }
0846:
0847:                final byte[] response;
0848:                try {
0849:                    response = FileUtil.getFileAsBytes(remoteResult
0850:                            .fetchTempZippedFile());
0851:                } catch (IOException e) {
0852:                    final String message = "Unable to get remote result file: "
0853:                            + remoteResult.getAgentDir().getAbsolutePath();
0854:                    logPrefixError(message, e);
0855:                    System.err.println(message + " - " + e.getMessage());
0856:                    throw new RuntimeException(message, e);
0857:                }
0858:
0859:                return response;
0860:            }
0861:
0862:            public void clearOutputFiles() {
0863:                try {
0864:                    if (logDir != null) {
0865:                        logPrefixDebug("Deleting contents of " + logDir);
0866:                        IO.delete(logDir);
0867:                    } else {
0868:                        logPrefixDebug("Skip delete agent logDir: " + logDir);
0869:                    }
0870:                    if (zippedLogs != null) {
0871:                        logPrefixDebug("Deleting log zip " + zippedLogs);
0872:                        zippedLogs.deleteOnExit();
0873:                        IO.delete(zippedLogs);
0874:                    } else {
0875:                        logPrefixError("Skipping delete of log zip, file path is null.");
0876:                    }
0877:
0878:                    if (outputDir != null) {
0879:                        logPrefixDebug("Deleting contents of " + outputDir);
0880:                        IO.delete(outputDir);
0881:                    } else {
0882:                        logPrefixDebug("Skip delete agent outputDir: "
0883:                                + outputDir);
0884:                    }
0885:                    if (zippedOutput != null) {
0886:                        logPrefixDebug("Deleting output zip " + zippedOutput);
0887:                        zippedOutput.deleteOnExit();
0888:                        IO.delete(zippedOutput);
0889:                    } else {
0890:                        logPrefixError("Skipping delete of output zip, file path is null.");
0891:                    }
0892:
0893:                    if (remoteResults != null) {
0894:                        for (int i = 0; i < remoteResults.length; i++) {
0895:                            logPrefixDebug("Deleting contents of "
0896:                                    + remoteResults[i].getAgentDir()
0897:                                            .getAbsolutePath());
0898:                            IO.delete(remoteResults[i].getAgentDir());
0899:
0900:                            final File tempZippedFile = remoteResults[i]
0901:                                    .fetchTempZippedFile();
0902:                            if (tempZippedFile != null) {
0903:                                logPrefixDebug("Deleting remote result zip "
0904:                                        + tempZippedFile.getAbsolutePath());
0905:                                tempZippedFile.deleteOnExit();
0906:                                IO.delete(tempZippedFile);
0907:                            }
0908:                        }
0909:                    }
0910:
0911:                    setBusy(false);
0912:
0913:                } catch (RuntimeException e) {
0914:                    logPrefixError("Error cleaning agent build files.", e);
0915:                    throw e;
0916:                }
0917:            }
0918:
0919:            private void doRestart() {
0920:                logPrefixInfo("Attempting agent restart.");
0921:
0922:                final BasicService basicService;
0923:                try {
0924:                    basicService = (BasicService) ServiceManager
0925:                            .lookup(BasicService.class.getName());
0926:                } catch (UnavailableServiceException e) {
0927:                    final String errMsg = "Couldn't find webstart Basic Service. Is Agent running outside of webstart?";
0928:                    logPrefixError(errMsg, e);
0929:                    throw new RuntimeException(errMsg, e);
0930:                }
0931:
0932:                // Normally, we would set the busy lock first, but here we should only set the lock after we are certain
0933:                // the required Webstart service is available.
0934:                synchronized (busyLock) {
0935:                    if (!isBusy()) {
0936:                        // claim agent so no new build can start
0937:                        claim();
0938:                    }
0939:                    // set project name to informative message in case agent is found while restarting
0940:                    projectName = "executingAgentRestart";
0941:                }
0942:
0943:                final URL codeBaseURL = basicService.getCodeBase();
0944:                logPrefixInfo("basicService.getCodeBase()="
0945:                        + codeBaseURL.toString());
0946:
0947:                // relaunch via new browser session
0948:                // @todo How to close the browser after jnlp is relaunched?
0949:                final URL relaunchURL;
0950:                try {
0951:                    relaunchURL = new URL(codeBaseURL, "agent.jnlp");
0952:                } catch (MalformedURLException e) {
0953:                    final String errMsg = "Error building webstart relaunch URL from "
0954:                            + codeBaseURL.toString();
0955:                    logPrefixError(errMsg, e);
0956:                    throw new RuntimeException(errMsg, e);
0957:                }
0958:                if (basicService.showDocument(relaunchURL)) {
0959:                    logPrefixInfo("Relaunched agent via URL: "
0960:                            + relaunchURL.toString()
0961:                            + ". Will kill current agent now.");
0962:                    doKill(); // don't wait for build finish, since we've already relaunched at this point.
0963:                } else {
0964:                    final String errMsg = "Failed to relaunch agent via URL: "
0965:                            + relaunchURL.toString();
0966:                    logPrefixError(errMsg);
0967:                    throw new RuntimeException(errMsg);
0968:                }
0969:            }
0970:
0971:            private void doKill() {
0972:                logPrefixInfo("Attempting agent kill.");
0973:                synchronized (busyLock) {
0974:                    if (!isBusy()) {
0975:                        // claim agent so no new build can start
0976:                        claim();
0977:                    }
0978:                    // set project name to informative message in case agent is found while shutting down
0979:                    projectName = "executingAgentKill";
0980:                }
0981:                BuildAgent.kill();
0982:                doKillExecuted = true;
0983:            }
0984:
0985:            /** Intended only for unit tests, indicating if doKill() call has completed. */
0986:            private boolean doKillExecuted;
0987:
0988:            boolean isDoKillExecuted() {
0989:                return doKillExecuted;
0990:            }
0991:
0992:            public void kill(final boolean afterBuildFinished)
0993:                    throws RemoteException {
0994:                setPendingKill();
0995:
0996:                if (!afterBuildFinished // Kill now, don't waiting for build to finish.
0997:                        || !isBusy()) { // Not busy, so kill now.
0998:
0999:                    doKill(); // calls back to this agent to terminate lookup stuff
1000:                } else if (isBusy()) {
1001:                    // do nothing. When claim is released, setBusy(false) will perform the kill
1002:                }
1003:                fireAgentStatusChanged();
1004:            }
1005:
1006:            public void restart(final boolean afterBuildFinished)
1007:                    throws RemoteException {
1008:                setPendingRestart();
1009:
1010:                if (!afterBuildFinished // Restart now, don't waiting for build to finish.
1011:                        || !isBusy()) { // Not busy, so Restart now.
1012:
1013:                    doRestart();
1014:                } else if (isBusy()) {
1015:                    // do nothing. When claim is released, setBusy(false) will perform the Restart
1016:                }
1017:                fireAgentStatusChanged();
1018:            }
1019:
1020:            public String asString() {
1021:                final StringBuffer sb = new StringBuffer();
1022:                sb.append("Machine Name: ");
1023:                sb.append(machineName);
1024:                sb.append(";\t");
1025:                sb.append("Started: ");
1026:                sb.append(dateStarted);
1027:
1028:                sb.append("\n\tBusy: ");
1029:                sb.append(isBusy);
1030:                sb.append(";\tSince: ");
1031:                sb.append(dateClaimed);
1032:                sb.append(";\tProject: ");
1033:                sb.append(projectName);
1034:                // include Progress if available
1035:                if (buildProgressRemote != null) {
1036:                    sb.append("\n\tProgress: ");
1037:                    try {
1038:                        sb.append(buildProgressRemote.getValueRemote());
1039:                    } catch (RemoteException e) {
1040:                        LOG.info("Error reading remote progress", e);
1041:                    }
1042:                }
1043:
1044:                sb.append("\n\tPending Restart: ");
1045:                sb.append(isPendingRestart);
1046:                sb.append(";\tPending Restart Since: ");
1047:                sb.append(pendingRestartSince);
1048:
1049:                sb.append("\n\tPending Kill: ");
1050:                sb.append(isPendingKill);
1051:                sb.append(";\tPending Kill Since: ");
1052:                sb.append(pendingKillSince);
1053:
1054:                sb.append("\n\tVersion: ");
1055:                sb.append(CCDistVersion.getVersion());
1056:                sb.append(" (Compiled: ");
1057:                sb.append(CCDistVersion.getVersionBuildDate());
1058:                sb.append(")");
1059:
1060:                return sb.toString();
1061:            }
1062:
1063:            public void setEntryOverrides(PropertyEntry[] entryOverrides) {
1064:                serviceContainer.setEntryOverrides(entryOverrides);
1065:                // this is done only to update agent UI info with new entries info
1066:                fireAgentStatusChanged();
1067:            }
1068:
1069:            public PropertyEntry[] getEntryOverrides() {
1070:                return serviceContainer.getEntryOverrides();
1071:            }
1072:
1073:            public void addAgentStatusListener(
1074:                    final BuildAgent.AgentStatusListener listener) {
1075:                agentStatusListeners.add(listener);
1076:            }
1077:
1078:            public void removeAgentStatusListener(
1079:                    final BuildAgent.AgentStatusListener listener) {
1080:                agentStatusListeners.remove(listener);
1081:            }
1082:
1083:            private void fireAgentStatusChanged() {
1084:                for (int i = 0; i < agentStatusListeners.size(); i++) {
1085:                    ((BuildAgent.AgentStatusListener) agentStatusListeners
1086:                            .get(i)).statusChanged(this);
1087:                }
1088:            }
1089:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.