Commit 5a0bec3d authored by eric.duminil's avatar eric.duminil
Browse files

Removing CRLF from repository.

parent 3f54801c
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
This script compiles and deploys SimStadt Platform (GUI and Building Library Editors) and all required modules
to the directory specified in property "deploy.dir.path". It can be used to perform a headless build.
Before executing check that all required modules/projects are enumerated in property "projects".
-->
<project default="deploy" name="RegionChooser" basedir="..">
<description>
Create a Jar file with RegionChooser libraries and executables
</description>
<property name="target.path" value="lib/region-chooser" />
<property name="projects" value="RegionChooser,GeoLibs" />
<import file="../SimStadt/deploy-common.xml" />
<target name="deploy" depends="unit-test">
<echo file="${deploy.dir}/RegionChooser.bat">
java -classpath lib/* -Xms512m -Xmx2g -Djava.util.logging.config.file=logging.properties eu.simstadt.regionchooser.RegionChooserFX
pause > nul
</echo>
<echo file="${deploy.dir}/RegionChooser.sh">
java -classpath 'lib/*' -Xms512m -Xmx2g -Djava.util.logging.config.file=logging.properties eu.simstadt.regionchooser.RegionChooserFX
</echo>
<chmod file="${deploy.dir}/RegionChooser.sh" perm="u+x" />
<echo file="${deploy.dir}/RegionChooser.command">
cd "$(dirname "$0")" # set the current working directory to the directory this script is in
java -classpath lib/* -Xms512m -Xmx2g -Djava.util.logging.config.file=logging.properties eu.simstadt.regionchooser.RegionChooserFX
</echo>
<chmod file="${deploy.dir}/RegionChooser.command" perm="u+x" />
</target>
<target name="unit-test" depends="test-common" unless="doNotTest">
<jacoco:coverage destfile="${reports.dir}/${ant.project.name}.exec" xmlns:jacoco="antlib:org.jacoco.ant">
<junit printsummary="yes" haltonfailure="yes" fork="true">
<classpath refid="test-classpath" />
<formatter type="xml" usefile="true" />
<formatter type="plain" usefile="true" />
<!-- RegionExtractor -->
<test name="eu.simstadt.regionchooser.RegionExtractorTests" haltonfailure="no" todir="${reports.dir}" />
</junit>
</jacoco:coverage>
</target>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
This script compiles and deploys SimStadt Platform (GUI and Building Library Editors) and all required modules
to the directory specified in property "deploy.dir.path". It can be used to perform a headless build.
Before executing check that all required modules/projects are enumerated in property "projects".
-->
<project default="deploy" name="RegionChooser" basedir="..">
<description>
Create a Jar file with RegionChooser libraries and executables
</description>
<property name="target.path" value="lib/region-chooser" />
<property name="projects" value="RegionChooser,GeoLibs" />
<import file="../SimStadt/deploy-common.xml" />
<target name="deploy" depends="unit-test">
<echo file="${deploy.dir}/RegionChooser.bat">
java -classpath lib/* -Xms512m -Xmx2g -Djava.util.logging.config.file=logging.properties eu.simstadt.regionchooser.RegionChooserFX
pause > nul
</echo>
<echo file="${deploy.dir}/RegionChooser.sh">
java -classpath 'lib/*' -Xms512m -Xmx2g -Djava.util.logging.config.file=logging.properties eu.simstadt.regionchooser.RegionChooserFX
</echo>
<chmod file="${deploy.dir}/RegionChooser.sh" perm="u+x" />
<echo file="${deploy.dir}/RegionChooser.command">
cd "$(dirname "$0")" # set the current working directory to the directory this script is in
java -classpath lib/* -Xms512m -Xmx2g -Djava.util.logging.config.file=logging.properties eu.simstadt.regionchooser.RegionChooserFX
</echo>
<chmod file="${deploy.dir}/RegionChooser.command" perm="u+x" />
</target>
<target name="unit-test" depends="test-common" unless="doNotTest">
<jacoco:coverage destfile="${reports.dir}/${ant.project.name}.exec" xmlns:jacoco="antlib:org.jacoco.ant">
<junit printsummary="yes" haltonfailure="yes" fork="true">
<classpath refid="test-classpath" />
<formatter type="xml" usefile="true" />
<formatter type="plain" usefile="true" />
<!-- RegionExtractor -->
<test name="eu.simstadt.regionchooser.RegionExtractorTests" haltonfailure="no" todir="${reports.dir}" />
</junit>
</jacoco:coverage>
</target>
</project>
\ No newline at end of file
package eu.simstadt.nf4j;
import java.io.File;
/**
* NFConnector lets you communicate with your novaFACTORY (nF) server instance.
*
* @param <I> The import job implementation which can be handled by the connector.
* @param <E> The export job implementation which can be handled by the connector.
*
* @author Marcel Bruse
*/
public interface Connector<I extends ImportJob<?>, E extends ExportJob<?>> {
/**
* Callers of this NFConnector want to know the actual version of the novaFACTORY and the versions of its
* HTTP/FTP/WPS/etc. interfaces against which this interface has been implemented.
*
* The various NovaFACTORY interfaces may change over time. Such changes force the SimStadt programmers to
* implement different versions of this NFConnector interface while keeping old implementations due to
* backward compatibility.
*
* @return The supported version of the novaFACTORY.
*/
public String supportsNFVersion();
/**
* Sends an export job to nF. The job has to be passed to the nF in an appropriate XML job file.
* The DTD of the XML format can be obtained from the nF installation or documentation.
* The contents of the XML job file are detailed in the job's descriptor instance. This method is supposed to
* update the status of the job and the job id.
*
* @param job The nF export job with description. If the job description is invalid, you will receive an
* InvalidJobDescriptorException.
*/
public void sendAndUpdateExportJob(E exportJob)
throws InvalidJobDescriptorException, FailedTransmissionException;
/**
* Sends an import job to nF. A given import job descriptor has to describe the import job file. This
* method has to build the file and to zip it. The zip file has to obey the internal structure defined
* in the nF manuals. Short description:
*
* Import jobs enable you to add, alter and delete CityGML top level objects (like buildings) in the nF.
* Every import job file has to contain a start file. Start files control the import process through their
* file names and their contents. The name of a start file has the following structure
*
* <Product>_<Tile>.start
*
* The start file should contain the level to which your manipulated CityGML should be imported. Your CityGML
* file and the ZIP archive to be sent has to be named after the following naming convention:
*
* <Product>_<Tile>_<Level>_<Operation>.gml
*
* Here operation can be ...
* - 'REP': Replaces whole existing buildings only,
* - 'REPUPD': Replaces whole existing buildings and adds new buildings,
* - 'UPD': Update, same as REP,
* - 'CHG': Change, same as REPUPD,
* - 'DEL': Deletes the geometry of a particular LOD,
* - 'DELALL': Deletes a whole building
*
* @param job The nF import job to be sent. It has to contain a valid description.
*/
public void sendAndUpdateImportJob(I importJob)
throws InvalidJobDescriptorException, FailedTransmissionException;
/**
* Returns the status of any existing export nF job.
*
* @param jobId The id of the export job for which you want to request the status.
* @return The status of any existing export nF job.
*/
public E requestExportJob(int jobId) throws FailedTransmissionException;
/**
* Returns the status of any existing import nF job.
*
* @param jobId The id of the import job for which you want to request the status.
* @return The status of any existing import nF job.
*/
public I requestImportJob(int jobId) throws FailedTransmissionException;
/**
* Downloads the result for a given nF export job and hands over the corresponding file handle.
*
* @param jobId The id of the export job for which the result should be loaded.
* @return A file handle to the result of the nF export job.
*/
public File requestExportJobResult(E exportJob) throws FailedTransmissionException;
}
package eu.simstadt.nf4j;
import java.io.File;
/**
* NFConnector lets you communicate with your novaFACTORY (nF) server instance.
*
* @param <I> The import job implementation which can be handled by the connector.
* @param <E> The export job implementation which can be handled by the connector.
*
* @author Marcel Bruse
*/
public interface Connector<I extends ImportJob<?>, E extends ExportJob<?>> {
/**
* Callers of this NFConnector want to know the actual version of the novaFACTORY and the versions of its
* HTTP/FTP/WPS/etc. interfaces against which this interface has been implemented.
*
* The various NovaFACTORY interfaces may change over time. Such changes force the SimStadt programmers to
* implement different versions of this NFConnector interface while keeping old implementations due to
* backward compatibility.
*
* @return The supported version of the novaFACTORY.
*/
public String supportsNFVersion();
/**
* Sends an export job to nF. The job has to be passed to the nF in an appropriate XML job file.
* The DTD of the XML format can be obtained from the nF installation or documentation.
* The contents of the XML job file are detailed in the job's descriptor instance. This method is supposed to
* update the status of the job and the job id.
*
* @param job The nF export job with description. If the job description is invalid, you will receive an
* InvalidJobDescriptorException.
*/
public void sendAndUpdateExportJob(E exportJob)
throws InvalidJobDescriptorException, FailedTransmissionException;
/**
* Sends an import job to nF. A given import job descriptor has to describe the import job file. This
* method has to build the file and to zip it. The zip file has to obey the internal structure defined
* in the nF manuals. Short description:
*
* Import jobs enable you to add, alter and delete CityGML top level objects (like buildings) in the nF.
* Every import job file has to contain a start file. Start files control the import process through their
* file names and their contents. The name of a start file has the following structure
*
* <Product>_<Tile>.start
*
* The start file should contain the level to which your manipulated CityGML should be imported. Your CityGML
* file and the ZIP archive to be sent has to be named after the following naming convention:
*
* <Product>_<Tile>_<Level>_<Operation>.gml
*
* Here operation can be ...
* - 'REP': Replaces whole existing buildings only,
* - 'REPUPD': Replaces whole existing buildings and adds new buildings,
* - 'UPD': Update, same as REP,
* - 'CHG': Change, same as REPUPD,
* - 'DEL': Deletes the geometry of a particular LOD,
* - 'DELALL': Deletes a whole building
*
* @param job The nF import job to be sent. It has to contain a valid description.
*/
public void sendAndUpdateImportJob(I importJob)
throws InvalidJobDescriptorException, FailedTransmissionException;
/**
* Returns the status of any existing export nF job.
*
* @param jobId The id of the export job for which you want to request the status.
* @return The status of any existing export nF job.
*/
public E requestExportJob(int jobId) throws FailedTransmissionException;
/**
* Returns the status of any existing import nF job.
*
* @param jobId The id of the import job for which you want to request the status.
* @return The status of any existing import nF job.
*/
public I requestImportJob(int jobId) throws FailedTransmissionException;
/**
* Downloads the result for a given nF export job and hands over the corresponding file handle.
*
* @param jobId The id of the export job for which the result should be loaded.
* @return A file handle to the result of the nF export job.
*/
public File requestExportJobResult(E exportJob) throws FailedTransmissionException;
}
package eu.simstadt.nf4j;
import java.io.File;
/**
* An export job is a proxy object for an actual nF export job. Export jobs are used to get data from your nF server.
* Every export job has to have a valid job descriptor and/or a job id.
*
* @author Marcel Bruse
*
* @param <D> The descriptor type for the export job implementation.
*/
public abstract class ExportJob<D extends ExportJobDescriptor> extends Job {
/** Every job should have a (valid) job descriptor. */
protected D descriptor;
/**
* This constructor forces the job to have a description and a connector instance.
*
* @param connector The job will use this connector to synchronize itself with the nF.
* @param descriptor The description of this job.
*/
public ExportJob(D descriptor, Connector<?, ?> connector) {
this.descriptor = descriptor;
this.connector = connector;
}
/**
* This constructor forces the job to have a id and a connector instance.
*
* @param id The job id. If you call updateStatus() and the nF "knows" the job id, then the job status
* will be updated. If you call updateStatus() and the job id is "unkown" on the server side, then
* @param connector The job will use this connector to synchronize itself with the nF.
*/
public ExportJob(int id, Connector<?, ?> connector) {
this.id = id;
this.connector = connector;
}
/**
* @return Returns the description of this job.
*/
public D getDescriptor() {
return descriptor;
}
/**
* Once an export job has been finished, the caller should use this method to obtain the actual CityGML file.
*
* @return A file handle to the resulting CityGML file.
* @throws FailedTransmissionException There could be a problem while accessing or downloading the file.
*/
public abstract File getResult() throws FailedTransmissionException;
}
package eu.simstadt.nf4j;
import java.io.File;
/**
* An export job is a proxy object for an actual nF export job. Export jobs are used to get data from your nF server.
* Every export job has to have a valid job descriptor and/or a job id.
*
* @author Marcel Bruse
*
* @param <D> The descriptor type for the export job implementation.
*/
public abstract class ExportJob<D extends ExportJobDescriptor> extends Job {
/** Every job should have a (valid) job descriptor. */
protected D descriptor;
/**
* This constructor forces the job to have a description and a connector instance.
*
* @param connector The job will use this connector to synchronize itself with the nF.
* @param descriptor The description of this job.
*/
public ExportJob(D descriptor, Connector<?, ?> connector) {
this.descriptor = descriptor;
this.connector = connector;
}
/**
* This constructor forces the job to have a id and a connector instance.
*
* @param id The job id. If you call updateStatus() and the nF "knows" the job id, then the job status
* will be updated. If you call updateStatus() and the job id is "unkown" on the server side, then
* @param connector The job will use this connector to synchronize itself with the nF.
*/
public ExportJob(int id, Connector<?, ?> connector) {
this.id = id;
this.connector = connector;
}
/**
* @return Returns the description of this job.
*/
public D getDescriptor() {
return descriptor;
}
/**
* Once an export job has been finished, the caller should use this method to obtain the actual CityGML file.
*
* @return A file handle to the resulting CityGML file.
* @throws FailedTransmissionException There could be a problem while accessing or downloading the file.
*/
public abstract File getResult() throws FailedTransmissionException;
}
package eu.simstadt.nf4j;
/**
* Implementations of this interface are known to be export job descriptions.
*
* @author Marcel Bruse
*/
public interface ExportJobDescriptor extends JobDescriptor {}
package eu.simstadt.nf4j;
/**
* Implementations of this interface are known to be export job descriptions.
*
* @author Marcel Bruse
*/
public interface ExportJobDescriptor extends JobDescriptor {}
package eu.simstadt.nf4j;
import java.io.File;
import java.util.ArrayList;
import javafx.application.Platform;
import netscape.javascript.JSObject;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import eu.simstadt.nf4j.async.AsyncExportJob;
import eu.simstadt.nf4j.async.Coord;
import eu.simstadt.nf4j.async.ExportJobDescription;
import eu.simstadt.nf4j.async.HTTPConnection;
import eu.simstadt.nf4j.async.JobStatusEvent;
import eu.simstadt.nf4j.async.JobStatusListener;
import eu.simstadt.nf4j.async.Layer;
/**
* This class contains client oriented export job tests. It will send an export job and listens to status updates. Every
* of the subsequent status' LOCAL, SENT, PENDING, RUNNING, FINISHED and DOWNLOAD have to be signaled to this test
* class.
*
* @author Marcel Bruse
*/
//TODO: DRY with SuccessfulExportJob
public class ExportJobFromJavaFXRegionChooser implements JobStatusListener
{
public AsyncExportJob job;
private JSObject novaFactoryOpenLayer;
public void processJob(Geometry poly, String productName, JSObject novaFactoryOpenLayer) throws InterruptedException {
this.novaFactoryOpenLayer = novaFactoryOpenLayer;
ExportJobDescription description = ExportJobDescription.getDefaultDescriptor();
description.setInitiator(String.valueOf((int) (Math.random() * 9999)));
String userName = System.getProperty("user.name");
description.setAccount(userName);
// description.setProduct("WU3");
description.setProduct(productName);
description.setJobnumber(userName);
ArrayList<Coord> regionPolygon = new ArrayList<>();
for (Coordinate point : poly.getCoordinates()) {
regionPolygon.add(new Coord(point.y, point.x));
}
description.setRegionPolygon(regionPolygon);
Layer layer = Layer.getDefaultLayer();
layer.setProduct(productName);
layer.setName("GML");
description.addLayer(layer);
job = new AsyncExportJob(description, new HTTPConnection("193.196.136.164"));
job.addJobStatusListener(this);
try {
job.send();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
} catch (InvalidJobDescriptorException ex) {
ex.printStackTrace();
}
}
@Override
public void jobStatusChanged(JobStatusEvent event) {
JobStatus status = (JobStatus) event.getSource();
// System.out.println(status);
if (status == JobStatus.LOCAL) {
novaFactoryOpenLayer.call("updateStatus", "REQUEST HAS BEEN PREPARED");
} else if (status == JobStatus.SENT) {
novaFactoryOpenLayer.call("updateStatus", "REQUEST HAS BEEN SENT");
} else if (status == JobStatus.PENDING) {
novaFactoryOpenLayer.call("updateStatus", "PENDING");
} else if (status == JobStatus.RUNNING) {
novaFactoryOpenLayer.call("updateStatus", "SERVER IS BUSY");
} else if (status == JobStatus.FINISHED) {
try {
novaFactoryOpenLayer.call("updateStatus", "SERVER IS DONE");
job.downloadResult();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
} else if (status == JobStatus.DOWNLOAD) {
try {
File file = job.getResult();
novaFactoryOpenLayer.call("updateStatus", "DOWNLOADED AS ZIP");
//TODO: Call downloadFinished if FAILED
Platform.runLater(new Runnable() {
@Override
public void run() {
novaFactoryOpenLayer.call("selectSaveFile", file.toString());
}
});
novaFactoryOpenLayer.call("downloadFinished");
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
}
}
package eu.simstadt.nf4j;
import java.io.File;
import java.util.ArrayList;
import javafx.application.Platform;
import netscape.javascript.JSObject;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import eu.simstadt.nf4j.async.AsyncExportJob;
import eu.simstadt.nf4j.async.Coord;
import eu.simstadt.nf4j.async.ExportJobDescription;
import eu.simstadt.nf4j.async.HTTPConnection;
import eu.simstadt.nf4j.async.JobStatusEvent;
import eu.simstadt.nf4j.async.JobStatusListener;
import eu.simstadt.nf4j.async.Layer;
/**
* This class contains client oriented export job tests. It will send an export job and listens to status updates. Every
* of the subsequent status' LOCAL, SENT, PENDING, RUNNING, FINISHED and DOWNLOAD have to be signaled to this test
* class.
*
* @author Marcel Bruse
*/
//TODO: DRY with SuccessfulExportJob
public class ExportJobFromJavaFXRegionChooser implements JobStatusListener
{
public AsyncExportJob job;
private JSObject novaFactoryOpenLayer;
public void processJob(Geometry poly, String productName, JSObject novaFactoryOpenLayer) throws InterruptedException {
this.novaFactoryOpenLayer = novaFactoryOpenLayer;
ExportJobDescription description = ExportJobDescription.getDefaultDescriptor();
description.setInitiator(String.valueOf((int) (Math.random() * 9999)));
String userName = System.getProperty("user.name");
description.setAccount(userName);
// description.setProduct("WU3");
description.setProduct(productName);
description.setJobnumber(userName);
ArrayList<Coord> regionPolygon = new ArrayList<>();
for (Coordinate point : poly.getCoordinates()) {
regionPolygon.add(new Coord(point.y, point.x));
}
description.setRegionPolygon(regionPolygon);
Layer layer = Layer.getDefaultLayer();
layer.setProduct(productName);
layer.setName("GML");
description.addLayer(layer);
job = new AsyncExportJob(description, new HTTPConnection("193.196.136.164"));
job.addJobStatusListener(this);
try {
job.send();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
} catch (InvalidJobDescriptorException ex) {
ex.printStackTrace();
}
}
@Override
public void jobStatusChanged(JobStatusEvent event) {
JobStatus status = (JobStatus) event.getSource();
// System.out.println(status);
if (status == JobStatus.LOCAL) {
novaFactoryOpenLayer.call("updateStatus", "REQUEST HAS BEEN PREPARED");
} else if (status == JobStatus.SENT) {
novaFactoryOpenLayer.call("updateStatus", "REQUEST HAS BEEN SENT");
} else if (status == JobStatus.PENDING) {
novaFactoryOpenLayer.call("updateStatus", "PENDING");
} else if (status == JobStatus.RUNNING) {
novaFactoryOpenLayer.call("updateStatus", "SERVER IS BUSY");
} else if (status == JobStatus.FINISHED) {
try {
novaFactoryOpenLayer.call("updateStatus", "SERVER IS DONE");
job.downloadResult();
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
} else if (status == JobStatus.DOWNLOAD) {
try {
File file = job.getResult();
novaFactoryOpenLayer.call("updateStatus", "DOWNLOADED AS ZIP");
//TODO: Call downloadFinished if FAILED
Platform.runLater(new Runnable() {
@Override
public void run() {
novaFactoryOpenLayer.call("selectSaveFile", file.toString());
}
});
novaFactoryOpenLayer.call("downloadFinished");
} catch (FailedTransmissionException ex) {
ex.printStackTrace();
}
}
}
}
\ No newline at end of file
package eu.simstadt.nf4j;
/**
* This exception may be thrown by classes of the nf4j package if/on ...
*
* - the connector is null
* - malformed URLs
* - missing or malformed XML reports
* - HTTP failures
*
* @author Marcel Bruse
*/
public class FailedTransmissionException extends Exception {
private static final long serialVersionUID = -3530932388888249528L;
/** An textual description of the error. */
private String message;
/** Standard constructor. */
public FailedTransmissionException() {}
/**
* Constructor with error message and without nested cause.
*
* @param message The error message.
*/
public FailedTransmissionException(String message) {
this(message, null);
}
/**
* Constructor with cause and without error message.
*
* @param cause The nested cause of this exception.
*/
public FailedTransmissionException(Throwable cause) {
this(null, cause);
}
/**
* Constructor with error message and nested cause.
*
* @param message The error message.
* @param cause The nested cause of this exception.
*/
public FailedTransmissionException(String message, Throwable cause) {
this.message = message;
initCause(cause);
}
/**
* @return Returns the error message, if present.
*/
@Override
public String getMessage() {
return message;
}
}
package eu.simstadt.nf4j;
/**
* This exception may be thrown by classes of the nf4j package if/on ...
*
* - the connector is null
* - malformed URLs
* - missing or malformed XML reports
* - HTTP failures
*
* @author Marcel Bruse
*/
public class FailedTransmissionException extends Exception {
private static final long serialVersionUID = -3530932388888249528L;
/** An textual description of the error. */
private String message;
/** Standard constructor. */
public FailedTransmissionException() {}
/**
* Constructor with error message and without nested cause.
*
* @param message The error message.
*/
public FailedTransmissionException(String message) {
this(message, null);
}
/**
* Constructor with cause and without error message.
*
* @param cause The nested cause of this exception.
*/
public FailedTransmissionException(Throwable cause) {
this(null, cause);
}
/**
* Constructor with error message and nested cause.
*
* @param message The error message.
* @param cause The nested cause of this exception.
*/
public FailedTransmissionException(String message, Throwable cause) {
this.message = message;
initCause(cause);
}
/**
* @return Returns the error message, if present.
*/
@Override
public String getMessage() {
return message;
}
}
package eu.simstadt.nf4j;
/**
* An import job is a proxy object for an actual nF import job. Import jobs are used to store data in your nF database.
* Every import job has to have a valid job descriptor and/or a job id.
*
* @author Marcel Bruse
*
* @param <D> The descriptor type for the import job implementation.
*/
public abstract class ImportJob<D extends ImportJobDescriptor> extends Job {
/** Every job should have a (valid) job descriptor. */
protected D descriptor;
/**
* This constructor forces the job to have a description and a connector instance.
*
* @param connector The job will use this connector to synchronize itself with the nF.
* @param descriptor The description of this job.
*/
public ImportJob(D descriptor, Connector<?, ?> connector) {
this.descriptor = descriptor;
this.connector = connector;
}
/**
* This constructor forces the job to have a id and a connector instance.
*
* @param id The job id. If you call updateStatus() and the nF "knows" the job id, then the job status
* will be updated. If you call updateStatus() and the job id is "unkown" on the server side, then
* @param connector The job will use this connector to synchronize itself with the nF.
*/
public ImportJob(int id, Connector<?, ?> connector) {
this.id = id;
this.connector = connector;
}
/**
* @return Returns the description of this job.
*/
public D getDescriptor() {
return descriptor;
}
}
package eu.simstadt.nf4j;
/**
* An import job is a proxy object for an actual nF import job. Import jobs are used to store data in your nF database.
* Every import job has to have a valid job descriptor and/or a job id.
*
* @author Marcel Bruse
*
* @param <D> The descriptor type for the import job implementation.
*/
public abstract class ImportJob<D extends ImportJobDescriptor> extends Job {
/** Every job should have a (valid) job descriptor. */
protected D descriptor;
/**
* This constructor forces the job to have a description and a connector instance.
*
* @param connector The job will use this connector to synchronize itself with the nF.
* @param descriptor The description of this job.
*/
public ImportJob(D descriptor, Connector<?, ?> connector) {
this.descriptor = descriptor;
this.connector = connector;
}
/**
* This constructor forces the job to have a id and a connector instance.
*
* @param id The job id. If you call updateStatus() and the nF "knows" the job id, then the job status
* will be updated. If you call updateStatus() and the job id is "unkown" on the server side, then
* @param connector The job will use this connector to synchronize itself with the nF.
*/
public ImportJob(int id, Connector<?, ?> connector) {
this.id = id;
this.connector = connector;
}
/**
* @return Returns the description of this job.
*/
public D getDescriptor() {
return descriptor;
}
}
package eu.simstadt.nf4j;
import java.io.File;
/**
* Implementations of this interface are known to implement import job descriptions.
*
* @author Marcel Bruse
*/
public interface ImportJobDescriptor extends JobDescriptor {
/**
* Sets the CityGML file which is supposed to be imported by the nF.
*
* @param file The CityGML file which is supposed to be imported by the nF.
*/
public void setCityGMLFile(File file);
/**
* @return Returns the CityGML file which is supposed to be imported by the nF.
*/
public File getCityGMLFile();
}
package eu.simstadt.nf4j;
import java.io.File;
/**
* Implementations of this interface are known to implement import job descriptions.
*
* @author Marcel Bruse
*/
public interface ImportJobDescriptor extends JobDescriptor {
/**
* Sets the CityGML file which is supposed to be imported by the nF.
*
* @param file The CityGML file which is supposed to be imported by the nF.
*/
public void setCityGMLFile(File file);
/**
* @return Returns the CityGML file which is supposed to be imported by the nF.
*/
public File getCityGMLFile();
}
package eu.simstadt.nf4j;
/**
* If your export and import job descriptions are invalid due to the job.isValid() method, then it is very likely
* that you will get this exception.
*
* @author Marcel Bruse
*/
public class InvalidJobDescriptorException extends Exception {
private static final long serialVersionUID = 2710340003578550634L;
}
package eu.simstadt.nf4j;
/**
* If your export and import job descriptions are invalid due to the job.isValid() method, then it is very likely
* that you will get this exception.
*
* @author Marcel Bruse
*/
public class InvalidJobDescriptorException extends Exception {
private static final long serialVersionUID = 2710340003578550634L;
}
package eu.simstadt.nf4j;
/**
* This job class bundles the three attributes of every nF job: Id, status and last job related nF (error) message.
*
* @author Marcel Bruse
*/
public abstract class Job {
/** The status of the job. There are different states for export and import jobs. */
protected JobStatus status;
/** The id of the job. */
protected int id;
/** The connection to the nF. */
protected Connector<?, ?> connector;
/**
* Every job has a status. This method returns it. Look up the different possible values in the JobStatus
* enumeration.
*
* @return Returns the status of this job.
*/
public JobStatus getStatus() {
return status;
}
/**
* Lets you set the status of this job.
*
* @param status The status of this job.
*/
protected void setStatus(JobStatus status) {
this.status = status;
}
/**
* @return Returns the id of this job.
*/
public int getId() {
return id;
}
/**
* Sets the id of this job.
*
* @param jobId The job id about to be set.
*/
public void setId(int jobId) {
this.id = jobId;
}
/**
* @return Returns the nF connector of this job.
*/
public Connector<?, ?> getConnector() {
return connector;
}
/**
* Sets the nF connector of this job.
*
* @param nFConnector The connector of this job.
*/
public void setConnector(Connector<?, ?> connector) {
this.connector = connector;
}
/**
* This method reads the job description, builds a job file from it and uses the connector to send the
* job file to nF. You may want to implement the file building and file sending parts in the
* connector in order to reuse them here.
*
* This method is a object oriented convenience method for NFConnector.sendXXXJobFile(). It is sensible
* to have this convenience method, because it offers the API user centralized controls over the jobs
* status chain without switching between job instances and connector instances. In the end, the user is
* not interested in the connection to the nF, but in the job itself.
*/
public abstract void send() throws InvalidJobDescriptorException, FailedTransmissionException;
/**
* Connects to the nF and refreshes the status of this job. If there is no nF connector set, or the job id
* is unkown by the nF, then this operation will throw a FailedTransmissionException.
*
* @throws FailedTransmissionException If the connection to the nF is broken you will get some of this.
*/
public abstract void updateStatus() throws FailedTransmissionException;
/**
* Sets the status of this job depending on the given nF status code. nF status codes will be sent
* to you in http responses. Export jobs and import jobs have different states. Consider this fact
* in your implementation.
*
* @param statusCode The nF status code for this job.
*/
public abstract void setStatusForCode(int statusCode);
}
package eu.simstadt.nf4j;
/**
* This job class bundles the three attributes of every nF job: Id, status and last job related nF (error) message.
*
* @author Marcel Bruse
*/
public abstract class Job {
/** The status of the job. There are different states for export and import jobs. */
protected JobStatus status;
/** The id of the job. */
protected int id;
/** The connection to the nF. */
protected Connector<?, ?> connector;
/**
* Every job has a status. This method returns it. Look up the different possible values in the JobStatus
* enumeration.
*
* @return Returns the status of this job.
*/
public JobStatus getStatus() {
return status;
}
/**
* Lets you set the status of this job.
*
* @param status The status of this job.
*/
protected void setStatus(JobStatus status) {
this.status = status;
}
/**
* @return Returns the id of this job.
*/
public int getId() {
return id;
}
/**
* Sets the id of this job.
*
* @param jobId The job id about to be set.
*/
public void setId(int jobId) {
this.id = jobId;
}
/**
* @return Returns the nF connector of this job.
*/
public Connector<?, ?> getConnector() {
return connector;
}
/**
* Sets the nF connector of this job.
*
* @param nFConnector The connector of this job.
*/
public void setConnector(Connector<?, ?> connector) {
this.connector = connector;
}
/**
* This method reads the job description, builds a job file from it and uses the connector to send the
* job file to nF. You may want to implement the file building and file sending parts in the
* connector in order to reuse them here.
*
* This method is a object oriented convenience method for NFConnector.sendXXXJobFile(). It is sensible
* to have this convenience method, because it offers the API user centralized controls over the jobs
* status chain without switching between job instances and connector instances. In the end, the user is
* not interested in the connection to the nF, but in the job itself.
*/
public abstract void send() throws InvalidJobDescriptorException, FailedTransmissionException;
/**
* Connects to the nF and refreshes the status of this job. If there is no nF connector set, or the job id
* is unkown by the nF, then this operation will throw a FailedTransmissionException.
*
* @throws FailedTransmissionException If the connection to the nF is broken you will get some of this.
*/
public abstract void updateStatus() throws FailedTransmissionException;
/**
* Sets the status of this job depending on the given nF status code. nF status codes will be sent
* to you in http responses. Export jobs and import jobs have different states. Consider this fact
* in your implementation.
*
* @param statusCode The nF status code for this job.
*/
public abstract void setStatusForCode(int statusCode);
}
package eu.simstadt.nf4j;
/**
* Every instance of this class describes an export or import job for the novaFACTORY. Instances of JobBuilder
* take JobDescriptions and build XML export and import job files out of it.
*
* @author Marcel Bruse
*/
public interface JobDescriptor {
/**
* @return The supported version of the XML import or export job format.
*/
public String supportsJobVersion();
/**
* @return Returns true, if the job description is complete and correct. False, otherwise.
*/
public boolean isValid();
}
package eu.simstadt.nf4j;
/**
* Every instance of this class describes an export or import job for the novaFACTORY. Instances of JobBuilder
* take JobDescriptions and build XML export and import job files out of it.
*
* @author Marcel Bruse
*/
public interface JobDescriptor {
/**
* @return The supported version of the XML import or export job format.
*/
public String supportsJobVersion();
/**
* @return Returns true, if the job description is complete and correct. False, otherwise.
*/
public boolean isValid();
}
package eu.simstadt.nf4j;
/**
* The list of all possible export and import job states.
*
* @author Marcel Bruse
*/
public enum JobStatus {
UNKNOWN(0), // A local NF4J code, may be set in rare cases, if synchronization with nF fails.
LOCAL(10), // Job has not been sent yet and is known by the local system only.
SENT(20), // Job has been sent or it is assumed that it has been set before.
PENDING(30), // Export job has been enqueued and waits for execution
READY_TO_RUN(31), // Same as PENDING, but for import jobs
APPROVE(32), // (?) For import jobs only. Read the nF documentation, seams to be never used.
RUNNING(40), // Job is running
APPROVE_RUNNING(41), // (?) For import jobs only. Read the nF documentation, seams to be never used.
FAILED(50), // Export job failed
ERROR(51), // Same as FAILED, but for import jobs
WARNING(52), // For import jobs only. There has been a minor problem
REJECT(53), // (?) For import jobs only. Read the nF documentation, seams to be never used.
REJECT_RUNNING(54), // (?) For import jobs only. Read the nF documentation, seams to be never used.
APPROVE_REJECT_ERROR(55), // (?) For import jobs only. Read the nF documentation, seams to be never used.
APPROVE_REJECT_OK(56), // (?) For import jobs only. Read the nF documentation, seams to be never used.
IMPORTED_WARNING(57), // (?) For import jobs only. Read the nF documentation, seams to be never used.
FINISHED(60), // Job finished
DOWNLOAD(70); // Export jobs only. CityGML has been download to the local file system
public static final String UNKNOWN_MESSAGE = "The state of the job is unknown.";
public static final String LOCAL_MESSAGE = "The job is known locally only. It has not been sent yet.";
public static final String SENT_MESSAGE = "The job has been sent, is enqueued at the nF and has a job id.";
public static final String PENDING_MESSAGE = "Job is pending.";
public static final String RUNNING_MESSAGE = "Job is running.";
public static final String FAILED_MESSAGE = "Job failed.";
public static final String FINISHED_MESSAGE = "Job is finished.";
public static final String WAITING_MESSAGE = "Job is waiting for a response from the remote nF instance.";
/**
* This constructor sets messages for some states.
*
* @param internalCode Our numerical SimStadt-internal code for the nF status codes.
*/
private JobStatus(int internalCode) {
switch (internalCode) {
case 0:
message = UNKNOWN_MESSAGE; break;
case 10:
message = LOCAL_MESSAGE; break;
case 20:
message = SENT_MESSAGE; break;
case 30:
case 31:
message = PENDING_MESSAGE; break;
case 40:
message = RUNNING_MESSAGE; break;
case 50:
case 51:
message = FAILED_MESSAGE; break;
case 60:
message = FINISHED_MESSAGE; break;
default:
message = "";
}
}
/**
* This message describes the status or gives hints about error states. This message may come from a
* nF response.
*/
private String message;
/**
* Sets a message for this status in order to describe the status.
*
* @param message A message to describe this status.
*/
public void setMessage(String message) {
this.message = message;
}
/**
* @return Returns a message which describes this status.
*/
public String getMessage() {
return message;
}
}
package eu.simstadt.nf4j;
/**
* The list of all possible export and import job states.
*
* @author Marcel Bruse
*/
public enum JobStatus {
UNKNOWN(0), // A local NF4J code, may be set in rare cases, if synchronization with nF fails.
LOCAL(10), // Job has not been sent yet and is known by the local system only.
SENT(20), // Job has been sent or it is assumed that it has been set before.
PENDING(30), // Export job has been enqueued and waits for execution
READY_TO_RUN(31), // Same as PENDING, but for import jobs
APPROVE(32), // (?) For import jobs only. Read the nF documentation, seams to be never used.
RUNNING(40), // Job is running
APPROVE_RUNNING(41), // (?) For import jobs only. Read the nF documentation, seams to be never used.
FAILED(50), // Export job failed
ERROR(51), // Same as FAILED, but for import jobs
WARNING(52), // For import jobs only. There has been a minor problem
REJECT(53), // (?) For import jobs only. Read the nF documentation, seams to be never used.
REJECT_RUNNING(54), // (?) For import jobs only. Read the nF documentation, seams to be never used.
APPROVE_REJECT_ERROR(55), // (?) For import jobs only. Read the nF documentation, seams to be never used.
APPROVE_REJECT_OK(56), // (?) For import jobs only. Read the nF documentation, seams to be never used.
IMPORTED_WARNING(57), // (?) For import jobs only. Read the nF documentation, seams to be never used.
FINISHED(60), // Job finished
DOWNLOAD(70); // Export jobs only. CityGML has been download to the local file system
public static final String UNKNOWN_MESSAGE = "The state of the job is unknown.";
public static final String LOCAL_MESSAGE = "The job is known locally only. It has not been sent yet.";
public static final String SENT_MESSAGE = "The job has been sent, is enqueued at the nF and has a job id.";
public static final String PENDING_MESSAGE = "Job is pending.";
public static final String RUNNING_MESSAGE = "Job is running.";
public static final String FAILED_MESSAGE = "Job failed.";
public static final String FINISHED_MESSAGE = "Job is finished.";
public static final String WAITING_MESSAGE = "Job is waiting for a response from the remote nF instance.";
/**
* This constructor sets messages for some states.
*
* @param internalCode Our numerical SimStadt-internal code for the nF status codes.
*/
private JobStatus(int internalCode) {
switch (internalCode) {
case 0:
message = UNKNOWN_MESSAGE; break;
case 10:
message = LOCAL_MESSAGE; break;
case 20:
message = SENT_MESSAGE; break;
case 30:
case 31:
message = PENDING_MESSAGE; break;
case 40:
message = RUNNING_MESSAGE; break;
case 50:
case 51:
message = FAILED_MESSAGE; break;
case 60:
message = FINISHED_MESSAGE; break;
default:
message = "";
}
}
/**
* This message describes the status or gives hints about error states. This message may come from a
* nF response.
*/
private String message;
/**
* Sets a message for this status in order to describe the status.
*
* @param message A message to describe this status.
*/
public void setMessage(String message) {
this.message = message;
}
/**
* @return Returns a message which describes this status.
*/
public String getMessage() {
return message;
}
}
package eu.simstadt.nf4j.async;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Optional;
import eu.simstadt.nf4j.ImportJob;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
/**
* Import jobs are requests to store, change or delete CityGML models. Every valid import job has an id and a status.
* This implementation offers non-blocking asynchronous send and poll operations, so that your main application has
* not to wait for the results. You may want to register your main application as a job status listeners at this job
* to get status updates from the asynchronous operations.
*
* @author Marcel Bruse
*/
public class AsyncImportJob extends ImportJob<ImportJobDescription> implements AsyncJob {
/**
* While polling for the current job status, the polling thread will sleep for this amount of seconds
* before each status update request.
*/
private final int DEFAULT_POLLING_INTERVAL = 5; // seconds
private boolean jobTransmissionTriggered = false;
private JobStatus lastPublishedJobStatus;
private Optional<String> lastEncounteredProblem = Optional.empty();
private Thread sendThread;
private Thread pollThread;
/** As long as this variable is true, the polling thread will be kept alive. */
private boolean keepPolling = true;
/**
* List of all registered job status listeners. Whenever the state of this job changes, these listeners
* will get informed.
*/
private LinkedList<JobStatusListener> jobListenerList = new LinkedList<>();
/**
* This constructor forces the job to have a description and a connector instance. Every job which
* is created by this constructor will have the status "local", because it is assumed that it has an unsent
* description and no job id yet.
*
* @param connector The job will use this connector to synchronize itself with the nF.
* @param descriptor The description of this job.
*/
public AsyncImportJob(ImportJobDescription descriptor, HTTPConnection connector) {
super(descriptor, connector);
status = JobStatus.LOCAL;
}
/**
* This constructor forces the job to have a id and a connector instance. Every job which is created by this
* constructor will have the status "sent", because it is assumed that the job is already enqueued at the
* nF job queue.
*
* @param id The job id. If you call updateStatus() and the nF "knows" the job id, then the job status
* will be updated. If you call updateStatus() and the job id is "unkown" on the server side, then
* @param connector The job will use this connector to synchronize itself with the nF.
*/
public AsyncImportJob(int id, HTTPConnection connector) {
super(id, connector);
status = JobStatus.SENT;
}
/**
* This method zips up an archive which includes the CityGML file to be imported as well as a nF start
* file. Both files will be preprocessed according to the set attributes of the job description.
*/
@Override
public synchronized void send() throws InvalidJobDescriptorException, FailedTransmissionException {
if (jobTransmissionTriggered) {
throw new FailedTransmissionException("Jobs cannot be sent twice!");
}
if (Objects.isNull(descriptor) || !descriptor.isValid()) {
throw new InvalidJobDescriptorException();
}
jobTransmissionTriggered = true;
notifyJobStatusListeners(); // Force the job to signal the LOCAL status
sendThread = new Thread(new SendImportJobTask(this));
sendThread.start();
}
/**
* Frequently queries the status of the remote nF export job and updates the local status accordingly.
* The queries will be performed asynchronously in a separate thread. Job status listener will be notified
* upon every new status change.
*
* @param interval Amount of seconds to wait before the next status update request will be sent to the
* nF server.
*
* @throws FailedTransmissionException If your job has not been sent yet, then you will get some of this.
*/
@Override
public synchronized void poll(int interval) throws FailedTransmissionException {
if (status.compareTo(JobStatus.SENT) < 0) {
throw new FailedTransmissionException("The job has not been sent to the nF yet!");
}
if (Objects.nonNull(pollThread)) {
pollThread.interrupt();
}
keepPolling = true;
pollThread = new Thread(new PollJobStatusTask(this, interval));
pollThread.start();
}
/**
* Convenience method for polling with a predefined default interval.
*
* @see poll(int)
*/
public synchronized void poll() throws FailedTransmissionException {
poll(DEFAULT_POLLING_INTERVAL);
}
/**
* Connects to the nF and refreshes the status of this job. If there is no nF connector set,
* this operation will throw a FailedTransmissionException.
*
* @throws FailedTransmissionException If the connection to the nF is broken you will get some of this.
*/
@Override
public synchronized void updateStatus() throws FailedTransmissionException {
if (Objects.nonNull(connector)) {
AsyncImportJob job = ((HTTPConnection) connector).requestImportJob(id);
JobStatus newStatus = job.getStatus();
if (newStatus.compareTo(JobStatus.SENT) > 0) {
setStatus(job.getStatus(), Optional.empty());
}
} else {
throw new FailedTransmissionException();
}
}
/**
* Sets the status of this job depending on the given nF status code. nF status codes will be sent
* to you in HTTP responses. Note, the nF status code differ from the internal job status codes of
* this library. Read the nF documentation for more information.
*
* @param statusCode The nF status code for this job.
*/
@Override
public synchronized void setStatusForCode(int statusCode) {
switch (statusCode) {
case 0:
setStatus(JobStatus.PENDING); break;
case 10:
setStatus(JobStatus.RUNNING); break;
case 20:
setStatus(JobStatus.ERROR); break;
case 25:
setStatus(JobStatus.WARNING); break;
case 30:
setStatus(JobStatus.FINISHED); break;
case 40:
setStatus(JobStatus.APPROVE); break;
case 45:
setStatus(JobStatus.REJECT); break;
case 50:
setStatus(JobStatus.APPROVE_RUNNING); break;
case 55:
setStatus(JobStatus.REJECT_RUNNING); break;
case 60:
setStatus(JobStatus.APPROVE_REJECT_ERROR); break;
case 70:
setStatus(JobStatus.APPROVE_REJECT_OK); break;
case 80:
setStatus(JobStatus.IMPORTED_WARNING); break;
default:
setStatus(JobStatus.UNKNOWN);
}
}
/**
* Registers a job status listener.
*
* @param jobListener The job status listener to be registered. This listener will receive updates about every
* progressing change of the job status. Meaning, the change to a particular status will only be signaled once
* to the listener.
*/
@Override
public void addJobStatusListener(JobStatusListener jobListener) {
jobListenerList.add(jobListener);
}
/**
* Unregisters a job status listener.
*
* @param jobListener The job status listener to be unregistered.
*/
@Override
public void removeJobStatusListener(JobStatusListener jobListener) {
jobListenerList.remove(jobListener);
}
/**
* Once the status of this job changes, all registered job status listeners will be notified.
* Listeners will only be notified of the status updates where the status of the job progresses and they will
* only be notified once about every singular status. If this status has been signaled already, then the
* listeners will not be notified again.
*/
@Override
public synchronized void notifyJobStatusListeners() {
if (Objects.isNull(lastPublishedJobStatus) || status.compareTo(lastPublishedJobStatus) > 0) {
JobStatusEvent event = new JobStatusEvent(status, this, lastEncounteredProblem);
for (JobStatusListener listener : jobListenerList) {
listener.jobStatusChanged(event);
}
lastPublishedJobStatus = status;
}
}
/**
* A convenience method to set a new job status and a status message at the same time. Status messages will be passed
* by asynchronous tasks instead of exception, because they cannot throw exceptions.
*
* @param jobStatus The new status of this job.
* @param message A status message. This message may describe a problem which occurred during an asynchronous task.
*/
protected synchronized void setStatus(JobStatus jobStatus, Optional<String> message) {
super.setStatus(jobStatus);
lastEncounteredProblem = message;
notifyJobStatusListeners();
}
/**
* Cancels all ongoing send and poll operations as soon as possible.
*/
@Override
public void cancel() {
keepPolling = false;
if (Objects.nonNull(sendThread)) {
sendThread.interrupt();
}
if (Objects.nonNull(pollThread)) {
pollThread.interrupt();
}
}
/**
* @return Returns true, if the polling thread should go on with its polling job. Otherwise, false.
*/
@Override
public boolean keepPolling() {
return keepPolling;
}
/**
* @return Returns true, if the job is definitely done. This is also the case, if the resulting CityGML
* file has been download. False, otherwise.
*/
@Override
public boolean hasFinished() {
return status == JobStatus.FINISHED;
}
/**
* @return Returns true, if the job has been failed. You may want to look up the "last encountered problem"
* string.
*/
@Override
public boolean hasFailed() {
return status == JobStatus.FAILED || status == JobStatus.ERROR;
}
/**
* Calls updateStatus() for you, since updateStatus() is a protected method. This method is used by the
* asynchronous PollJobStatusTask class.
*/
@Override
public void triggerStatusUpdate() throws FailedTransmissionException {
updateStatus();
}
package eu.simstadt.nf4j.async;
import java.util.LinkedList;
import java.util.Objects;
import java.util.Optional;
import eu.simstadt.nf4j.ImportJob;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.InvalidJobDescriptorException;
import eu.simstadt.nf4j.JobStatus;
/**
* Import jobs are requests to store, change or delete CityGML models. Every valid import job has an id and a status.
* This implementation offers non-blocking asynchronous send and poll operations, so that your main application has
* not to wait for the results. You may want to register your main application as a job status listeners at this job
* to get status updates from the asynchronous operations.
*
* @author Marcel Bruse
*/
public class AsyncImportJob extends ImportJob<ImportJobDescription> implements AsyncJob {
/**
* While polling for the current job status, the polling thread will sleep for this amount of seconds
* before each status update request.
*/
private final int DEFAULT_POLLING_INTERVAL = 5; // seconds
private boolean jobTransmissionTriggered = false;
private JobStatus lastPublishedJobStatus;
private Optional<String> lastEncounteredProblem = Optional.empty();
private Thread sendThread;
private Thread pollThread;
/** As long as this variable is true, the polling thread will be kept alive. */
private boolean keepPolling = true;
/**
* List of all registered job status listeners. Whenever the state of this job changes, these listeners
* will get informed.
*/
private LinkedList<JobStatusListener> jobListenerList = new LinkedList<>();
/**
* This constructor forces the job to have a description and a connector instance. Every job which
* is created by this constructor will have the status "local", because it is assumed that it has an unsent
* description and no job id yet.
*
* @param connector The job will use this connector to synchronize itself with the nF.
* @param descriptor The description of this job.
*/
public AsyncImportJob(ImportJobDescription descriptor, HTTPConnection connector) {
super(descriptor, connector);
status = JobStatus.LOCAL;
}
/**
* This constructor forces the job to have a id and a connector instance. Every job which is created by this
* constructor will have the status "sent", because it is assumed that the job is already enqueued at the
* nF job queue.
*
* @param id The job id. If you call updateStatus() and the nF "knows" the job id, then the job status
* will be updated. If you call updateStatus() and the job id is "unkown" on the server side, then
* @param connector The job will use this connector to synchronize itself with the nF.
*/
public AsyncImportJob(int id, HTTPConnection connector) {
super(id, connector);
status = JobStatus.SENT;
}
/**
* This method zips up an archive which includes the CityGML file to be imported as well as a nF start
* file. Both files will be preprocessed according to the set attributes of the job description.
*/
@Override
public synchronized void send() throws InvalidJobDescriptorException, FailedTransmissionException {
if (jobTransmissionTriggered) {
throw new FailedTransmissionException("Jobs cannot be sent twice!");
}
if (Objects.isNull(descriptor) || !descriptor.isValid()) {
throw new InvalidJobDescriptorException();
}
jobTransmissionTriggered = true;
notifyJobStatusListeners(); // Force the job to signal the LOCAL status
sendThread = new Thread(new SendImportJobTask(this));
sendThread.start();
}
/**
* Frequently queries the status of the remote nF export job and updates the local status accordingly.
* The queries will be performed asynchronously in a separate thread. Job status listener will be notified
* upon every new status change.
*
* @param interval Amount of seconds to wait before the next status update request will be sent to the
* nF server.
*
* @throws FailedTransmissionException If your job has not been sent yet, then you will get some of this.
*/
@Override
public synchronized void poll(int interval) throws FailedTransmissionException {
if (status.compareTo(JobStatus.SENT) < 0) {
throw new FailedTransmissionException("The job has not been sent to the nF yet!");
}
if (Objects.nonNull(pollThread)) {
pollThread.interrupt();
}
keepPolling = true;
pollThread = new Thread(new PollJobStatusTask(this, interval));
pollThread.start();
}
/**
* Convenience method for polling with a predefined default interval.
*
* @see poll(int)
*/
public synchronized void poll() throws FailedTransmissionException {
poll(DEFAULT_POLLING_INTERVAL);
}
/**
* Connects to the nF and refreshes the status of this job. If there is no nF connector set,
* this operation will throw a FailedTransmissionException.
*
* @throws FailedTransmissionException If the connection to the nF is broken you will get some of this.
*/
@Override
public synchronized void updateStatus() throws FailedTransmissionException {
if (Objects.nonNull(connector)) {
AsyncImportJob job = ((HTTPConnection) connector).requestImportJob(id);
JobStatus newStatus = job.getStatus();
if (newStatus.compareTo(JobStatus.SENT) > 0) {
setStatus(job.getStatus(), Optional.empty());
}
} else {
throw new FailedTransmissionException();
}
}
/**
* Sets the status of this job depending on the given nF status code. nF status codes will be sent
* to you in HTTP responses. Note, the nF status code differ from the internal job status codes of
* this library. Read the nF documentation for more information.
*
* @param statusCode The nF status code for this job.
*/
@Override
public synchronized void setStatusForCode(int statusCode) {
switch (statusCode) {
case 0:
setStatus(JobStatus.PENDING); break;
case 10:
setStatus(JobStatus.RUNNING); break;
case 20:
setStatus(JobStatus.ERROR); break;
case 25:
setStatus(JobStatus.WARNING); break;
case 30:
setStatus(JobStatus.FINISHED); break;
case 40:
setStatus(JobStatus.APPROVE); break;
case 45:
setStatus(JobStatus.REJECT); break;
case 50:
setStatus(JobStatus.APPROVE_RUNNING); break;
case 55:
setStatus(JobStatus.REJECT_RUNNING); break;
case 60:
setStatus(JobStatus.APPROVE_REJECT_ERROR); break;
case 70:
setStatus(JobStatus.APPROVE_REJECT_OK); break;
case 80:
setStatus(JobStatus.IMPORTED_WARNING); break;
default:
setStatus(JobStatus.UNKNOWN);
}
}
/**
* Registers a job status listener.
*
* @param jobListener The job status listener to be registered. This listener will receive updates about every
* progressing change of the job status. Meaning, the change to a particular status will only be signaled once
* to the listener.
*/
@Override
public void addJobStatusListener(JobStatusListener jobListener) {
jobListenerList.add(jobListener);
}
/**
* Unregisters a job status listener.
*
* @param jobListener The job status listener to be unregistered.
*/
@Override
public void removeJobStatusListener(JobStatusListener jobListener) {
jobListenerList.remove(jobListener);
}
/**
* Once the status of this job changes, all registered job status listeners will be notified.
* Listeners will only be notified of the status updates where the status of the job progresses and they will
* only be notified once about every singular status. If this status has been signaled already, then the
* listeners will not be notified again.
*/
@Override
public synchronized void notifyJobStatusListeners() {
if (Objects.isNull(lastPublishedJobStatus) || status.compareTo(lastPublishedJobStatus) > 0) {
JobStatusEvent event = new JobStatusEvent(status, this, lastEncounteredProblem);
for (JobStatusListener listener : jobListenerList) {
listener.jobStatusChanged(event);
}
lastPublishedJobStatus = status;
}
}
/**
* A convenience method to set a new job status and a status message at the same time. Status messages will be passed
* by asynchronous tasks instead of exception, because they cannot throw exceptions.
*
* @param jobStatus The new status of this job.
* @param message A status message. This message may describe a problem which occurred during an asynchronous task.
*/
protected synchronized void setStatus(JobStatus jobStatus, Optional<String> message) {
super.setStatus(jobStatus);
lastEncounteredProblem = message;
notifyJobStatusListeners();
}
/**
* Cancels all ongoing send and poll operations as soon as possible.
*/
@Override
public void cancel() {
keepPolling = false;
if (Objects.nonNull(sendThread)) {
sendThread.interrupt();
}
if (Objects.nonNull(pollThread)) {
pollThread.interrupt();
}
}
/**
* @return Returns true, if the polling thread should go on with its polling job. Otherwise, false.
*/
@Override
public boolean keepPolling() {
return keepPolling;
}
/**
* @return Returns true, if the job is definitely done. This is also the case, if the resulting CityGML
* file has been download. False, otherwise.
*/
@Override
public boolean hasFinished() {
return status == JobStatus.FINISHED;
}
/**
* @return Returns true, if the job has been failed. You may want to look up the "last encountered problem"
* string.
*/
@Override
public boolean hasFailed() {
return status == JobStatus.FAILED || status == JobStatus.ERROR;
}
/**
* Calls updateStatus() for you, since updateStatus() is a protected method. This method is used by the
* asynchronous PollJobStatusTask class.
*/
@Override
public void triggerStatusUpdate() throws FailedTransmissionException {
updateStatus();
}
}
\ No newline at end of file
package eu.simstadt.nf4j.async;
import eu.simstadt.nf4j.FailedTransmissionException;
/**
* An asynchronous job will be sent in a non-blocking fashion, so that the main thread can proceed after calling
* the send() operation. It queries the status of its remote nF counterpart frequently in a separate thread, so that
* the job operations don't have to wait. Every asynchronous job should maintain a list of job status listeners.
* Every registered job status listener should be notified upon significant and new job status changes.
*
* @author Marcel Bruse
*/
public interface AsyncJob {
/**
* Frequently queries the status of the remote nF export job and updates the local status accordingly.
* The queries will be performed asynchronously in a separate thread. Job status listener will be notified
* upon every new status change.
*
* @param interval Amount of seconds to wait before the next status update request will be sent to the
* nF server.
*
* @throws FailedTransmissionException If your job has not been sent yet, then you will get some of this.
*/
public void poll(int interval) throws FailedTransmissionException;
/**
* Cancels all ongoing asynchronous operations of this job as soon as possible. Operations to be canceled
* might be send, poll and download operations.
*/
public void cancel();
/**
* Registers a job status listener at this job. The listener will then be notified upon job status changes.
*
* @param jobListener The job status listener to be registered at this job.
*/
public void addJobStatusListener(JobStatusListener jobListener);
/**
* Unregisters a job status listener for this job. The listener will not be notified about job status changes
* anymore.
*
* @param jobListener The job status listener to be unregistered.
*/
public void removeJobStatusListener(JobStatusListener jobListener);
/**
* Once the status of this job changes, all registered job status listeners will be notified.
*/
public void notifyJobStatusListeners();
/**
* Convenience method.
*
* @return Returns true, if the job is definitely done.
*/
public boolean hasFinished();
/**
* Convenience method.
*
* @return Returns true, if the job was unable to recover from a serious problem.
*/
public boolean hasFailed();
/**
* Asynchronous polling tasks may query this flag frequently in order to decide to proceed or not.
*
* @return Returns true, if the polling task should continue polling for status updates.
*/
public boolean keepPolling();
/**
* Calls updateStatus() for you, since updateStatus() is a protected method. This method is used by the
* asynchronous PollJobStatusTask class.
*
* @throws FailedTransmissionException If something goes wrong during the update process, you will get some
* of this.
*/
public void triggerStatusUpdate() throws FailedTransmissionException;
}
package eu.simstadt.nf4j.async;
import eu.simstadt.nf4j.FailedTransmissionException;
/**
* An asynchronous job will be sent in a non-blocking fashion, so that the main thread can proceed after calling
* the send() operation. It queries the status of its remote nF counterpart frequently in a separate thread, so that
* the job operations don't have to wait. Every asynchronous job should maintain a list of job status listeners.
* Every registered job status listener should be notified upon significant and new job status changes.
*
* @author Marcel Bruse
*/
public interface AsyncJob {
/**
* Frequently queries the status of the remote nF export job and updates the local status accordingly.
* The queries will be performed asynchronously in a separate thread. Job status listener will be notified
* upon every new status change.
*
* @param interval Amount of seconds to wait before the next status update request will be sent to the
* nF server.
*
* @throws FailedTransmissionException If your job has not been sent yet, then you will get some of this.
*/
public void poll(int interval) throws FailedTransmissionException;
/**
* Cancels all ongoing asynchronous operations of this job as soon as possible. Operations to be canceled
* might be send, poll and download operations.
*/
public void cancel();
/**
* Registers a job status listener at this job. The listener will then be notified upon job status changes.
*
* @param jobListener The job status listener to be registered at this job.
*/
public void addJobStatusListener(JobStatusListener jobListener);
/**
* Unregisters a job status listener for this job. The listener will not be notified about job status changes
* anymore.
*
* @param jobListener The job status listener to be unregistered.
*/
public void removeJobStatusListener(JobStatusListener jobListener);
/**
* Once the status of this job changes, all registered job status listeners will be notified.
*/
public void notifyJobStatusListeners();
/**
* Convenience method.
*
* @return Returns true, if the job is definitely done.
*/
public boolean hasFinished();
/**
* Convenience method.
*
* @return Returns true, if the job was unable to recover from a serious problem.
*/
public boolean hasFailed();
/**
* Asynchronous polling tasks may query this flag frequently in order to decide to proceed or not.
*
* @return Returns true, if the polling task should continue polling for status updates.
*/
public boolean keepPolling();
/**
* Calls updateStatus() for you, since updateStatus() is a protected method. This method is used by the
* asynchronous PollJobStatusTask class.
*
* @throws FailedTransmissionException If something goes wrong during the update process, you will get some
* of this.
*/
public void triggerStatusUpdate() throws FailedTransmissionException;
}
package eu.simstadt.nf4j.async;
/**
* This enumeration lists some of the "well known texts" (WKT) which are used to identify
* coordinate reference systems (CRS).
*
* @author Marcel Bruse
*
*/
public enum CRSWKT {
EPSG_31467("EPSG:31467"), // DHDN Gauss-Kruger zone 3
EPSG_4326("EPSG:4326"); // WGS 84
/** The well known texts for a CRS. */
public String wkt;
/** Constructor for well known texts. */
private CRSWKT(String wkt) {
this.wkt = wkt;
}
}
package eu.simstadt.nf4j.async;
/**
* This enumeration lists some of the "well known texts" (WKT) which are used to identify
* coordinate reference systems (CRS).
*
* @author Marcel Bruse
*
*/
public enum CRSWKT {
EPSG_31467("EPSG:31467"), // DHDN Gauss-Kruger zone 3
EPSG_4326("EPSG:4326"); // WGS 84
/** The well known texts for a CRS. */
public String wkt;
/** Constructor for well known texts. */
private CRSWKT(String wkt) {
this.wkt = wkt;
}
}
package eu.simstadt.nf4j.async;
/**
* Another class for coordinates. Is there a general purpose class for this kind of applications?
* This might be an intermediate representation for WGS 84 coordinates.
*
* @author Marcel Bruse
*/
public class Coord {
/** The latitude of the geographic position. */
public double latitude;
/** The longitude of the geographic position. */
public double longitude;
/** Standard constructor.
*
* @param latitude The latitude of the WGS 84 coordinate.
* @param longitude The longitude of the WGS 84 coordinate.
*/
public Coord(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
}
package eu.simstadt.nf4j.async;
/**
* Another class for coordinates. Is there a general purpose class for this kind of applications?
* This might be an intermediate representation for WGS 84 coordinates.
*
* @author Marcel Bruse
*/
public class Coord {
/** The latitude of the geographic position. */
public double latitude;
/** The longitude of the geographic position. */
public double longitude;
/** Standard constructor.
*
* @param latitude The latitude of the WGS 84 coordinate.
* @param longitude The longitude of the WGS 84 coordinate.
*/
public Coord(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
}
package eu.simstadt.nf4j.async;
import java.io.File;
import java.util.Optional;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.JobStatus;
/**
* This task downloads export job results asynchronously within its jobs separate download thread.
* You can cancel this task by calling job.cancel().
*
* @author Marcel Bruse
*/
public class DownloadTask implements Runnable {
/** The finished export job for which you want to download a CityGML result. */
private AsyncExportJob job;
/**
* Constructor with finished(!) asynchronous export job.
*
* @param job The export job for which you want to download the CityGML result.
*/
public DownloadTask(AsyncExportJob job) {
this.job = job;
}
/**
* This method performs the download operation asynchronously in the export jobs download thread.
* Job status listeners will be notified upon the finished download.
*/
@Override
public void run() {
HTTPConnection connector = (HTTPConnection) job.getConnector();
try {
File file = connector.requestExportJobResult(job);
job.setResult(file);
job.setStatus(JobStatus.DOWNLOAD, Optional.empty());
} catch (FailedTransmissionException ex) {
// Conditions have been checked by main thread. No exception handling needed.
}
}
}
package eu.simstadt.nf4j.async;
import java.io.File;
import java.util.Optional;
import eu.simstadt.nf4j.FailedTransmissionException;
import eu.simstadt.nf4j.JobStatus;
/**
* This task downloads export job results asynchronously within its jobs separate download thread.
* You can cancel this task by calling job.cancel().
*
* @author Marcel Bruse
*/
public class DownloadTask implements Runnable {
/** The finished export job for which you want to download a CityGML result. */
private AsyncExportJob job;
/**
* Constructor with finished(!) asynchronous export job.
*
* @param job The export job for which you want to download the CityGML result.
*/
public DownloadTask(AsyncExportJob job) {
this.job = job;
}
/**
* This method performs the download operation asynchronously in the export jobs download thread.
* Job status listeners will be notified upon the finished download.
*/
@Override
public void run() {
HTTPConnection connector = (HTTPConnection) job.getConnector();
try {
File file = connector.requestExportJobResult(job);
job.setResult(file);
job.setStatus(JobStatus.DOWNLOAD, Optional.empty());
} catch (FailedTransmissionException ex) {
// Conditions have been checked by main thread. No exception handling needed.
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment