Commit 76e8b032 authored by Misagh Moayyed's avatar Misagh Moayyed
Browse files

Merge pull request #1165 from Unicon/transactional-aop

Configure JPA transactions based on AOP
parents 47e36d26 01aad3bb
......@@ -500,7 +500,7 @@ public final class CentralAuthenticationServiceImpl implements CentralAuthentica
throws AuthenticationException, TicketException {
final Set<Credential> sanitizedCredentials = sanitizeCredentials(credentials);
if (sanitizedCredentials.size() > 0) {
if (!sanitizedCredentials.isEmpty()) {
final Authentication authentication = this.authenticationManager.authenticate(credentials);
final TicketGrantingTicket ticketGrantingTicket = new TicketGrantingTicketImpl(
......
......@@ -69,18 +69,12 @@ public class SessionMonitor implements Monitor<SessionStatus> {
}
/**
* {@inheritDoc}
*/
@Override
public String getName() {
return SessionMonitor.class.getSimpleName();
}
/**
* {@inheritDoc}
*/
@Override
public SessionStatus observe() {
try {
......
......@@ -18,11 +18,10 @@
*/
package org.jasig.cas.services;
import org.jasig.inspektr.audit.annotation.Audit;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.inspektr.audit.annotation.Audit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotNull;
import java.util.Collection;
......@@ -62,14 +61,13 @@ public final class DefaultServicesManagerImpl implements ReloadableServicesManag
}
/**
* @deprecated As of 4.1. Use {@link #DefaultServicesManagerImpl(ServiceRegistryDao)}
* instead. The <code>defaultAttributes</code> parameter is no longer used. Attributes are configured
* per service definition in the services registry. See {@link RegisteredService#getAttributeReleasePolicy()}
* for more details.
* Constructs an instance of the {@link DefaultServicesManagerImpl} where the default RegisteredService
* can include a set of default attributes to use if no services are defined in the registry.
*
* @deprecated
* <p>As of 4.1. Use {@link #DefaultServicesManagerImpl(ServiceRegistryDao)}
* instead. The <code>defaultAttributes</code> parameter is no longer used. Attributes are configured
* per service definition in the services registry. See {@link RegisteredService#getAttributeReleasePolicy()}
* for more details.</p>
*
* @param serviceRegistryDao the Service Registry Dao.
* @param defaultAttributes the list of default attributes to use.
......@@ -81,7 +79,6 @@ public final class DefaultServicesManagerImpl implements ReloadableServicesManag
LOGGER.warn("This constructor is deprecated and will be removed in future CAS versions");
}
@Transactional(readOnly = false)
@Audit(action = "DELETE_SERVICE", actionResolverName = "DELETE_SERVICE_ACTION_RESOLVER",
resourceResolverName = "DELETE_SERVICE_RESOURCE_RESOLVER")
@Override
......@@ -97,9 +94,7 @@ public final class DefaultServicesManagerImpl implements ReloadableServicesManag
return r;
}
/**
* {@inheritDoc}
*/
@Override
public RegisteredService findServiceBy(final Service service) {
final Collection<RegisteredService> c = convertToTreeSet();
......@@ -130,9 +125,10 @@ public final class DefaultServicesManagerImpl implements ReloadableServicesManag
* @return the tree set
*/
protected TreeSet<RegisteredService> convertToTreeSet() {
return new TreeSet<RegisteredService>(this.services.values());
return new TreeSet<>(this.services.values());
}
@Override
public Collection<RegisteredService> getAllServices() {
return Collections.unmodifiableCollection(convertToTreeSet());
}
......@@ -142,7 +138,6 @@ public final class DefaultServicesManagerImpl implements ReloadableServicesManag
return findServiceBy(service) != null;
}
@Transactional(readOnly = false)
@Audit(action = "SAVE_SERVICE", actionResolverName = "SAVE_SERVICE_ACTION_RESOLVER",
resourceResolverName = "SAVE_SERVICE_RESOURCE_RESOLVER")
@Override
......
---
layout: default
title: CAS - JPA Service Registry
---
# JPA Service Registry
Stores registered service data in a database.
Support is enabled by adding the following module into the Maven overlay:
{% highlight xml %}
<dependency>
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server-support-jdbc</artifactId>
<version>${cas.version}</version>
</dependency>
{% endhighlight %}
## Schema
The following schema shall be generated by CAS automatically
for brand new deployments, and must be massaged when doing CAS upgrades.
{% highlight sql %}
create table RegisteredServiceImpl (
expression_type VARCHAR(15) DEFAULT 'ant' not null,
id bigint generated by default as identity (start with 1),
access_strategy blob(255),
attribute_release blob(255),
description varchar(255) not null,
evaluation_order integer not null,
logo varchar(255),
logout_type integer,
logout_url varchar(255),
name varchar(255) not null,
proxy_policy blob(255),
required_handlers blob(255),
public_key blob(255),
serviceId varchar(255) not null,
theme varchar(255),
username_attr blob(255),
primary key (id)
)
{% endhighlight %}
## Configuration
The following configuration template may be applied to `deployerConfigContext.xml` to provide for persistent
registered service storage. The configuration assumes a `dataSource` bean is defined in the context.
{% highlight xml %}
<bean
id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${database.driverClass:org.hsqldb.jdbcDriver}"
p:jdbcUrl="${database.url:jdbc:hsqldb:mem:cas-ticket-registry}"
p:user="${database.user:sa}"
p:password="${database.password:}"
p:initialPoolSize="${database.pool.minSize:6}"
p:minPoolSize="${database.pool.minSize:6}"
p:maxPoolSize="${database.pool.maxSize:18}"
p:maxIdleTimeExcessConnections="${database.pool.maxIdleTime:1000}"
p:checkoutTimeout="${database.pool.maxWait:2000}"
p:acquireIncrement="${database.pool.acquireIncrement:16}"
p:acquireRetryAttempts="${database.pool.acquireRetryAttempts:5}"
p:acquireRetryDelay="${database.pool.acquireRetryDelay:2000}"
p:idleConnectionTestPeriod="${database.pool.idleConnectionTestPeriod:30}"
p:preferredTestQuery="${database.pool.connectionHealthQuery:select 1}"
/>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<util:list id="packagesToScan">
<value>org.jasig.cas.services</value>
<value>org.jasig.cas.ticket</value>
<value>org.jasig.cas.adaptors.jdbc</value>
</util:list>
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
id="jpaVendorAdapter"
p:generateDdl="true"
p:showSql="true" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="jpaVendorAdapter"
p:packagesToScan-ref="packagesToScan">
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${database.dialect:org.hibernate.dialect.HSQLDialect}</prop>
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
<prop key="hibernate.jdbc.batch_size">${database.batchSize:1}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="delete*" read-only="false"/>
<tx:method name="save*" read-only="false"/>
<tx:method name="update*" read-only="false"/>
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="servicesManagerOperations" expression="execution(* org.jasig.cas.services.JpaServiceRegistryDaoImpl.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicesManagerOperations"/>
</aop:config>
<bean id="serviceRegistryDao"
class="org.jasig.cas.services.JpaServiceRegistryDaoImpl" />
{% endhighlight %}
add props and config for xml
\ No newline at end of file
......@@ -5,9 +5,17 @@ title: CAS - JPA Ticket Registry
# JPA Ticket Registry
The JPA Ticket Registry allows CAS to store client authenticated state data (tickets) in a database back-end such as MySQL.
The JPA Ticket Registry allows CAS to store client authenticated state
data (tickets) in a database back-end such as MySQL.
<div class="alert alert-warning"><strong>Usage Warning!</strong><p>Using a RDBMS as the back-end persistence choice for Ticket Registry state management is a fairly unnecessary and complicated process. Ticket registries generally do not need the durability that comes with RDBMS and unless you are already outfitted with clustered RDBMS technology and the resources to manage it, the complexity is likely not worth the trouble. Given the proliferation of hardware virtualization and the redundancy and vertical scaling they often provide, more suitable recommendation would be the default in-memory ticket registry for a single node CAS deployment and distributed cache-based registries for higher availability.</p></div>
<div class="alert alert-warning"><strong>Usage Warning!</strong><p>Using a RDBMS as
the back-end persistence choice for Ticket Registry state management is a fairly unnecessary and complicated
process. Ticket registries generally do not need the durability that comes with RDBMS and unless
you are already outfitted with clustered RDBMS technology and the resources to manage it,
the complexity is likely not worth the trouble. Given the proliferation of hardware virtualization
and the redundancy and vertical scaling they often provide, more suitable recommendation would be
the default in-memory ticket registry for a single node CAS deployment and distributed cache-based
registries for higher availability.</p></div>
# Configuration
......@@ -15,6 +23,25 @@ The JPA Ticket Registry allows CAS to store client authenticated state data (tic
- Adjust the `src/main/webapp/WEB-INF/spring-configuration/ticketRegistry.xml` with the following:
{% highlight xml %}
<bean
id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${database.driverClass:org.hsqldb.jdbcDriver}"
p:jdbcUrl="${database.url:jdbc:hsqldb:mem:cas-ticket-registry}"
p:user="${database.user:sa}"
p:password="${database.password:}"
p:initialPoolSize="${database.pool.minSize:6}"
p:minPoolSize="${database.pool.minSize:6}"
p:maxPoolSize="${database.pool.maxSize:18}"
p:maxIdleTimeExcessConnections="${database.pool.maxIdleTime:1000}"
p:checkoutTimeout="${database.pool.maxWait:2000}"
p:acquireIncrement="${database.pool.acquireIncrement:16}"
p:acquireRetryAttempts="${database.pool.acquireRetryAttempts:5}"
p:acquireRetryDelay="${database.pool.acquireRetryDelay:2000}"
p:idleConnectionTestPeriod="${database.pool.idleConnectionTestPeriod:30}"
p:preferredTestQuery="${database.pool.connectionHealthQuery:select 1}"
/>
<bean id="ticketRegistry" class="org.jasig.cas.ticket.registry.JpaTicketRegistry" />
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
......@@ -26,78 +53,76 @@ The JPA Ticket Registry allows CAS to store client authenticated state data (tic
</util:list>
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
id="jpaVendorAdapter"
p:generateDdl="true"
p:showSql="true" />
id="jpaVendorAdapter"
p:generateDdl="true"
p:showSql="true" />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="jpaVendorAdapter"
p:packagesToScan-ref="packagesToScan">
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="jpaVendorAdapter"
p:packagesToScan-ref="packagesToScan">
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">${database.dialect}</prop>
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
<prop key="hibernate.jdbc.batch_size">${database.batchSize}</prop>
</props>
<props>
<prop key="hibernate.dialect">${database.dialect:org.hibernate.dialect.HSQLDialect}</prop>
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
<prop key="hibernate.jdbc.batch_size">${database.batchSize:1}</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
p:entityManagerFactory-ref="entityManagerFactory" />
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="delete*" read-only="false"/>
<tx:method name="save*" read-only="false"/>
<tx:method name="update*" read-only="false"/>
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="servicesManagerOperations" expression="execution(* org.jasig.cas.services.JpaServiceRegistryDaoImpl.*(..))"/>
<aop:pointcut id="ticketRegistryOperations" expression="execution(* org.jasig.cas.ticket.registry.JpaTicketRegistry.*(..))"/>
<aop:pointcut id="ticketRegistryLockingOperations" expression="execution(* org.jasig.cas.ticket.registry.support.JpaLockingStrategy.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="servicesManagerOperations"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="ticketRegistryOperations"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="ticketRegistryLockingOperations"/>
</aop:config>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="ticketRegistryCleaner"
class="org.jasig.cas.ticket.registry.support.DefaultTicketRegistryCleaner"
class="org.jasig.cas.ticket.registry.support.DefaultTicketRegistryCleaner"
c:centralAuthenticationService-ref="centralAuthenticationService"
c:ticketRegistry-ref="ticketRegistry"
p:lock-ref="cleanerLock"/>
<bean id="cleanerLock" class="org.jasig.cas.ticket.registry.support.JpaLockingStrategy"
p:uniqueId="${host.name}"
p:applicationId="cas-ticket-registry-cleaner" />
p:uniqueId="${host.name}"
p:applicationId="cas-ticket-registry-cleaner" />
<bean id="jobDetailTicketRegistryCleaner"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
p:targetObject-ref="ticketRegistryCleaner"
p:targetMethod="clean" />
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"
p:targetObject-ref="ticketRegistryCleaner"
p:targetMethod="clean" />
<bean id="triggerJobDetailTicketRegistryCleaner"
class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"
p:jobDetail-ref="jobDetailTicketRegistryCleaner"
p:startDelay="20000"
p:repeatInterval="5000000" />
class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"
p:jobDetail-ref="jobDetailTicketRegistryCleaner"
p:startDelay="20000"
p:repeatInterval="5000000" />
{% endhighlight %}
The above snippet assumes that data source information and connection details are defined.
- Configure other JPA dependencies:
In the `pom.xml` file of the Maven overlay, adjust for the following dependencies:
{% highlight xml %}
...
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.core.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>${hibernate.core.version}</version>
<scope>runtime</scope>
</dependency>
...
{% endhighlight %}
##Cleaner Locking Strategy
The above shows a JPA 2.0 implementation of an exclusive, non-reentrant lock, `JpaLockingStrategy`, to be used with the JPA-backed ticket registry.
The above shows a JPA 2.0 implementation of an exclusive, non-reentrant lock,
`JpaLockingStrategy`, to be used with the JPA-backed ticket registry.
This will configure the cleaner with the following defaults:
......@@ -112,16 +137,23 @@ This will configure the cleaner with the following defaults:
# Database Configuration
## JDBC Driver
CAS must have access to the appropriate JDBC driver for the database. Once you have obtained the appropriate driver and configured the data source, place the JAR inside the lib directory of your web server environment (i.e. `$TOMCAT_HOME/lib`)
CAS must have access to the appropriate JDBC driver for the database. Once you have obtained
the appropriate driver and configured the data source, place the JAR inside the lib directory
of your web server environment (i.e. `$TOMCAT_HOME/lib`)
## Schema
If the user has sufficient privileges on start up, the database tables should be created. The database user MUST have `CREATE/ALTER` privileges to take advantage of automatic schema generation and schema updates.
If the user has sufficient privileges on start up, the database tables should be created.
The database user MUST have `CREATE/ALTER` privileges to take advantage of automatic
schema generation and schema updates.
## Deadlocks
The Hibernate SchemaExport DDL creation tool *may* fail to create two very import indices when generating the ticket tables. The absence of these indices dramatically increases the potential for database deadlocks under load.
If the indices were not created you should manually create them before placing your CAS configuration into a production environment.
The Hibernate SchemaExport DDL creation tool *may* fail to create two very import indices
when generating the ticket tables. The absence of these indices dramatically increases the
potential for database deadlocks under load.
If the indices were not created you should manually create them before placing your CAS
configuration into a production environment.
To review indices, you may use the following MYSQL-based sample code below:
......@@ -155,7 +187,10 @@ CREATE INDEX "TGT_TGT_FK_I"
## Ticket Cleanup
The use `JpaLockingStrategy` is strongly recommended for HA environments where multiple nodes are attempting ticket cleanup on a shared database. `JpaLockingStrategy` can auto-generate the schema for the target platform. A representative schema is provided below that applies to PostgreSQL:
The use `JpaLockingStrategy` is strongly recommended for HA environments where
multiple nodes are attempting ticket cleanup on a shared database.
`JpaLockingStrategy` can auto-generate the schema for the target platform.
A representative schema is provided below that applies to PostgreSQL:
{% highlight sql %}
......@@ -169,37 +204,17 @@ ALTER TABLE locks ADD CONSTRAINT pk_locks
PRIMARY KEY (application_id);
{% endhighlight %}
<div class="alert alert-warning"><strong>Platform-Specific Issues</strong><p>The exact DDL to create the LOCKS table may differ from the above. For example, on Oracle platforms the `expiration_date` column must be of type `DAT`E. Use the `JpaLockingStrategy` which can create and update the schema automatically to avoid platform-specific schema issues.</p></div>
<div class="alert alert-warning"><strong>Platform-Specific Issues</strong><p>The exact DDL to create
the LOCKS table may differ from the above. For example, on Oracle platforms
the `expiration_date` column must be of type `DAT`E. Use the `JpaLockingStrategy`
which can create and update the schema automatically to avoid platform-specific schema issues.</p></div>
## Connection Pooling
It is ***strongly*** recommended that database connection pooling be used in a production environment. The following configuration makes use of the c3p0 connection pooling library, which would replace the `dataSource` bean found in the `ticketRegistry.xml` example above:
{% highlight xml %}
...
<bean
id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
p:driverClass="${database.driverClass}"
p:jdbcUrl="${database.url}"
p:user="${database.user}"
p:password="${database.password}"
p:initialPoolSize="${database.pool.minSize}"
p:minPoolSize="${database.pool.minSize}"
p:maxPoolSize="${database.pool.maxSize}"
p:maxIdleTimeExcessConnections="${database.pool.maxIdleTime}"
p:checkoutTimeout="${database.pool.maxWait}"
p:acquireIncrement="${database.pool.acquireIncrement}"
p:acquireRetryAttempts="${database.pool.acquireRetryAttempts}"
p:acquireRetryDelay="${database.pool.acquireRetryDelay}"
p:idleConnectionTestPeriod="${database.pool.idleConnectionTestPeriod}"
p:preferredTestQuery="${database.pool.connectionHealthQuery}"
/>
...
{% endhighlight %}
The following pool configuration parameters are provided for information only and may serve as a reasonable starting point for configuring a production database connection pool.
It is ***strongly*** recommended that database connection pooling be used in a p
production environment. Based on the example above, the following pool configuration parameters are provided
for information only and may serve as a reasonable starting point for configuring a production database connection pool.
<div class="alert alert-info"><strong>Usage Tip</strong><p>Note the health check query is specific to PostgreSQL.</p></div>
......@@ -241,20 +256,10 @@ database.pool.connectionHealthQuery=select 1
# when an error is encountered during acquisition.
database.pool.acquireRetryAttempts=5
# Amount of time in ms to wait between successive aquire retry attempts.
# Amount of time in ms to wait between successive acquire retry attempts.
database.pool.acquireRetryDelay=2000
{% endhighlight %}
The following maven dependency for the library must be included in your Maven overlay:
{% highlight xml %}
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>${c3p0.version}</version>
<scope>runtime</scope>
</dependency>
{% endhighlight %}
# Platform Considerations
......@@ -277,7 +282,11 @@ InnoDB tables are easily specified via the use of the following Hibernate dialec
###BLOB vs LONGBLOB
Hibernate on recent versions of MySQL (e.g. 5.1) properly maps the `@Lob` JPA annotation onto type `LONGBLOB`, which is very important since these fields commonly store serialized graphs of Java objects that grow proportionally with CAS SSO session lifetime. Under some circumstances, Hibernate may treat these columns as type `BLOB`, which have storage limits that are easily exceeded. It is recommended that the generated schema be reviewed and any BLOB type columns be converted to `LONGBLOB`.
Hibernate on recent versions of MySQL (e.g. 5.1) properly maps the `@Lob` JPA annotation onto type `LONGBLOB`,
which is very important since these fields commonly store serialized graphs of Java objects that
grow proportionally with CAS SSO session lifetime. Under some circumstances, Hibernate may treat
these columns as type `BLOB`, which have storage limits that are easily exceeded. It is
recommended that the generated schema be reviewed and any BLOB type columns be converted to `LONGBLOB`.
The following MySQL statement would change this `SERVICES_GRANTED_ACCESS_TO` column's type to `LONGBLOB`:
......
---
layout: default
title: CAS - JSON Service Registry
---
# JSON Service Registry
This DAO reads services definitions from JSON configuration files at the application context initialization time.
JSON files are
expected to be found inside a configured directory location and this DAO will recursively look through
the directory structure to find relevant JSON files.
{% highlight xml %}
<bean id="serviceRegistryDao" class="org.jasig.cas.services.JsonServiceRegistryDao"
c:configDirectory="file:/etc/cas/json" />
{% endhighlight %}
A sample JSON file follows:
{% highlight json %}
{
"@class" : "org.jasig.cas.services.RegexRegisteredService",
"id" : 103935657744185,
"description" : "Service description",
"serviceId" : "https://**",
"name" : "testJsonFile",
"theme" : "testtheme",
"proxyPolicy" : {
"@class" : "org.jasig.cas.services.RegexMatchingRegisteredServiceProxyPolicy",
"pattern" : "https://.+"
},
"accessStrategy" : {
"@class" : "org.jasig.cas.services.DefaultRegisteredServiceAccessStrategy"
},
"evaluationOrder" : 1000,
"usernameAttributeProvider" : {
"@class" : "org.jasig.cas.services.DefaultRegisteredServiceUsernameProvider"
},
"logoutType" : "BACK_CHANNEL",
"requiredHandlers" : [ "java.util.HashSet", [ "handler1", "handler2" ] ],
"attributeReleasePolicy" : {
"@class" : "org.jasig.cas.services.ReturnAllowedAttributeReleasePolicy",
"attributeFilter" : {
"@class" : "org.jasig.cas.services.support.RegisteredServiceRegexAttributeFilter",
"pattern" : "\\w+"
},
"allowedAttributes" : [ "java.util.ArrayList", [ "uid", "cn", "sn" ] ]
}
}
{% endhighlight %}
<div class="alert alert-warning"><strong>Clustering Services</strong><p>
You MUST consider that if your CAS server deployment is clustered, each CAS node in the cluster must have
access to the same set of JSON configuration files as the other, or you may have to devise a strategy to keep
changes synchronized from one node to the next.
</p></div>
The JSON service registry is also able to auto detect changes to the specified directory. It will monitor changes to recognize
file additions, removals and updates and will auto-refresh CAS so changes do happen instantly.
The naming convention for new JSON files is recommended to be the following:
{% highlight bash %}
JSON fileName = serviceName + "-" + serviceNumericId + ".json"
{% endhighlight %}
Based on the above formula, for example the above JSON snippet shall be named: `testJsonFile-103935657744185.json`
<div class="alert alert-warning"><strong>Duplicate Services</strong><p>
As you add more files to the directory, you need to be absolutely sure that no two service definitions
will have the same id. If this happens, loading one definition will stop loading the other. While service ids
can be chosen arbitrarily, make sure all service numeric identifiers are unique. CAS will also output warnings
if duplicate data is found.
</p></div>
\ No newline at end of file
---
layout: default
title: CAS - LDAP Service Registry
---
# LDAP Service Registry
Service registry implementation which stores the services in a LDAP Directory.
Uses an instance of `LdapRegisteredServiceMapper`, that by default is `DefaultLdapRegisteredServiceMapper`
in order to configure settings for retrieval, search and persistence of service definitions.
By default, entries are assigned the `objectclass` `casRegisteredService`
attribute and are looked up by the `uid` attribute.
Support is enabled by adding the following module into the Maven overlay:
{% highlight xml %}
<dependency>