1
2
3
4
5
6 package org.tailormap.api.geotools.featuresources;
7
8 import static org.tailormap.api.persistence.helper.GeoToolsHelper.crsToString;
9
10 import java.io.IOException;
11 import java.lang.invoke.MethodHandles;
12 import java.util.Arrays;
13 import java.util.HashMap;
14 import java.util.List;
15 import java.util.Map;
16 import org.apache.commons.lang3.StringUtils;
17 import org.geotools.api.data.DataStore;
18 import org.geotools.api.data.DataStoreFinder;
19 import org.geotools.api.data.ResourceInfo;
20 import org.geotools.api.data.SimpleFeatureSource;
21 import org.geotools.api.feature.simple.SimpleFeatureType;
22 import org.geotools.api.feature.type.AttributeDescriptor;
23 import org.geotools.api.feature.type.AttributeType;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.tailormap.api.persistence.TMFeatureSource;
27 import org.tailormap.api.persistence.TMFeatureType;
28 import org.tailormap.api.persistence.helper.GeoToolsHelper;
29 import org.tailormap.api.persistence.json.TMAttributeDescriptor;
30 import org.tailormap.api.persistence.json.TMAttributeType;
31 import org.tailormap.api.persistence.json.TMFeatureTypeInfo;
32 import org.tailormap.api.persistence.json.TMServiceCaps;
33 import org.tailormap.api.persistence.json.TMServiceInfo;
34
35 public abstract class FeatureSourceHelper {
36 private static final Logger logger =
37 LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
38
39 public DataStore createDataStore(TMFeatureSource tmfs) throws IOException {
40 return createDataStore(tmfs, null);
41 }
42
43 public abstract DataStore createDataStore(TMFeatureSource tmfs, Integer timeout) throws IOException;
44
45 public SimpleFeatureSource openGeoToolsFeatureSource(TMFeatureType tmft, Integer timeout) throws IOException {
46 DataStore ds = createDataStore(tmft.getFeatureSource(), timeout);
47 return ds.getFeatureSource(tmft.getName());
48 }
49
50 public void loadCapabilities(TMFeatureSource tmfs) throws IOException {
51 loadCapabilities(tmfs, null);
52 }
53
54 public DataStore openDatastore(Map<String, Object> params, String passwordKey) throws IOException {
55 Map<String, Object> logParams = new HashMap<>(params);
56 String passwd = (String) params.get(passwordKey);
57 if (passwd != null) {
58 logParams.put(passwordKey, String.valueOf(new char[passwd.length()]).replace("\0", "*"));
59 }
60 logger.debug("Opening datastore using parameters: {}", logParams);
61 DataStore ds;
62 try {
63 ds = DataStoreFinder.getDataStore(params);
64 } catch (Exception e) {
65 throw new IOException("Cannot open datastore using parameters: " + logParams, e);
66 }
67 if (ds == null) {
68 throw new IOException("No datastore found using parameters " + logParams);
69 }
70 return ds;
71 }
72
73 public void loadCapabilities(TMFeatureSource tmfs, Integer timeout) throws IOException {
74 DataStore ds = createDataStore(tmfs, timeout);
75 try {
76 if (StringUtils.isBlank(tmfs.getTitle())) {
77 tmfs.setTitle(ds.getInfo().getTitle());
78 }
79
80 org.geotools.api.data.ServiceInfo si = ds.getInfo();
81 tmfs.setServiceCapabilities(new TMServiceCaps()
82 .serviceInfo(new TMServiceInfo()
83 .title(si.getTitle())
84 .keywords(si.getKeywords())
85 .description(si.getDescription())
86 .publisher(si.getPublisher())
87 .schema(si.getSchema())
88 .source(si.getSource())));
89
90 List<String> typeNames = Arrays.asList(ds.getTypeNames());
91 logger.info(
92 "Type names for {} {}: {}",
93 tmfs.getProtocol().getValue(),
94 tmfs.getProtocol() == TMFeatureSource.Protocol.WFS ? tmfs.getUrl() : tmfs.getJdbcConnection(),
95 typeNames);
96
97 tmfs.getFeatureTypes().removeIf(tmft -> {
98 if (!typeNames.contains(tmft.getName())) {
99 logger.info("Feature type removed: {}", tmft.getName());
100 return true;
101 } else {
102 return false;
103 }
104 });
105
106 for (String typeName : typeNames) {
107 TMFeatureType pft = tmfs.getFeatureTypes().stream()
108 .filter(ft -> ft.getName().equals(typeName))
109 .findFirst()
110 .orElseGet(() -> new TMFeatureType()
111 .setName(typeName)
112 .setFeatureSource(tmfs)
113
114 .setWriteable(tmfs.getProtocol() == TMFeatureSource.Protocol.JDBC));
115 if (!tmfs.getFeatureTypes().contains(pft)) {
116 tmfs.getFeatureTypes().add(pft);
117 }
118 try {
119 logger.debug("Get feature source from GeoTools datastore for type \"{}\"", typeName);
120 SimpleFeatureSource gtFs = ds.getFeatureSource(typeName);
121 ResourceInfo info = gtFs.getInfo();
122 if (info != null) {
123 pft.setTitle(info.getTitle());
124 pft.setInfo(getFeatureTypeInfo(pft, info, gtFs));
125 pft.getAttributes().clear();
126
127 SimpleFeatureType gtFt = gtFs.getSchema();
128 String primaryKeyName = null;
129 for (AttributeDescriptor gtAttr : gtFt.getAttributeDescriptors()) {
130 AttributeType type = gtAttr.getType();
131 Boolean isPk = (Boolean) gtAttr.getUserData().get("org.geotools.jdbc.pk.column");
132 if (null != isPk && isPk) {
133 logger.debug(
134 "Found primary key attribute \"{}\" for type \"{}\"",
135 gtAttr.getLocalName(),
136 typeName);
137 primaryKeyName = gtAttr.getLocalName();
138 }
139 TMAttributeDescriptor tmAttr = new TMAttributeDescriptor()
140 .name(gtAttr.getLocalName())
141 .type(GeoToolsHelper.toAttributeType(type))
142 .nullable(gtAttr.isNillable())
143 .defaultValue(
144 gtAttr.getDefaultValue() == null
145 ? null
146 : gtAttr.getDefaultValue().toString())
147 .description(
148 type.getDescription() == null
149 ? null
150 : type.getDescription().toString());
151 if (tmAttr.getType() == TMAttributeType.OBJECT) {
152 tmAttr.setUnknownTypeClassName(type.getBinding().getName());
153 }
154 pft.getAttributes().add(tmAttr);
155 }
156 pft.setPrimaryKeyAttribute(primaryKeyName);
157 }
158 } catch (Exception e) {
159 logger.error("Exception reading feature type \"{}\"", typeName, e);
160 }
161 }
162 } finally {
163 ds.dispose();
164 }
165 }
166
167 protected TMFeatureTypeInfo getFeatureTypeInfo(TMFeatureType pft, ResourceInfo info, SimpleFeatureSource gtFs) {
168 return new TMFeatureTypeInfo()
169 .keywords(info.getKeywords())
170 .description(info.getDescription())
171 .bounds(GeoToolsHelper.fromEnvelope(info.getBounds()))
172 .crs(crsToString(info.getCRS()));
173 }
174 }