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)
44 throws IOException;
45
46 public SimpleFeatureSource openGeoToolsFeatureSource(TMFeatureType tmft, Integer timeout)
47 throws IOException {
48 DataStore ds = createDataStore(tmft.getFeatureSource(), timeout);
49 return ds.getFeatureSource(tmft.getName());
50 }
51
52 public void loadCapabilities(TMFeatureSource tmfs) throws IOException {
53 loadCapabilities(tmfs, null);
54 }
55
56 public DataStore openDatastore(Map<String, Object> params, String passwordKey)
57 throws IOException {
58 Map<String, Object> logParams = new HashMap<>(params);
59 String passwd = (String) params.get(passwordKey);
60 if (passwd != null) {
61 logParams.put(passwordKey, String.valueOf(new char[passwd.length()]).replace("\0", "*"));
62 }
63 logger.debug("Opening datastore using parameters: {}", logParams);
64 DataStore ds;
65 try {
66 ds = DataStoreFinder.getDataStore(params);
67 } catch (Exception e) {
68 throw new IOException("Cannot open datastore using parameters: " + logParams, e);
69 }
70 if (ds == null) {
71 throw new IOException("No datastore found using parameters " + logParams);
72 }
73 return ds;
74 }
75
76 public void loadCapabilities(TMFeatureSource tmfs, Integer timeout) throws IOException {
77 DataStore ds = createDataStore(tmfs, timeout);
78 try {
79 if (StringUtils.isBlank(tmfs.getTitle())) {
80 tmfs.setTitle(ds.getInfo().getTitle());
81 }
82
83 org.geotools.api.data.ServiceInfo si = ds.getInfo();
84 tmfs.setServiceCapabilities(
85 new TMServiceCaps()
86 .serviceInfo(
87 new TMServiceInfo()
88 .title(si.getTitle())
89 .keywords(si.getKeywords())
90 .description(si.getDescription())
91 .publisher(si.getPublisher())
92 .schema(si.getSchema())
93 .source(si.getSource())));
94
95 List<String> typeNames = Arrays.asList(ds.getTypeNames());
96 logger.info(
97 "Type names for {} {}: {}",
98 tmfs.getProtocol().getValue(),
99 tmfs.getProtocol() == TMFeatureSource.Protocol.WFS
100 ? tmfs.getUrl()
101 : tmfs.getJdbcConnection(),
102 typeNames);
103
104 tmfs.getFeatureTypes()
105 .removeIf(
106 tmft -> {
107 if (!typeNames.contains(tmft.getName())) {
108 logger.info("Feature type removed: {}", tmft.getName());
109 return true;
110 } else {
111 return false;
112 }
113 });
114
115 for (String typeName : typeNames) {
116 TMFeatureType pft =
117 tmfs.getFeatureTypes().stream()
118 .filter(ft -> ft.getName().equals(typeName))
119 .findFirst()
120 .orElseGet(
121 () ->
122 new TMFeatureType()
123 .setName(typeName)
124 .setFeatureSource(tmfs)
125
126 .setWriteable(tmfs.getProtocol() == TMFeatureSource.Protocol.JDBC));
127 if (!tmfs.getFeatureTypes().contains(pft)) {
128 tmfs.getFeatureTypes().add(pft);
129 }
130 try {
131 logger.debug("Get feature source from GeoTools datastore for type \"{}\"", typeName);
132 SimpleFeatureSource gtFs = ds.getFeatureSource(typeName);
133 ResourceInfo info = gtFs.getInfo();
134 if (info != null) {
135 pft.setTitle(info.getTitle());
136 pft.setInfo(getFeatureTypeInfo(pft, info, gtFs));
137 pft.getAttributes().clear();
138
139 SimpleFeatureType gtFt = gtFs.getSchema();
140 String primaryKeyName = null;
141 for (AttributeDescriptor gtAttr : gtFt.getAttributeDescriptors()) {
142 AttributeType type = gtAttr.getType();
143 Boolean isPk = (Boolean) gtAttr.getUserData().get("org.geotools.jdbc.pk.column");
144 if (null != isPk && isPk) {
145 logger.debug(
146 "Found primary key attribute \"{}\" for type \"{}\"",
147 gtAttr.getLocalName(),
148 typeName);
149 primaryKeyName = gtAttr.getLocalName();
150 }
151 TMAttributeDescriptor tmAttr =
152 new TMAttributeDescriptor()
153 .name(gtAttr.getLocalName())
154 .type(GeoToolsHelper.toAttributeType(type))
155 .nullable(gtAttr.isNillable())
156 .defaultValue(
157 gtAttr.getDefaultValue() == null
158 ? null
159 : gtAttr.getDefaultValue().toString())
160 .description(
161 type.getDescription() == null ? null : type.getDescription().toString());
162 if (tmAttr.getType() == TMAttributeType.OBJECT) {
163 tmAttr.setUnknownTypeClassName(type.getBinding().getName());
164 }
165 pft.getAttributes().add(tmAttr);
166 }
167 pft.setPrimaryKeyAttribute(primaryKeyName);
168 }
169 } catch (Exception e) {
170 logger.error("Exception reading feature type \"{}\"", typeName, e);
171 }
172 }
173 } finally {
174 ds.dispose();
175 }
176 }
177
178 protected TMFeatureTypeInfo getFeatureTypeInfo(
179 TMFeatureType pft, ResourceInfo info, SimpleFeatureSource gtFs) {
180 return new TMFeatureTypeInfo()
181 .keywords(info.getKeywords())
182 .description(info.getDescription())
183 .bounds(GeoToolsHelper.fromEnvelope(info.getBounds()))
184 .crs(crsToString(info.getCRS()));
185 }
186 }