/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.extension.distribution.internal.job;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.Condition;
import javax.inject.Inject;
import javax.inject.Provider;
import org.xwiki.component.manager.ComponentLookupException;
import org.xwiki.extension.distribution.internal.DistributionManager;
import org.xwiki.extension.distribution.internal.job.step.DistributionStep;
import org.xwiki.extension.distribution.internal.job.step.DistributionStep.State;
import org.xwiki.extension.job.InstallRequest;
import org.xwiki.extension.distribution.internal.job.step.ReportDistributionStep;
import org.xwiki.extension.distribution.internal.job.step.WelcomeDistributionStep;
import org.xwiki.job.AbstractJob;
import org.xwiki.job.Request;
import org.xwiki.job.event.status.JobStatus;
import org.xwiki.wiki.descriptor.WikiDescriptorManager;
/**
* @version $Id: aa90dbe0f9281e2a1bac8d433defb78a4fa26fd0 $
* @since 5.0M1
*/
public abstract class AbstractDistributionJob<R extends DistributionRequest>
extends AbstractJob<R, DistributionJobStatus> implements DistributionJob
{
/**
* The component used to get information about the current distribution.
*/
@Inject
protected DistributionManager distributionManager;
@Inject
protected Provider<WikiDescriptorManager> wikiDescriptorManagerProvider;
/**
* Condition to wait for ready state.
*/
protected final Condition readyCondition = lock.newCondition();
@Override
public String getType()
{
return "distribution";
}
protected String getWiki()
{
return getRequest().getWiki();
}
protected boolean isMainWiki()
{
return this.wikiDescriptorManagerProvider.get().getMainWikiId().equals(getWiki());
}
protected DistributionJobStatus createNewDistributionStatus(DistributionRequest request,
List<DistributionStep> steps)
{
return new DistributionJobStatus(request, this.observationManager, this.loggerManager, steps);
}
protected abstract List<DistributionStep> createSteps();
@Override
protected R castRequest(Request request)
{
DistributionRequest distributionRequest;
if (request instanceof DistributionRequest) {
distributionRequest = (DistributionRequest) request;
} else {
distributionRequest = new DistributionRequest(request);
}
return (R) distributionRequest;
}
@Override
protected DistributionJobStatus createNewStatus(R request)
{
List<DistributionStep> steps = createSteps();
if (getRequest().isInteractive()) {
// Add Welcome step
try {
DistributionStep welcomeStep = this.componentManager
.<DistributionStep>getInstance(DistributionStep.class, WelcomeDistributionStep.ID);
welcomeStep.setState(State.COMPLETED);
steps.add(0, welcomeStep);
} catch (ComponentLookupException e1) {
this.logger.error("Failed to get step instance for id [{}]", WelcomeDistributionStep.ID);
}
// Add Report step
try {
DistributionStep welcomeStep = this.componentManager
.<DistributionStep>getInstance(DistributionStep.class, ReportDistributionStep.ID);
welcomeStep.setState(State.COMPLETED);
steps.add(welcomeStep);
} catch (ComponentLookupException e1) {
this.logger.error("Failed to get step instance for id [{}]", ReportDistributionStep.ID);
}
}
// Create status
DistributionJobStatus status = createNewDistributionStatus(request, steps);
if (this.distributionManager.getDistributionExtension() != null) {
DistributionJobStatus previousStatus = getPreviousStatus();
if (previousStatus != null && previousStatus.getDistributionExtension() != null
&& !Objects.equals(previousStatus.getDistributionExtension(),
this.distributionManager.getDistributionExtension())) {
status.setPreviousDistributionExtension(previousStatus.getDistributionExtension());
status.setPreviousDistributionExtensionUI(previousStatus.getDistributionExtensionUI());
}
status.setDistributionExtension(this.distributionManager.getDistributionExtension().getId());
status.setDistributionExtensionUI(getUIExtensionId());
}
return status;
}
protected DistributionStep getStep(List<DistributionStep> steps, String stepId)
{
for (DistributionStep step : steps) {
if (step.getId().equals(stepId)) {
return step;
}
}
return null;
}
@Override
protected void runInternal() throws Exception
{
List<DistributionStep> steps = getStatus().getSteps();
this.progressManager.pushLevelProgress(steps.size(), this);
// Initialize steps
WelcomeDistributionStep welcomeStep = (WelcomeDistributionStep) getStep(steps, WelcomeDistributionStep.ID);
ReportDistributionStep reportStep = (ReportDistributionStep) getStep(steps, ReportDistributionStep.ID);
for (DistributionStep step : steps) {
step.initialize(this);
// Enable Welcome step if one of the steps is enabled
if (step.getState() == null) {
if (welcomeStep != null) {
welcomeStep.setState(null);
}
if (reportStep != null) {
reportStep.setState(null);
}
}
}
// Execute steps
try {
for (int index = 0; index < steps.size(); ++index) {
this.progressManager.startStep(this);
getStatus().setCurrentStateIndex(index);
DistributionStep step = steps.get(index);
// Prepare step
step.prepare();
// Execute step
if (step.getState() == null) {
if (getRequest().isInteractive()) {
DistributionQuestion question = new DistributionQuestion(step);
// Waiting to start
signalReady();
getStatus().ask(question);
if (question.getAction() != null) {
switch (question.getAction()) {
case CANCEL:
for (; index < steps.size(); ++index) {
steps.get(index).setState(DistributionStep.State.CANCELED);
}
case SKIP:
index = steps.size() - 1;
break;
default:
break;
}
}
// Save the status so that we remember the answer even if the DW is stopped before the end
this.store.storeAsync(this.status);
} else {
step.executeNonInteractive();
}
}
this.progressManager.endStep(this);
}
} finally {
this.progressManager.popLevelProgress(this);
}
}
// DistributionJob
@Override
public DistributionStep getCurrentStep()
{
return getStatus().getCurrentStep();
}
@Override
protected void jobFinished(Throwable exception)
{
super.jobFinished(exception);
signalReady();
}
private void signalReady()
{
this.lock.lock();
try {
this.readyCondition.signalAll();
} finally {
this.lock.unlock();
}
}
@Override
public void awaitReady() throws InterruptedException
{
if (getStatus() == null || getStatus().getState() == JobStatus.State.RUNNING) {
this.lock.lockInterruptibly();
try {
this.readyCondition.await();
} finally {
this.lock.unlock();
}
}
}
}