class Project implements Serializable, Runnable {

    protected void build() throws CruiseControlException {
        if (projectConfig == null) {
            throw new IllegalStateException("projectConfig must be set on project before calling build()");
        }

        if (stopped) {
            LOG.warn("not building project " + name + " because project has been stopped.");
            return;
        }

        // If the force only flag is set, only build if forced
        // or if the last build faild and we want to build on failures
        if (forceOnly && !buildForced && !(!wasLastBuildSuccessful && buildAfterFailed)) {
            info("not building because project is forceOnly and build not forced.");
            return;
        }


        final boolean buildWasForced = buildForced;

        try {
            setBuildStartTime(new Date());
            final Schedule schedule = projectConfig.getSchedule();
            if (schedule == null) {
                throw new IllegalStateException("project must have a schedule");
            }
            if (schedule.isPaused(buildStartTime)) {
                // a regularly scheduled paused
                // is different than ProjectState.PAUSED
                return;
            }

            // @todo Add Progress param to Bootstrapper API?
            bootstrap();

            final String target = useAndResetBuildTargetIfBuildWasForced(buildWasForced);

            // @todo Add Progress param to ModificationSet API?
            // getModifications will only return null if we don't need to build
            final Element modifications = getModifications(buildWasForced);

            if (modifications == null) {
                return;
            }

            // Using local reference to avoid NPE if config.xml is updated during build
            final Log buildLog = projectConfig.getLog();

            buildLog.addContent(modifications);

            final Date now;
            if (projectConfig.getModificationSet() != null
                    && projectConfig.getModificationSet().getTimeOfCheck() != null) {

                now = projectConfig.getModificationSet().getTimeOfCheck();
            } else {
                now = new Date();
            }

            if (getLabelIncrementer().isPreBuildIncrementer()) {
                label = getLabelIncrementer().incrementLabel(label, buildLog.getContent());
            }

            // collect project information
            buildLog.addContent(getProjectPropertiesElement(now));

            setState(ProjectState.BUILDING);
            final Element builderLog = schedule.build(buildCounter, lastBuild, now, getProjectPropertiesMap(now),
                    target, progress);

            buildLog.addContent(builderLog.detach());

            boolean buildSuccessful = buildLog.wasBuildSuccessful();
            fireResultEvent(new BuildResultEvent(this, buildSuccessful));

            if (!getLabelIncrementer().isPreBuildIncrementer() && buildSuccessful) {
                label = getLabelIncrementer().incrementLabel(label, buildLog.getContent());
            }

            setState(ProjectState.MERGING_LOGS);
            buildLog.writeLogFile(now);

            // If we only want to build after a check in, even when broken, set the last build to now,
            // regardless of success or failure (buildAfterFailed = false in config.xml)
            if (!buildAfterFailed) {
                lastBuild = now;
            }

            // If this was a successful build, update both last build and last successful build
            if (buildSuccessful) {
                lastBuild = now;
                lastSuccessfulBuild = now;
                info("build successful");
            } else {
                info("build failed");
            }

            buildCounter++;
            setWasLastBuildSuccessful(buildSuccessful);

            // also need to reset forced flag before serializing, unless buildForced var is transient
            //resetBuildForcedOnlyIfBuildWasForced(buildWasForced);
            serializeProject();

            // @todo Add Progress param to Publisher API?
            publish(buildLog);
            buildLog.reset();
        } finally {
            resetBuildForcedOnlyIfBuildWasForced(buildWasForced);
            setState(ProjectState.IDLE);
        }
    }

    public  Project();

}