-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Backport to 2.25.x][Community Plugin] Features-Autopopulate
- Loading branch information
Showing
14 changed files
with
1,042 additions
and
0 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
doc/en/user/source/community/features-autopopulate/index.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
.. _community_wfsautopopulate: | ||
|
||
Features-Autopopulate Extension | ||
=============================== | ||
|
||
The Features Autopopulate plug-in listens to transactions (so far only issued by WFS), and autopopulates the feature type attributes according to the values retrieved from the properties file. | ||
|
||
The plugin uses a custom TransactionCallback that alters the insert/update WFS-T operations, forcing in specific values into them, based on configuration files. | ||
|
||
To support configuration for multiple layers, the easiest thing is to place a configuration, file in the directories of the layers themselves, pretty much like the featureinfo templates. | ||
|
||
A "transactionCustomizer.properties" file that contains a set of names and CQL expressions | ||
e.g.: | ||
|
||
``` | ||
UTENTE=env('GSUSER') # this will be replaced with the current user see @EnviromentInjectionCallback | ||
AGGIORNAMENTO=now() # this will be replaced with the current date | ||
``` | ||
|
||
To keep things simple, the expressions will just use environment variables, but not see the other values provided in the update/insert, and will not be differentiated by insert/update cases. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
94 changes: 94 additions & 0 deletions
94
src/community/features-autopopulate/features-autopopulate-core/pom.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<parent> | ||
<groupId>org.geoserver.community</groupId> | ||
<artifactId>gs-features-autopopulate</artifactId> | ||
<version>2.26-SNAPSHOT</version> | ||
</parent> | ||
|
||
<groupId>org.geoserver.community</groupId> | ||
<artifactId>gs-features-autopopulate-core</artifactId> | ||
<packaging>jar</packaging> | ||
<name>Features Autopopulate Core</name> | ||
<description>Features autopopulate core functionality, protocol agnostic</description> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.geoserver</groupId> | ||
<artifactId>gs-main</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geoserver</groupId> | ||
<artifactId>gs-wfs</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geoserver.web</groupId> | ||
<artifactId>gs-web-core</artifactId> | ||
<version>${project.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geotools</groupId> | ||
<artifactId>gt-main</artifactId> | ||
<version>${gt.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geoserver</groupId> | ||
<artifactId>gs-main</artifactId> | ||
<version>${project.version}</version> | ||
<classifier>tests</classifier> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geoserver</groupId> | ||
<artifactId>gs-ows</artifactId> | ||
<classifier>tests</classifier> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geoserver</groupId> | ||
<artifactId>gs-platform</artifactId> | ||
<classifier>tests</classifier> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geotools</groupId> | ||
<artifactId>gt-sample-data</artifactId> | ||
<version>${gt.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geotools</groupId> | ||
<artifactId>gt-jdbc</artifactId> | ||
<version>${gt.version}</version> | ||
<classifier>tests</classifier> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geotools</groupId> | ||
<artifactId>gt-jdbc</artifactId> | ||
<version>${gt.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.geotools.jdbc</groupId> | ||
<artifactId>gt-jdbc-postgis</artifactId> | ||
<version>${gt.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.easymock</groupId> | ||
<artifactId>easymock</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.mockito</groupId> | ||
<artifactId>mockito-core</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
131 changes: 131 additions & 0 deletions
131
...ures-autopopulate-core/src/main/java/org/geoserver/autopopulate/AutopopulateTemplate.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* (c) 2024 Open Source Geospatial Foundation - all rights reserved | ||
* This code is licensed under the GPL 2.0 license, available at the root | ||
* application directory. | ||
*/ | ||
package org.geoserver.autopopulate; | ||
|
||
import java.io.IOException; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
import java.util.Properties; | ||
import java.util.logging.Logger; | ||
import org.geoserver.platform.GeoServerExtensions; | ||
import org.geoserver.platform.GeoServerResourceLoader; | ||
import org.geoserver.security.PropertyFileWatcher; | ||
import org.geotools.api.filter.expression.Expression; | ||
import org.geotools.filter.text.cql2.CQLException; | ||
import org.geotools.filter.text.ecql.ECQL; | ||
import org.geotools.util.logging.Logging; | ||
|
||
/** | ||
* AutopopulateTemplate class is used to load the properties from the file and store them in a map | ||
* for further use. | ||
* | ||
* <p>It also provides the methods to get the properties and set the properties. | ||
* | ||
* @author Alessio Fabiani, GeoSolutions SRL, alessio.fabiani@geosolutionsgroup.com | ||
*/ | ||
public class AutopopulateTemplate { | ||
/** logger */ | ||
private static final Logger LOGGER = Logging.getLogger(AutopopulateTransactionCallback.class); | ||
/** The properties map */ | ||
private final Map<String, String> propertiesMap; | ||
|
||
/** The file watcher */ | ||
private final PropertyFileWatcher watcher; | ||
|
||
/** | ||
* Constructs the template loader. | ||
* | ||
* @param filePath The file path to load the properties from | ||
*/ | ||
public AutopopulateTemplate(String filePath) { | ||
this.propertiesMap = new HashMap<>(); | ||
GeoServerResourceLoader loader = GeoServerExtensions.bean(GeoServerResourceLoader.class); | ||
this.watcher = new PropertyFileWatcher(loader.get(filePath)); | ||
loadProperties(filePath); | ||
} | ||
|
||
/** | ||
* Load the properties from the file and store them in the map. | ||
* | ||
* @param filePath The file path to load the properties from | ||
*/ | ||
private void loadProperties(String filePath) { | ||
try { | ||
Properties properties = this.watcher.getProperties(); | ||
if (properties == null) { | ||
LOGGER.warning("Unable to load the properties file: " + filePath); | ||
return; | ||
} else { | ||
for (String key : properties.stringPropertyNames()) { | ||
String expression = properties.getProperty(key); | ||
propertiesMap.put(key, expression); | ||
|
||
// First check on the Syntax of the expression | ||
try { | ||
Expression ecql = ECQL.toExpression(expression); | ||
if (ecql != null) { | ||
propertiesMap.put(key, ecql.evaluate(null, String.class)); | ||
} | ||
} catch (CQLException e) { | ||
LOGGER.warning( | ||
"Unable to parse the following Expression" + e.getSyntaxError()); | ||
} | ||
} | ||
} | ||
} catch (IOException e) { | ||
// Handle file loading error here | ||
LOGGER.severe("Unable to load the properties file: " + e.getMessage()); | ||
throw new RuntimeException("Unable to load the properties file: " + e.getMessage()); | ||
} | ||
} | ||
|
||
/** | ||
* Get the property from the map. | ||
* | ||
* @param key The key to get the property | ||
* @return The property value | ||
*/ | ||
public String getProperty(String key) { | ||
return propertiesMap.get(key); | ||
} | ||
|
||
/** | ||
* Get all the properties from the map. | ||
* | ||
* @return The properties map | ||
*/ | ||
public Map<String, String> getAllProperties() { | ||
return propertiesMap; | ||
} | ||
|
||
/** | ||
* Check if the template file has been modified on the filesystem. | ||
* | ||
* @return true if the template file has been modified and must be reloaded, false otherwise | ||
*/ | ||
public boolean needsReload() { | ||
if (watcher != null) return watcher.isStale(); | ||
return true; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "AutopopulateTemplate{" + "propertiesMap=" + propertiesMap + '}'; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
if (this == o) return true; | ||
if (!(o instanceof AutopopulateTemplate)) return false; | ||
AutopopulateTemplate that = (AutopopulateTemplate) o; | ||
return Objects.equals(propertiesMap, that.propertiesMap); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(propertiesMap); | ||
} | ||
} |
118 changes: 118 additions & 0 deletions
118
...utopopulate-core/src/main/java/org/geoserver/autopopulate/AutopopulateTemplateLoader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
/* (c) 2024 Open Source Geospatial Foundation - all rights reserved | ||
* This code is licensed under the GPL 2.0 license, available at the root | ||
* application directory. | ||
*/ | ||
package org.geoserver.autopopulate; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.util.logging.Logger; | ||
import org.geoserver.catalog.ResourceInfo; | ||
import org.geoserver.config.GeoServerDataDirectory; | ||
import org.geoserver.platform.GeoServerExtensions; | ||
import org.geoserver.platform.GeoServerResourceLoader; | ||
import org.geoserver.platform.resource.Resources; | ||
|
||
/** | ||
* AutopopulateTemplateLoader class is used to load the template from the file system and return the | ||
* AutopopulateTemplate object. | ||
* | ||
* <p>It also provides the methods to load the template from the file system. | ||
* | ||
* @author Alessio Fabiani, GeoSolutions SRL, alessio.fabiani@geosolutionsgroup.com | ||
*/ | ||
public class AutopopulateTemplateLoader { | ||
|
||
/** logger */ | ||
private static final Logger LOGGER = | ||
org.geotools.util.logging.Logging.getLogger(AutopopulateTemplateLoader.class); | ||
/** | ||
* Feature type directory to load template against. Its presence is mutually exclusive with | ||
* coverageName | ||
*/ | ||
protected ResourceInfo resource; | ||
/** GeoServer data directory */ | ||
GeoServerDataDirectory dd; | ||
|
||
/** | ||
* Constructs the template loader. | ||
* | ||
* @param rl The geoserver resource loader | ||
* @param resource The resource to load the template from | ||
*/ | ||
public AutopopulateTemplateLoader(GeoServerResourceLoader rl, ResourceInfo resource) { | ||
this( | ||
rl == null | ||
? new GeoServerDataDirectory( | ||
GeoServerExtensions.bean(GeoServerResourceLoader.class)) | ||
: new GeoServerDataDirectory(rl), | ||
resource); | ||
} | ||
|
||
/** | ||
* Constructs the template loader. | ||
* | ||
* @param dd The geoserver data directory | ||
* @param resource The resource to load the template from | ||
*/ | ||
public AutopopulateTemplateLoader(GeoServerDataDirectory dd, ResourceInfo resource) { | ||
this.dd = dd; | ||
this.resource = resource; | ||
} | ||
|
||
/** | ||
* Load the template from the file system. | ||
* | ||
* @param path The path to the template | ||
* @return The AutopopulateTemplate object | ||
* @throws IOException If the template cannot be loaded | ||
*/ | ||
public AutopopulateTemplate loadTemplate(String path) throws IOException { | ||
File template = null; | ||
|
||
// template look up order | ||
// 1. Relative to resource | ||
// 2. Relative to store of the resource | ||
// 3. Relative to workspace of resource | ||
// 4. Relative to workspaces directory | ||
if (resource != null) { | ||
// first check relative to set resource | ||
template = Resources.file(dd.get(resource, path)); | ||
|
||
if (template == null) { | ||
// next try relative to the store | ||
template = Resources.file(dd.get(resource.getStore(), path)); | ||
} | ||
|
||
if (template == null) { | ||
// next try relative to the workspace | ||
template = Resources.file(dd.get(resource.getStore().getWorkspace(), path)); | ||
} | ||
|
||
if (template == null) { | ||
// try global supplementary files | ||
template = Resources.file(dd.getWorkspaces(path)); | ||
} | ||
|
||
if (template != null) { | ||
return new AutopopulateTemplate(template.getAbsolutePath()); | ||
} | ||
|
||
if (resource.getStore() != null && resource.getStore().getWorkspace() != null) { | ||
// next try relative to the workspace | ||
template = Resources.file(dd.get(resource.getStore().getWorkspace(), path)); | ||
|
||
if (template == null) { | ||
// try global supplementary files | ||
template = Resources.file(dd.getWorkspaces(path)); | ||
} | ||
|
||
if (template != null) { | ||
return new AutopopulateTemplate(template.getAbsolutePath()); | ||
} | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
} |
Oops, something went wrong.