Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CityDoctor
CityDoctor2
Commits
6d0d664e
Commit
6d0d664e
authored
Sep 10, 2026
by
Matthias Betz
Browse files
Merge remote-tracking branch 'remotes/origin/dev' into dev_baris_aabb_auto_mergeable
parents
a7ed4b64
cc4fe260
Pipeline
#12443
passed with stage
in 2 minutes and 42 seconds
Changes
133
Pipelines
1
Show whitespace changes
Inline
Side-by-side
.run/CityDoctorGUIStarter.run.xml
View file @
6d0d664e
...
@@ -13,4 +13,18 @@
...
@@ -13,4 +13,18 @@
<option
name=
"Make"
enabled=
"true"
/>
<option
name=
"Make"
enabled=
"true"
/>
</method>
</method>
</configuration>
</configuration>
<configuration
default=
"false"
name=
"CityDoctorGUIStarter"
type=
"Application"
factoryName=
"Application"
nameIsGenerated=
"true"
>
<option
name=
"MAIN_CLASS_NAME"
value=
"de.hft.stuttgart.citydoctor2.gui.CityDoctorGUIStarter"
/>
<module
name=
"CityDoctorGUI"
/>
<option
name=
"WORKING_DIRECTORY"
value=
"$PROJECT_DIR$/CityDoctorParent/Extensions/CityDoctorGUI"
/>
<extension
name=
"coverage"
>
<pattern>
<option
name=
"PATTERN"
value=
"de.hft.stuttgart.citydoctor2.gui.*"
/>
<option
name=
"ENABLED"
value=
"true"
/>
</pattern>
</extension>
<method
v=
"2"
>
<option
name=
"Make"
enabled=
"true"
/>
</method>
</configuration>
</component>
</component>
\ No newline at end of file
CHANGELOG.md
View file @
6d0d664e
...
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
...
@@ -5,6 +5,36 @@ All notable changes to this project will be documented in this file.
The format is based on
[
Keep a Changelog
](
https://keepachangelog.com/en/1.1.0/
)
,
The format is based on
[
Keep a Changelog
](
https://keepachangelog.com/en/1.1.0/
)
,
and this project adheres to
[
Semantic Versioning
](
https://semver.org/spec/v2.0.0.html
)
.
and this project adheres to
[
Semantic Versioning
](
https://semver.org/spec/v2.0.0.html
)
.
## [Unreleased]
### Changed
-
Replaced the JavaFX-based 3D view with an OpenGL renderer (openglfx/JOGL) for smooth display of
large city models. Picking is now GPU-based; the 3D view requires OpenGL 3.3 (Windows/Linux).
## [3.18.3] (2026-04-16)
### Fixes
-
Fixed FragmentedSurface errors reporting roof BoundarySurfaces as ground BoundarySurfaces
-
Fixed degenerated triangles causing false-positive detection of AllPolygonsWrongOrientation errors
-
Fixed -db_location and -db_settings arguments not being parsed when starting the GUI from the CLI
## [3.18.2] (2026-02-24)
### Added
-
CLI parameter "-db_location" for changing the location of the database-file of the embedded database. This parameter will
override the location settings of the DB-settings file.
-
CLI parameter "-db_settings" for giving a path to a DB-settings .properties file.
-
Added -db_location pointing to the system's temp directory to the start scripts for the binaries
-
Added buttons for (un)checking all Geometric or Semantic requirements
-
Added a Hyperlink to CityDoctor's homepage with detailed explanations for each requirement
-
The GUI will now show a loading spinner while waiting for Schematron to finish.
### Fixes
-
Connected CityDoctorHealer to the embedded database
-
Various small fixes and improvements to performance
## [3.18.1] (2025-12-10)
## [3.18.1] (2025-12-10)
### Hotfix
### Hotfix
...
...
CityDoctorParent/CityDoctorCheckResult/pom.xml
View file @
6d0d664e
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
<parent>
<parent>
<groupId>
de.hft.stuttgart
</groupId>
<groupId>
de.hft.stuttgart
</groupId>
<artifactId>
CityDoctorParent
</artifactId>
<artifactId>
CityDoctorParent
</artifactId>
<version>
3.18.
1
</version>
<version>
3.18.
3
</version>
</parent>
</parent>
<artifactId>
CityDoctorCheckResult
</artifactId>
<artifactId>
CityDoctorCheckResult
</artifactId>
<dependencies>
<dependencies>
...
...
CityDoctorParent/CityDoctorEdge/pom.xml
View file @
6d0d664e
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
<parent>
<parent>
<groupId>
de.hft.stuttgart
</groupId>
<groupId>
de.hft.stuttgart
</groupId>
<artifactId>
CityDoctorParent
</artifactId>
<artifactId>
CityDoctorParent
</artifactId>
<version>
3.18.
1
</version>
<version>
3.18.
3
</version>
</parent>
</parent>
<artifactId>
CityDoctorEdge
</artifactId>
<artifactId>
CityDoctorEdge
</artifactId>
<dependencies>
<dependencies>
...
...
CityDoctorParent/CityDoctorEdge/src/test/java/de/hft/stuttgart/citydoctor2/edge/IntersectionErrorsTest.java
View file @
6d0d664e
...
@@ -41,7 +41,7 @@ public class IntersectionErrorsTest {
...
@@ -41,7 +41,7 @@ public class IntersectionErrorsTest {
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
,
Orientation
.
OUTWARD
);
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
,
Orientation
.
OUTWARD
);
ConcretePolygon
p1
=
new
ConcretePolygon
();
ConcretePolygon
p1
=
new
ConcretePolygon
();
geom
.
addPolygon
(
p1
);
LinearRing
ext1
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
LinearRing
ext1
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
p1
.
setExteriorRing
(
ext1
);
p1
.
setExteriorRing
(
ext1
);
...
@@ -66,6 +66,7 @@ public class IntersectionErrorsTest {
...
@@ -66,6 +66,7 @@ public class IntersectionErrorsTest {
ext1
.
addVertex
(
v1
);
ext1
.
addVertex
(
v1
);
ConcretePolygon
p2
=
new
ConcretePolygon
();
ConcretePolygon
p2
=
new
ConcretePolygon
();
geom
.
addPolygon
(
p2
);
LinearRing
ext2
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
LinearRing
ext2
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
p2
.
setExteriorRing
(
ext2
);
p2
.
setExteriorRing
(
ext2
);
...
@@ -88,8 +89,8 @@ public class IntersectionErrorsTest {
...
@@ -88,8 +89,8 @@ public class IntersectionErrorsTest {
ext2
.
addVertex
(
v8
);
ext2
.
addVertex
(
v8
);
ext2
.
addVertex
(
v5
);
ext2
.
addVertex
(
v5
);
geom
.
addPolygon
(
p1
);
geom
.
addPolygon
(
p2
);
geom
.
prepareForChecking
();
geom
.
prepareForChecking
();
...
...
CityDoctorParent/CityDoctorModel/pom.xml
View file @
6d0d664e
...
@@ -6,7 +6,7 @@
...
@@ -6,7 +6,7 @@
<parent>
<parent>
<groupId>
de.hft.stuttgart
</groupId>
<groupId>
de.hft.stuttgart
</groupId>
<artifactId>
CityDoctorParent
</artifactId>
<artifactId>
CityDoctorParent
</artifactId>
<version>
3.18.
1
</version>
<version>
3.18.
3
</version>
</parent>
</parent>
<properties>
<properties>
<versionString>
${project.version}-${git.commit.id.abbrev}
</versionString>
<versionString>
${project.version}-${git.commit.id.abbrev}
</versionString>
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/AbstractErrorVisitor.java
View file @
6d0d664e
...
@@ -37,7 +37,7 @@ import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
...
@@ -37,7 +37,7 @@ import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import
de.hft.stuttgart.citydoctor2.check.error.SolidError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
Unf
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
F
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError
;
import
de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError
;
import
de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError
;
import
de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError
;
import
de.hft.stuttgart.citydoctor2.check.error.XMLValidationError
;
import
de.hft.stuttgart.citydoctor2.check.error.XMLValidationError
;
...
@@ -181,7 +181,7 @@ public abstract class AbstractErrorVisitor implements ErrorVisitor {
...
@@ -181,7 +181,7 @@ public abstract class AbstractErrorVisitor implements ErrorVisitor {
}
}
@Override
@Override
public
void
visit
(
Surface
Unf
ragmentedError
err
)
{
public
void
visit
(
Surface
F
ragmentedError
err
)
{
}
}
@Override
@Override
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Check.java
View file @
6d0d664e
...
@@ -345,7 +345,7 @@ public abstract non-sealed class Check implements CheckableVisitor {
...
@@ -345,7 +345,7 @@ public abstract non-sealed class Check implements CheckableVisitor {
* @param config sometimes there are global parameters which can be used by
* @param config sometimes there are global parameters which can be used by
* checks. Those are be stored in this container
* checks. Those are be stored in this container
*/
*/
public
void
init
(
Map
<
String
,
String
>
params
,
ParserConfiguration
config
)
{
public
void
init
(
Map
<
CheckId
,
Map
<
String
,
String
>
>
params
,
ParserConfiguration
config
)
{
}
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorVisitor.java
View file @
6d0d664e
...
@@ -55,7 +55,7 @@ import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
...
@@ -55,7 +55,7 @@ import de.hft.stuttgart.citydoctor2.check.error.SchematronError;
import
de.hft.stuttgart.citydoctor2.check.error.SolidError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
Unf
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
F
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError
;
import
de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError
;
import
de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError
;
import
de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError
;
import
de.hft.stuttgart.citydoctor2.check.error.XMLValidationError
;
import
de.hft.stuttgart.citydoctor2.check.error.XMLValidationError
;
...
@@ -135,7 +135,7 @@ public interface ErrorVisitor {
...
@@ -135,7 +135,7 @@ public interface ErrorVisitor {
public
void
visit
(
SchematronError
err
);
public
void
visit
(
SchematronError
err
);
public
void
visit
(
Surface
Unf
ragmentedError
err
);
public
void
visit
(
Surface
F
ragmentedError
err
);
public
void
visit
(
DegeneratedRingError
err
);
public
void
visit
(
DegeneratedRingError
err
);
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/HealingMethod.java
View file @
6d0d664e
...
@@ -48,7 +48,7 @@ import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError;
...
@@ -48,7 +48,7 @@ import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError;
import
de.hft.stuttgart.citydoctor2.check.error.SchematronError
;
import
de.hft.stuttgart.citydoctor2.check.error.SchematronError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
Unf
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
F
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError
;
import
de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError
;
import
de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError
;
import
de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError
;
import
de.hft.stuttgart.citydoctor2.check.error.XMLValidationError
;
import
de.hft.stuttgart.citydoctor2.check.error.XMLValidationError
;
...
@@ -189,7 +189,7 @@ public interface HealingMethod {
...
@@ -189,7 +189,7 @@ public interface HealingMethod {
return
false
;
return
false
;
}
}
default
boolean
visit
(
Surface
Unf
ragmentedError
err
,
ModificationListener
l
)
{
default
boolean
visit
(
Surface
F
ragmentedError
err
,
ModificationListener
l
)
{
return
false
;
return
false
;
}
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/QualityAdeErrorVisitor.java
View file @
6d0d664e
...
@@ -55,7 +55,7 @@ import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError;
...
@@ -55,7 +55,7 @@ import de.hft.stuttgart.citydoctor2.check.error.RingTooFewPointsError;
import
de.hft.stuttgart.citydoctor2.check.error.SchematronError
;
import
de.hft.stuttgart.citydoctor2.check.error.SchematronError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidNotClosedError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.check.error.SolidSelfIntError
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
Unf
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.check.error.Surface
F
ragmentedError
;
import
de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError
;
import
de.hft.stuttgart.citydoctor2.check.error.TooFewPolygonsError
;
import
de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError
;
import
de.hft.stuttgart.citydoctor2.check.error.UnknownCheckError
;
import
de.hft.stuttgart.citydoctor2.check.error.XMLValidationError
;
import
de.hft.stuttgart.citydoctor2.check.error.XMLValidationError
;
...
@@ -367,7 +367,7 @@ public class QualityAdeErrorVisitor extends AbstractErrorVisitor {
...
@@ -367,7 +367,7 @@ public class QualityAdeErrorVisitor extends AbstractErrorVisitor {
}
}
@Override
@Override
public
void
visit
(
Surface
Unf
ragmentedError
err
)
{
public
void
visit
(
Surface
F
ragmentedError
err
)
{
// not translated
// not translated
}
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Requirement.java
View file @
6d0d664e
...
@@ -63,7 +63,7 @@ public class Requirement implements Serializable {
...
@@ -63,7 +63,7 @@ public class Requirement implements Serializable {
public
static
final
Requirement
R_GE_R_NULL_AREA
=
new
Requirement
(
"R_GE_R_NULL_AREA"
,
RequirementType
.
GEOMETRY
);
public
static
final
Requirement
R_GE_R_NULL_AREA
=
new
Requirement
(
"R_GE_R_NULL_AREA"
,
RequirementType
.
GEOMETRY
);
public
static
final
Requirement
R_SE_BS_GROUND_UNFRAGMENTED
=
new
Requirement
(
"R_SE_BS_GROUND_UNFRAGMENTED"
,
RequirementType
.
SEMANTIC
);
public
static
final
Requirement
R_SE_BS_GROUND_UNFRAGMENTED
=
new
Requirement
(
"R_SE_BS_GROUND_UNFRAGMENTED"
,
RequirementType
.
SEMANTIC
);
public
static
final
Requirement
R_SE_BS_ROOF_UNFRAGMENTED
=
new
Requirement
(
"R_SE_BS_
G
RO
UND
_UNFRAGMENTED"
,
RequirementType
.
SEMANTIC
);
public
static
final
Requirement
R_SE_BS_ROOF_UNFRAGMENTED
=
new
Requirement
(
"R_SE_BS_RO
OF
_UNFRAGMENTED"
,
RequirementType
.
SEMANTIC
);
public
static
final
Requirement
R_SE_BS_IS_CEILING
=
new
Requirement
(
"R_SE_BS_IS_CEILING"
,
RequirementType
.
SEMANTIC
);
public
static
final
Requirement
R_SE_BS_IS_CEILING
=
new
Requirement
(
"R_SE_BS_IS_CEILING"
,
RequirementType
.
SEMANTIC
);
public
static
final
Requirement
R_SE_BS_IS_FLOOR
=
new
Requirement
(
"R_SE_BS_IS_FLOOR"
,
RequirementType
.
SEMANTIC
);
public
static
final
Requirement
R_SE_BS_IS_FLOOR
=
new
Requirement
(
"R_SE_BS_IS_FLOOR"
,
RequirementType
.
SEMANTIC
);
public
static
final
Requirement
R_SE_BS_IS_WALL
=
new
Requirement
(
"R_SE_BS_IS_WALL"
,
RequirementType
.
SEMANTIC
);
public
static
final
Requirement
R_SE_BS_IS_WALL
=
new
Requirement
(
"R_SE_BS_IS_WALL"
,
RequirementType
.
SEMANTIC
);
...
@@ -83,6 +83,7 @@ public class Requirement implements Serializable {
...
@@ -83,6 +83,7 @@ public class Requirement implements Serializable {
defaultParameters
.
add
(
new
DefaultParameter
(
UPPER_ANGLE_NAME
,
"135"
,
Unit
.
DEGREE
));
defaultParameters
.
add
(
new
DefaultParameter
(
UPPER_ANGLE_NAME
,
"135"
,
Unit
.
DEGREE
));
R_SE_BS_IS_WALL
.
parameters
=
Collections
.
unmodifiableList
(
defaultParameters
);
R_SE_BS_IS_WALL
.
parameters
=
Collections
.
unmodifiableList
(
defaultParameters
);
defaultParameters
=
new
ArrayList
<>();
defaultParameters
.
add
(
new
DefaultParameter
(
MAX_ANGLE_DEVIATION
,
"1"
,
Unit
.
DEGREE
));
defaultParameters
.
add
(
new
DefaultParameter
(
MAX_ANGLE_DEVIATION
,
"1"
,
Unit
.
DEGREE
));
R_SE_BS_ROOF_UNFRAGMENTED
.
parameters
=
Collections
.
unmodifiableList
(
defaultParameters
);
R_SE_BS_ROOF_UNFRAGMENTED
.
parameters
=
Collections
.
unmodifiableList
(
defaultParameters
);
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/Surface
Unf
ragmentedError.java
→
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/Surface
F
ragmentedError.java
View file @
6d0d664e
...
@@ -36,7 +36,7 @@ import java.io.Serial;
...
@@ -36,7 +36,7 @@ import java.io.Serial;
* @author Matthias Betz
* @author Matthias Betz
*
*
*/
*/
public
class
Surface
Unf
ragmentedError
implements
CheckError
{
public
class
Surface
F
ragmentedError
implements
CheckError
{
@Serial
@Serial
private
static
final
long
serialVersionUID
=
3146243879393474196L
;
private
static
final
long
serialVersionUID
=
3146243879393474196L
;
...
@@ -44,7 +44,7 @@ public class SurfaceUnfragmentedError implements CheckError {
...
@@ -44,7 +44,7 @@ public class SurfaceUnfragmentedError implements CheckError {
private
final
BoundarySurface
bs
;
private
final
BoundarySurface
bs
;
private
final
double
angleDeviation
;
private
final
double
angleDeviation
;
public
Surface
Unf
ragmentedError
(
BoundarySurface
bs
,
double
angleDerivation
)
{
public
Surface
F
ragmentedError
(
BoundarySurface
bs
,
double
angleDerivation
)
{
this
.
bs
=
bs
;
this
.
bs
=
bs
;
this
.
angleDeviation
=
angleDerivation
;
this
.
angleDeviation
=
angleDerivation
;
}
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/CityObjectCache.java
View file @
6d0d664e
...
@@ -3,6 +3,7 @@ package de.hft.stuttgart.citydoctor2.database;
...
@@ -3,6 +3,7 @@ package de.hft.stuttgart.citydoctor2.database;
import
de.hft.stuttgart.citydoctor2.datastructure.CityObject
;
import
de.hft.stuttgart.citydoctor2.datastructure.CityObject
;
import
de.hft.stuttgart.citydoctor2.datastructure.GmlId
;
import
de.hft.stuttgart.citydoctor2.datastructure.GmlId
;
import
java.util.Collection
;
import
java.util.List
;
import
java.util.List
;
import
java.util.stream.Stream
;
import
java.util.stream.Stream
;
...
@@ -21,6 +22,8 @@ public interface CityObjectCache {
...
@@ -21,6 +22,8 @@ public interface CityObjectCache {
*/
*/
CityObject
get
(
GmlId
id
);
CityObject
get
(
GmlId
id
);
Collection
<
CityObject
>
getAll
(
List
<
GmlId
>
ids
);
/**
/**
* Replaces a CityObject with another one
* Replaces a CityObject with another one
* @param id GmlID of the CityObject that is to be replaced
* @param id GmlID of the CityObject that is to be replaced
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseCacheLoader.java
0 → 100644
View file @
6d0d664e
package
de.hft.stuttgart.citydoctor2.database
;
import
com.github.benmanes.caffeine.cache.CacheLoader
;
import
de.hft.stuttgart.citydoctor2.exceptions.EmbeddedDatabaseHandlerException
;
import
org.jspecify.annotations.NonNull
;
import
java.util.Map
;
import
java.util.Set
;
public
class
DatabaseCacheLoader
<
K
,
V
>
implements
CacheLoader
<
K
,
V
>
{
private
final
Unmarshaller
<
K
,
V
>
unmarshaller
;
private
final
BatchUnmarshaller
<
K
,
V
>
batchUnmarshaller
;
public
DatabaseCacheLoader
(
Unmarshaller
<
K
,
V
>
unmarshaller
,
BatchUnmarshaller
<
K
,
V
>
batchUnmarshaller
){
this
.
unmarshaller
=
unmarshaller
;
this
.
batchUnmarshaller
=
batchUnmarshaller
;
}
@Override
public
@NonNull
V
load
(
@NonNull
K
key
)
throws
EmbeddedDatabaseHandlerException
{
return
unmarshaller
.
unmarshall
(
key
);
}
@Override
@NonNull
public
Map
<
K
,
V
>
loadAll
(
@NonNull
Set
<?
extends
K
>
keys
)
throws
EmbeddedDatabaseHandlerException
{
return
batchUnmarshaller
.
unmarshall
(
keys
);
}
public
interface
Unmarshaller
<
K
,
V
>
{
V
unmarshall
(
K
key
)
throws
EmbeddedDatabaseHandlerException
;
}
public
interface
BatchUnmarshaller
<
K
,
V
>
{
Map
<
K
,
V
>
unmarshall
(
Set
<?
extends
K
>
keys
)
throws
EmbeddedDatabaseHandlerException
;
}
}
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DatabaseSettings.java
0 → 100644
View file @
6d0d664e
package
de.hft.stuttgart.citydoctor2.database
;
import
de.hft.stuttgart.citydoctor2.exceptions.EmbeddedDatabaseHandlerException
;
import
de.hft.stuttgart.citydoctor2.utils.Localization
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
java.io.BufferedReader
;
import
java.io.File
;
import
java.io.FileReader
;
import
java.io.IOException
;
import
java.nio.file.InvalidPathException
;
import
java.nio.file.Path
;
import
java.nio.file.Paths
;
import
java.util.Properties
;
public
final
class
DatabaseSettings
{
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
DatabaseSettings
.
class
);
private
static
final
Properties
props
=
new
Properties
();
private
DatabaseSettings
()
{}
static
{
File
propFile
=
new
File
(
"DBSettings.properties"
);
if
(
propFile
.
exists
())
{
loadPropertiesFromFile
(
propFile
);
}
}
public
static
void
loadPropertiesFromFile
(
File
file
)
throws
EmbeddedDatabaseHandlerException
{
try
(
BufferedReader
bis
=
new
BufferedReader
(
new
FileReader
(
file
)))
{
props
.
load
(
bis
);
}
catch
(
IOException
e
)
{
throw
new
EmbeddedDatabaseHandlerException
(
e
);
}
}
public
static
void
setDBLocation
(
String
dbLocation
)
throws
EmbeddedDatabaseHandlerException
{
Path
dbPath
=
Paths
.
get
(
dbLocation
).
toAbsolutePath
();
File
dbLocationFile
=
dbPath
.
toFile
();
if
(!
dbLocationFile
.
isFile
())
{
dbPath
=
dbPath
.
resolve
(
"cd_db"
);
}
Path
dbParentDirectoryPath
=
dbPath
.
getParent
();
if
(
dbParentDirectoryPath
.
toFile
().
mkdirs
()){
logger
.
trace
(
"Created parent directories for database location"
);
}
if
(!
dbParentDirectoryPath
.
toFile
().
canWrite
())
{
throw
new
EmbeddedDatabaseHandlerException
(
"Missing write permissions for database location:"
+
dbLocation
);
}
props
.
setProperty
(
"database.name"
,
dbPath
.
getFileName
().
toString
());
props
.
setProperty
(
"database.directory"
,
dbParentDirectoryPath
.
toAbsolutePath
().
toString
());
}
public
static
EmbeddedDatabaseConfiguration
getConfig
()
{
EmbeddedDatabaseConfiguration
defaultConfig
=
new
EmbeddedDatabaseConfiguration
();
if
(
props
.
isEmpty
())
{
logger
.
trace
(
"No database settings found or specified, using default config"
);
return
defaultConfig
;
}
String
name
=
props
.
getProperty
(
"database.name"
);
String
directory
=
props
.
getProperty
(
"database.directory"
);
String
poolSizeString
=
props
.
getProperty
(
"database.connectionPoolSize"
);
String
inMemoryModeString
=
props
.
getProperty
(
"database.inMemoryMode"
);
String
attemptFallbackString
=
props
.
getProperty
(
"database.attemptFallback"
);
String
debugModeString
=
props
.
getProperty
(
"database.debugMode"
);
String
tempModeString
=
props
.
getProperty
(
"database.tempMode"
);
boolean
inMemoryMode
=
Boolean
.
parseBoolean
(
inMemoryModeString
);
boolean
attemptFallback
=
attemptFallbackString
==
null
||
Boolean
.
parseBoolean
(
attemptFallbackString
);
boolean
debugMode
=
Boolean
.
parseBoolean
(
debugModeString
);
boolean
tempMode
=
Boolean
.
parseBoolean
(
tempModeString
);
int
poolSize
=
defaultConfig
.
connectionPoolSize
();
if
(
poolSizeString
!=
null
&&
!
poolSizeString
.
isBlank
()){
try
{
int
size
=
Integer
.
parseInt
(
poolSizeString
);
if
(
size
<
1
)
{
logger
.
warn
(
Localization
.
getText
(
"DatabaseSettings.poolSizeLessThanOne"
));
}
else
poolSize
=
size
;
}
catch
(
NumberFormatException
e
)
{
logger
.
warn
(
Localization
.
getText
(
"DatabaseSettings.poolSizeNaN"
));
}
}
if
(
name
==
null
||
name
.
isBlank
()){
name
=
defaultConfig
.
databaseName
();
}
if
(
directory
==
null
||
directory
.
isBlank
()){
directory
=
defaultConfig
.
databaseDirectory
();
}
try
{
Paths
.
get
(
directory
,
name
);
}
catch
(
InvalidPathException
e
)
{
if
(!
inMemoryMode
)
{
// Only warn if in-memory mode is not being used
logger
.
warn
(
Localization
.
getText
(
"DatabaseSettings.invalidPath"
));
name
=
defaultConfig
.
databaseName
();
directory
=
defaultConfig
.
databaseDirectory
();
}
}
return
new
EmbeddedDatabaseConfiguration
(
name
,
directory
,
poolSize
,
inMemoryMode
,
attemptFallback
,
debugMode
,
tempMode
);
}
}
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseConfiguration.java
0 → 100644
View file @
6d0d664e
package
de.hft.stuttgart.citydoctor2.database
;
import
java.io.File
;
import
java.nio.file.Paths
;
import
java.util.StringJoiner
;
/**
* Record containing the configuration parameters for the setup of the embedded database
* @param databaseName Name of the database
* @param databaseDirectory Directory of the database. Relative paths will be resolved from the current working directory.
* CityDoctor will create the directory if it does not exist, and will overwrite an existing
* database-file if its name matches databaseName
* @param connectionPoolSize Size of the connection pool
* @param inMemoryMode If true, CityDoctor will create the embedded database in RAM
* @param attemptFallback If true, CityDoctor will attempt to create the embedded database in RAM if creation of the
* database-file fails. Will be ignored if inMemoryMode is true
* @param debugMode If true, CityDoctor will start the embedded database in automatic mixed mode to allow access to the
* database during runtime.
* @param tempFileMode If true, CityDoctor will create the database in the system's temp directory. Will be ignored if
* inMemoryMode is true
*/
public
record
EmbeddedDatabaseConfiguration
(
String
databaseName
,
String
databaseDirectory
,
int
connectionPoolSize
,
boolean
inMemoryMode
,
boolean
attemptFallback
,
boolean
debugMode
,
boolean
tempFileMode
)
{
private
static
final
String
IN_MEMORY_PREFIX
=
"mem:"
;
private
static
final
String
JDBC_DRIVER_PREFIX
=
"jdbc:h2:"
;
private
static
final
String
AUTO_SERVER_PARAMETER
=
"AUTO_SERVER=TRUE"
;
private
static
final
String
KEEP_ALIVE_PARAMETER
=
"DB_CLOSE_DELAY=-1"
;
/**
* Instantiates the default configuration for the embedded database.
*/
EmbeddedDatabaseConfiguration
()
{
this
(
"cd_db"
,
File
.
separator
+
"database"
+
File
.
separator
,
Runtime
.
getRuntime
().
availableProcessors
(),
false
,
true
,
false
,
false
);
}
public
static
EmbeddedDatabaseConfiguration
getTestConfig
()
{
return
new
EmbeddedDatabaseConfiguration
(
"cd_db"
,
File
.
separator
+
"database"
+
File
.
separator
,
Runtime
.
getRuntime
().
availableProcessors
(),
true
,
true
,
true
,
false
);
}
public
String
wrapUrlWithH2JdbcDriver
(
String
url
){
return
JDBC_DRIVER_PREFIX
+
url
+
getH2ParametersSuffix
();
}
public
String
getInMemoryH2Url
()
{
return
wrapUrlWithH2JdbcDriver
(
IN_MEMORY_PREFIX
+
databaseName
);
}
public
String
getFallbackH2Url
(){
return
getInMemoryH2Url
()
+
";"
+
KEEP_ALIVE_PARAMETER
;
}
public
String
getH2FileUrl
(){
return
wrapUrlWithH2JdbcDriver
(
Paths
.
get
(
databaseDirectory
).
resolve
(
databaseName
).
toString
());
}
public
String
getH2ParametersSuffix
(){
StringJoiner
joiner
=
new
StringJoiner
(
";"
,
";"
,
""
);
if
(
debugMode
){
joiner
.
add
(
AUTO_SERVER_PARAMETER
);
}
if
(
inMemoryMode
){
joiner
.
add
(
KEEP_ALIVE_PARAMETER
);
}
return
joiner
.
toString
();
}
}
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java
View file @
6d0d664e
...
@@ -8,25 +8,34 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
...
@@ -8,25 +8,34 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.GmlId
;
import
de.hft.stuttgart.citydoctor2.datastructure.GmlId
;
import
de.hft.stuttgart.citydoctor2.exceptions.EmbeddedDatabaseHandlerException
;
import
de.hft.stuttgart.citydoctor2.utils.Localization
;
import
de.hft.stuttgart.citydoctor2.utils.Localization
;
import
org.apache.commons.lang3.SerializationException
;
import
org.apache.commons.lang3.SerializationException
;
import
org.apache.commons.lang3.SerializationUtils
;
import
org.apache.commons.lang3.SerializationUtils
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
org.apache.logging.log4j.Logger
;
import
org.h2gis.functions.factory.H2GISDBFactory
;
import
org.h2gis.utilities.wrapper.DataSourceWrapper
;
import
org.h2gis.utilities.wrapper.DataSourceWrapper
;
import
org.h2gis.functions.factory.H2GISFunctions
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayInputStream
;
import
java.io.File
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStream
;
import
java.io.InvalidClassException
;
import
java.io.InvalidClassException
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.sql.Array
;
import
java.sql.Connection
;
import
java.sql.Connection
;
import
java.sql.DriverManager
;
import
java.sql.PreparedStatement
;
import
java.sql.PreparedStatement
;
import
java.sql.ResultSet
;
import
java.sql.ResultSet
;
import
java.sql.SQLException
;
import
java.sql.SQLException
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.Objects
;
import
java.util.Set
;
/**
/**
* Utility class for setup and access of the embedded database.
* Utility class for setup and access of the embedded database.
...
@@ -34,34 +43,71 @@ import java.util.Map;
...
@@ -34,34 +43,71 @@ import java.util.Map;
public
class
EmbeddedDatabaseHandler
{
public
class
EmbeddedDatabaseHandler
{
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
EmbeddedDatabaseHandler
.
class
);
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
EmbeddedDatabaseHandler
.
class
);
private
static
final
String
DB_NAME
=
"/database/cd_db"
;
private
final
EmbeddedDatabaseConfiguration
config
;
private
DataSourceWrapper
dataSource
;
private
DataSourceWrapper
dataSource
;
private
File
tempFileDBLocation
=
null
;
public
EmbeddedDatabaseHandler
(){
public
EmbeddedDatabaseHandler
(
EmbeddedDatabaseConfiguration
dbConfig
){
config
=
dbConfig
;
String
jdbcUrl
;
try
{
try
{
String
jdbcurl
=
H2GISDBFactory
.
createDataSource
(
DB_NAME
,
if
(
config
.
inMemoryMode
())
{
true
,
";"
).
getConnection
().
getMetaData
().
getURL
();
jdbcUrl
=
createEmbeddedDataBase
(
config
.
getInMemoryH2Url
());
}
else
if
(
config
.
tempFileMode
()){
Path
tmpDir
=
Files
.
createTempDirectory
(
config
.
databaseDirectory
());
tmpDir
.
toFile
().
deleteOnExit
();
String
tempFileUrl
=
tmpDir
.
resolve
(
config
.
databaseName
()).
toFile
().
getCanonicalPath
();
jdbcUrl
=
createEmbeddedDataBase
(
config
.
wrapUrlWithH2JdbcDriver
(
tempFileUrl
));
tempFileDBLocation
=
new
File
(
tempFileUrl
+
".mv.db"
);
tempFileDBLocation
.
deleteOnExit
();
}
else
{
jdbcUrl
=
createEmbeddedDataBase
(
config
.
getH2FileUrl
());
}
logger
.
debug
(
"1] {}"
,
jdbcUrl
);
logger
.
debug
(
"1] {}"
,
jdbcurl
);
}
catch
(
Exception
e
)
{
HikariDataSource
ds
=
new
HikariDataSource
();
logger
.
fatal
(
Localization
.
getText
(
"DatabaseHandler.setupFailure"
));
logger
.
fatal
(
e
.
getMessage
());
if
(
config
.
attemptFallback
()){
logger
.
info
(
"Attempting fallback to in-memory database"
);
try
{
jdbcUrl
=
createEmbeddedDataBase
(
config
.
getFallbackH2Url
());
}
catch
(
Exception
e1
)
{
throw
new
EmbeddedDatabaseHandlerException
(
"Fallback to in-memory database failed, embedded database could not be created"
,
e1
);
}
logger
.
info
(
"Fallback to in-memory database succeeded"
);
logger
.
warn
(
"The in-memory database is limited by the system's available RAM. Loading big CityGML files can lead to OutOfMemory errors."
);
}
else
{
throw
new
EmbeddedDatabaseHandlerException
(
"Embedded database could not be created"
);
}
ds
.
setJdbcUrl
(
jdbcurl
);
}
setupHikariPool
(
jdbcUrl
);
setupFeaturesTable
();
}
private
void
setupHikariPool
(
String
jdbcUrl
){
HikariDataSource
ds
=
new
HikariDataSource
();
ds
.
setJdbcUrl
(
jdbcUrl
);
ds
.
setUsername
(
"sa"
);
ds
.
setUsername
(
"sa"
);
ds
.
setPassword
(
"sa"
);
ds
.
setPassword
(
"sa"
);
int
coreCount
=
Runtime
.
getRuntime
().
availableProcessors
();
ds
.
setMaximumPoolSize
(
config
.
connectionPoolSize
());
ds
.
setMaximumPoolSize
(
coreCount
*
5
);
ds
.
setMinimumIdle
(
coreCount
*
5
);
dataSource
=
new
DataSourceWrapper
(
ds
);
dataSource
=
new
DataSourceWrapper
(
ds
);
setupFeaturesTable
();
//Cleanup hook
//Cleanup hook
Runtime
.
getRuntime
().
addShutdownHook
(
new
Thread
(
ds:
:
close
));
Runtime
.
getRuntime
().
addShutdownHook
(
new
Thread
(
ds:
:
close
));
}
catch
(
Exception
e
)
{
logger
.
fatal
(
Localization
.
getText
(
"DatabaseHandler.setupFailure"
));
logger
.
fatal
(
e
.
getMessage
());
}
}
// Embedded database can only ever be accessed by the local machine, suppress linter-warning about password leak
@SuppressWarnings
(
"java:S6437"
)
private
String
createEmbeddedDataBase
(
String
dbUrl
)
throws
SQLException
{
try
(
Connection
con
=
DriverManager
.
getConnection
(
dbUrl
,
"sa"
,
"sa"
)){
H2GISFunctions
.
load
(
con
);
return
con
.
getMetaData
().
getURL
();
}
}
public
File
getTempFileDBLocation
(){
return
this
.
tempFileDBLocation
;
}
}
...
@@ -69,22 +115,26 @@ public class EmbeddedDatabaseHandler {
...
@@ -69,22 +115,26 @@ public class EmbeddedDatabaseHandler {
try
(
Connection
con
=
dataSource
.
getConnection
())
{
try
(
Connection
con
=
dataSource
.
getConnection
())
{
try
(
PreparedStatement
dropPs
=
con
.
prepareStatement
(
"DROP TABLE IF EXISTS features"
)){
try
(
PreparedStatement
dropPs
=
con
.
prepareStatement
(
"DROP TABLE IF EXISTS features"
)){
dropPs
.
executeUpdate
();
dropPs
.
executeUpdate
();
if
(
logger
.
isDebugEnabled
())
{
logger
.
trace
(
"Dropped existing features table"
);
logger
.
debug
(
"Dropped existing features table"
);
}
}
try
(
PreparedStatement
dropIndexPs
=
con
.
prepareStatement
(
"DROP INDEX IF EXISTS FEATURES_SPATIAL_INDEX"
)){
dropIndexPs
.
executeUpdate
();
logger
.
trace
(
"Dropped existing spatial index"
);
}
}
try
(
PreparedStatement
createPs
=
con
.
prepareStatement
(
"CREATE TABLE features (gmlid VARCHAR(255)"
+
try
(
PreparedStatement
createPs
=
con
.
prepareStatement
(
"CREATE TABLE features (gmlid VARCHAR(255)"
+
" PRIMARY KEY, bbox GEOMETRY, data BLOB, errors BOOLEAN);"
))
{
" PRIMARY KEY, bbox GEOMETRY, data BLOB, errors BOOLEAN);"
))
{
createPs
.
executeUpdate
();
createPs
.
executeUpdate
();
if
(
logger
.
isDebugEnabled
())
{
logger
.
trace
(
"Created features table"
);
logger
.
debug
(
"Created features table"
);
}
}
//CREATE SPATIAL INDEX GEO_TABLE_SPATIAL_INDEX ON GEO_TABLE(THE_GEOM); <- Create spatial id
try
(
PreparedStatement
createIndexPs
=
con
.
prepareStatement
(
"CREATE SPATIAL INDEX FEATURES_SPATIAL_INDEX ON features(bbox);"
)){
createIndexPs
.
executeUpdate
();
logger
.
trace
(
"Created spatial index"
);
}
}
}
catch
(
SQLException
e
)
{
}
catch
(
SQLException
e
)
{
logger
.
fatal
(
Localization
.
getText
(
"DatabaseHandler.tableFailure"
));
logger
.
fatal
(
Localization
.
getText
(
"DatabaseHandler.tableFailure"
));
logger
.
fatal
(
e
.
getMessage
());
logger
.
fatal
(
e
.
getMessage
());
throw
new
EmbeddedDatabaseHandlerException
(
"Could not setup features table in database"
,
e
);
}
}
}
}
...
@@ -96,8 +146,7 @@ public class EmbeddedDatabaseHandler {
...
@@ -96,8 +146,7 @@ public class EmbeddedDatabaseHandler {
try
(
Connection
con
=
dataSource
.
getConnection
())
{
try
(
Connection
con
=
dataSource
.
getConnection
())
{
try
(
PreparedStatement
ps
=
con
.
prepareStatement
(
"MERGE INTO features VALUES (?, ?, ?, ?)"
))
{
try
(
PreparedStatement
ps
=
con
.
prepareStatement
(
"MERGE INTO features VALUES (?, ?, ?, ?)"
))
{
ps
.
setString
(
1
,
co
.
getGmlId
().
toString
());
ps
.
setString
(
1
,
co
.
getGmlId
().
toString
());
String
bbox
=
BoundingBox
.
of
(
co
).
to2DWkt
();
ps
.
setString
(
2
,
co
.
getBbox
().
to2DWkt
());
ps
.
setString
(
2
,
bbox
);
ps
.
setBoolean
(
4
,
co
.
containsAnyError
());
ps
.
setBoolean
(
4
,
co
.
containsAnyError
());
if
(
logger
.
isDebugEnabled
())
{
if
(
logger
.
isDebugEnabled
())
{
...
@@ -198,7 +247,7 @@ public class EmbeddedDatabaseHandler {
...
@@ -198,7 +247,7 @@ public class EmbeddedDatabaseHandler {
}
}
}
}
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
throw
new
Runtime
Exception
(
e
);
throw
new
EmbeddedDatabaseHandler
Exception
(
e
);
}
}
}
catch
(
SQLException
e
)
{
}
catch
(
SQLException
e
)
{
logger
.
error
(
Localization
.
getText
(
"DatabaseHandler.unmarshallingFailure"
),
id
);
logger
.
error
(
Localization
.
getText
(
"DatabaseHandler.unmarshallingFailure"
),
id
);
...
@@ -208,6 +257,47 @@ public class EmbeddedDatabaseHandler {
...
@@ -208,6 +257,47 @@ public class EmbeddedDatabaseHandler {
return
null
;
return
null
;
}
}
public
Map
<
GmlId
,
CityObject
>
unmarshallAllIds
(
Set
<?
extends
GmlId
>
ids
){
try
(
Connection
con
=
dataSource
.
getConnection
())
{
try
(
PreparedStatement
ps
=
con
.
prepareStatement
(
"SELECT data FROM features WHERE ARRAY_CONTAINS(? ,gmlid)"
))
{
String
[]
idStrings
=
ids
.
stream
().
map
(
Objects:
:
toString
).
toArray
(
String
[]::
new
);
Array
array
=
con
.
createArrayOf
(
"VARCHAR(255)"
,
idStrings
);
ps
.
setArray
(
1
,
array
);
ResultSet
rs
=
ps
.
executeQuery
();
boolean
encounteredError
=
false
;
Map
<
GmlId
,
CityObject
>
objects
=
new
HashMap
<>();
while
(
rs
.
next
())
{
try
(
InputStream
is
=
rs
.
getBinaryStream
(
"data"
))
{
CityObject
co
=
SerializationUtils
.
deserialize
(
is
);
// Rebuild the adjacency maps
co
.
accept
(
new
CheckableUtilsVisitor
()
{
@Override
public
void
check
(
Geometry
geom
)
{
geom
.
updateVertices
();
}
});
objects
.
put
(
co
.
getGmlId
(),
co
);
}
catch
(
InvalidClassException
|
ClassCastException
|
SerializationException
e
){
//Cant get the id of the failed Feature, as the deserialization didn't work.
encounteredError
=
true
;
}
}
if
(
encounteredError
){
logger
.
error
(
"Deserialization of one or more Features failed"
);
}
return
objects
;
}
catch
(
IOException
e
)
{
throw
new
EmbeddedDatabaseHandlerException
(
e
);
}
}
catch
(
SQLException
e
)
{
logger
.
error
(
Localization
.
getText
(
"DatabaseHandler.unmarshallingFailure"
),
ids
);
logger
.
error
(
e
.
getMessage
());
}
return
null
;
}
/**
/**
* Retrieves a List of GmlIds of CityObjects that contain any {@link de.hft.stuttgart.citydoctor2.check.CheckError CheckError}.
* Retrieves a List of GmlIds of CityObjects that contain any {@link de.hft.stuttgart.citydoctor2.check.CheckError CheckError}.
* @return a List of GmlIds of CityObjects containing any CheckError
* @return a List of GmlIds of CityObjects containing any CheckError
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java
View file @
6d0d664e
...
@@ -8,7 +8,9 @@ import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
...
@@ -8,7 +8,9 @@ import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.LogManager
;
import
org.apache.logging.log4j.Logger
;
import
org.apache.logging.log4j.Logger
;
import
java.util.Collection
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.ConcurrentHashMap
;
...
@@ -42,12 +44,18 @@ public class FeatureCache implements CityObjectCache{
...
@@ -42,12 +44,18 @@ public class FeatureCache implements CityObjectCache{
private
final
ConcurrentHashMap
<
Thread
,
GmlId
>
evictionLocks
=
new
ConcurrentHashMap
<>();
private
final
ConcurrentHashMap
<
Thread
,
GmlId
>
evictionLocks
=
new
ConcurrentHashMap
<>();
public
FeatureCache
()
{
public
FeatureCache
()
{
handler
=
new
EmbeddedDatabaseHandler
();
this
(
DatabaseSettings
.
getConfig
());
}
public
FeatureCache
(
EmbeddedDatabaseConfiguration
dbConfig
){
handler
=
new
EmbeddedDatabaseHandler
(
dbConfig
);
DatabaseCacheLoader
.
Unmarshaller
<
GmlId
,
CityObject
>
single
=
handler:
:
unmarshallCityObject
;
DatabaseCacheLoader
.
BatchUnmarshaller
<
GmlId
,
CityObject
>
batch
=
handler:
:
unmarshallAllIds
;
cache
=
Caffeine
.
newBuilder
().
maximumSize
(
3000
).
removalListener
((
GmlId
key
,
CityObject
value
,
RemovalCause
cause
)
->
{
cache
=
Caffeine
.
newBuilder
().
maximumSize
(
3000
).
removalListener
((
GmlId
key
,
CityObject
value
,
RemovalCause
cause
)
->
{
if
(
value
!=
null
&&
cause
.
wasEvicted
()
&&
isFeatureMarshallable
(
value
)){
if
(
cause
.
wasEvicted
()
&&
isFeatureMarshallable
(
value
)){
handler
.
marshallCityObject
(
value
);
handler
.
marshallCityObject
(
value
);
}
}
}).
build
(
handler:
:
unmarshallCityObject
);
}).
build
(
new
DatabaseCacheLoader
<>(
single
,
batch
)
);
}
}
/**
/**
...
@@ -111,6 +119,11 @@ public class FeatureCache implements CityObjectCache{
...
@@ -111,6 +119,11 @@ public class FeatureCache implements CityObjectCache{
return
cache
.
get
(
id
);
return
cache
.
get
(
id
);
}
}
@Override
public
Collection
<
CityObject
>
getAll
(
List
<
GmlId
>
ids
){
Map
<
GmlId
,
CityObject
>
map
=
cache
.
getAll
(
new
HashSet
<>(
ids
));
return
map
.
values
();
}
@Override
@Override
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/UnconnectedCache.java
View file @
6d0d664e
...
@@ -5,6 +5,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
...
@@ -5,6 +5,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import
de.hft.stuttgart.citydoctor2.datastructure.GmlId
;
import
de.hft.stuttgart.citydoctor2.datastructure.GmlId
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -37,6 +38,12 @@ public class UnconnectedCache implements CityObjectCache{
...
@@ -37,6 +38,12 @@ public class UnconnectedCache implements CityObjectCache{
return
cache
.
get
(
id
);
return
cache
.
get
(
id
);
}
}
@Override
public
Collection
<
CityObject
>
getAll
(
List
<
GmlId
>
ids
)
{
return
ids
.
stream
().
map
(
cache:
:
get
).
toList
();
}
@Override
@Override
public
void
replace
(
GmlId
id
,
CityObject
cityObject
)
{
public
void
replace
(
GmlId
id
,
CityObject
cityObject
)
{
cache
.
put
(
id
,
cityObject
);
cache
.
put
(
id
,
cityObject
);
...
...
Prev
1
2
3
4
5
…
7
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment