The Codion server provides RMI and HTTP connection options to clients.
1. Features
-
Firewall friendly RMI; uses one way communication without callbacks, uses two ports, one for the RMI Registry and one for client connections
-
Integrated web server for serving HTTP client connections, based on Javalin and Jetty
-
All user authentication left to the database by default
-
Comprehensive administration and monitoring facilities via the ServerMonitor
-
Featherweight server with moderate memory and CPU usage
2. Security
Here’s a great overview of RMI security risks and mitigations.
2.1. Authentication
The Codion server does not perform any user authentication by default, it leaves that up the underlying database. An authentication layer can be added by implementing an Authenticator and registering it with the ServiceLoader.
2.2. RMI SSL encryption
To enable SSL encryption between client and server, create a keystore and truststore pair and set the following system properties.
2.4. Serialization filtering
The framework provides a way to configure a ObjectInputFilter for deserialization, by specifying a ObjectInputFilterFactory implementation class with the following system property.
codion.server.objectInputFilterFactoryClassName=\
my.serialization.filter.MyObjectInputFilterFactory
2.4.1. Pattern filter
To use the built-in pattern based serialization filter, set the following system property.
codion.server.objectInputFilterFactoryClassName=\
is.codion.common.rmi.server.SerializationFilterFactory
To use serialization filter patterns specified in a string, set the following system property.
codion.server.codion.server.serialization.filter.pattern=pattern1;pattern2
This is equivalent to setting the following:
jdk.serialFilter=pattern1;pattern2
To use the serialization pattern filter based on patterns in a file, set the following system property.
The file may contain all the patterns in a single line, using the ; delimiter or one pattern per line, without a delimiter. Lines starting with '#' are skipped as comments.
codion.server.codion.server.serialization.filter.patternFile=config/patterns.txt
codion.server.serialization.filter.patternFile=classpath:patterns.txt
A list of deserialized classes can be created during a server dry-run by adding the following system property. The file containing all classes deserialized during the run is written to disk on server shutdown.
codion.server.serialization.filter.dryRunFile=deserialized.txt
Example whitelist
ch.qos.logback.classic.Level
com.sun.proxy.$Proxy*
java.lang.Boolean
java.lang.Character
java.lang.Double
java.lang.Enum
java.lang.Float
java.lang.Integer
java.lang.Long
java.lang.Number
java.lang.Object
java.lang.String
java.lang.reflect.Proxy
java.math.BigDecimal
java.math.BigInteger
java.time.LocalDate
java.time.LocalDateTime
java.time.LocalTime
java.time.Ser
java.time.ZoneId
java.time.ZoneRegion
java.util.ArrayList
java.util.Arrays$ArrayList
java.util.Collections$EmptyList
java.util.Collections$EmptyMap
java.util.Collections$SingletonList
java.util.Collections$SingletonMap
java.util.Collections$SingletonSet
java.util.Collections$UnmodifiableCollection
java.util.Collections$UnmodifiableList
java.util.Collections$UnmodifiableMap
java.util.Collections$UnmodifiableSet
java.util.Collections$UnmodifiableRandomAccessList
java.util.Date
java.util.HashMap
java.util.HashSet
java.util.LinkedHashMap
java.util.LinkedHashSet
java.util.Locale
java.util.Map$Entry
java.util.UUID
net.sf.jasperreports.compilers.ConstantExpressionEvaluation
net.sf.jasperreports.compilers.FieldEvaluation
net.sf.jasperreports.compilers.ReportExpressionEvaluationData
net.sf.jasperreports.engine.JRPropertiesMap
net.sf.jasperreports.engine.JasperReport
net.sf.jasperreports.engine.JRBand
net.sf.jasperreports.engine.JRExpressionChunk
net.sf.jasperreports.engine.JRField
net.sf.jasperreports.engine.JRParameter
net.sf.jasperreports.engine.JRQueryChunk
net.sf.jasperreports.engine.JRVariable
net.sf.jasperreports.engine.base.JRBaseBand
net.sf.jasperreports.engine.base.JRBaseBoxBottomPen
net.sf.jasperreports.engine.base.JRBaseBoxLeftPen
net.sf.jasperreports.engine.base.JRBaseBoxPen
net.sf.jasperreports.engine.base.JRBaseBoxRightPen
net.sf.jasperreports.engine.base.JRBaseBoxTopPen
net.sf.jasperreports.engine.base.JRBaseDataset
net.sf.jasperreports.engine.base.JRBaseElement
net.sf.jasperreports.engine.base.JRBaseElementGroup
net.sf.jasperreports.engine.base.JRBaseExpression
net.sf.jasperreports.engine.base.JRBaseExpressionChunk
net.sf.jasperreports.engine.base.JRBaseField
net.sf.jasperreports.engine.base.JRBaseLineBox
net.sf.jasperreports.engine.base.JRBaseParagraph
net.sf.jasperreports.engine.base.JRBaseParameter
net.sf.jasperreports.engine.base.JRBasePen
net.sf.jasperreports.engine.base.JRBaseQuery
net.sf.jasperreports.engine.base.JRBaseQueryChunk
net.sf.jasperreports.engine.base.JRBaseReport
net.sf.jasperreports.engine.base.JRBaseSection
net.sf.jasperreports.engine.base.JRBaseStaticText
net.sf.jasperreports.engine.base.JRBaseTextElement
net.sf.jasperreports.engine.base.JRBaseTextField
net.sf.jasperreports.engine.base.JRBaseVariable
net.sf.jasperreports.engine.design.JRReportCompileData
net.sf.jasperreports.engine.type.CalculationEnum
net.sf.jasperreports.engine.type.EvaluationTimeEnum
net.sf.jasperreports.engine.type.HorizontalTextAlignEnum
net.sf.jasperreports.engine.type.IncrementTypeEnum
net.sf.jasperreports.engine.type.OrientationEnum
net.sf.jasperreports.engine.type.PositionTypeEnum
net.sf.jasperreports.engine.type.PrintOrderEnum
net.sf.jasperreports.engine.type.ResetTypeEnum
net.sf.jasperreports.engine.type.RunDirectionEnum
net.sf.jasperreports.engine.type.SectionTypeEnum
net.sf.jasperreports.engine.type.SplitTypeEnum
net.sf.jasperreports.engine.type.StretchTypeEnum
net.sf.jasperreports.engine.type.TextAdjustEnum
net.sf.jasperreports.engine.type.WhenResourceMissingTypeEnum
is.codion.common.Conjunction
is.codion.common.Operator
is.codion.common.db.operation.DefaultFunctionType
is.codion.common.db.operation.DefaultProcedureType
is.codion.common.db.report.AbstractReport
is.codion.common.db.report.DefaultReportType
is.codion.common.rmi.client.DefaultConnectionRequest
is.codion.common.user.DefaultUser
is.codion.common.version.DefaultVersion
is.codion.framework.db.DefaultSelect
is.codion.framework.db.DefaultUpdate
is.codion.framework.domain.DefaultDomainType
is.codion.framework.domain.entity.attribute.DefaultAttribute
is.codion.framework.domain.entity.attribute.DefaultAttribute$DefaultType
is.codion.framework.domain.entity.attribute.DefaultColumn
is.codion.framework.domain.entity.attribute.DefaultForeignKey
is.codion.framework.domain.entity.attribute.DefaultForeignKey$DefaultReference
is.codion.framework.domain.entity.condition.AbstractCondition
is.codion.framework.domain.entity.condition.AbstractColumnCondition
is.codion.framework.domain.entity.condition.DefaultAllCondition
is.codion.framework.domain.entity.condition.DefaultConditionCombination
is.codion.framework.domain.entity.condition.DefaultCustomCondition
is.codion.framework.domain.entity.condition.AbstractColumnCondition
is.codion.framework.domain.entity.condition.DualValueColumnCondition
is.codion.framework.domain.entity.condition.MultiValueColumnCondition
is.codion.framework.domain.entity.condition.SingleValueColumnCondition
is.codion.framework.domain.entity.condition.DefaultConditionType
is.codion.framework.domain.entity.DefaultEntity
is.codion.framework.domain.entity.DefaultEntity$EntityInvoker
is.codion.framework.domain.entity.DefaultEntityType
is.codion.framework.domain.entity.DefaultForeignKey
is.codion.framework.domain.entity.DefaultForeignKey$DefaultReference
is.codion.framework.domain.entity.DefaultKey
is.codion.framework.domain.entity.DefaultOrderBy
is.codion.framework.domain.entity.DefaultOrderBy$DefaultOrderByColumn
is.codion.framework.domain.entity.Entity
is.codion.framework.domain.entity.ImmutableEntity
is.codion.framework.domain.entity.OrderBy$NullOrder
is.codion.demos.chinook.domain.Chinook
is.codion.demos.chinook.domain.Chinook$Invoice
is.codion.demos.chinook.domain.Chinook$Playlist$RandomPlaylistParameters
is.codion.demos.chinook.domain.Chinook$Track
is.codion.demos.chinook.domain.Chinook$Track$RaisePriceParameters
is.codion.demos.employees.domain.*
is.codion.demos.world.domain.api.*
is.codion.demos.petclinic.domain.*
is.codion.plugin.jasperreports.DefaultJRReportType
3. Configuration
3.1. Example configuration file
# Database configuration
codion.db.url=jdbc:h2:mem:h2db
codion.db.useOptimisticLocking=true
codion.db.countQueries=true
codion.db.initScripts=\
../config/employees/create_schema.sql,\
../config/chinook/create_schema.sql,\
../config/petstore/create_schema.sql,\
../config/world/create_schema.sql
# The admin user credentials, used by the server monitor application
codion.server.admin.user=scott:tiger
# Client logging disabled by default
codion.server.clientLogging=false
# A connection pool based on this user is created on startup
codion.server.connectionPoolUsers=scott:tiger
# The port used by clients
codion.server.port=2222
# The port for the admin interface, used by the server monitor
codion.server.admin.port=4444
# RMI Registry port
codion.server.registryPort=1099
# Any auxiliary servers to run alongside this server
codion.server.auxiliaryServerFactoryClassNames=\
is.codion.framework.servlet.EntityServletServerFactory
# The http port
codion.server.http.port=8080
# Specifies whether or not to use https
codion.server.http.secure=false
# The ObjectInputFilterFactory class to use
codion.server.objectInputFilterFactoryClassName=\
is.codion.common.rmi.server.SerializationFilterFactory
# The serialization pattern file to use for RMI deserialization filtering
codion.server.serialization.filter.patternFile=\
../config/serialization-whitelist.txt
# RMI configuration
java.rmi.server.hostname=localhost
java.rmi.server.randomIDs=true
# SSL configuration
javax.net.ssl.keyStore=../config/keystore.jks
javax.net.ssl.keyStorePassword=crappypass
# Used to connect to the server to shut it down
#codion.client.trustStore=../config/truststore.jks
4. Code examples
Absolute bare-bones examples of how to run the EntityServer and connect to it.
4.1. RMI
Database database = H2DatabaseFactory
.createDatabase("jdbc:h2:mem:testdb",
"src/main/sql/create_schema.sql");
EntityServerConfiguration configuration =
EntityServerConfiguration.builder(SERVER_PORT, REGISTRY_PORT)
.domainClassNames(List.of(Store.class.getName()))
.database(database)
.sslEnabled(false)
.build();
EntityServer server = EntityServer.startServer(configuration);
RemoteEntityConnectionProvider connectionProvider =
RemoteEntityConnectionProvider.builder()
.port(SERVER_PORT)
.registryPort(REGISTRY_PORT)
.domainType(Store.DOMAIN)
.user(parse("scott:tiger"))
.clientType("ClientServer")
.build();
EntityConnection connection = connectionProvider.connection();
List<Entity> customers = connection.select(all(Customer.TYPE));
customers.forEach(System.out::println);
connection.close();
server.shutdown();
4.2. HTTP
Database database = H2DatabaseFactory
.createDatabase("jdbc:h2:mem:testdb",
"src/main/sql/create_schema.sql");
EntityService.HTTP_SERVER_PORT.set(HTTP_PORT);
EntityServerConfiguration configuration =
EntityServerConfiguration.builder(SERVER_PORT, REGISTRY_PORT)
.domainClassNames(List.of(Store.class.getName()))
.database(database)
.sslEnabled(false)
.auxiliaryServerFactoryClassNames(List.of(EntityServiceFactory.class.getName()))
.build();
EntityServer server = EntityServer.startServer(configuration);
HttpEntityConnectionProvider connectionProvider =
HttpEntityConnectionProvider.builder()
.port(HTTP_PORT)
.https(false)
.domainType(Store.DOMAIN)
.user(parse("scott:tiger"))
.clientType("ClientServer")
.build();
EntityConnection connection = connectionProvider.connection();
List<Entity> customers = connection.select(all(Customer.TYPE));
customers.forEach(System.out::println);
connection.close();
server.shutdown();