Clustering CAS 3.3.5, Jboss Cache 3.1 And Jboss AS 5.1

After sometimes to research and figuring how to make CAS 3.3.5 work with Jboss Cache 3.1 finally i could make it worked together nicely.

There so many issue to make this work became possible, and the worse things is there is no good documentation for CAS 3.3.5 to work with Jboss Cache 3.1. That is one of my reason to release this post.

Before you start this implementation, you need to read this. Those tutorial is working for jboss version 4.x and that is also the basic idea to create CAS working in Jboss AS 5.1

The earliest step to start this tutorial you have to create a folder as your CAS working directory and put a pom.xml file like this in that folder

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!-- change depends on your groupId -->
    <groupId>org.jug.id.brainmaster</groupId>
    <!-- change depends on your artifactId -->
    <artifactId>cas-tutorial</artifactId>
    <packaging>war</packaging>
    <!-- change depends on your version -->
    <version>1.0-SNAPSHOT</version>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <warName>cas</warName>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.jasig.cas</groupId>
            <artifactId>cas-server-webapp</artifactId>
            <version>${cas.version}</version>
            <type>war</type>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <properties>
        <cas.version>3.3.5</cas.version>
    </properties>

        <repositories>
        <repository>
            <id>ja-sig</id>
            <url>http://oss.sonatype.org/content/repositories/releases/</url>
        </repository>
    </repositories>
</project>

Try to build your maven project. Go to your working folder, then run “mvn clean install -DskipTests=true“.

If that is succeed then you will see a folder called target in your working folder.

Second step you need to add the following repository under your to fetch the jboss cache artifact from jboss repository.

        <repository>
            <id>maven2.repository.jboss.org</id>
            <name>JBoss Repository</name>
            <url>http://repository.jboss.org/maven2</url>
        </repository>

and add this under your dependencies tag

        <dependency>
            <groupId>org.jboss.cache</groupId>
            <artifactId>jbosscache-core</artifactId>
            <version>3.1.0.GA</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.jasig.cas</groupId>
            <artifactId>cas-server-integration-jboss</artifactId>
            <version>${cas.version}</version>
            <scope>runtime</scope>
            <exclusions>
                <exclusion>
                    <groupId>concurrent</groupId>
                    <artifactId>concurrent</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>jboss</groupId>
                    <artifactId>jboss-serialization</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>jboss</groupId>
                    <artifactId>jboss-jmx</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>jboss</groupId>
                    <artifactId>jboss-common</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>jboss</groupId>
                    <artifactId>jboss-j2ee</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>jboss</groupId>
                    <artifactId>jboss-minimal</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>jboss</groupId>
                    <artifactId>jboss-system</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>jboss</groupId>
                    <artifactId>jbosscache-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

Ok now we have completed set up our maven environment.
Please note you will need to excluded the conflict library from your cas.war before you could deploy it on your Jboss AS 5.1.
For that case please change your maven-war-plugin configuration to excluding library which already own by jboss common lib

            &lt;plugin&gt;
                &lt;artifactId&gt;maven-war-plugin&lt;/artifactId&gt;
                &lt;executions&gt;
                    &lt;execution&gt;
                        &lt;id&gt;default&lt;/id&gt;
                        &lt;phase&gt;package&lt;/phase&gt;
                        &lt;goals&gt;
                            &lt;goal&gt;war&lt;/goal&gt;
                        &lt;/goals&gt;
                        &lt;configuration&gt;
                            &lt;warName&gt;cas&lt;/warName&gt;
                            &lt;packagingExcludes&gt;
                                WEB-INF/classes/log4j.properties
                                WEB-INF/lib/commons-collections-2.1.1.jar,
                                WEB-INF/lib/commons-lang-2.2.jar,
                                WEB-INF/lib/ejb3-persistence-1.0.1.GA.jar,
                                WEB-INF/lib/persistence-api-1.0.jar,
                                WEB-INF/lib/hibernate-3.2.6.ga.jar,
                                WEB-INF/lib/hibernate-annotations-3.3.1.GA.jar,
                                WEB-INF/lib/hibernate-commons-annotations-3.0.0.ga.jar,
                                WEB-INF/lib/jta-1.0.1B.jar,
                                WEB-INF/lib/servlet-api-2.4.jar,
                            &lt;/packagingExcludes&gt;
                        &lt;/configuration&gt;
                    &lt;/execution&gt;
                &lt;/executions&gt;
            &lt;/plugin&gt;

By default Cas project will not give you the source code, since we build it from the pom.xml. So, to make it work with our own implementation you need to override the files in your working directory. Let’s get to our own IDE, in this case i use eclipse but you could use your preferred one.

Create a eclipse project using maven, mvn eclipse:eclipse, or if you had m2eclipse plugin you could import your working directory as your project directory in eclipse.

Clustering CAS using JbossTicketRegistry in CAS 3.3.5 default will use the Jboss Cache 2.2.0 but Jboss AS 5.1 bundle with Jboss Cache 3.1 this will make the JbossTicketRegistry not working in Jboss AS 5.1 so we need to hack a little those JbossTicketRegistry.

