Skip to content

Commit

Permalink
Merge pull request #1668 from gritGmbH/enhancement/lob-converter-3.6
Browse files Browse the repository at this point in the history
Fix issue where CustomConverter is ignored in table-driven feature mapping mode and provide exemplary implementations [3.6]
  • Loading branch information
copierrj committed Apr 10, 2024
2 parents 126098c + 08dd80c commit 3347ecd
Show file tree
Hide file tree
Showing 8 changed files with 596 additions and 10 deletions.
Expand Up @@ -77,6 +77,10 @@
<groupId>net.gcardone.junidecode</groupId>
<artifactId>junidecode</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
</dependencies>

</project>
Expand Up @@ -48,18 +48,14 @@
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import jakarta.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

import org.deegree.commons.jdbc.SQLIdentifier;
import org.deegree.commons.jdbc.TableName;
import org.deegree.commons.tom.gml.property.PropertyType;
Expand All @@ -76,13 +72,17 @@
import org.deegree.feature.persistence.sql.FeatureTypeMapping;
import org.deegree.feature.persistence.sql.GeometryStorageParams;
import org.deegree.feature.persistence.sql.MappedAppSchema;
import org.deegree.sqldialect.SortCriterion;
import org.deegree.feature.persistence.sql.expressions.TableJoin;
import org.deegree.feature.persistence.sql.id.AutoIDGenerator;
import org.deegree.feature.persistence.sql.id.FIDMapping;
import org.deegree.feature.persistence.sql.id.IDGenerator;
import org.deegree.feature.persistence.sql.jaxb.*;
import org.deegree.feature.persistence.sql.jaxb.AbstractParticleJAXB;
import org.deegree.feature.persistence.sql.jaxb.FIDMappingJAXB;
import org.deegree.feature.persistence.sql.jaxb.FIDMappingJAXB.ColumnJAXB;
import org.deegree.feature.persistence.sql.jaxb.FeatureTypeMappingJAXB;
import org.deegree.feature.persistence.sql.jaxb.GeometryParticleJAXB;
import org.deegree.feature.persistence.sql.jaxb.Join;
import org.deegree.feature.persistence.sql.jaxb.PrimitiveParticleJAXB;
import org.deegree.feature.persistence.sql.rules.GeometryMapping;
import org.deegree.feature.persistence.sql.rules.Mapping;
import org.deegree.feature.persistence.sql.rules.PrimitiveMapping;
Expand All @@ -95,6 +95,7 @@
import org.deegree.filter.expression.ValueReference;
import org.deegree.gml.schema.GMLSchemaInfoSet;
import org.deegree.sqldialect.SQLDialect;
import org.deegree.sqldialect.SortCriterion;
import org.deegree.sqldialect.filter.DBField;
import org.deegree.sqldialect.filter.MappingExpression;
import org.deegree.workspace.Workspace;
Expand Down Expand Up @@ -354,7 +355,7 @@ else if (propDecl instanceof GeometryParticleJAXB) {
}
pt = new SimplePropertyType(propName, minOccurs, maxOccurs, primType, null, null);
m = new PrimitiveMapping(path, minOccurs == 0, mapping, ((SimplePropertyType) pt).getPrimitiveType(), jc,
null);
propDecl.getCustomConverter());
}
else if (propDecl instanceof GeometryParticleJAXB) {
GeometryParticleJAXB geomDecl = (GeometryParticleJAXB) propDecl;
Expand All @@ -381,7 +382,8 @@ else if (propDecl instanceof GeometryParticleJAXB) {
}
CoordinateDimension dim = crs.getDimension() == 3 ? DIM_2 : DIM_3;
pt = new GeometryPropertyType(propName, minOccurs, maxOccurs, null, null, type, dim, INLINE);
m = new GeometryMapping(path, minOccurs == 0, mapping, type, new GeometryStorageParams(crs, srid, dim), jc);
m = new GeometryMapping(path, minOccurs == 0, mapping, type, new GeometryStorageParams(crs, srid, dim), jc,
propDecl.getCustomConverter());
}
else {
LOG.warn("Unhandled property declaration '{}'. Skipping it.", propDecl.getClass());
Expand Down
@@ -0,0 +1,116 @@
/*----------------------------------------------------------------------------
This file is part of deegree, http://deegree.org/
Copyright (C) 2001-2024 by:
- Department of Geography, University of Bonn -
and
- lat/lon GmbH -
and
- grit graphische Informationstechnik Beratungsgesellschaft mbH -
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option)
any later version.
This library 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 Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact information:
grit graphische Informationstechnik Beratungsgesellschaft mbH
Landwehrstr. 143, 59368 Werne
Germany
https://www.grit.de/
lat/lon GmbH
Aennchenstr. 19, 53177 Bonn
Germany
http://lat-lon.de/
Department of Geography, University of Bonn
Prof. Dr. Klaus Greve
Postfach 1147, 53001 Bonn
Germany
http://www.geographie.uni-bonn.de/deegree/
e-mail: info@deegree.org
----------------------------------------------------------------------------*/
package org.deegree.feature.persistence.sql.converter;

import static org.slf4j.LoggerFactory.getLogger;

import org.deegree.commons.tom.primitive.BaseType;
import org.deegree.commons.tom.primitive.PrimitiveType;
import org.deegree.commons.tom.primitive.PrimitiveValue;
import org.deegree.feature.persistence.sql.SQLFeatureStore;
import org.deegree.feature.persistence.sql.jaxb.CustomConverterJAXB;
import org.deegree.feature.persistence.sql.rules.Mapping;
import org.deegree.feature.persistence.sql.rules.PrimitiveMapping;
import org.slf4j.Logger;

/**
* Base for building a custom converter on top of primitive mappings of strings
*
* @see BinaryBase64PrimitiveConverter
* @see BinaryDataUrlPrimitiveConverter
* @see CharacterPrimitiveConverter
* @author <a href="mailto:reichhelm@grit.de">Stephan Reichhelm</a>
*/
public abstract class AbstractStringPrimitiveConverter implements CustomParticleConverter<PrimitiveValue> {

private static final Logger LOG = getLogger(AbstractStringPrimitiveConverter.class);

protected final PrimitiveType pt = new PrimitiveType(BaseType.STRING);

private String column = null;

protected int maxLen = 256 * 1024 * 1024; // Default limit of 256 MiB

protected int sqlType;

protected AbstractStringPrimitiveConverter(int defaultSqlType) {
this.sqlType = defaultSqlType;
}

@Override
public String getSelectSnippet(String tableAlias) {
if (tableAlias != null) {
if (column.startsWith("'") || column.contains(" ")) {
return column.replace("$0", tableAlias);
}
return tableAlias + "." + column;
}
return column;
}

@Override
public String getSetSnippet(PrimitiveValue particle) {
return "?";
}

@Override
public void init(Mapping mapping, SQLFeatureStore fs) {
if (mapping.getConverter() == null) {
return;
}
for (CustomConverterJAXB.Param p : mapping.getConverter().getParam()) {
if ("max-length".equalsIgnoreCase(p.getName())) {
maxLen = Math.max(1, Integer.parseInt(p.getValue()));
}
if ("sql-type".equalsIgnoreCase(p.getName())) {
sqlType = Integer.parseInt(p.getValue());
}
}
if (mapping instanceof PrimitiveMapping) {
column = ((PrimitiveMapping) mapping).getMapping().toString();
}
else {
LOG.error("Converter cannot be used for mapping path {}", mapping.getPath());
}
}

}
@@ -0,0 +1,146 @@
/*----------------------------------------------------------------------------
This file is part of deegree, http://deegree.org/
Copyright (C) 2001-2024 by:
- Department of Geography, University of Bonn -
and
- lat/lon GmbH -
and
- grit graphische Informationstechnik Beratungsgesellschaft mbH -
This library is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2.1 of the License, or (at your option)
any later version.
This library 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 Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this library; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact information:
grit graphische Informationstechnik Beratungsgesellschaft mbH
Landwehrstr. 143, 59368 Werne
Germany
https://www.grit.de/
lat/lon GmbH
Aennchenstr. 19, 53177 Bonn
Germany
http://lat-lon.de/
Department of Geography, University of Bonn
Prof. Dr. Klaus Greve
Postfach 1147, 53001 Bonn
Germany
http://www.geographie.uni-bonn.de/deegree/
e-mail: info@deegree.org
----------------------------------------------------------------------------*/
package org.deegree.feature.persistence.sql.converter;

import static org.slf4j.LoggerFactory.getLogger;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Types;
import java.util.Base64;
import org.apache.commons.io.IOUtils;
import org.deegree.commons.tom.primitive.PrimitiveValue;
import org.deegree.feature.persistence.sql.SQLFeatureStore;
import org.deegree.feature.persistence.sql.rules.Mapping;
import org.slf4j.Logger;

/**
* Converts binary database columns from/to primitive strings encoded as Base64
* <p>
* Note that the maximum length of allowed data is limited to prevent Denial of Service
* Attacks. The allowed maximum length can be set through the max-length parameter in
* bytes (see {@link AbstractStringPrimitiveConverter#init(Mapping, SQLFeatureStore)}).
* </p>
*
* @see <a href="https://www.rfc-editor.org/rfc/rfc4648.txt">The Base16, Base32, and
* Base64 Data Encodings</a>
* @author <a href="mailto:reichhelm@grit.de">Stephan Reichhelm</a>
*/
public class BinaryBase64PrimitiveConverter extends AbstractStringPrimitiveConverter {

private static final Logger LOG = getLogger(BinaryBase64PrimitiveConverter.class);

protected final Base64.Decoder decoder;

protected final Base64.Encoder encoder;

protected BinaryBase64PrimitiveConverter(Base64.Encoder enc, Base64.Decoder dec) {
super(Types.BLOB);
this.decoder = dec;
this.encoder = enc;
}

public BinaryBase64PrimitiveConverter() {
this(Base64.getEncoder(), Base64.getDecoder());
}

String formatInput(String value) throws SQLException {
return value;
}

String formatOutput(String value) throws SQLException {
return value;
}

@Override
public PrimitiveValue toParticle(ResultSet rs, int colIndex) throws SQLException {
try (InputStream is = rs.getBinaryStream(colIndex)) {
if (is == null) {
return null;
}
byte[] raw = IOUtils.toByteArray(is);
return new PrimitiveValue(formatOutput(encoder.encodeToString(raw)), pt);
}
catch (IOException ioe) {
LOG.trace("Exception", ioe);
throw new SQLException("Conversation from binary to Base64 failed: " + ioe.getMessage());
}
}

@Override
public void setParticle(PreparedStatement stmt, PrimitiveValue particle, int paramIndex) throws SQLException {
final String val;
if (particle.getValue() != null) {
val = particle.getValue().toString();
}
else {
val = null;
}
if (val == null) {
try {
stmt.setNull(paramIndex, sqlType);
}
catch (SQLFeatureNotSupportedException ignored) {
stmt.setString(paramIndex, null);
}
}
else if (val.length() > (((float) maxLen / 3) * 4 * 1.1)) {
// NOTE encoded is 4/3 the size of the not encoded content, but 10 percent is
// added for linebreak etc.
throw new SQLException("Maximum length of " + maxLen + " bytes exceeded in pre check.");
}
else {
byte[] raw = decoder.decode(formatInput(val));
if (raw.length > maxLen) {
throw new SQLException("Maximum length of " + maxLen + " bytes exceeded.");
}

stmt.setBinaryStream(paramIndex, new ByteArrayInputStream(raw), raw.length);
}
}

}

0 comments on commit 3347ecd

Please sign in to comment.