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
50d7e64f
Commit
50d7e64f
authored
Dec 09, 2025
by
Luna Riegel
Browse files
Feature: Add configuration of embedded database
parent
61859d4f
Changes
7
Show whitespace changes
Inline
Side-by-side
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/DataBaseSettings.java
0 → 100644
View file @
50d7e64f
package
de.hft.stuttgart.citydoctor2.database
;
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.Paths
;
import
java.util.Properties
;
public
final
class
DataBaseSettings
{
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
DataBaseSettings
.
class
);
private
static
final
Properties
props
;
private
DataBaseSettings
()
{}
static
{
props
=
new
Properties
();
File
propFile
=
new
File
(
"DBSettings.properties"
);
if
(
propFile
.
exists
())
{
try
(
BufferedReader
bis
=
new
BufferedReader
(
new
FileReader
(
propFile
)))
{
props
.
load
(
bis
);
}
catch
(
IOException
e
)
{
logger
.
error
(
"Failed to load database settings"
,
e
);
}
}
// props.setProperty("name","cd_db");
// props.setProperty("directory","/database/");
// props.setProperty("poolSize","10");
// props.setProperty("inMemoryMode","false");
// props.setProperty("attemptFallback","true");
// props.setProperty("debugMode","false");
}
public
static
EmbeddedDatabaseConfiguration
getConfig
()
{
EmbeddedDatabaseConfiguration
defaultConfig
=
new
EmbeddedDatabaseConfiguration
();
if
(
props
.
isEmpty
())
{
logger
.
debug
(
"No database settings found, using default configuration"
);
return
defaultConfig
;
}
String
name
=
props
.
getProperty
(
"name"
);
String
directory
=
props
.
getProperty
(
"directory"
);
String
poolSizeString
=
props
.
getProperty
(
"poolSize"
);
String
inMemoryModeString
=
props
.
getProperty
(
"inMemoryMode"
);
String
attemptFallbackString
=
props
.
getProperty
(
"attemptFallback"
);
String
debugModeString
=
props
.
getProperty
(
"debugMode"
);
boolean
inMemoryMode
=
Boolean
.
parseBoolean
(
inMemoryModeString
);
boolean
attemptFallback
=
attemptFallbackString
==
null
||
Boolean
.
parseBoolean
(
attemptFallbackString
);
boolean
debugMode
=
Boolean
.
parseBoolean
(
debugModeString
);
int
poolSize
;
try
{
poolSize
=
Integer
.
parseInt
(
poolSizeString
);
if
(
poolSize
<
1
)
{
//TODO: Localize String
logger
.
warn
(
"poolSize property cannot be less than 1, using default value"
);
poolSize
=
defaultConfig
.
connectionPoolSize
();
}
}
catch
(
NumberFormatException
e
)
{
//TODO: Localize String
logger
.
warn
(
"poolSize property must be an integer, using default value"
);
poolSize
=
defaultConfig
.
connectionPoolSize
();
}
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
//TODO: Localize String
logger
.
warn
(
"Properties for directory and name do not form a valid path, using default values"
);
name
=
defaultConfig
.
databaseName
();
directory
=
defaultConfig
.
databaseDirectory
();
}
}
return
new
EmbeddedDatabaseConfiguration
(
name
,
directory
,
poolSize
,
inMemoryMode
,
attemptFallback
,
debugMode
);
}
}
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseConfiguration.java
0 → 100644
View file @
50d7e64f
package
de.hft.stuttgart.citydoctor2.database
;
import
java.io.File
;
/**
* 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.
*/
public
record
EmbeddedDatabaseConfiguration
(
String
databaseName
,
String
databaseDirectory
,
int
connectionPoolSize
,
boolean
inMemoryMode
,
boolean
attemptFallback
,
boolean
debugMode
)
{
private
static
final
String
IN_MEMORY_PREFIX
=
"jdbc:h2:mem"
;
private
static
final
String
AUTO_SERVER_PARAMETER
=
";AUTO_SERVER=TRUE"
;
/**
* Instantiates the default configuration for the embedded database.
*/
EmbeddedDatabaseConfiguration
()
{
this
(
"cd_db"
,
File
.
separator
+
"database"
+
File
.
separator
,
Runtime
.
getRuntime
().
availableProcessors
(),
false
,
true
,
false
);
}
public
String
getInMemoryH2Url
()
{
return
IN_MEMORY_PREFIX
+
databaseName
;
}
public
String
getH2FileUrl
(){
return
databaseDirectory
+
databaseName
;
}
public
String
getH2ParametersSuffix
(){
if
(
debugMode
){
return
AUTO_SERVER_PARAMETER
;
}
else
{
return
";"
;
}
}
}
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDatabaseHandler.java
View file @
50d7e64f
...
@@ -8,6 +8,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
...
@@ -8,6 +8,7 @@ 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
;
...
@@ -16,6 +17,7 @@ import org.apache.logging.log4j.Logger;
...
@@ -16,6 +17,7 @@ import org.apache.logging.log4j.Logger;
import
org.h2gis.functions.factory.H2GISDBFactory
;
import
org.h2gis.functions.factory.H2GISDBFactory
;
import
org.h2gis.utilities.wrapper.DataSourceWrapper
;
import
org.h2gis.utilities.wrapper.DataSourceWrapper
;
import
javax.sql.DataSource
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStream
;
...
@@ -34,34 +36,54 @@ import java.util.Map;
...
@@ -34,34 +36,54 @@ 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
;
public
EmbeddedDatabaseHandler
(){
public
EmbeddedDatabaseHandler
(
EmbeddedDatabaseConfiguration
dbConfig
){
config
=
dbConfig
;
String
jdbcUrl
;
try
{
try
{
String
jdbc
u
rl
=
H2GISDBFactory
.
createDataSource
(
DB_NAME
,
jdbc
U
rl
=
createEmbeddedDataBase
(
config
.
getH2FileUrl
());
true
,
";AUTO_SERVER=TRUE"
).
getConnection
().
getMetaData
().
getURL
(
);
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
.
getInMemoryH2Url
());
}
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
());
}
}
private
String
createEmbeddedDataBase
(
String
dbUrl
)
throws
SQLException
{
DataSource
source
=
H2GISDBFactory
.
createDataSource
(
dbUrl
,
true
,
config
.
getH2ParametersSuffix
());
try
(
Connection
con
=
source
.
getConnection
())
{
return
con
.
getMetaData
().
getURL
();
}
}
}
...
@@ -85,6 +107,7 @@ public class EmbeddedDatabaseHandler {
...
@@ -85,6 +107,7 @@ public class EmbeddedDatabaseHandler {
}
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
);
}
}
}
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/database/FeatureCache.java
View file @
50d7e64f
...
@@ -42,7 +42,11 @@ public class FeatureCache implements CityObjectCache{
...
@@ -42,7 +42,11 @@ 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
);
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
(
value
!=
null
&&
cause
.
wasEvicted
()
&&
isFeatureMarshallable
(
value
)){
handler
.
marshallCityObject
(
value
);
handler
.
marshallCityObject
(
value
);
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/exceptions/EmbeddedDatabaseHandlerException.java
0 → 100644
View file @
50d7e64f
package
de.hft.stuttgart.citydoctor2.exceptions
;
import
java.io.Serial
;
public
class
EmbeddedDatabaseHandlerException
extends
RuntimeException
{
@Serial
private
static
final
long
serialVersionUID
=
-
7848325704234347425L
;
public
EmbeddedDatabaseHandlerException
(
String
message
)
{
super
(
message
);
}
public
EmbeddedDatabaseHandlerException
(
String
message
,
Throwable
cause
)
{
super
(
message
,
cause
);
}
}
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/citygml3/Citygml3FeatureMapper.java
View file @
50d7e64f
...
@@ -166,7 +166,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
...
@@ -166,7 +166,7 @@ public class Citygml3FeatureMapper extends ObjectWalker {
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
Citygml3FeatureMapper
.
class
);
private
static
final
Logger
logger
=
LogManager
.
getLogger
(
Citygml3FeatureMapper
.
class
);
private
static
FeatureCache
featureCache
=
new
FeatureCache
();
private
static
final
FeatureCache
featureCache
=
new
FeatureCache
();
private
final
CityDoctorModel
model
;
private
final
CityDoctorModel
model
;
private
final
ParserConfiguration
config
;
private
final
ParserConfiguration
config
;
private
final
Path
directory
;
private
final
Path
directory
;
...
...
CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/database/EmbeddedDataBaseHandlerTest.java
View file @
50d7e64f
...
@@ -29,12 +29,10 @@ import static org.junit.Assert.fail;
...
@@ -29,12 +29,10 @@ import static org.junit.Assert.fail;
public
class
EmbeddedDataBaseHandlerTest
{
public
class
EmbeddedDataBaseHandlerTest
{
private
EmbeddedDatabaseHandler
handler
;
private
EmbeddedDatabaseHandler
handler
=
new
EmbeddedDatabaseHandler
(
new
EmbeddedDatabaseConfiguration
());
@Before
@Before
public
void
resetDatabase
()
{
public
void
resetDatabase
()
{
try
{
try
{
handler
=
new
EmbeddedDatabaseHandler
();
handler
.
setupFeaturesTable
();
handler
.
setupFeaturesTable
();
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
fail
(
e
.
getMessage
());
fail
(
e
.
getMessage
());
...
...
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