/* * Copyright (c) 2015 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.io.wfs; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.core.runtime.IProgressMonitor; import eu.esdihumboldt.hale.common.core.io.IOProviderConfigurationException; import eu.esdihumboldt.hale.common.core.io.ProgressIndicator; import eu.esdihumboldt.hale.common.core.io.Value; import eu.esdihumboldt.hale.common.core.io.impl.SubtaskProgressIndicator; import eu.esdihumboldt.hale.common.core.io.report.IOReport; import eu.esdihumboldt.hale.common.core.io.report.IOReporter; import eu.esdihumboldt.hale.common.core.io.report.impl.IOMessageImpl; import eu.esdihumboldt.hale.common.instance.graph.reference.ReferenceGraph; import eu.esdihumboldt.hale.common.instance.graph.reference.impl.XMLInspector; import eu.esdihumboldt.hale.common.instance.io.impl.AbstractGeoInstanceWriter; import eu.esdihumboldt.hale.common.instance.model.InstanceCollection; /** * WFS writer that publishes partitioned data sets * * @author Simon Templer */ public class PartitioningWFSWriter extends AbstractGeoInstanceWriter implements WFSWriter, WFSConstants { /** * Name of the parameter defining the instance threshold. */ public static final String PARAM_INSTANCES_THRESHOLD = "instancesPerRequest"; /** * Default value for the instance threshold. */ public static final int DEFAULT_INSTANCES_THRESHOLD = 15000; @Override protected IOReport execute(final ProgressIndicator progress, final IOReporter reporter) throws IOProviderConfigurationException, IOException { progress.begin("Upload to WFS-T", IProgressMonitor.UNKNOWN); try { progress.setCurrentTask("Partitioning data"); // create a reference graph ReferenceGraph<String> rg = new ReferenceGraph<String>(new XMLInspector(), getInstances()); // partition the graph int threshold = getParameter(PARAM_INSTANCES_THRESHOLD).as(Integer.class, DEFAULT_INSTANCES_THRESHOLD); Iterator<InstanceCollection> parts = rg.partition(threshold); int partCount = 0; final AtomicBoolean failed = new AtomicBoolean(); if (parts.hasNext()) { ExecutorService requestThread = Executors.newSingleThreadExecutor(); while (parts.hasNext() && !progress.isCanceled()) { partCount++; SubtaskProgressIndicator partitionProgress = new SubtaskProgressIndicator( progress); // only used for first partitioning if (partCount == 1) partitionProgress.begin("Assembling part " + partCount, ProgressIndicator.UNKNOWN); final InstanceCollection part = parts.next(); // not thread // safe if (partCount == 1) partitionProgress.end(); progress.setCurrentTask("Upload part " + partCount + ((part.hasSize()) ? (" (" + part.size() + " instances)") : (""))); final int currentPart = partCount; requestThread.submit(new Runnable() { @Override public void run() { try { IOReport report = uploadInstances(part, reporter, new SubtaskProgressIndicator(progress)); if (!report.isSuccess()) { failed.set(true); reporter.error(new IOMessageImpl("Upload of part " + currentPart + " - " + report.getSummary(), null)); } else { reporter.info(new IOMessageImpl("Upload of part " + currentPart + " - " + report.getSummary(), null)); } } catch (Exception e) { failed.set(true); reporter.error(new IOMessageImpl("Upload of part " + currentPart + " failed", e)); } } }); } // wait for requests completion requestThread.shutdown(); if (!requestThread.awaitTermination(24, TimeUnit.HOURS)) { reporter.error(new IOMessageImpl( "Timeout reached waiting for completion of WFS requests", null)); } reporter.setSuccess(!failed.get() && reporter.getErrors().isEmpty()); if (!reporter.isSuccess()) { reporter.setSummary("Errors during upload to WFS-T, please see the report."); } else { reporter.setSummary("Successfully uploaded data via WFS-T"); } } else { reporter.setSuccess(false); reporter.setSummary("Partitioning yielded no instances to upload"); } } catch (Exception e) { reporter.error(new IOMessageImpl("Error during attempt to upload to WFS-T", e)); reporter.setSuccess(false); } finally { progress.end(); } return reporter; } /** * Upload instances via the WFS-T interface. * * @param instances the instances to upload * @param reporter the reporter * @param progress the progress indicator * @return the * @throws IOProviderConfigurationException - if the I/O provider was not * configured properly * @throws IOException - if an I/O operation fails */ protected IOReport uploadInstances(final InstanceCollection instances, final IOReporter reporter, final ProgressIndicator progress) throws IOProviderConfigurationException, IOException { SimpleWFSWriter writer = new SimpleWFSWriter() { @Override public IOReporter createReporter() { // reuse parent reporter return reporter; } }; // copy configuration Map<String, Value> config = new HashMap<>(); this.storeConfiguration(config); writer.loadConfiguration(config); // apply configuration not based on parameters writer.setServiceProvider(getServiceProvider()); writer.setTargetSchema(getTargetSchema()); writer.setTarget(getTarget()); writer.setInstances(instances); return writer.execute(progress); } @Override public boolean isPassthrough() { return false; } @Override public boolean isCancelable() { return true; } @Override public void setWFSVersion(WFSVersion version) { setParameter(PARAM_WFS_VERSION, Value.of(version.versionString)); } @Override public WFSVersion getWFSVersion() { String versionString = getParameter(PARAM_WFS_VERSION).as(String.class); if (versionString == null) { return null; } else return WFSVersion.fromString(versionString, null); } @Override protected String getDefaultTypeName() { return "WFS-T"; } }