/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.constellation.webservice.map.component;
import static org.geotoolkit.utility.parameter.ParametersExt.createGroup;
import static org.geotoolkit.utility.parameter.ParametersExt.getOrCreateGroup;
import static org.geotoolkit.utility.parameter.ParametersExt.getOrCreateValue;
import static org.geotoolkit.style.StyleConstants.DEFAULT_LINE_SYMBOLIZER;
import static org.geotoolkit.style.StyleConstants.DEFAULT_POINT_SYMBOLIZER;
import static org.geotoolkit.style.StyleConstants.DEFAULT_POLYGON_SYMBOLIZER;
import static org.geotoolkit.style.StyleConstants.DEFAULT_RASTER_SYMBOLIZER;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.sis.util.logging.Logging;
import org.constellation.admin.SpringHelper;
import org.constellation.business.IProviderBusiness;
import org.constellation.business.IStyleBusiness;
import org.constellation.configuration.ConfigDirectory;
import org.constellation.configuration.ConfigurationException;
import org.constellation.configuration.CstlConfigurationRuntimeException;
import org.constellation.database.api.jooq.tables.pojos.Provider;
import org.constellation.process.ConstellationProcessFactory;
import org.constellation.process.provider.CreateProviderDescriptor;
import org.constellation.provider.DataProvider;
import org.constellation.provider.DataProviders;
import org.constellation.provider.ProviderFactory;
import org.constellation.provider.StyleProvider;
import org.constellation.provider.StyleProviders;
import org.geotoolkit.factory.FactoryFinder;
import org.geotoolkit.process.ProcessDescriptor;
import org.geotoolkit.process.ProcessException;
import org.geotoolkit.process.ProcessFinder;
import org.geotoolkit.style.DefaultExternalGraphic;
import org.geotoolkit.style.DefaultGraphic;
import org.geotoolkit.style.DefaultOnlineResource;
import org.geotoolkit.style.DefaultPointSymbolizer;
import org.geotoolkit.style.MutableStyle;
import org.geotoolkit.style.MutableStyleFactory;
import org.geotoolkit.util.FileUtilities;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.style.GraphicalSymbol;
import org.opengis.util.NoSuchIdentifierException;
import org.springframework.context.annotation.DependsOn;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
/**
* Specific setup for map service
*
* @author Guilhem Legal (Geomatys)
* @author Alexis Manin (Geomatys)
* @author Cédric Briançon (Geomatys)
*/
@Named
@DependsOn("database-initer")
public class SetupBusiness {
private static final Logger LOGGER = Logging.getLogger("org.constellation.webservice.map.component");
private static final String DEFAULT_RESOURCES = "/org/constellation/map/setup.zip";
@Inject
private IStyleBusiness styleBusiness;
@Inject
private IProviderBusiness providerBusiness;
@PostConstruct
public void contextInitialized() {
LOGGER.log(Level.INFO, "=== Initialize Application ===");
try {
// Try to load postgresql driver for further use
Class.forName("org.postgresql.ds.PGSimpleDataSource");
LOGGER.log(Level.INFO, "postgresql loading success!");
} catch (ClassNotFoundException ex) {
LOGGER.log(Level.INFO, ex.getLocalizedMessage(), ex);
}
SpringHelper.executeInTransaction(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus arg0) {
WithDefaultResources defaultResourcesDeployed = deployDefaultResources(ConfigDirectory.getDataDirectory().toPath());
LOGGER.log(Level.INFO, "initializing default styles ...");
defaultResourcesDeployed.initializeDefaultStyles();
LOGGER.log(Level.INFO, "initializing temporary styles ...");
defaultResourcesDeployed.initializeDefaultTempStyles();
LOGGER.log(Level.INFO, "initializing vector data ...");
defaultResourcesDeployed.initializeDefaultVectorData();
LOGGER.log(Level.INFO, "initializing raster data ...");
defaultResourcesDeployed.initializeDefaultRasterData();
LOGGER.log(Level.INFO, "initializing properties ...");
defaultResourcesDeployed.initializeDefaultProperties();
}
});
}
/**
* Invoked when the module needs to be shutdown.
*/
@PreDestroy
public void contextDestroyed() {
DataProviders.getInstance().dispose();
StyleProviders.getInstance().dispose();
}
public static Path pathTransform(final Path dst, final Path zipath) {
Path ret = dst;
for (final Path component : zipath)
ret = ret.resolve(component.getFileName().toString());
return ret;
}
public WithDefaultResources deployDefaultResources(final Path path) {
Path setupZip = new File(ConfigDirectory.getDataDirectory(), "setup.zip").toPath();
try (InputStream in = SetupBusiness.class.getResourceAsStream(DEFAULT_RESOURCES)) {
LOGGER.info("Found " + DEFAULT_RESOURCES);
Files.copy(in, setupZip, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
throw new CstlConfigurationRuntimeException(e);
}
try (FileSystem fs = FileSystems.newFileSystem(setupZip, null)) {
final Path root = fs.getPath("/");
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
Path dst = pathTransform(path, file);
Files.copy(file, dst, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) throws IOException {
FileVisitResult result = FileVisitResult.CONTINUE;
Path dst = pathTransform(path, dir);
Files.createDirectories(dst);
return result;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return super.postVisitDirectory(dir, exc);
}
});
} catch (IOException e) {
throw new CstlConfigurationRuntimeException(e);
}
return new WithDefaultResources();
}
private class WithDefaultResources {
/**
* Initialize default styles for generic data.
*/
private void initializeDefaultStyles() {
// Create default SLD provider containing default styles.
StyleProvider provider = StyleProviders.getInstance().getProvider("sld");
final String sldPath = ConfigDirectory.getStyleDirectory().getPath();
if (provider == null) {
// Acquire SLD provider service instance.
ProviderFactory sldService = null;
for (final ProviderFactory service : StyleProviders.getInstance().getFactories()) {
if (service.getName().equals("sld")) {
sldService = service;
break;
}
}
if (sldService == null) {
LOGGER.log(Level.WARNING, "SLD provider service not found.");
return;
}
// Prepare create provider process inputs.
final ParameterDescriptorGroup sourceDesc = sldService.getProviderDescriptor();
final ParameterValueGroup source = sourceDesc.createValue();
source.parameter("id").setValue("sld");
source.parameter("providerType").setValue("sld");
source.groups("sldFolder").get(0).parameter("path").setValue(sldPath);
// Create SLD provider.
try {
final ProcessDescriptor desc = ProcessFinder.getProcessDescriptor(ConstellationProcessFactory.NAME,
CreateProviderDescriptor.NAME);
final ParameterValueGroup inputs = desc.getInputDescriptor().createValue();
inputs.parameter(CreateProviderDescriptor.PROVIDER_TYPE_NAME).setValue("sld");
inputs.parameter(CreateProviderDescriptor.SOURCE_NAME).setValue(source);
desc.createProcess(inputs).call();
} catch (NoSuchIdentifierException ignore) { // should never
// happen
LOGGER.log(Level.SEVERE, "4th dimension", ignore);
} catch (ProcessException ex) {
LOGGER.log(Level.WARNING, "An error occurred when creating default SLD provider.", ex);
return;
}
// Retrieve created provider instance.
provider = StyleProviders.getInstance().getProvider("sld");
}
final File dstImages = new File(ConfigDirectory.getDataDirectory(), "images");
try {
if (dstImages.exists()) {
if (!dstImages.isDirectory() || (dstImages.isDirectory() && dstImages.listFiles().length == 0)) {
final File src = FileUtilities.getDirectoryFromResource("org/constellation/map/setup/images");
FileUtilities.copy(src, dstImages);
}
} else {
dstImages.mkdir();
final File src = FileUtilities.getDirectoryFromResource("org/constellation/map/setup/images");
FileUtilities.copy(src, dstImages);
}
} catch (IOException ex) {
LOGGER.log(Level.WARNING, ex.getLocalizedMessage(), ex);
}
// Fill default SLD provider.
final MutableStyleFactory SF = (MutableStyleFactory) FactoryFinder.getStyleFactory(null);
try {
if (provider.get("default-point") == null) {
final MutableStyle style = SF.style(DEFAULT_POINT_SYMBOLIZER);
style.setName("default-point");
style.featureTypeStyles().get(0).rules().get(0).setName("default-point");
styleBusiness.createStyle("sld", style);
}
if (provider.get("default-point-sensor") == null) {
final MutableStyle style = SF.style(DEFAULT_POINT_SYMBOLIZER);
style.setName("default-point-sensor");
style.featureTypeStyles().get(0).rules().get(0).setName("default-point-sensor");
// Marker
final File markerFile = new File(dstImages, "marker_normal.png");
final DefaultOnlineResource onlineResource = new DefaultOnlineResource(markerFile.toURI(), "", "", "marker_normal.png",
null, null);
final DefaultExternalGraphic graphSymb = (DefaultExternalGraphic) SF.externalGraphic(onlineResource, "png", null);
final List<GraphicalSymbol> symbs = new ArrayList<>();
symbs.add(graphSymb);
final DefaultGraphic graphic = (DefaultGraphic) SF.graphic(symbs, null, null, null, null, null);
final DefaultPointSymbolizer pointSymbolizer = (DefaultPointSymbolizer) SF.pointSymbolizer("default-point-sensor", "",
null, null, graphic);
style.featureTypeStyles().get(0).rules().get(0).symbolizers().clear();
style.featureTypeStyles().get(0).rules().get(0).symbolizers().add(pointSymbolizer);
styleBusiness.createStyle("sld", style);
}
if (provider.get("default-point-sensor-selected") == null) {
final MutableStyle style = SF.style(DEFAULT_POINT_SYMBOLIZER);
style.setName("default-point-sensor-selected");
style.featureTypeStyles().get(0).rules().get(0).setName("default-point-sensor-selected");
// Marker
final File markerFile = new File(dstImages, "marker_selected.png");
final DefaultOnlineResource onlineResource = new DefaultOnlineResource(markerFile.toURI(), "", "",
"marker_selected.png", null, null);
final DefaultExternalGraphic graphSymb = (DefaultExternalGraphic) SF.externalGraphic(onlineResource, "png", null);
final List<GraphicalSymbol> symbs = new ArrayList<>();
symbs.add(graphSymb);
final DefaultGraphic graphic = (DefaultGraphic) SF.graphic(symbs, null, null, null, null, null);
final DefaultPointSymbolizer pointSymbolizer = (DefaultPointSymbolizer) SF.pointSymbolizer(
"default-point-sensor-selected", "", null, null, graphic);
style.featureTypeStyles().get(0).rules().get(0).symbolizers().clear();
style.featureTypeStyles().get(0).rules().get(0).symbolizers().add(pointSymbolizer);
styleBusiness.createStyle("sld", style);
}
if (provider.get("default-line") == null) {
final MutableStyle style = SF.style(DEFAULT_LINE_SYMBOLIZER);
style.setName("default-line");
style.featureTypeStyles().get(0).rules().get(0).setName("default-line");
styleBusiness.createStyle("sld", style);
}
if (provider.get("default-polygon") == null) {
final MutableStyle style = SF.style(DEFAULT_POLYGON_SYMBOLIZER);
style.setName("default-polygon");
style.featureTypeStyles().get(0).rules().get(0).setName("default-polygon");
styleBusiness.createStyle("sld", style);
}
if (provider.get("default-raster") == null) {
final MutableStyle style = SF.style(DEFAULT_RASTER_SYMBOLIZER);
style.setName("default-raster");
style.featureTypeStyles().get(0).rules().get(0).setName("default-raster");
styleBusiness.createStyle("sld", style);
}
} catch (ConfigurationException ex) {
LOGGER.log(Level.WARNING, "An error occurred when creating default styles for default SLD provider.", ex);
}
}
/**
* Initialize default temporary styles for generic data.
*/
private void initializeDefaultTempStyles() {
// Create default SLD provider containing default styles.
StyleProvider provider = StyleProviders.getInstance().getProvider("sld_temp");
final String sldPath = ConfigDirectory.getStyleTempDirectory().getPath();
if (provider == null) {
// Acquire SLD provider service instance.
ProviderFactory sldService = null;
for (final ProviderFactory service : StyleProviders.getInstance().getFactories()) {
if (service.getName().equals("sld")) {
sldService = service;
break;
}
}
if (sldService == null) {
LOGGER.log(Level.WARNING, "SLD temp provider service not found.");
return;
}
// Prepare create provider process inputs.
final ParameterDescriptorGroup sourceDesc = sldService.getProviderDescriptor();
final ParameterValueGroup source = sourceDesc.createValue();
source.parameter("id").setValue("sld_temp");
source.parameter("providerType").setValue("sld");
source.groups("sldFolder").get(0).parameter("path").setValue(sldPath);
// Create SLD provider.
try {
final ProcessDescriptor desc = ProcessFinder.getProcessDescriptor(ConstellationProcessFactory.NAME,
CreateProviderDescriptor.NAME);
final ParameterValueGroup inputs = desc.getInputDescriptor().createValue();
inputs.parameter(CreateProviderDescriptor.PROVIDER_TYPE_NAME).setValue("sld");
inputs.parameter(CreateProviderDescriptor.SOURCE_NAME).setValue(source);
desc.createProcess(inputs).call();
} catch (NoSuchIdentifierException ignore) { // should never
// happen
} catch (ProcessException ex) {
LOGGER.log(Level.WARNING, "An error occurred when creating default SLD temp provider.", ex);
}
}
}
/**
* Returns {@code true} if provider exists for given identifier, otherwise return {@code false}
* @param identifier given provider identifier
* @return boolean
*/
private boolean providerExists(final String identifier) {
//check in loaded provider instances
DataProvider provider = DataProviders.getInstance().getProvider(identifier);
if(provider != null) {
return true;
}
//check in database
final Provider prov = providerBusiness.getProvider(identifier);
return prov != null;
}
/**
* Initialize default vector data for displaying generic features in
* data editors.
*/
private void initializeDefaultVectorData() {
final File dst = new File(ConfigDirectory.getDataDirectory(), "shapes");
try {
final String featureStoreStr = "feature-store";
final String shpProvName = "generic_shp";
if (! providerExists(shpProvName)) {
// Acquire SHP provider service instance.
ProviderFactory shpService = null;
for (final ProviderFactory service : DataProviders.getInstance().getFactories()) {
if (service.getName().equals(featureStoreStr)) {
shpService = service;
break;
}
}
if (shpService == null) {
LOGGER.log(Level.WARNING, "SHP provider service not found.");
return;
}
final ParameterValueGroup source = shpService.getProviderDescriptor().createValue();
getOrCreateValue(source, "id").setValue(shpProvName);
getOrCreateValue(source, "load_all").setValue(true);
getOrCreateValue(source, "providerType").setValue("vector");
getOrCreateValue(source, "create_dataset").setValue(false);
final ParameterValueGroup choice = getOrCreateGroup(source, "choice");
final ParameterValueGroup shpConfig = createGroup(choice, "ShapefileParametersFolder");
getOrCreateValue(shpConfig, "url").setValue(dst.toURI().toURL());
getOrCreateValue(shpConfig, "namespace").setValue("no namespace");
// Create SHP Folder provider.
try {
final ProcessDescriptor desc = ProcessFinder.getProcessDescriptor(ConstellationProcessFactory.NAME,
CreateProviderDescriptor.NAME);
final ParameterValueGroup inputs = desc.getInputDescriptor().createValue();
inputs.parameter(CreateProviderDescriptor.PROVIDER_TYPE_NAME).setValue(featureStoreStr);
inputs.parameter(CreateProviderDescriptor.SOURCE_NAME).setValue(source);
desc.createProcess(inputs).call();
} catch (NoSuchIdentifierException ignore) { // should never
// happen
} catch (ProcessException ex) {
LOGGER.log(Level.WARNING, "An error occurred when creating default SHP provider.", ex);
}
}
} catch (IOException ex) {
LOGGER.log(Level.INFO, ex.getLocalizedMessage(), ex);
}
}
/**
* Initialize default raster data for displaying generic features in
* data editors.
*/
private void initializeDefaultRasterData() {
final File dst = new File(ConfigDirectory.getDataDirectory(), "raster");
try {
final String coverageFileStr = "coverage-store";
final String tifProvName = "generic_world_tif";
if (! providerExists(tifProvName)) {
// Acquire TIFF provider service instance.
ProviderFactory tifService = null;
for (final ProviderFactory service : DataProviders.getInstance().getFactories()) {
if (service.getName().equals(coverageFileStr)) {
tifService = service;
break;
}
}
if (tifService == null) {
LOGGER.log(Level.WARNING, "TIFF provider service not found.");
return;
}
final ParameterValueGroup source = tifService.getProviderDescriptor().createValue();
getOrCreateValue(source, "id").setValue(tifProvName);
getOrCreateValue(source, "load_all").setValue(true);
getOrCreateValue(source, "providerType").setValue("raster");
getOrCreateValue(source, "create_dataset").setValue(false);
final ParameterValueGroup choice = getOrCreateGroup(source, "choice");
final ParameterValueGroup tifConfig = createGroup(choice, "FileCoverageStoreParameters");
final File dstTif = new File(dst, "cloudsgrey.tiff");
getOrCreateValue(tifConfig, "path").setValue(dstTif.toURI().toURL());
getOrCreateValue(tifConfig, "namespace").setValue("no namespace");
// Create SHP Folder provider.
try {
final ProcessDescriptor desc = ProcessFinder.getProcessDescriptor(ConstellationProcessFactory.NAME,
CreateProviderDescriptor.NAME);
final ParameterValueGroup inputs = desc.getInputDescriptor().createValue();
inputs.parameter(CreateProviderDescriptor.PROVIDER_TYPE_NAME).setValue(coverageFileStr);
inputs.parameter(CreateProviderDescriptor.SOURCE_NAME).setValue(source);
desc.createProcess(inputs).call();
} catch (NoSuchIdentifierException ignore) { // should never
// happen
} catch (ProcessException ex) {
LOGGER.log(Level.WARNING, "An error occurred when creating default TIFF provider.", ex);
}
}
} catch (IOException ex) {
LOGGER.log(Level.INFO, ex.getLocalizedMessage(), ex);
}
}
/**
* Initialize default properties values if not exist.
*/
private void initializeDefaultProperties() {
//nothing to do for now
}
}
}