Skip to content

Commit 6a30b18

Browse files
elahrvivazjnh5y
authored andcommitted
GEOMESA-482 fixing geoserver namespace issues
* Implementing namespace aware methods in AccumuloDataStore, FeatureStore, FeatureSource * Allowing complex namespaces in SimpleFeatureTypes
1 parent 4d946a8 commit 6a30b18

File tree

6 files changed

+86
-39
lines changed

6 files changed

+86
-39
lines changed

geomesa-core/src/main/scala/org/locationtech/geomesa/core/data/AccumuloDataStore.scala

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import com.google.common.collect.ImmutableSortedSet
2323
import com.typesafe.scalalogging.slf4j.Logging
2424
import org.apache.accumulo.core.client._
2525
import org.apache.accumulo.core.client.admin.TimeType
26-
import org.apache.accumulo.core.client.mock.MockConnector
2726
import org.apache.accumulo.core.client.security.tokens.AuthenticationToken
2827
import org.apache.accumulo.core.data.{Key, Value}
2928
import org.apache.accumulo.core.file.keyfunctor.{ColumnFamilyFunctor, RowFunctor}
@@ -32,6 +31,7 @@ import org.apache.hadoop.io.Text
3231
import org.geotools.data._
3332
import org.geotools.data.simple.SimpleFeatureSource
3433
import org.geotools.factory.Hints
34+
import org.geotools.feature.NameImpl
3535
import org.geotools.geometry.jts.ReferencedEnvelope
3636
import org.geotools.process.vector.TransformProcess
3737
import org.locationtech.geomesa.core
@@ -44,7 +44,7 @@ import org.locationtech.geomesa.core.security.AuthorizationsProvider
4444
import org.locationtech.geomesa.data.TableSplitter
4545
import org.locationtech.geomesa.utils.geotools.SimpleFeatureTypes
4646
import org.locationtech.geomesa.utils.geotools.SimpleFeatureTypes.{FeatureSpec, NonGeomAttributeSpec}
47-
import org.opengis.feature.`type`.AttributeDescriptor
47+
import org.opengis.feature.`type`.{AttributeDescriptor, Name}
4848
import org.opengis.feature.simple.SimpleFeatureType
4949
import org.opengis.filter.Filter
5050
import org.opengis.referencing.crs.CoordinateReferenceSystem
@@ -184,7 +184,7 @@ class AccumuloDataStore(val connector: Connector,
184184
* @param attributes
185185
*/
186186
def updateIndexedAttributes(featureName: String, attributes: String): Unit = {
187-
val FeatureSpec(existing, _) = SimpleFeatureTypes.parse(getAttributes(featureName))
187+
val FeatureSpec(existing, _) = SimpleFeatureTypes.parse(getAttributes(featureName).getOrElse(""))
188188
val FeatureSpec(updated, _) = SimpleFeatureTypes.parse(attributes)
189189
// check that the only changes are to non-geometry index flags
190190
val ok = existing.length == updated.length &&
@@ -605,12 +605,18 @@ class AccumuloDataStore(val connector: Connector,
605605

606606
// NB: By default, AbstractDataStore is "isWriteable". This means that createFeatureSource returns
607607
// a featureStore
608-
override def getFeatureSource(featureName: String): SimpleFeatureSource = {
609-
validateMetadata(featureName)
610-
if(!cachingConfig) new AccumuloFeatureStore(this, featureName)
611-
else new AccumuloFeatureStore(this, featureName) with CachingFeatureSource
608+
override def getFeatureSource(typeName: Name): SimpleFeatureSource = {
609+
validateMetadata(typeName.getLocalPart)
610+
if (cachingConfig) {
611+
new AccumuloFeatureStore(this, typeName) with CachingFeatureSource
612+
} else {
613+
new AccumuloFeatureStore(this, typeName)
614+
}
612615
}
613616

617+
override def getFeatureSource(typeName: String): SimpleFeatureSource =
618+
getFeatureSource(new NameImpl(typeName))
619+
614620
/**
615621
* Reads the index schema format out of the metadata
616622
*
@@ -627,7 +633,7 @@ class AccumuloDataStore(val connector: Connector,
627633
* @return
628634
*/
629635
private def getAttributes(featureName: String) =
630-
metadata.read(featureName, ATTRIBUTES_KEY).getOrElse(EMPTY_STRING)
636+
metadata.read(featureName, ATTRIBUTES_KEY)
631637

632638
/**
633639
* Reads the feature encoding from the metadata. Defaults to TEXT if there is no metadata.
@@ -693,24 +699,24 @@ class AccumuloDataStore(val connector: Connector,
693699
* @return the corresponding feature type (schema) for this feature name,
694700
* or NULL if this feature name does not appear to exist
695701
*/
696-
override def getSchema(featureName: String): SimpleFeatureType =
697-
getAttributes(featureName) match {
698-
case attributes if attributes.isEmpty =>
699-
null
700-
case attributes =>
701-
val sft = SimpleFeatureTypes.createType(featureName, attributes)
702-
val dtgField = metadata.read(featureName, DTGFIELD_KEY)
703-
.getOrElse(core.DEFAULT_DTG_PROPERTY_NAME)
704-
val indexSchema = metadata.read(featureName, SCHEMA_KEY).orNull
705-
// If no data is written, we default to 'false' in order to support old tables.
706-
val sharingBoolean = metadata.read(featureName, SHARED_TABLES_KEY).getOrElse("false")
707-
708-
sft.getUserData.put(core.index.SF_PROPERTY_START_TIME, dtgField)
709-
sft.getUserData.put(core.index.SF_PROPERTY_END_TIME, dtgField)
710-
sft.getUserData.put(core.index.SFT_INDEX_SCHEMA, indexSchema)
711-
core.index.setTableSharing(sft, new java.lang.Boolean(sharingBoolean))
712-
sft
713-
}
702+
override def getSchema(featureName: String): SimpleFeatureType = getSchema(new NameImpl(featureName))
703+
704+
override def getSchema(name: Name): SimpleFeatureType = {
705+
val featureName = name.getLocalPart
706+
getAttributes(featureName).map { attributes =>
707+
val sft = SimpleFeatureTypes.createType(name.getURI, attributes)
708+
val dtgField = metadata.read(featureName, DTGFIELD_KEY).getOrElse(core.DEFAULT_DTG_PROPERTY_NAME)
709+
val indexSchema = metadata.read(featureName, SCHEMA_KEY).orNull
710+
// If no data is written, we default to 'false' in order to support old tables.
711+
val sharingBoolean = metadata.read(featureName, SHARED_TABLES_KEY).getOrElse("false")
712+
713+
sft.getUserData.put(core.index.SF_PROPERTY_START_TIME, dtgField)
714+
sft.getUserData.put(core.index.SF_PROPERTY_END_TIME, dtgField)
715+
sft.getUserData.put(core.index.SFT_INDEX_SCHEMA, indexSchema)
716+
core.index.setTableSharing(sft, new java.lang.Boolean(sharingBoolean))
717+
sft
718+
}.orNull
719+
}
714720

715721
// Implementation of Abstract method
716722
def getFeatureReader(featureName: String): AccumuloFeatureReader = getFeatureReader(featureName, Query.ALL)

geomesa-core/src/main/scala/org/locationtech/geomesa/core/data/AccumuloFeatureSource.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import org.locationtech.geomesa.core.process.query.QueryVisitor
2828
import org.locationtech.geomesa.core.process.tube.TubeVisitor
2929
import org.locationtech.geomesa.core.process.unique.AttributeVisitor
3030
import org.opengis.feature.FeatureVisitor
31+
import org.opengis.feature.`type`.Name
3132
import org.opengis.feature.simple.{SimpleFeature, SimpleFeatureType}
3233
import org.opengis.filter.Filter
3334
import org.opengis.filter.sort.SortBy
@@ -39,7 +40,7 @@ trait AccumuloAbstractFeatureSource extends AbstractFeatureSource {
3940
import org.locationtech.geomesa.utils.geotools.Conversions._
4041

4142
val dataStore: AccumuloDataStore
42-
val featureName: String
43+
val featureName: Name
4344

4445
def addFeatureListener(listener: FeatureListener) {}
4546

@@ -69,7 +70,7 @@ trait AccumuloAbstractFeatureSource extends AbstractFeatureSource {
6970
override def getFeatures(filter: Filter): SimpleFeatureCollection = getFeatures(new Query(getSchema().getTypeName, filter))
7071
}
7172

72-
class AccumuloFeatureSource(val dataStore: AccumuloDataStore, val featureName: String)
73+
class AccumuloFeatureSource(val dataStore: AccumuloDataStore, val featureName: Name)
7374
extends AccumuloAbstractFeatureSource
7475

7576
class AccumuloFeatureCollection(source: SimpleFeatureSource, query: Query)

geomesa-core/src/main/scala/org/locationtech/geomesa/core/data/AccumuloFeatureStore.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ import org.geotools.filter.FunctionExpressionImpl
2626
import org.geotools.geometry.jts.ReferencedEnvelope
2727
import org.geotools.process.vector.TransformProcess.Definition
2828
import org.opengis.feature.GeometryAttribute
29-
import org.opengis.feature.`type`.{AttributeDescriptor, GeometryDescriptor}
29+
import org.opengis.feature.`type`.{AttributeDescriptor, GeometryDescriptor, Name}
3030
import org.opengis.feature.simple.{SimpleFeature, SimpleFeatureType}
3131
import org.opengis.filter.expression.PropertyName
3232
import org.opengis.filter.identity.FeatureId
3333

34-
class AccumuloFeatureStore(val dataStore: AccumuloDataStore, val featureName: String)
34+
class AccumuloFeatureStore(val dataStore: AccumuloDataStore, val featureName: Name)
3535
extends AbstractFeatureStore with AccumuloAbstractFeatureSource {
3636
override def addFeatures(collection: FeatureCollection[SimpleFeatureType, SimpleFeature]): JList[FeatureId] = {
3737
writeBounds(collection.getBounds)
@@ -40,7 +40,7 @@ class AccumuloFeatureStore(val dataStore: AccumuloDataStore, val featureName: St
4040

4141
def writeBounds(envelope: ReferencedEnvelope) {
4242
if(envelope != null)
43-
dataStore.writeBounds(featureName, envelope)
43+
dataStore.writeBounds(featureName.getLocalPart, envelope)
4444
}
4545
}
4646

geomesa-core/src/test/scala/org/locationtech/geomesa/core/data/AccumuloDataStoreTest.scala

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ import org.geotools.data._
3131
import org.geotools.data.collection.ListFeatureCollection
3232
import org.geotools.data.simple.SimpleFeatureStore
3333
import org.geotools.factory.{CommonFactoryFinder, Hints}
34-
import org.geotools.feature.DefaultFeatureCollection
3534
import org.geotools.feature.simple.SimpleFeatureBuilder
35+
import org.geotools.feature.{DefaultFeatureCollection, NameImpl}
3636
import org.geotools.filter.text.cql2.CQL
3737
import org.geotools.geometry.jts.JTSFactoryFinder
3838
import org.geotools.process.vector.TransformProcess
@@ -430,6 +430,25 @@ class AccumuloDataStoreTest extends Specification {
430430
"fid-1=testType|POINT (45 49)" mustEqual DataUtilities.encodeFeature(f)
431431
}
432432
}
433+
434+
"handle requests with namespaces" in {
435+
// create the data store
436+
val ns = "mytestns"
437+
val sftName = "namespacetest"
438+
val sft = SimpleFeatureTypes.createType(sftName, s"name:String,dtg:Date,*geom:Point:srid=4326")
439+
sft.getUserData.put(SF_PROPERTY_START_TIME, "dtg")
440+
ds.createSchema(sft)
441+
442+
val schemaWithoutNs = ds.getSchema(sftName)
443+
444+
schemaWithoutNs.getName.getNamespaceURI must beNull
445+
schemaWithoutNs.getName.getLocalPart mustEqual sftName
446+
447+
val schemaWithNs = ds.getSchema(new NameImpl(ns, sftName))
448+
449+
schemaWithNs.getName.getNamespaceURI mustEqual ns
450+
schemaWithNs.getName.getLocalPart mustEqual sftName
451+
}
433452
}
434453

435454
"handle IDL correctly" in {
@@ -1050,6 +1069,7 @@ class AccumuloDataStoreTest extends Specification {
10501069
res.head must beGreaterThan(res(1))
10511070
}
10521071
}
1072+
10531073
}
10541074

10551075
def buildTestIndexSchemaFormat(featureName: String) = new IndexSchemaBuilder("~").randomNumber(3).constant(featureName).geoHash(0, 3).date("yyyyMMdd").nextPart().geoHash(3, 2).nextPart().id().build()

geomesa-utils/src/main/scala/org/locationtech/geomesa/utils/geotools/SimpleFeatureTypes.scala

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ object SimpleFeatureTypes {
1818
val TABLE_SPLITTER_OPTIONS = "table.splitter.options"
1919

2020
def createType(nameSpec: String, spec: String): SimpleFeatureType = {
21-
val (namespace, name) =
22-
nameSpec.split(":").toList match {
23-
case n :: Nil => (null, n)
24-
case ns :: n :: Nil => (ns, n)
25-
case _ => throw new IllegalArgumentException(s"Invalid feature name: $nameSpec")
26-
}
21+
val nsIndex = nameSpec.lastIndexOf(':')
22+
val (namespace, name) = if (nsIndex == -1 || nsIndex == nameSpec.length - 1) {
23+
(null, nameSpec)
24+
} else {
25+
(nameSpec.substring(0, nsIndex), nameSpec.substring(nsIndex + 1))
26+
}
2727

2828
val FeatureSpec(attributeSpecs, opts) = parse(spec)
2929

geomesa-utils/src/test/scala/org/locationtech/geomesa/utils/geotools/SimpleFeatureTypesTest.scala

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,32 @@ class SimpleFeatureTypesTest extends Specification {
2323
val geomDescriptor = sft.getGeometryDescriptor
2424
geomDescriptor.getLocalName must be equalTo "geom"
2525
}
26-
2726
"encode an sft properly" >> {
2827
SimpleFeatureTypes.encodeType(sft) must be equalTo "id:Integer:index=false,dtg:Date:index=false,*geom:Point:srid=4326:index=true"
2928
}
3029
}
3130

31+
"handle namespaces" >> {
32+
"simple ones" >> {
33+
val sft = SimpleFeatureTypes.createType("ns:testing", "dtg:Date,*geom:Point:srid=4326")
34+
sft.getName.getLocalPart mustEqual "testing"
35+
sft.getName.getNamespaceURI mustEqual "ns"
36+
sft.getTypeName mustEqual("testing")
37+
}
38+
"complex ones" >> {
39+
val sft = SimpleFeatureTypes.createType("http://geomesa/ns:testing", "dtg:Date,*geom:Point:srid=4326")
40+
sft.getName.getLocalPart mustEqual "testing"
41+
sft.getName.getNamespaceURI mustEqual "http://geomesa/ns"
42+
sft.getTypeName mustEqual("testing")
43+
}
44+
"invalid ones" >> {
45+
val sft = SimpleFeatureTypes.createType("http://geomesa/ns:testing:", "dtg:Date,*geom:Point:srid=4326")
46+
sft.getName.getLocalPart mustEqual "http://geomesa/ns:testing:"
47+
sft.getName.getNamespaceURI must beNull
48+
sft.getTypeName mustEqual("http://geomesa/ns:testing:")
49+
}
50+
}
51+
3252
"handle empty srid" >> {
3353
val sft = SimpleFeatureTypes.createType("testing", "id:Integer:index=false,*geom:Point:index=true")
3454
(sft.getGeometryDescriptor.getCoordinateReferenceSystem must not).beNull

0 commit comments

Comments
 (0)