/**
* OrbisGIS is a java GIS application dedicated to research in GIScience.
* OrbisGIS is developed by the GIS group of the DECIDE team of the
* Lab-STICC CNRS laboratory, see <http://www.lab-sticc.fr/>.
*
* The GIS group of the DECIDE team is located at :
*
* Laboratoire Lab-STICC – CNRS UMR 6285
* Equipe DECIDE
* UNIVERSITÉ DE BRETAGNE-SUD
* Institut Universitaire de Technologie de Vannes
* 8, Rue Montaigne - BP 561 56017 Vannes Cedex
*
* OrbisGIS is distributed under GPL 3 license.
*
* Copyright (C) 2007-2014 CNRS (IRSTV FR CNRS 2488)
* Copyright (C) 2015-2017 CNRS (Lab-STICC UMR CNRS 6285)
*
* This file is part of OrbisGIS.
*
* OrbisGIS is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OrbisGIS is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* OrbisGIS. If not, see <http://www.gnu.org/licenses/>.
*
* For more information, please consult: <http://www.orbisgis.org/>
* or contact directly:
* info_at_ orbisgis.org
*/
package org.orbisgis.toolboxeditor;
import net.opengis.ows._2.AcceptVersionsType;
import net.opengis.ows._2.CodeType;
import net.opengis.ows._2.GetCapabilitiesType.AcceptLanguages;
import net.opengis.ows._2.MetadataType;
import net.opengis.wps._2_0.*;
import net.opengis.wps._2_0.ObjectFactory;
import org.h2gis.utilities.JDBCUtilities;
import org.h2gis.utilities.SFSUtilities;
import org.h2gis.utilities.TableLocation;
import org.orbisgis.corejdbc.DataManager;
import org.orbisgis.corejdbc.DatabaseProgressionListener;
import org.orbisgis.corejdbc.StateEvent;
import org.orbisgis.frameworkapi.CoreWorkspace;
import org.orbisgis.sif.UIFactory;
import org.orbisgis.sif.components.OpenFilePanel;
import org.orbisgis.sif.components.OpenFolderPanel;
import org.orbisgis.sif.components.actions.ActionCommands;
import org.orbisgis.sif.components.actions.ActionDockingListener;
import org.orbisgis.sif.components.actions.DefaultAction;
import org.orbisgis.sif.docking.DockingManager;
import org.orbisgis.sif.docking.DockingPanel;
import org.orbisgis.sif.docking.DockingPanelParameters;
import org.orbisgis.sif.edition.EditorDockable;
import org.orbisgis.sif.edition.EditorManager;
import org.orbisgis.toolboxeditor.dataui.DataUIManager;
import org.orbisgis.toolboxeditor.editor.log.LogEditableElement;
import org.orbisgis.toolboxeditor.editor.log.LogEditor;
import org.orbisgis.toolboxeditor.editor.process.ProcessEditableElement;
import org.orbisgis.toolboxeditor.editor.process.ProcessEditor;
import org.orbisgis.toolboxeditor.utils.Job;
import org.orbisgis.toolboxeditor.utils.ToolBoxIcon;
import org.orbiswps.client.api.WpsClient;
import org.orbiswps.client.api.utils.ProcessExecutionType;
import org.orbiswps.client.api.utils.WpsJobStateListener;
import org.orbiswps.server.WpsServer;
import org.orbiswps.server.controller.process.ProcessIdentifier;
import org.orbiswps.server.model.*;
import org.orbiswps.server.utils.ProcessMetadata;
import org.orbiswps.server.utils.WpsServerListener;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xnap.commons.i18n.I18n;
import org.xnap.commons.i18n.I18nFactory;
import javax.swing.*;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.awt.event.ActionListener;
import java.beans.EventHandler;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.net.URI;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.orbisgis.toolboxeditor.utils.Job.*;
/**
* Implementation of the InternalWpsClient for Orbisgis.
*
* @author Sylvain PALOMINOS
* @author Erwan Bocher
**/
@Component(immediate = true, service = {DockingPanel.class, ToolboxWpsClient.class, WpsClient.class})
public class WpsClientImpl
implements DockingPanel, ToolboxWpsClient, PropertyChangeListener, WpsServerListener, DatabaseProgressionListener {
private static final String TOOLBOX_PROPERTIES = "toolbox.properties";
private static final String PROPERTY_SOURCES = "PROPERTY_SOURCES";
/** String of the action Refresh. */
private static final String ACTION_REFRESH = "ACTION_REFRESH";
/** Client Local (language). */
private static final String LANG = Locale.getDefault().toString();
/** String reference of the ToolBox used for DockingFrame. */
public static final String TOOLBOX_REFERENCE = "orbistoolbox";
/** I18N object */
private static final I18n I18N = I18nFactory.getI18n(WpsClientImpl.class);
/**Array of the table type accepted. */
private static final String[] SHOWN_TABLE_TYPES = new String[]{"TABLE","LINKED TABLE","VIEW","EXTERNAL"};
/** Logger */
private static final Logger LOGGER = LoggerFactory.getLogger(WpsClientImpl.class);
/** Docking parameters used by DockingFrames. */
private DockingPanelParameters parameters;
/** Displayed JPanel. */
private ToolBoxPanel toolBoxPanel;
/** Object creating the UI corresponding to the data. */
private DataUIManager dataUIManager;
/** EditableElement associated to the logEditor. */
private LogEditableElement lee;
/** EditorDockable for the displaying of the running processes log. */
private LogEditor le;
/** List of open EditorDockable. Used to close them when the ToolBox is close (Not stopped, just not visible). */
private List<EditorDockable> openEditorList;
/** OrbigGIS DockingManager. */
private DockingManager dockingManager;
/** OrbigGIS ExecutorService. */
private ExecutorService executorService;
/** OrbisGIS DataManager. */
private DataManager dataManager;
/** OrbisGIS WpsServer. */
private WpsServer wpsServer;
/** OrbisGIS CoreWorkspace. */
private CoreWorkspace workspace;
/** List of JobStateListener listening for the Job state execution. */
private List<WpsJobStateListener> jobStateListenerList;
/** Map of the running job. */
private Map<UUID, Job> jobMap;
private EditorManager editorManager;
/** Boolean indicating if a refresh task has been scheduled or not. */
private boolean isRefreshScheduled = false;
/** True if the database is H2, false otherwise. */
private boolean isH2;
/** List of map containing the table with their basic information.
* It is used as a buffer to avoid to reload all the table list to save time.
*/
private List<Map<JdbcProperties, String>> tableList = new ArrayList<>();
/** True if an updates happen while another on is running. */
private boolean updateWhileAwaitingRefresh = false;
/** True if a swing runnable is pending to refresh the content of the table list, false otherwise. */
private AtomicBoolean awaitingRefresh=new AtomicBoolean(false);
private Map<String, String> processUriPath = new HashMap<>();
private Map<URI, Map<ProcessMetadata.INTERNAL_METADATA, Object>> processMetadataMap = new HashMap<>();
/***************************/
/** WpsClientImpl methods **/
/***************************/
/** OSGI active/deactivate, set/unset methods **/
@Activate
public void activate(){
if(wpsServer != null){
wpsServer.setDataSource(dataManager.getDataSource());
wpsServer.setExecutorService(executorService);
}
toolBoxPanel = new ToolBoxPanel(this);
dataUIManager = new DataUIManager(this);
parameters = new DockingPanelParameters();
parameters.setTitle(I18N.tr("ToolBox"));
parameters.setTitleIcon(ToolBoxIcon.getIcon(ToolBoxIcon.ORBIS_TOOLBOX));
parameters.setCloseable(true);
parameters.setName(TOOLBOX_REFERENCE);
ActionCommands dockingActions = new ActionCommands();
parameters.setDockActions(dockingActions.getActions());
dockingActions.addPropertyChangeListener(new ActionDockingListener(parameters));
dockingActions.addAction(
new DefaultAction(ACTION_REFRESH,
ACTION_REFRESH,
I18N.tr("Refresh the selected node."),
ToolBoxIcon.getIcon(ToolBoxIcon.REFRESH),
EventHandler.create(ActionListener.class, this, "refreshAvailableScripts"),
null)
);
//Creates the LogEditableElement and the LogEditor
lee = new LogEditableElement();
openEditorList = new ArrayList<>();
le = new LogEditor(lee);
dockingManager.addDockingPanel(le);
openEditorList.add(le);
jobStateListenerList = new ArrayList<>();
jobMap = new HashMap<>();
if(dataManager != null) {
//Install database listeners
dataManager.addDatabaseProgressionListener(this, StateEvent.DB_STATES.STATE_STATEMENT_END);
//Call readDatabase when a SourceManager fire an event
onDataManagerChange();
}
else{
LOGGER.warn(I18N.tr("Warning, no DataManager found."));
}
testDBForMultiProcess();
if(workspace != null) {
Properties tbProperties = new Properties();
//Load the property file
File propertiesFile = new File(workspace.getWorkspaceFolder() + File.separator + TOOLBOX_PROPERTIES);
if (propertiesFile.exists()) {
try {
tbProperties.load(new FileInputStream(propertiesFile));
} catch (IOException e) {
LOGGER.warn(I18N.tr("Unable to restore previous configuration of the ToolBox."));
tbProperties = new Properties();
}
}
//Properties loading
Object prop = tbProperties.getProperty(PROPERTY_SOURCES);
if(prop != null && !prop.toString().isEmpty()){
String str = prop.toString();
for(String s : str.split(";")){
File f = new File(s);
addLocalSource(f.toURI(), null, true, new File(f.getParent()).getName());
}
}
}
else{
LOGGER.warn("Warning, no CoreWorkspace found. Unable to load the previous state.");
}
refreshAvailableScripts();
}
@Deactivate
public void deactivate() {
//Removes all the EditorDockable that were added
for (EditorDockable ed : openEditorList) {
dockingManager.removeDockingPanel(ed.getDockingParameters().getName());
}
openEditorList = new ArrayList<>();
toolBoxPanel.dispose();
//Try to save the local files loaded.
try {
Properties tbProperties = new Properties();
String path = "";
for(Map.Entry<String, String> entry : processUriPath.entrySet()){
if(!path.isEmpty()){
path+=";";
}
path+=entry.getValue();
}
//Save the open process source path
tbProperties.setProperty(PROPERTY_SOURCES, path);
tbProperties.store(
new FileOutputStream(workspace.getWorkspaceFolder() + File.separator + TOOLBOX_PROPERTIES),
I18N.tr("Save of the OrbisGIS toolBox"));
} catch (IOException e) {
LOGGER.warn(I18N.tr("Unable to save ToolBox state."));
}
}
@Reference
public void setWpsServer(WpsServer wpsServer) {
this.wpsServer = wpsServer;
this.wpsServer.addWpsServerListener(this);
}
public void unsetWpsServer(WpsServer wpsServer) {
this.wpsServer.removeWpsServerListener(this);
this.wpsServer = null;
}
@Reference
public void setDataManager(DataManager dataManager) {
this.dataManager = dataManager;
}
public void unsetDataManager(DataManager dataManager) {
this.dataManager = null;
}
public DataManager getDataManager(){
return dataManager;
}
@Reference
public void setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
}
public void unsetExecutorService(ExecutorService executorService) {
this.executorService = null;
}
public ExecutorService getExecutorService(){
return executorService;
}
@Reference
public void setDockingManager(DockingManager dockingManager) {
this.dockingManager = dockingManager;
}
public void unsetDockingManager(DockingManager dockingManager) {
this.dockingManager = null;
}
@Reference
public void setEditorManager(EditorManager editorManager) {
this.editorManager = editorManager;
}
public void unsetEditorManager(EditorManager editorManager) {
this.editorManager = null;
}
@Reference
public void setCoreWorkspace(CoreWorkspace workspace) {
this.workspace = workspace;
}
public void unsetCoreWorkspace(CoreWorkspace workspace) {
this.workspace = null;
}
/** Other methods **/
/**
* Uses the request object to ask it to the WPS service, get the result and unmarshall it.
* @param request The request to ask to the WPS service.
* @return The result object.
*/
private Object askRequest(Object request){
Marshaller marshaller;
Unmarshaller unmarshaller;
try {
marshaller = JaxbContainer.JAXBCONTEXT.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
unmarshaller = JaxbContainer.JAXBCONTEXT.createUnmarshaller();
} catch (JAXBException e) {
LoggerFactory.getLogger(WpsClient.class).error(
I18N.tr("Unable to create the marshall objects.\nCause : {0}.", e.getMessage()));
return null;
}
//Marshall the WpsService ask
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
marshaller.marshal(request, out);
} catch (JAXBException e) {
LoggerFactory.getLogger(WpsClient.class).error(
I18N.tr("Unable to marshall the request object : {0}.\nCause : {1}.",
request.getClass().getName(), e.getMessage()!=null?e.getMessage():e.getCause().getMessage()));
return null;
}
DataInputStream in = new DataInputStream(new ByteArrayInputStream(out.toByteArray()));
//Ask the WpsService
ByteArrayOutputStream xml = (ByteArrayOutputStream) wpsServer.callOperation(in);
//unmarshall the WpsService answer
InputStream resultResultXml = new ByteArrayInputStream(xml.toByteArray());
Object resultObject;
try {
resultObject = unmarshaller.unmarshal(resultResultXml);
} catch (JAXBException e) {
LoggerFactory.getLogger(WpsClient.class).error(I18N.tr("Unable to marshall the answer xml.\nCause : {0}.",
e.getMessage()));
return null;
}
if(resultObject instanceof JAXBElement){
resultObject = ((JAXBElement)resultObject).getValue();
}
return resultObject;
}
/**
* Ask to the Wps server the getCapabilities request and returns the list of ProcessSummaryType.
*
* @return The list of ProcessSummaryType.
*/
public List<ProcessSummaryType> getCapabilities(){
//Sets the getCapabilities request
GetCapabilitiesType getCapabilities = new GetCapabilitiesType();
//Sets the language
AcceptLanguages acceptLanguages = new AcceptLanguages();
acceptLanguages.getLanguage().add(LANG);
acceptLanguages.getLanguage().add("*");
getCapabilities.setAcceptLanguages(acceptLanguages);
//Sets the version
AcceptVersionsType acceptVersions = new AcceptVersionsType();
acceptVersions.getVersion().add("2.0.0");
getCapabilities.setAcceptVersions(acceptVersions);
JAXBElement<GetCapabilitiesType> request = new ObjectFactory().createGetCapabilities(getCapabilities);
//Ask the service
Object capabilities = askRequest(request);
//Retrieve the process list from the Capabilities answer
if(capabilities != null && capabilities instanceof WPSCapabilitiesType){
WPSCapabilitiesType wpsCapabilities = (WPSCapabilitiesType) capabilities;
if(wpsCapabilities.getContents() != null && wpsCapabilities.getContents().getProcessSummary() != null){
return wpsCapabilities.getContents().getProcessSummary();
}
}
return new ArrayList<>();
}
/**
* Return the list of the ProcessOffering contained by the WpsService corresponding to the given CodeType.
* @param processIdentifier Identifier of the processes asked.
* @return The list of the ProcessOffering
*/
public List<ProcessOffering> getProcessOffering(URI processIdentifier){
//Sets the describeProcess request
DescribeProcess describeProcess = new DescribeProcess();
//Sets the language
describeProcess.setLang(LANG);
//Sets the process
CodeType codeType = new CodeType();
codeType.setValue(processIdentifier.toString());
describeProcess.getIdentifier().add(codeType);
//Ask the service
Object answer = askRequest(describeProcess);
//Retrieve the process list from the Capabilities answer
if(answer != null && answer instanceof ProcessOfferings){
ProcessOfferings processOfferings = (ProcessOfferings) answer;
if(processOfferings.getProcessOffering() != null && !processOfferings.getProcessOffering().isEmpty()){
return processOfferings.getProcessOffering();
}
}
return new ArrayList<>();
}
@Override
public DockingPanelParameters getDockingParameters() {
return parameters;
}
@Override
public JComponent getComponent() {
return toolBoxPanel;
}
/**
* Close the given EditorDockable if it was add by the ToolBox (contained by openEditorList).
* @param ed EditorDockable to close.
*/
public void killEditor(EditorDockable ed) {
if(openEditorList.contains(ed)){
dockingManager.removeDockingPanel(ed.getDockingParameters().getName());
}
openEditorList.remove(ed);
}
/**
* Open a file browser to find a local script folder and add it.
* Used in an EvenHandler in view.ui.ToolBoxPanel
*/
public void addNewLocalSource(){
OpenFolderPanel openFolderPanel = new OpenFolderPanel("ToolBox.AddSource", I18N.tr("Add a source"));
openFolderPanel.getFileChooser();
openFolderPanel.loadState();
openFolderPanel.setAcceptAllFileFilterUsed(false);
//Wait the window answer and if the user validate set and run the export thread.
if(UIFactory.showDialog(openFolderPanel)){
addLocalSource(openFolderPanel.getSelectedFile().toURI());
openFolderPanel.saveState();
}
}
/**
* Open a file browser to find a local script folder and add it.
* Used in an EvenHandler in view.ui.ToolBoxPanel
*/
public void addNewLocalScript(){
OpenFilePanel openFilePanel = new OpenFilePanel("ToolBox.AddSource", I18N.tr("Add a source"));
openFilePanel.getFileChooser();
openFilePanel.loadState();
openFilePanel.setAcceptAllFileFilterUsed(false);
openFilePanel.addFilter("groovy", "Groovy script");
openFilePanel.setCurrentFilter(0);
//Wait the window answer and if the user validate set and run the export thread.
if(UIFactory.showDialog(openFilePanel)){
for(File file : openFilePanel.getSelectedFiles()) {
addLocalSource(file.toURI());
openFilePanel.saveState();
}
}
}
/**
* Adds a folder as a local script source.
* @param uri Folder URI where the script are located.
*/
public void addLocalSource(URI uri){
addLocalSource(uri, null, true, new File(uri).getName());
}
/**
* Adds a folder as a local script source.
* @param uri Folder URI where the script are located.
* @param iconName Name of the icon to use for this node.
*/
private void addLocalSource(URI uri, String[] iconName, boolean isDefaultScript, String nodePath){
File file = new File(uri);
if(file.isFile()){
List<ProcessIdentifier> piList = wpsServer.addProcess(file);
Map<ProcessMetadata.INTERNAL_METADATA, Object> metadataMap = new HashMap<>();
metadataMap.put(ProcessMetadata.INTERNAL_METADATA.IS_REMOVABLE, isDefaultScript);
metadataMap.put(ProcessMetadata.INTERNAL_METADATA.NODE_PATH, nodePath);
metadataMap.put(ProcessMetadata.INTERNAL_METADATA.ICON_ARRAY, iconName);
addProcessMetadata(uri, metadataMap);
processUriPath.put(piList.get(0).getProcessDescriptionType().getIdentifier().getValue(), new File(uri).getAbsolutePath());
}
//If the folder doesn't contains only folders, add it
else if(file.isDirectory()){
boolean isFolderAdd = false;
for(File f : file.listFiles()){
if(f.isFile()){
if(!isFolderAdd){
toolBoxPanel.addFolder(file.toURI(), file.getParentFile().toURI());
isFolderAdd = true;
}
wpsServer.addProcess(f);
Map<ProcessMetadata.INTERNAL_METADATA, Object> metadataMap = new HashMap<>();
metadataMap.put(ProcessMetadata.INTERNAL_METADATA.IS_REMOVABLE, isDefaultScript);
metadataMap.put(ProcessMetadata.INTERNAL_METADATA.NODE_PATH, nodePath);
metadataMap.put(ProcessMetadata.INTERNAL_METADATA.ICON_ARRAY, iconName);
addProcessMetadata(uri, metadataMap);
}
}
}
refreshAvailableScripts();
}
/**
* Link the different input/output together like the JDBCTable with its JDBCColumns,
* the JDBCColumns with its JDBCValues ...
* @param p Process to link.
*/
private void link(ProcessDescriptionType p){
//Link the JDBCColumn with its JDBCTable
for(InputDescriptionType i : p.getInput()){
if(i.getDataDescription().getValue() instanceof JDBCColumn){
JDBCColumn jdbcColumn = (JDBCColumn)i.getDataDescription().getValue();
for(InputDescriptionType jdbcTable : p.getInput()){
if(jdbcTable.getIdentifier().getValue().equals(jdbcColumn.getJDBCTableIdentifier().toString())){
((JDBCTable)jdbcTable.getDataDescription().getValue()).addJDBCColumn(jdbcColumn);
}
}
}
}
//Link the JDBCValue with its JDBCColumn and its JDBCTable
for(InputDescriptionType i : p.getInput()){
if(i.getDataDescription().getValue() instanceof JDBCValue){
JDBCValue jdbcValue = (JDBCValue)i.getDataDescription().getValue();
for(InputDescriptionType input : p.getInput()){
if(input.getIdentifier().getValue().equals(jdbcValue.getJDBCColumnIdentifier().toString())){
JDBCColumn jdbcColumn = (JDBCColumn)input.getDataDescription().getValue();
jdbcColumn.addJDBCValue(jdbcValue);
jdbcValue.setJDBCTableIdentifier(jdbcColumn.getJDBCTableIdentifier());
}
}
}
}
//Link the JDBCColumn with its JDBCTable
for(OutputDescriptionType o : p.getOutput()){
if(o.getDataDescription().getValue() instanceof JDBCColumn){
JDBCColumn jdbcColumn = (JDBCColumn)o.getDataDescription().getValue();
for(OutputDescriptionType jdbcTable : p.getOutput()){
if(jdbcTable.getIdentifier().getValue().equals(jdbcColumn.getJDBCTableIdentifier().toString())){
((JDBCTable)jdbcTable.getDataDescription().getValue()).addJDBCColumn(jdbcColumn);
}
}
}
}
//Link the JDBCValue with its JDBCColumn and its JDBCTable
for(OutputDescriptionType o : p.getOutput()){
if(o.getDataDescription().getValue() instanceof JDBCValue){
JDBCValue jdbcValue = (JDBCValue)o.getDataDescription().getValue();
for(OutputDescriptionType output : p.getOutput()){
if(output.getIdentifier().getValue().equals(jdbcValue.getJDBCColumnIdentifier().toString())){
JDBCColumn jdbcColumn = (JDBCColumn)output.getDataDescription().getValue();
jdbcColumn.addJDBCValue(jdbcValue);
jdbcValue.setJDBCTableIdentifier(jdbcColumn.getJDBCTableIdentifier());
}
}
}
}
}
/**
* Open the UI of the process selected in the ToolBoxPanel.
*/
public void openProcess(){
this.onDataManagerChange();
openProcess(toolBoxPanel.getSelectedNode().getIdentifier(),
new HashMap<URI, Object>(), ProcessExecutionType.STANDARD);
}
/**
* Once the process(es) is(are) configured and run, add it(them) to the LogEditor and removes the ProcessEditor (close it).
* @param pe ProcessEditor to close.
* @param job Job to validate.
*/
public void validateInstance(ProcessEditor pe, Job job){
this.jobMap.put(job.getId(), job);
//Adds the process information to the log managing classes (LogEditableElement, LogEditor and Job)
ProcessEditableElement processEditableElement = (ProcessEditableElement) pe.getEditableElement();
le.addNewLog(processEditableElement.getProcess(), job);
job.addPropertyChangeListener(this);
job.addPropertyChangeListener(lee);
}
/**
* Returns a deep copy of the given process.
* The copy is generated by requesting to the WPS server the process.
*
* @param processIdentifier Identifier of the process to copy.
* @return A deep copy of the process.
*/
public ProcessDescriptionType getProcessCopy(URI processIdentifier){
List<ProcessOffering> processOfferingList = getProcessOffering(processIdentifier);
if(!processOfferingList.isEmpty()){
ProcessDescriptionType processCopy = processOfferingList.get(0).getProcess();
link(processCopy);
return processCopy;
}
return null;
}
/**
* Verify if the given process is a well formed script.
* @param processIdentifier Identifier of the process.
* @return True if the file is well formed, false otherwise.
*/
public boolean checkProcess(URI processIdentifier){
String processPath = processUriPath.get(processIdentifier.toString());
if(processPath == null){
return true;
}
else if(!new File(processPath).exists()) {
this.removeProcess(processIdentifier);
processUriPath.remove(processIdentifier.toString());
return false;
}
else {
return true;
}
}
/**
* Remove the selected process in the tree.
*/
public void removeProcess(URI processIdentifier){
processUriPath.remove(processIdentifier.toString());
wpsServer.removeProcess(processIdentifier);
}
/**
* Returns the DataUIManager.
* @return The DataUIManager.
*/
public DataUIManager getDataUIManager(){
return dataUIManager;
}
private void fireJobStateEvent(StatusInfo statusInfo){
List<WpsJobStateListener> list = new ArrayList<>();
list.addAll(jobStateListenerList);
for(WpsJobStateListener listener : list){
if(listener.getJobID() != null && listener.getJobID().equals(UUID.fromString(statusInfo.getJobID()))){
switch(statusInfo.getStatus()){
case "ACCEPTED":
listener.onJobAccepted();
break;
case "RUNNING":
listener.onJobRunning();
break;
case "SUCCEEDED":
listener.onJobSuccess();
break;
case "FAILED":
listener.onJobFailed();
break;
}
}
}
}
/*******************************/
/** InternalWpsClient methods **/
/*******************************/
/** Internal WPS methods **/
@Override
public UUID executeInternalProcess(URI processIdentifier, Map<URI, Object> dataMap, WpsJobStateListener listener) {
ProcessDescriptionType process = getInternalProcess(processIdentifier);
//If there is a listener for this process execution, register it
if(listener != null) {
addJobListener(listener);
}
//Call the process execution and get the StatusInfo object answer
StatusInfo statusInfo = executeProcess(processIdentifier, dataMap);
//Get the Server job id and build a client side job
UUID jobID = UUID.fromString(statusInfo.getJobID());
Job job = new Job(jobID, process);
job.addPropertyChangeListener(this);
job.setStartTime(System.currentTimeMillis());
job.setStatus(statusInfo);
this.jobMap.put(jobID, job);
//Returns the job id
return jobID;
}
@Override
public ProcessDescriptionType getInternalProcess(URI processIdentifier) {
List<ProcessOffering> processOfferingList = getProcessOffering(processIdentifier);
if(processOfferingList.isEmpty()) {
return null;
}
else{
return processOfferingList.get(0).getProcess();
}
}
@Override
public void openProcess(URI processIdentifier,
Map<URI, Object> defaultValuesMap,
ProcessExecutionType type) {
//Get the list of ProcessOffering
List<ProcessOffering> listProcess = getProcessOffering(processIdentifier);
if(listProcess == null || listProcess.isEmpty()){
LoggerFactory.getLogger(WpsClient.class).warn(I18N.tr("Unable to retrieve the process {0}.",
processIdentifier.toString()));
return;
}
//Get the process
ProcessDescriptionType process = listProcess.get(0).getProcess();
for(MetadataType metadata : process.getMetadata()){
if(metadata.getRole().equalsIgnoreCase(ProcessMetadata.CONFIGURATION_MODE_NAME) &&
metadata.getTitle().equalsIgnoreCase(ProcessMetadata.CONFIGURATION_MODE.STANDARD_MODE_ONLY.name()) &&
type.equals(ProcessExecutionType.BASH)){
type = ProcessExecutionType.STANDARD;
defaultValuesMap = new HashMap<>();
}
}
//Link the JDBCTable with the JDBCColumn and the JDBCColumn with the JDBCValue
link(process);
//Open the ProcessEditor
Map<URI, Object> joinedDefaultValuesMap = new HashMap<>();
joinedDefaultValuesMap.putAll(dataUIManager.getInputDefaultValues(listProcess.get(0).getProcess()));
if(defaultValuesMap != null) {
joinedDefaultValuesMap.putAll(defaultValuesMap);
}
ProcessOffering processOffering = listProcess.get(0);
URI processUri = URI.create(processOffering.getProcess().getIdentifier().getValue());
ProcessEditableElement processEditableElement = new ProcessEditableElement(processOffering , processUri, joinedDefaultValuesMap);
processEditableElement.setProcessExecutionType(type);
editorManager.openEditable(processEditableElement);
}
/** Other methods **/
@Override
public void refreshAvailableScripts(){
isRefreshScheduled = false;
//Removes all the processes from the UI of the toolbox
toolBoxPanel.cleanAll();
//Adds all the available processes
for(ProcessSummaryType processSummary : getCapabilities()) {
URI uri = URI.create(processSummary.getIdentifier().getValue());
toolBoxPanel.addProcess(processSummary, processMetadataMap.get(uri));
}
}
@Override
public List<String> getTableList(List<DataType> dataTypes, List<DataType> excludedTypes) {
List<String> list = new ArrayList<>();
String defaultSchema = (isH2)?"PUBLIC":"public";
//Read the tableList to get the desired tables
for(Map<JdbcProperties, String> map : tableList){
if(map.containsKey(JdbcProperties.TABLE_LOCATION)) {
TableLocation tablelocation = TableLocation.parse(map.get(JdbcProperties.TABLE_LOCATION), isH2);
boolean isValid = false;
if((dataTypes == null || dataTypes.isEmpty()) && (excludedTypes == null || excludedTypes.isEmpty())){
isValid = true;
}
else if(map.containsKey(JdbcProperties.COLUMN_TYPE)) {
try (Connection connection = dataManager.getDataSource().getConnection()) {
Map<String, Integer> types = SFSUtilities.getGeometryTypes(connection, tablelocation);
for (Map.Entry<String, Integer> entry : types.entrySet()) {
if(dataTypes != null) {
for (DataType dataType : dataTypes) {
if (dataType.isDataTypeEquivalent(DataType.getGeometryType(entry.getValue()))) {
isValid = true;
}
}
}
if(excludedTypes != null) {
for (DataType dataType : excludedTypes) {
if (dataType.isDataTypeEquivalent(DataType.getGeometryType(entry.getValue()))) {
isValid = false;
}
}
}
}
} catch (SQLException e) {
LOGGER.error(I18N.tr("Unable to get the connection.\nCause : {0}.",
e.getMessage()));
}
}
else {
try (Connection connection = dataManager.getDataSource().getConnection()) {
//Get the metadata of the table
ResultSet rs = connection.createStatement().executeQuery(String.format("select * from %s limit 1", tablelocation.getTable()));
ResultSetMetaData metaData = rs.getMetaData();
//For each column, get its DataType
for(int columnId = 1; columnId <= metaData.getColumnCount(); ++columnId) {
String columnTypeName = metaData.getColumnTypeName(columnId);
if(!columnTypeName.equalsIgnoreCase("geometry")) {
DataType dataType = DataType.getDataType(metaData.getColumnType(columnId));
//Tests if the DataType is compatible with the acceptedTypes and excludedTypes.
if(dataTypes != null && !dataTypes.isEmpty()) {
for (DataType acceptedType : dataTypes) {
if (dataType.equals(acceptedType)) {
isValid = true;
}
}
}
else{
isValid = true;
}
if(excludedTypes != null && !excludedTypes.isEmpty()) {
for (DataType excludedType : excludedTypes) {
if (excludedType.equals(dataType)) {
isValid = false;
}
}
}
}
}
} catch (SQLException e) {
LOGGER.error(I18N.tr("Unable to get the connection.\nCause : {0}.",
e.getMessage()));
}
}
if (isValid) {
//If the table is in the default schema, just add its name
if (tablelocation.getSchema(defaultSchema).equals(defaultSchema)) {
list.add(tablelocation.getTable());
}
//If not, add the schema name '.' the table name (SCHEMA.TABLE)
else {
list.add(tablelocation.getSchema() + "." + tablelocation.getTable());
}
}
}
}
return list;
}
@Override
public List<Map<JdbcProperties, Object>> getColumnInformation(String tableName){
List<Map<JdbcProperties, Object>> mapList = new ArrayList<>();
try(Connection connection = dataManager.getDataSource().getConnection()) {
//Get the list of the columns of a table
ResultSet rs1 = connection.createStatement().executeQuery(String.format("select * from %s limit 1", tableName));
ResultSetMetaData metaData = rs1.getMetaData();
//If the column isn't a geometry, add it to the map
for(int i=1; i<=metaData.getColumnCount(); i++){
if(!metaData.getColumnTypeName(i).equalsIgnoreCase("GEOMETRY")){
Map<JdbcProperties, Object> map = new HashMap<>();
map.put(JdbcProperties.COLUMN_NAME, metaData.getColumnLabel(i));
map.put(JdbcProperties.COLUMN_TYPE, metaData.getColumnTypeName(i));
map.put(JdbcProperties.COLUMN_SRID, 0);
map.put(JdbcProperties.COLUMN_DIMENSION, 0);
mapList.add(map);
}
}
//Once the non geometric columns are get, do the same with the geometric one.
Statement statement = connection.createStatement();
String query = "SELECT * FROM GEOMETRY_COLUMNS WHERE F_TABLE_NAME LIKE '" +
TableLocation.parse(tableName).getTable() + "';";
ResultSet rs = statement.executeQuery(query);
while (rs.next()) {
Map<JdbcProperties, Object> map = new HashMap<>();
//Case of H2 database
if(isH2) {
map.put(JdbcProperties.COLUMN_NAME, rs.getString(4));
map.put(JdbcProperties.COLUMN_TYPE, SFSUtilities.getGeometryTypeNameFromCode(rs.getInt(6)));
map.put(JdbcProperties.COLUMN_SRID, rs.getInt(8));
map.put(JdbcProperties.COLUMN_DIMENSION, rs.getInt(7));
}
//Other case
else{
map.put(JdbcProperties.COLUMN_NAME, rs.getString(4));
map.put(JdbcProperties.COLUMN_TYPE, rs.getString(7));
map.put(JdbcProperties.COLUMN_SRID, rs.getInt(6));
map.put(JdbcProperties.COLUMN_DIMENSION, rs.getInt(5));
}
mapList.add(map);
}
} catch (SQLException e) {
LOGGER.error(I18N.tr("Unable to get the column INFORMATION of the table {0} information.\nCause : {1}.",
tableName, e.getMessage()));
}
return mapList;
}
@Override
public List<String> getColumnList(String tableName, List<DataType> dataTypes, List<DataType> excludedTypes){
if(dataTypes == null){
dataTypes = new ArrayList<>();
}
if(excludedTypes == null){
excludedTypes = new ArrayList<>();
}
List<String> columnList = new ArrayList<>();
try(Connection connection = dataManager.getDataSource().getConnection()) {
DatabaseMetaData dmd = connection.getMetaData();
TableLocation tablelocation = TableLocation.parse(tableName, isH2);
ResultSet result = dmd.getColumns(tablelocation.getCatalog(), tablelocation.getSchema(),
tablelocation.getTable(), "%");
while(result.next()){
if (!dataTypes.isEmpty()) {
for (DataType dataType : dataTypes) {
String type = result.getObject(6).toString();
if(type.equalsIgnoreCase("GEOMETRY")){
int geomType = SFSUtilities.getGeometryType(connection, tablelocation,
result.getObject(4).toString().toUpperCase());
if (dataType.isDataTypeEquivalent(DataType.getGeometryType(geomType))) {
columnList.add(result.getObject(4).toString());
}
}
else {
if (dataType.equals(DataType.getDataType(result.getObject(6).toString().toUpperCase()))) {
columnList.add(result.getObject(4).toString());
}
}
}
} else if(!excludedTypes.isEmpty()){
boolean accepted = true;
for (DataType dataType : excludedTypes) {
if (dataType.equals(DataType.getDataType(result.getObject(6).toString()))) {
accepted = false;
}
}
if(accepted) {
columnList.add(result.getObject(4).toString());
}
}else{
columnList.add(result.getObject(4).toString());
}
}
} catch (SQLException e) {
LOGGER.error(I18N.tr("Unable to get the table {0} column list.\nCause : {1}.", tableName, e.getMessage()));
}
return columnList;
}
@Override
public List<String> getValueList(String tableName, String columnName) {
List<String> values = new ArrayList<>();
try(Connection connection = dataManager.getDataSource().getConnection()) {
tableName = TableLocation.parse(tableName, isH2).toString();
values.addAll(JDBCUtilities.getUniqueFieldValues(connection,
tableName,
columnName));
} catch (SQLException e) {
LOGGER.error(I18N.tr("Unable to get the column {0}.{1} value list.\nCause : {2}.",
tableName, columnName, e.getMessage()));
}
return values;
}
@Override
public List<String> getSRIDList(){
List<String> sridList = new ArrayList<>();
try(Connection connection = dataManager.getDataSource().getConnection()) {
PreparedStatement statement = connection.prepareStatement("SELECT srid, AUTH_NAME FROM SPATIAL_REF_SYS");
ResultSet resultSet = statement.executeQuery();
while (resultSet.next()) {
sridList.add(resultSet.getString("AUTH_NAME") + ":" +resultSet.getInt("srid"));
}
} catch (SQLException e) {
LOGGER.error(I18N.tr("Error on getting the SRID list.\nCause : {0}.", e.getMessage()));
}
return sridList;
}
@Override
public void addJobListener(WpsJobStateListener listener) {
if(!jobStateListenerList.contains(listener)) {
jobStateListenerList.add(listener);
}
}
@Override
public void removeJobListener(WpsJobStateListener listener) {
jobStateListenerList.remove(listener);
}
@Override
public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
if(propertyChangeEvent.getPropertyName().equals(CANCEL)){
UUID jobID = (UUID)propertyChangeEvent.getNewValue();
StatusInfo statusInfo = this.dismissJob(jobID);
Job job = jobMap.get(jobID);
job.setStatus(statusInfo);
}
if(propertyChangeEvent.getPropertyName().equals(REFRESH_STATUS)){
UUID jobID = (UUID)propertyChangeEvent.getNewValue();
StatusInfo statusInfo = this.getJobStatus(jobID);
Job job = jobMap.get(jobID);
if(job != null) {
job.setStatus(statusInfo);
}
}
if(propertyChangeEvent.getPropertyName().equals(GET_RESULTS)){
UUID jobID = (UUID)propertyChangeEvent.getNewValue();
Job job = jobMap.get(jobID);
Result result = this.getJobResult(jobID);
job.setResult(result);
}
}
@Override
public void onScriptAdd() {
//If no refresh task has been scheduled, creates on an schedule it in two seconds from now
if(!isRefreshScheduled){
isRefreshScheduled = true;
Runnable refreshRunnable = new RefreshRunnable(this);
final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.schedule(refreshRunnable, 2, TimeUnit.SECONDS);
}
}
@Override
public void onScriptRemoved() {
//If no refresh task has been scheduled, creates on an schedule it in two seconds from now
if(!isRefreshScheduled){
isRefreshScheduled = true;
Runnable refreshRunnable = new RefreshRunnable(this);
final ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.schedule(refreshRunnable, 1, TimeUnit.SECONDS);
}
}
/***********************/
/** WpsClient methods **/
/***********************/
/** WPS methods **/
@Override
public StatusInfo getJobStatus(UUID jobID) {
//Build the GetStatus object used to request a job status to the WPS server.
GetStatus getStatus = new GetStatus();
getStatus.setJobID(jobID.toString());
//Launch the request on the server
StatusInfo result = (StatusInfo) askRequest(getStatus);
//Fire a job state event to tells the JobStateListeners that a job state has been updated.
fireJobStateEvent(result);
return result;
}
@Override
public Result getJobResult(UUID jobID) {
jobMap.remove(jobID);
//Build the GetResult object used to request a job result to the WPS server.
GetResult getResult = new GetResult();
getResult.setJobID(jobID.toString());
//Launch the execution on the server
return (Result) askRequest(getResult);
}
@Override
public StatusInfo dismissJob(UUID jobID) {
//Build the Dismiss object used to request the dismiss of a job to the WPS server.
Dismiss dismiss = new Dismiss();
dismiss.setJobID(jobID.toString());
//Launch the execution on the server
StatusInfo result = (StatusInfo) askRequest(dismiss);
//Fire a job state event to tells the JobStateListeners that a job state has been updated.
fireJobStateEvent(result);
return result;
}
@Override
public StatusInfo executeProcess(URI processIdentifier, Map<URI,Object> dataMap) {
//Get the ProcessDescriptionType corresponding to the process identifier
ProcessDescriptionType process = getInternalProcess(processIdentifier);
//Build the ExecuteRequest object
ExecuteRequestType executeRequest = new ExecuteRequestType();
//Sets the identifier of the process to execute
executeRequest.setIdentifier(process.getIdentifier());
//For each entry of the map, test if it is an input or an output and so add it to the execute request
for(Map.Entry<URI, Object> entry : dataMap.entrySet()){
//Sets the inputs
List<DataInputType> inputList = executeRequest.getInput();
//Test if the entry is one of the inputs
for(InputDescriptionType input : process.getInput()){
if(URI.create(input.getIdentifier().getValue()).equals(entry.getKey())){
//Build the data object containing the data on the input
Data data = new Data();
if(entry.getValue() == null) {
data.getContent().add(null);
}
else {
data.getContent().add(entry.getValue().toString());
}
//Build the DataInput object containing the input identifier and the data to process
DataInputType dataInput = new DataInputType();
dataInput.setId(entry.getKey().toString());
dataInput.setData(data);
//Register the DataInput
inputList.add(dataInput);
}
}
//Sets the outputs
List<OutputDefinitionType> outputList = executeRequest.getOutput();
//Test if the entry is one of the outputs
for(OutputDescriptionType output : process.getOutput()){
if(URI.create(output.getIdentifier().getValue()).equals(entry.getKey())){
//Build the OutputDefinition object which contains the processing information for the output
OutputDefinitionType out = new OutputDefinitionType();
out.setId(entry.getKey().toString());
out.setTransmission(DataTransmissionModeType.VALUE);
out.setMimeType("text/plain");
outputList.add(out);
}
}
}
//Launch the execution on the server
JAXBElement<ExecuteRequestType> jaxbElement = new ObjectFactory().createExecute(executeRequest);
//Return the StatusInfo answer of the server
return (StatusInfo) askRequest(jaxbElement);
}
/*********************/
/** Utility classes **/
/*********************/
/**
* Class implementing the runnable interface. It is used to create a task which aim is to refresh in the given
* WpsClientImpl the list of processes.
*/
private class RefreshRunnable implements Runnable{
/** Client to refresh */
private WpsClientImpl wpsClient;
RefreshRunnable(WpsClientImpl wpsClient){
this.wpsClient = wpsClient;
}
@Override
public void run() {
wpsClient.refreshAvailableScripts();
}
}
/**
* Test the database an returns if it allows the wps service to run more than one process at the same time.
* @return True if more than one process can be run at the same time, false otherwise.
*/
private boolean testDBForMultiProcess(){
try(Connection connection = dataManager.getDataSource().getConnection()) {
if(dataManager != null){
isH2 = JDBCUtilities.isH2DataBase(connection.getMetaData());
if(isH2){
wpsServer.setDatabase(WpsServer.Database.H2GIS);
}
else{
wpsServer.setDatabase(WpsServer.Database.POSTGIS);
}
if(isH2) {
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery("select VALUE from INFORMATION_SCHEMA.SETTINGS AS s where NAME = 'MVCC';");
result.next();
if (!result.getString(1).equals("TRUE")) {
return false;
}
result = statement.executeQuery("select VALUE from INFORMATION_SCHEMA.SETTINGS AS s where NAME = 'MULTI_THREADED';");
result.next();
if (!result.getString(1).equals("1")) {
return false;
}
}
return true;
}
} catch (SQLException e) {
LOGGER.error(e.getMessage());
}
return false;
}
/*******************************************************/
/** Methods for the listening of the database update. **/
/*******************************************************/
/**
* Method called when a change happens in the DataManager (i.e. a table suppression, a table add ...)
*/
public void onDataManagerChange() {
//If not actually doing a refresh, do it.
if(!awaitingRefresh.getAndSet(true)) {
ReadDataManagerOnSwingThread worker = new ReadDataManagerOnSwingThread(this);
ExecutorService executorService = getExecutorService();
if(executorService != null){
executorService.execute(worker);
}
else{
worker.run();
}
} else {
updateWhileAwaitingRefresh = true;
}
}
@Override
public void progressionUpdate(StateEvent state) {
if (state.isUpdateDatabaseStructure()) {
onDataManagerChange();
}
}
/**
* Read the table list in the database
*/
private void readDatabase() {
List<Map<JdbcProperties, String>> newTables = new ArrayList<>();
try (Connection connection = dataManager.getDataSource().getConnection()) {
final String defaultCatalog = connection.getCatalog();
String defaultSchema = "PUBLIC";
try {
if (connection.getSchema() != null) {
defaultSchema = connection.getSchema();
}
} catch (AbstractMethodError | Exception ex) {
// Driver has been compiled with JAVA 6, or is not implemented
}
// Fetch Geometry tables
Map<String,String> tableGeometry = new HashMap<>();
try(Statement st = connection.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM "+defaultSchema+".geometry_columns")) {
while(rs.next()) {
tableGeometry.put(new TableLocation(rs.getString("F_TABLE_CATALOG"),
rs.getString("F_TABLE_SCHEMA"), rs.getString("F_TABLE_NAME")).toString(), rs.getString("TYPE"));
}
} catch (SQLException ex) {
LOGGER.warn(I18N.tr("Geometry columns information of tables are not available.", ex));
}
// Fetch all tables
try(ResultSet rs = connection.getMetaData().getTables(null, null, null, SHOWN_TABLE_TYPES)) {
while(rs.next()) {
Map<JdbcProperties, String> tableAttr = new HashMap<>();
TableLocation location = new TableLocation(rs);
if(location.getCatalog().isEmpty()) {
// PostGIS return empty catalog on metadata
location = new TableLocation(defaultCatalog, location.getSchema(), location.getTable());
}
// Make Label
StringBuilder label = new StringBuilder(addQuotesIfNecessary(location.getTable()));
if(!location.getSchema().isEmpty() && !location.getSchema().equalsIgnoreCase(defaultSchema)) {
label.insert(0, ".");
label.insert(0, addQuotesIfNecessary(location.getSchema()));
}
if(!location.getCatalog().isEmpty() && !location.getCatalog().equalsIgnoreCase(defaultCatalog)) {
label.insert(0, ".");
label.insert(0, addQuotesIfNecessary(location.getCatalog()));
}
// Shortcut location for H2 database
TableLocation shortLocation;
if(isH2) {
shortLocation = new TableLocation("",
location.getSchema().equals(defaultSchema) ? "" : location.getSchema(),
location.getTable());
} else {
shortLocation = new TableLocation(location.getCatalog().equalsIgnoreCase(defaultCatalog) ?
"" : location.getCatalog(),
location.getCatalog().equalsIgnoreCase(defaultCatalog) &&
location.getSchema().equalsIgnoreCase(defaultSchema) ? "" : location.getSchema(),
location.getTable());
}
tableAttr.put(JdbcProperties.TABLE_LOCATION, shortLocation.toString(isH2));
tableAttr.put(JdbcProperties.TABLE_LABEL, label.toString());
String type = tableGeometry.get(location.toString());
if(type != null) {
tableAttr.put(JdbcProperties.COLUMN_TYPE, type);
}
newTables.add(tableAttr);
}
}
tableList.clear();
tableList.addAll(newTables);
} catch (SQLException ex) {
LOGGER.error(I18N.tr("Cannot read the table list", ex));
}
}
/**
* If needed, quote the table location part
* @param tableLocationPart Table location part to quote.
* @return Quoted table location part.
*/
private static String addQuotesIfNecessary(String tableLocationPart) {
if(tableLocationPart.contains(".")) {
return "\""+tableLocationPart+"\"";
} else {
return tableLocationPart;
}
}
/**
* Refresh the list
*/
private static class ReadDataManagerOnSwingThread implements Runnable {
private WpsClientImpl wpsClient;
private ReadDataManagerOnSwingThread(WpsClientImpl wpsClient) {
this.wpsClient = wpsClient;
}
@Override
public void run() {
wpsClient.readDatabase();
//Refresh the list on the swing thread
wpsClient.awaitingRefresh.set(false);
// An update occurs during fetching tables
if(wpsClient.updateWhileAwaitingRefresh) {
wpsClient.updateWhileAwaitingRefresh = false;
wpsClient.onDataManagerChange();
}
}
}
@Override
public void addProcessMetadata(URI uri, Map<ProcessMetadata.INTERNAL_METADATA, Object> map) {
processMetadataMap.put(uri, map);
}
@Override
public void removeProcessMetadata(URI uri) {
processMetadataMap.remove(uri);
}
}