org.integratedmodelling.engine.modelling.runtime.Task Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (C) 2007, 2015:
*
* - Ferdinando Villa - integratedmodelling.org - any
* other authors listed in @author annotations
*
* All rights reserved. This file is part of the k.LAB software suite, meant to enable
* modular, collaborative, integrated development of interoperable data and model
* components. For details, see http://integratedmodelling.org.
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the Affero General Public License Version 3 or any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the Affero General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite
* 330, Boston, MA 02111-1307, USA. The license is also available at:
* https://www.gnu.org/licenses/agpl.html
*******************************************************************************/
package org.integratedmodelling.engine.modelling.runtime;
import java.util.List;
import org.integratedmodelling.api.knowledge.IConcept;
import org.integratedmodelling.api.knowledge.IProperty;
import org.integratedmodelling.api.modelling.IActiveDirectObservation;
import org.integratedmodelling.api.modelling.IActiveSubject;
import org.integratedmodelling.api.modelling.IDirectObserver;
import org.integratedmodelling.api.modelling.IEvent;
import org.integratedmodelling.api.modelling.IKnowledgeObject;
import org.integratedmodelling.api.modelling.IModel;
import org.integratedmodelling.api.modelling.IModelBean;
import org.integratedmodelling.api.modelling.IObservableSemantics;
import org.integratedmodelling.api.modelling.ISubject;
import org.integratedmodelling.api.modelling.resolution.IDataflow;
import org.integratedmodelling.api.modelling.resolution.IResolution;
import org.integratedmodelling.api.modelling.scheduling.ITransition;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.monitoring.Messages;
import org.integratedmodelling.api.runtime.IContext;
import org.integratedmodelling.common.beans.Transition;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.interfaces.NetworkSerializable;
import org.integratedmodelling.common.monitoring.Monitor;
import org.integratedmodelling.common.owl.Knowledge;
import org.integratedmodelling.common.utils.CamelCase;
import org.integratedmodelling.common.utils.MiscUtilities;
import org.integratedmodelling.common.vocabulary.NS;
import org.integratedmodelling.common.vocabulary.ObservableSemantics;
import org.integratedmodelling.engine.scripting.ModelProxy;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabRuntimeException;
import org.integratedmodelling.exceptions.KlabValidationException;
import com.ibm.icu.text.NumberFormat;
/*
* Tasks basically drive the context through observations. It's a little awkward without a
* Task being a member of Context, but I'd rather keep the class files shorter although
* the continuous reference to the context member looks weird.
* @author ferdinando.villa
*/
public class Task extends AbstractBaseTask implements NetworkSerializable {
private Context context;
private Object observable;
private boolean optional = false;
private List scenarios;
private IActiveSubject target;
private ITransition lastTransition;
private IDataflow dataflow;
private IResolution resolution;
private String exception;
/*
* when true, this task runs temporal transitions on a previously initialized context.
*/
private boolean runTask;
/**
* Created for first observations by {@link Context#observeInternal}
*
* @param context
* @param monitor
* @param scenarios
*/
Task(Context context, IMonitor monitor, List scenarios) {
super(monitor);
this.context = context;
this.session = context.getSession();
this.scenarios = scenarios;
this.description = (context.getSubject() == null ? context.deferredObservableId
: context.getSubject().getName());
this.target = (IActiveSubject) context.getSubject();
}
/**
* Created for secondary observations by {@link Context#observeInternal}
*
* @param context
* @param monitor
* @param scenarios
*/
Task(Context context, IMonitor monitor, List scenarios, Object observable,
ISubject targetSubject) {
this(context, monitor, scenarios);
this.target = (IActiveSubject) targetSubject;
this.observable = observable;
this.description = this.observable + " in " + this.context.getSubject().getName();
}
/**
* When this is used, run() will run the temporal actions. Use through
* {@link #newTemporalTransitionTask()} for understandable semantics.
*/
private Task(Context context, IMonitor monitor) {
super(monitor);
this.context = context;
this.session = context.getSession();
runTask = true;
this.target = (IActiveSubject) context.getSubject();
this.description = "temporal contextualization of "
+ this.context.getSubject().getName();
}
/**
* Create
*
* @param context
* @param monitor
* @return a task that will run the temporal transitions in the passed context.
*/
public static Task newTemporalTransitionTask(Context context, IMonitor monitor) {
return new Task(context, monitor);
}
@Override
public IContext finish() {
for (;;) {
synchronized (this.status) {
if (this.status == Status.FINISHED || this.status == Status.ERROR
|| this.status == Status.INTERRUPTED) {
break;
}
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
this.status = Status.INTERRUPTED;
Thread.currentThread().interrupt();
}
}
return this.context;
}
@Override
public void run() {
/*
* use a task monitor with this task's ID after resolution.
*/
IMonitor monitor = ((Monitor) this.context.getMonitor()).get(this);
monitor.send(this);
try {
if (this.context.isDeferred) {
this.context.resolveObservable(this.monitor);
this.target = (IActiveSubject) this.context.getSubject();
}
/*
* legitimately null when we re-observe a ROI from the client side without
* other observables.
*/
if (this.target != null) {
((Subject) this.target).setMonitor(monitor);
if (this.runTask) {
monitor.info("running temporal transitions", Messages.INFOCLASS_MODEL);
this.target.contextualize();
} else {
/*
* establish the context - either a previous one or a new one if we're
* observing a new subject - and notify the listener.
*/
if (this.observable == null) {
observeSubject(monitor);
} else {
/*
* ensure strings and the like are resolved to something valid.
*/
this.observable = resolveObservable(this.observable);
observeInContext(monitor);
}
}
}
} catch (Exception e) {
synchronized (this.status) {
this.status = Status.ERROR;
}
this.exception = MiscUtilities.throwableToString(e);
monitor.send(Messages.TASK_FAILED);
// monitor.error(e);
} finally {
if (this.status != Status.ERROR) {
monitor.send(Messages.TASK_FINISHED);
}
this.endTime = System.currentTimeMillis();
synchronized (this.status) {
if (this.status != Status.ERROR) {
this.status = Status.FINISHED;
}
}
}
}
private void observeSubject(IMonitor monitor) throws KlabException {
try {
/*
* record original observation
*/
// IProvenance.Action userAction = context.provenance
// .add(context.provenance.getUser(), new ObservationAction(),
// context.provenance
// .get(target));
((Subject) this.target).setContext(this.context);
((Subject) this.target).setMonitor(monitor);
this.context
.setCoverage(((Subject) this.target)
.initialize(this.context.resolutionContext, null /* userAction */, monitor));
this.context.status = Context.INITIALIZED;
if (this.context.getCoverage().isEmpty()) {
monitor.error("subject initialization failed: empty coverage");
}
} catch (Throwable e) {
monitor.error(e);
}
}
private void observeInContext(IMonitor monitor) throws KlabException {
if (this.observable instanceof IDirectObserver) {
/*
* add subject or event.
*/
IActiveDirectObservation subj = KLAB.MFACTORY
.createSubject((IDirectObserver) this.observable, context, this.target, monitor);
if (subj instanceof ISubject) {
((Subject) this.context.getSubject()).addSubject((ISubject) subj, null);
} else if (subj instanceof IEvent) {
((Subject) this.context.getSubject()).addEvent((IEvent) subj);
}
return;
}
((Subject) this.target).setMonitor(monitor);
IObservableSemantics observable = makeObservable(this.observable);
if (observable.getModel() != null && observable.getModel().getErrorCount() > 0) {
// TODO find where the errors are stored and report them. They're in
// _errors
// but no way to get
// them out.
monitor.error("model " + observable.getModel().getId()
+ " has runtime errors");
throw new KlabException("cannot observe model "
+ observable.getModel().getId()
+ ": runtime errors");
}
/*
* record user action
*/
// IProvenance.Action userAction = context.provenance
// .add(context.provenance.getUser(), new ObservationAction(), context.provenance
// .get(observable));
this.context
.setCoverage(((Subject) this.target)
.observe(observable, this.scenarios, optional, /* userAction */ null, monitor));
if (!this.context.getCoverage().isEmpty()) {
monitor.info("observation of " + observable.getFormalName() + " in "
+ this.context.getSubject().getName()
+ " covers " + NumberFormat.getPercentInstance()
.format(this.context.getCoverage()
.getCoverage()), Messages.INFOCLASS_MODEL);
} else {
monitor.warn("observation of " + observable.getFormalName() + " in "
+ this.context.getSubject().getName()
+ " is empty");
}
}
Object resolveObservable(Object observable) throws KlabException {
// CallTracer.indent("resolveObservable()", this, observable);
Object ret = null;
if (observable instanceof String) {
if (observable.toString().contains(":")) {
ret = Knowledge.parse(observable.toString());
if (ret instanceof IProperty) {
throw new KlabValidationException("relationships cannot be observed directly: "
+ ret);
}
} else {
ret = KLAB.MMANAGER.findModelObject(observable.toString());
}
} else if (observable instanceof IKnowledgeObject) {
ret = KLAB.c(((IKnowledgeObject) observable).getName());
} else if (observable instanceof IConcept || observable instanceof IModel
|| observable instanceof IDirectObserver) {
ret = observable;
} else if (observable instanceof ModelProxy) {
ret = ((ModelProxy) observable).getModel();
}
if (ret == null) {
// CallTracer.msg("ERROR: unable to observe the given observable parameter.");
// CallTracer.unIndent();
throw new KlabValidationException("unable to observe " + observable);
}
// CallTracer.msg("returning valid observable: " +
// CallTracer.detailedDescription(ret));
// CallTracer.unIndent();
return ret;
}
public static IObservableSemantics makeObservable(Object observable) {
// CallTracer.indent("makeObservable()", this, observable);
IObservableSemantics result;
if (observable instanceof IModel) {
result = new ObservableSemantics((IModel) observable, ((IModel) observable)
.getId());
// CallTracer.msg("got Observable result for IModel parameter: "
// + CallTracer.detailedDescription(result));
// CallTracer.unIndent();
return result;
} else if (observable instanceof IConcept) {
result = new ObservableSemantics((IConcept) observable, NS
.getObservationTypeFor((IConcept) observable),
// TODO use current subject type for inherency
CamelCase.toLowerCase(((IConcept) observable).getLocalName(), '-'));
// CallTracer.msg("got Observable result for IConcept parameter: "
// + CallTracer.detailedDescription(result));
// CallTracer.unIndent();
return result;
}
// CallTracer.msg("parameter was not IConcept or IModel. returning null.");
// CallTracer.unIndent();
return null;
}
@Override
public IContext getContext() {
return this.context;
}
@SuppressWarnings("unchecked")
@Override
public T serialize(Class desiredClass) {
if (!desiredClass
.isAssignableFrom(org.integratedmodelling.common.beans.Task.class)) {
throw new KlabRuntimeException("cannot serialize a Task to a "
+ desiredClass.getCanonicalName());
}
org.integratedmodelling.common.beans.Task ret = new org.integratedmodelling.common.beans.Task();
ret.setId(getTaskId());
ret.setDescription(getDescription());
ret.setEndTime(getEndTime());
ret.setStartTime(getStartTime());
ret.setStatus(getStatus().name());
ret.setContextId(getContext().getId());
ret.setSessionId(getContext().getSession().getId());
ret.setRunTask(runTask);
if (lastTransition != null) {
ret.setCurrentTransition(KLAB.MFACTORY
.adapt(lastTransition, Transition.class));
}
if (exception != null) {
ret.setException(exception);
exception = null;
}
return (T) ret;
}
@Override
public IDataflow getDataflow() {
return this.dataflow;
}
public void setCurrentTransition(ITransition result) {
lastTransition = result;
}
@Override
public IResolution getResolution() {
return this.resolution;
}
}