Create a class, for example JugIdCacheTicketRegistry.

public class JugIdCacheTicketRegistry extends AbstractDistributedTicketRegistry {

    @SuppressWarnings(&quot;unused&quot;)
    private static final String FQN_TICKET = &quot;ticket&quot;;

    @NotNull
    private Cache&lt;Object, Object&gt; cache;

    @Override
    protected void updateTicket(Ticket ticket) {
        try {
            this.cache.put(&quot;ticket&quot;, ticket.getId(),
                    new MarshalledValue(ticket));
        } catch (CacheException e) {
            throw new RuntimeException(e);
        } catch (NotSerializableException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void addTicket(Ticket ticket) {
        try {
            if (this.log.isDebugEnabled()) {
                this.log.debug(&quot;Adding ticket to registry for: &quot;
                        + ticket.getId());
            }
            this.cache.put(&quot;ticket&quot;, ticket.getId(),
                    new MarshalledValue(ticket));
        } catch (CacheException e) {
            this.log.error(e, e);
            throw new RuntimeException(e);
        } catch (NotSerializableException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean deleteTicket(String ticketId) {
        try {
            if (this.log.isDebugEnabled()) {
                this.log
                        .debug(&quot;Removing ticket from registry for: &quot; + ticketId);
            }
            return (this.cache.remove(&quot;ticket&quot;, ticketId) != null);
        } catch (CacheException e) {
            this.log.error(e, e);
        }
        return false;
    }

    @Override
    public Ticket getTicket(String ticketId) {
        try {
            if (this.log.isDebugEnabled())
                this.log.debug(&quot;Retrieving ticket from registry for: &quot;
                        + ticketId);

            MarshalledValue val = (MarshalledValue) this.cache.get(&quot;ticket&quot;,
                    ticketId);
            return getProxiedTicketInstance((Ticket) val.get());
        } catch (CacheException e) {
            this.log.error(e, e);
        } catch (IOException e) {
            this.log.error(e, e);
        } catch (ClassNotFoundException e) {
            this.log.error(e, e);
        }
        return null;
    }

    @Override
    public Collection&lt;Ticket&gt; getTickets() {
        try {
            Node&lt;Object, Object&gt; node = this.cache.getNode(&quot;ticket&quot;);
            if (node == null) {
                return Collections.emptyList();
            }
            Set&lt;Object&gt; keys = node.getKeys();
            List&lt;Ticket&gt; list = new ArrayList&lt;Ticket&gt;();
            for (Object key : keys) {
                MarshalledValue val = (MarshalledValue)node.get(key);
                list.add((Ticket)val.get());
            }
            return list;
        } catch (Exception e) {
            return Collections.emptyList();
        }
    }

    public void setCache(Cache&lt;Object, Object&gt; cache) {
        this.cache = cache;
    }
}

And create JugIdCasConnectionFactory

public class JugIdCasConnectionFactory implements FactoryBean, InitializingBean, DisposableBean {

    @SuppressWarnings(&quot;unused&quot;)
    private final Log log;
    private Cache&lt;Object, Object&gt; cache;
    private CacheManager cacheManager;
   
    public JugIdCasConnectionFactory() {
        this.log = LogFactory.getLog(super.getClass());
    }
   
    @Override
    public Object getObject() throws Exception {
        return this.cache;
    }

    @SuppressWarnings(&quot;unchecked&quot;)
    @Override
    public Class&lt;Cache&gt; getObjectType() {
         return Cache.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Context context = new InitialContext();
        cacheManager = (CacheManager) context.lookup(&quot;java:CacheManager&quot;);
        this.cache = cacheManager.getCache(&quot;ha-partition&quot;, true);
       
    }

    @Override
    public void destroy() throws Exception {
        this.cache.destroy();
    }
}

you can also change this (“ha-partition”) value into another option which provided by jboss, by default ha-partition will use synchronize replication for it cache. Another alternative you can use “field-granularity-session-cache” for asynchronous replication.

To complete all the puzzle there something else that we need to configure.
Create folder under the WEB-INF/spring-configuration and make sure

Create a file name with “applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<!-- Default Configuration provided by cas -->
    <description>
        This is the main Spring configuration file with some of the main "core" classes defined. You shouldn't really modify this unless you know what you're doing!
    </description>

    <!-- Message source for this context, loaded from localized "messages_xx" files -->
    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basename="messages" />

    <bean id="servicesManager" class="org.jasig.cas.services.DefaultServicesManagerImpl">
        <constructor-arg index="0" ref="serviceRegistryDao" />
    </bean>

    <!-- Job to periodically reload services from service registry. This job is needed for a clustered CAS environment since service changes in one CAS node are not known to the other until a reload. -->
    <bean id="serviceRegistryReloaderJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean" p:targetObject-ref="servicesManager" p:targetMethod="reload" />

    <bean id="periodicServiceRegistryReloaderTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean" p:jobDetail-ref="serviceRegistryReloaderJobDetail" p:startDelay="120000" p:repeatInterval="120000" />

    <bean id="httpClient" class="org.jasig.cas.util.HttpClient" p:readTimeout="5000" p:connectionTimeout="5000" />

    <bean id="persistentIdGenerator" class="org.jasig.cas.authentication.principal.ShibbolethCompatiblePersistentIdGenerator" p:salt="casrocks" />

    <!-- CentralAuthenticationService -->
    <bean id="centralAuthenticationService" class="org.jasig.cas.CentralAuthenticationServiceImpl" p:ticketGrantingTicketExpirationPolicy-ref="grantingTicketExpirationPolicy" p:serviceTicketExpirationPolicy-ref="serviceTicketExpirationPolicy" p:authenticationManager-ref="authenticationManager" p:ticketGrantingTicketUniqueTicketIdGenerator-ref="ticketGrantingTicketUniqueIdGenerator" p:ticketRegistry-ref="ticketRegistry" p:servicesManager-ref="servicesManager" p:persistentIdGenerator-ref="persistentIdGenerator" p:uniqueTicketIdGeneratorsForService-ref="uniqueIdGeneratorsMap" />

    <bean id="proxy10Handler" class="org.jasig.cas.ticket.proxy.support.Cas10ProxyHandler" />

    <bean id="proxy20Handler" class="org.jasig.cas.ticket.proxy.support.Cas20ProxyHandler" p:httpClient-ref="httpClient" p:uniqueTicketIdGenerator-ref="proxy20TicketUniqueIdGenerator" />

    <!-- ADVISORS -->
    <bean id="advisorAutoProxyCreator" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

    <bean id="validationAnnotationBeanPostProcessor" class="org.inspektr.common.ioc.validation.ValidationAnnotationBeanPostProcessor" />

    <!-- The scheduler bean wires up any triggers that define scheduled tasks -->
    <bean id="scheduler" class="org.jasig.cas.util.AutowiringSchedulerFactoryBean" />
   
    <!-- our two new Classes, don't change the id -->
    <bean id="ticketRegistry" class="org.jug.id.brainmaster.jboss.cas.JugIdCacheTicketRegistry" p:cache-ref="cache" />
    <bean id="cache" class="org.jug.id.brainmaster.jboss.cas.JugIdCasConnectionFactory"/>

Create file “ticketGrantingTicketCookieGenerator.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    <description>
        Defines the cookie that stores the TicketGrantingTicket.  You most likely should never modify these (especially the "secure" property).
        You can change the name if you want to make it harder for people to guess.
    </description>
    <!--  change the domain with your current configuration -->
    <bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
        p:cookieSecure="true"
        p:cookieMaxAge="-1"
        p:cookieName="CASTGC"
        p:cookiePath="/cas"
        p:cookieDomain="yourdomain.com" />
</beans>

Create file “warnCookieGenerator.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    <description>
    This Spring Configuration file describes the cookie used to store the WARN parameter so that a user is warned whenever the CAS service
    is used.  You would modify this if you wanted to change the cookie path or the name.
    </description>
   
    <!--  change the domain with your current configuration -->
    <bean id="warnCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
        p:cookieSecure="true"
        p:cookieMaxAge="-1"
        p:cookieName="CASPRIVACY"
        p:cookiePath="/cas"
        p:cookieDomain="yourdomain.com" />
</beans>

Create file “ticketRegistry.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:p="http://www.springframework.org/schema/p"
      xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
    <description>Configuration for the default TicketRegistry which stores the tickets in-memory and cleans them out as specified intervals.</description>
       
    <!-- Ticket Registry -->
    <bean id="ticketRegistry" class="org.jug.id.brainmaster.jboss.cas.JugIdCacheTicketRegistry" />

    <!--Quartz -->
    <!-- TICKET REGISTRY CLEANER -->
    <bean id="ticketRegistryCleaner" class="org.jasig.cas.ticket.registry.support.DefaultTicketRegistryCleaner" p:ticketRegistry-ref="ticketRegistry" />
   
    <bean id="jobDetailTicketRegistryCleaner" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean" p:targetObject-ref="ticketRegistryCleaner" p:targetMethod="clean" />
   
    <bean id="triggerJobDetailTicketRegistryCleaner" class="org.springframework.scheduling.quartz.SimpleTriggerBean" p:jobDetail-ref="jobDetailTicketRegistryCleaner" p:startDelay="20000" p:repeatInterval="5000000" />

</beans>

Try to build your project again and copy the cas.war in your target directory into your deployment folder. you will find your ticket id put in the cache using jmx-console, that is mean you had success to configure your ticket using jboss cache.

Incoming search terms:

  • cas jboss cache (6)
  • CAS Jbosscache (1)
  • clustering cas 3 3 (1)
  • jboss 5 1 cache manager (1)
0saves
If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.

Tagged , , , . Bookmark the permalink.

One Response to Clustering CAS 3.3.5, Jboss Cache 3.1 And Jboss AS 5.1

  1. thuns says:

    pake bahasa indonesia napa…! susah amat bahasanya :P

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>