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