...
Each subproject creates a JAR or WAR file. The first project is cas-server-core and it builds a JAR file containing about 95% or more of all the CAS code. It has to be built first because all the other projects depend on it. After that, there are projects to create optional components that you may or may not choose to use.
Building the WAR
The CAS WAR that you actually run in the Web server is built in two steps in two Maven projects.
Apereo distributes a project called cas-server-webapp to create an initial template WAR file. This WAR is not particularly useful, but it contains at least a starter version of all the Spring XML used to configure CAS and many CSS, JSP, and HTML pages. It also contains a WEB-INF/lib with the basic JAR libraries needed by a CAS system. This unmodified Apereo WAR file is a "template" that Yale modifies or updates to create our final WAR.
Although you can modify the cas-server-webapp project directly, this results in a directory with a mixture of Apereo files and Yale files, and the next time you get a new CAS release from Apereo you have to sift through them to find out which ones have been changed by Apereo.
Apereo recommends using the WAR Overlay feature of Maven. Yale creates a second WAR project called cas-server-yale-webapp that contains only the files that Yale has changed or added. Generally the WAR Overlay includes:
- Yale Java source for Spring Beans that are slightly modified versions of standard Apereo beans.
- Yale "branded" look and feel (CSS, JSP, HTML).
- Spring XML to configure Apereo options that Yale has selected and a few Yale additions.
- A pom.xml file with dependency statements for Apereo optional and Yale additional JAR files referenced by the Spring XML that will be added to WEB-INF/lib.
Maven realizes this is a WAR Overlay project because the cas-server-webapp WAR is referenced as a dependency. Since one WAR cannot be included inside another, this is a Maven idiom that everything in this project modifies or adds files to the cas-server-webapp template WAR file. Files in the Yale Overlay project replace files with the same name in the Template cas-server-webapp project. Files with new names are added to the output WAR. All the XML, CSS, HTML, JSP, and other files in the template WAR that do not appear in the Yale WAR Overlay project are simply copied to the output WAR as is.
While the WAR Overlay process provides a way to update, replace, or add files, it does not provide any way to delete files you no longer want. However, the Maven WAR building plugin can be configured in the WAR Overlay POM with an "exclude" list of files to remove from the template WAR. There are two cases where this is used:
- Apereo builds CAS for Tomcat, which provides only a limited number of libraries to the applications, and so it will run on older versions of Java. Yale runs CAS under JBoss which provides a larger number of libraries standard, and JBoss expects later versions of Java. JBoss complains at startup when it finds that a WAR file contains JARs in its WEB-INF/lib that conflict with libraries built into JBoss. We exclude the libraries that JBoss or modern Java provide by default.
- Sometimes Apereo code depends on a slightly older version of a JAR file, but Yale code requires a slightly newer version of the JAR. If cas-server-webapp had a dependency on 1.2.3 and Yale depends on 1.2.4, then the normal WAR Overlay process would copy both versions of the JAR file (1.2.3 and also 1.2.4) to the output WEB-INF/lib. So we add an Exclude statement to delete the old version (1.2.3) to make sure the version we want is the one we get.
Version Numbers and Project Structure
The first Apereo version of CAS was 3.0.0 and now there are CAS 4.0.x versions. CAS 1 and CAS 2 were written at Yale and are no longer meaningful, but this means that the Maven version numbers starting 1.x and 2.x are free and will never be used by Apereo. So Yale internally is reusing the 1.x Version numbers for Yale modules and for Yale modified versions of Apereo modules. Since Yale only periodically updates its CAS version, we skip over a lot of Apereo version numbers:
Apero Version | Yale Version |
---|---|
3.4.x | 1.0.x |
3.5.x | 1.1.x |
4.0.x | 1.2.x |
This becomes important (and confusing) when Yale has to make a change to an Apereo JAR file to fix a problem we cannot ignore and cannot wait for a new Apereo release. In the Maven repositories there will be a vanilla Apereo artifact ending in *-4.0.2.jar and Yale will then add its own artifact named *-1.2.0.jar. If the artifact is one of the optional libraries, then we will simply add the 1.2.0 dependency to the WAR Overlay project, but if this is cas-server-core or anything else in the template WAR built by the cas-server-webapp project then we also have to add the -4.0.2.jar file to the Exclude list of the Maven WAR plugin configuration in the WAR Overlay POM file.
Although there may be additional code in various stages of development, a CAS release at Yale only depends on three Yale projects:
- cas-server-expired-password-change (to prompt the user to change a password that is a year old)
- cas-server-support-CushyTicketRegistry (only the configuration module is currently used to configure ehcache)
- cas-server-yale-webapp (the WAR Overlay with all the Yale configuration and HTML changes)
In CAS 3 development Yale created a source project that included the standard Apereo source along with the three additional Yale projects in a single directory. The advantage of this project structure is that Yale was recompiling all the source and was building at least formal artifacts with the Yale version numbers for all the Apereo code. If Yale ever wanted to change an Apereo module, all it had to do was to make a change in the Apereo source and then change the dependency version number for that artifact in the WAR Overlay from the vanilla 3.5.x to the Yale 1.1.x. It made modifications to Apereo easy, but it created a lot of work when you upgraded to a new CAS release where you ended up changing the project structure of a bunch of Apereo modules you were never really going to change.
So in CAS 4 we change the Yale project structure to a much simpler and cleaner approach. All the vanilla Apereo code is removed initially. If there are modification we need to make to an Apereo module, that will be a bunch of work we can do later on when it is needed. The Yale project then consists of only the three Yale added projects as subdirectories of a parent CAS project, plus a parent POM as the only file in that parent directory.
The Yale parent POM is a modified version of the parent POM in the vanilla Apereo CAS 4.0.2 source directory. First, we change the version number from 4.0.2 to Yale's 1.2.0 because that is the default version of any Yale written or modified code. Then we comment out all the processing that Apereo has added to Maven to do open source project housekeeping. When you build an open source project for general distribution, there are Maven plugins that make sure that your comment files list the open source licenses of all the open source dependency libraries you use, and plugins that complain about syle (missing JavaDoc commments for example), and plugins that do special release packaging. Yale is a customer and our projects only have to run at Yale, so we don't need all those checks.
We should note that there are two distinct meanings to the term "parent" in a Parent POM.
The parent directory is a Maven project with a POM file with a packaging type of "pom". For example, the Apereo parent POM for CAS 4.0.0 begins with the following declaration:
Code Block |
---|
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server</artifactId>
<packaging>pom</packaging>
<version>4.0.0</version> |
This file is processed when anyone does a Maven "mvn install" operation on the parent directory. In this case, the important part of the Parent Directory POM is that it contains "<module>cas-server-core</module>" statements for every subdirectory that builds an artifact. Maven processes each <module> statement in the order they appear in the Parent Directory POM file, so this file can be written so that a JAR file is built and stored in the Maven repository before a subsequent module is compiled that depends on that JAR file.
There is, however, a second meaning of "parent" that relates to the projects in the subdirectories. Each subproject POM file contains a <parent> reference:
Code Block |
---|
<parent>
<artifactId>cas-server</artifactId>
<groupId>org.jasig.cas</groupId>
<version>4.0.0</version>
</parent> |
The <parent> directory contains DepedencyManagement statements, Maven plugin configuration statements, and <parameter> definitions that are shared by all the subprojects that reference it. This ensures that, for example, all the subdirectory projects are complied with the same version of the Apache commons-io JAR library.
It is a common Maven programming convention that the <parent> POM is also the Parent Directory POM. That is the way that Apereo organizes CAS and it is the way that Yale organizes its CAS additions project, but it is not a requirement. Technically, the Parent simply has to be a project of type "<packaging>pom</packaging>" that has already been processed by Maven and has already been stored in the Maven local repository before it is referenced in a <parent> statement of a subproject POM. When the <parent> POM is also the Parent Directory POM and Maven is invoked to build the parent directory, then by definition the Parent Directory POM is processed first and is stored in the Maven Repository before any of the subdirectory projects are built. However, Shibboleth 3 is an example of another approach where the <parent> POM is itself a subdirectory and the Parent Directory POM but is the first <module> statement and is therefore built and stored in the local Maven repository before any of the subprojects that reference it.
The vanilla Apereo CAS source project has its Parent Directory POM that establishes the global defaults and builds all the Apereo artifacts at version 4.0.2. You can recompile them in your sandbox or you can simply let Maven download the artifacts from the Yale Artifactory network server or from the Internet. Then the Yale CAS source project has its modified version of the Apereo Parent Directory POM with all the open source project boilerplate processing commented out and maybe a few version numbers incremented (for example, Yale changes the Java compiler versions from 1.6 to 1.7 for its own coding). The WAR Overlay (cas-server-yale-webapp) project can get the template WAR from an instance previously complied on the sandbox machine and stored in the local Maven repository, or it can get the standard version of the template WAR complied by Apereo and stored on the internet servers. Either way the template WAR is the same file (except for timestamps) and the WAR Overlay processing produces the same result.
In CAS 3.5.x (Yale version 1.1.x) the Yale CAS source project contains the three Yale subprojects, but it also contains a bunch of vanilla Apereo source subprojects. Some of them build Yale versions of Apereo modules (that is, a cas-server-core-1.1.1.jar file), but just because we build a Yale version of the artifact doesn't mean that it is ever used or deployed anywhere. If you look at the WAR Overlay project (cas-server-yale-webapp) you may find that the <dependency> statement in that POM references the vanilla Apereo Version number (3.5.2.1) and so the Yale version of the artifact we have just compiled is stored but ignored. If you are debugging a probem and need to add trace statements to the vanilla Apereo code on the Sandbox, you can temporarily change the Version number in the WAR Overlay to use your modified code, but then after the problem has been resolved you can revert the WAR Overlay POM to its standard settings and go back to using vanilla Apereo code.
Because the CAS 4.0.x (Yale version 1.2.x) project does not normally contain a copy of any Apereo project, making temporary changes for sandbox debugging is a much larger amount of work. You have to build a custom version of the Apereo JAR file, and that means making all the necessary changes to the project and subproject directories to make it work. It is important if you do this to NEVER compile a modified version of the Apereo source and store it in the local Maven repository under the vanilla Apereo version number. Once you have stored your own modified version of cas-server-core-4.0.2.jar then without some custom cleanup on your part it will override the real vanilla version of the module and you will always get the modified version on that Sandbox machine. So if you decide to do some work on a previously vanilla Apereo project, the first thing to do is to change the Version number in the project POM.
Why Not Vanilla?
Apereo code contains some bugs or sloppy coding. Yale has found them and reported them informally, but there has not been much interest in fixing them. Some examples of differences of opinion or bugs:
- The "service=" parameter value on the CAS login has to exactly match the value of the same parameter on the validate, serviceValidate, or samlValidate request. There is a difference of option about how carefully they have to match. The JASIG code matched the entire string excluding JSESSIONID if it was present. Yale believes that the entire query string (everything after the "?") should be excluded, and maybe the match should stop with context (https://servername/context/stuff-to-be-ingnored). This changes the substrings used in the equals() test.
- When you are having a CAS problem, you may want to insert additional logging. For example, if the validate request is failing you may want to print out the exact service= strings being compared and the point at which they differ.
- Apereo defaults all registered services to be able to Proxy, but it seems better if the default is that they cannot Proxy unless you explicitly authorize it. This involves changing the default value for allowedToProxy in AbstractRegisteredService.java from "true" to "false".
- AbstractTicket is a Serializable class, but Apereo source forgot to give it a VersionUID. As a result, every time you deploy a new version of cas-server-core you have to "cold start" CAS by wiping out all the current tickets. You should only have to do that if you actually have changed the Ticket classes, not just because you recompiled the library to change something that has nothing to do with Tickets.
- Yale does not use Single Sign-Out, but code in TicketGrantingTicketImpl added to support that option has a very small but non-zero chance of throwing a ConcurrentAccessException and possibly screwing CAS up. Since we don't need it, Yale might just comment out the statement that causes the problem.
Each of these is a one line change, and only the first is user visible and the last is important for reliablity. If CAS was not open source we would probably just live with the old code, but we have the chance to fix/change it.
Whether we use vanilla Apereo code or make Yale modifications depends on Yale requirements, staffing, and management. There is a cost and commitment to maintaining a modification, but there is also a problem if some application does not work correctly or if some Yale need is not being properly addressed.
The Parent pom.xml
The top level directory is the "parent" project. Typically it is named "cas-server" although this is simply a choice you make when you check the source out from SVN into Eclipse. The only thing in the parent project is its pom.xml file, and a few README-type files.
Changing the parent pom.xml file is one of the most complicated problems of migration from one CAS release to another. Any other file in CAS, whether Java source or HTML has pretty much the same purpose at Yale that it had in the original Apereo code.
However, the top level pom.xml file is not just about compiling the source and building the template WAR file (which is the part Yale needs). Apereo is in the business of maintaining an Open Source Project that is distributed widely on the Internet. A large part of their concern is to make sure that all the legal boilerplate is properly maintained. Do the files all have a proper copyright notice? Are all the licenses to the dependency files properly listed? Maven has some impressive support for doing all this open source release management, but unless that is something you specifically have learned, setting up the legal boilerplate is a daunting task.
Yale is a CAS customer. We may have our own files, but unless and until we contribute them back to Apereo we do not care if a file that nobody sees outside Yale has a proper open source copyright notice and license declaration. If we try to use the Apereo top level pom.xml file as distributed, then every time we try to cut a Yale release we get error messages for all the boilerplate that is missing from our own files. So we generally modify the Apereo top level POM file to remove the "open source project and distribution management" junk we don't need. This has to be done again each time we get a new release of CAS.
Every so often Apereo generates a release that has some new processing step added to the build, and that in turn requires adding something to the top level pom.xml file. When that happens we have to notice it and add the same configuration item to our version of the file.
The Yale top level pom.xml file contains the following sections:
- Version - The Yale pom.xml has Yale version IDs (1.1.2-SNAPSHOT) instead of Apereo (3.5.2.1).
- SCM - defines the Yale Subversion server where all Yale projects maintain their production source.
- Repositories - defines Yale's Artifactory, but merged with other sources for artifacts from the Apereo pom.
- pluginManagement - Maven is a modular system of extensible functions implemented by optional "plugin" modules. Yale has to maintain the plugin configurations that actually build the code (the AspectJ processing plugin for example) while dumping the open source project boilerplate managing steps (the maven-license-plugin and maven-notice-plugin for example). We also reconfigure some plugins. For example, when you are done working on Version 1.1.1-SNAPSHOT and want to create the official 1.1.1 Yale version of CAS and then reset the project to begin work on 1.1.2-SNAPSHOT, you call the Yale Jenkins Build job "Perform Maven Release" operation, which in turn uses the maven-release-plugin. At Yale we find that running the JUnit tests during this process is not only slow but a bad idea, so we change the configuration of the release plugin in the parent pom to bypass tests.
- dependencyMangement - the top level pom.xml lists standard version numbers for dependency JAR libraries shared by all the projects at compile and run time. Unfortunately, when Yale develops its own code it may need a later version of the same library. For example, Apereo is happy with commons-io Version 2.0, but Yale code needs Version 2.4. Apache is smart enough to make the 2.4 library downward compatible with programs written for 2.0, but swapping a newer better version of a library is tricky. The first step is that our top level pom.xml declares the 2.4 version to be the one we want so that all our projects use the same version. This also ensures that the 2.4 version of the commons-io JAR will be merged into the WAR during the Overlay processing.
- However, there is a final processing step that has to be done in the WAR Overlay project and is related to but not directly specified in the parent pom.xml file. Because we are using some unmodified CAS 3.5.2.1 modules (particularly cas-server-webapp) and then we merge in new libraries from the WAR Overlay project, the resulting cas.war file would have a WEB-INF/lib with both commons-io-2.0.jar (from vanilla cas-server-webapp) and commons-io-2.4.jar from the Yale Overlay WAR project. So we have to add an exclude statement in the configuration of maven-war-plugin in the WAR Overlay project to remove the unwanted commons-io-2.0.jar file). Generally speaking, we remove any file that the build process would normally include that is bad for JBoss or where there are multiple versions of the same code and we only want to keep the latest version.
- modules - The directory contains the source and pom files for all the Apereo code. However, we only want to compile things that have been changed. Maven only looks at subdirectories mentioned by a <module> statement in the top level pom.xml file. Initially we comment out all the Apereo <module> statements and add statements for the Yale subprojects. When an Apereo project is to be changed, then it is reassigned a Yale (1.1.x-SNAPSHOT) ID and its <module> statement is uncommented. From one CAS release to another the inventory of Apereo projects and <module> statements changes and has to be updated even if they start out all commented out.
- Parameters - At the end of the POM there are <parameter> statements that Apereo uses to specify the version numbers of dependency libraries and Java.
Subversion
One weakness of Subversion is that it tracks history based on the name and path of a file. If you rename or move a file, Subversion "loses" its history. Git does not suffer from this problem, and if we really did serious ongoing "development" in CAS all the time Git would be a much better source repository for us to use.
However, Subversion is the Yale standard for source used in production deployment, so everything has to be checked into Subversion at some point if only to put it into the DEV, TEST, and PROD computers in the machine room. Since Yale does not do continuous CAS development, but rather generates a CAS release and then runs it for years before starting work on a new release, a break in source control file history across that two year gap is acceptable.
So in any given cycle of CAS development, you begin by obtaining a copy of the current CAS release from Apereo. You then replace the top level pom.xml with a new one created by merging any changes made by Apereo since the last release with the prior Yale top level pom.xml (and this has to be done manually by someone who understands Maven). Then you merge in the Yale added subdirectories.
This becomes a new project in the /cas area of Yale Subversion. Because it is nominally new, you start without history. If you want to see the history for Apereo code, use Git. If you want to see the actual history of Yale modules, find the Subversion directory of previous CAS development projects.
The ReleaseNotes.html file in in the root of the cas-server project will identify the current state of new directories and any modified files. This should be a descriptive match for the physical code change history in SVNWith CAS 3 Yale starts with the Apereo CAS source directory tree and adds new subproject subdirectories for the Yale added code.
Starting with CAS 4, Yale creates a separate Yale-CAS project directory containing only our subprojects, but we copy the Apereo parent pom.xml file from the distributed Apereo Source distribution and modify it so it becomes a Yale specific file.
Building the WAR
Before you build the WAR, you have to build all the JAR files it will contain. Apereo JAR files can be downloaded by Maven from the Internet so they do not have to be rebuilt. Yale JAR files, and any Apereo JAR files that Yale decides to modify, have be be locally compiled and stored. Yale new and modified artifacts always have a Yale specific Version ID that distinguishes them from the Apereo CAS artifacts.
The CAS WAR that you actually run in the Web server is built in two steps in two Maven projects.
Apereo distributes a project called cas-server-webapp to create an initial template WAR file. This WAR is not particularly useful, but it contains at least a starter version of all the Spring XML used to configure CAS and many CSS, JSP, and HTML pages. It also contains a WEB-INF/lib with the basic JAR libraries needed by a CAS system. This unmodified Apereo WAR file is a "template" that Yale modifies or updates to create our final WAR.
Although you can modify the cas-server-webapp project directly, this results in a directory with a mixture of Apereo files and Yale files, and the next time you get a new CAS release from Apereo you have to sift through them to find out which ones have been changed by Apereo.
Apereo recommends using the WAR Overlay feature of Maven. Yale creates a second WAR project called cas-server-yale-webapp that contains only the files that Yale has changed or added. Generally the WAR Overlay includes:
- Yale Java source for Spring Beans that are slightly modified versions of standard Apereo beans.
- Yale "branded" look and feel (CSS, JSP, HTML).
- Spring XML to configure CAS options that Yale has selected and a few Yale additions.
- A pom.xml file with dependency statements for Apereo optional and Yale additional JAR files referenced by the Spring XML that will be added to WEB-INF/lib.
A normal WAR project simply takes all the files in the project and uses them to build a WAR. A WAR Overlay project, however, starts with a template WAR that has already been built. Maven knows that it is working with a WAR Overlay project when it discovers that the first <dependency> in the POM is a file of type "war". Since one WAR cannot contain another WAR inside it, Maven knows that it is supposed to start with the template (dependency) WAR and update it by replacing or adding files from this project to build the output WAR file.
The Template WAR built by cas-server-webapp contains most of the Spring XML configuration, an initial set of CSS, HTML, and JSP files, and almost all the JAR files needed by CAS in its WEB-INF/lib. It also contains the cas-server-core JAR because which has 95% of all the CAS code.
The Yale WAR Overlay project replaces the CSS, JS, HTML, and JSP files associated with the CAS login function to provide the Yale "look and feel" of the CAS login page that everyone expects. It also replaces some of the subsequent confirmation or error pages to also have the same look.
Yale has selected several CAS optional modules. We use cas-server-support-ldap because we authenticate user passwords to AD using LDAP protocol, and cas-server-integration-ehcache because we use ehcache to replicate tickets between CAS servers. These optional JAR files will have been built by Apereo and are available as Maven artifacts, but they were not included in the cas-server-webapp template because they are optional. So the Yale WAR Overly has to add these JAR files to its list of dependencies and configure these options in new or modified Spring XML files. We also must include the JAR files build by the two Yale projects.
While the WAR Overlay process provides a way to update, replace, or add files, it does not provide any way to delete files you no longer want. However, Maven builds a WAR using the maven-war-plugin which can be configured in the POM file. One configuration option is the <packagingExcludes> list that lists a file (or wildcard set of files) in the Template WAR that are to be removed instead of being copied to the output WAR. There are two cases where this is used:
- Some JAR files used by the cas-server-webapp project to build a WAR that will run in Tomcat are inappropriate for JBoss. JBoss provides its own versions of some JAR files, so the JAR file provided by the application should be deleted.
- If you build everything with a single Maven environment then Maven will ensure that the latest required version of any given JAR file is used by all modules. However, the cas-server-webapp WAR file is built by Apereo with the latest version of all the libraries it uses. Yale has a few of its own projects that it builds in its own Maven environment, and every so often Yale will use a later version of a JAR file than Apereo required. So if cas-server-webapp had a dependency on 1.2.3 of some JAR file and Yale depends on 1.2.4, then the normal WAR Overlay process would copy both versions of the JAR file (1.2.3 and also 1.2.4) to the output WEB-INF/lib. So we add a packagingExcludes statement in the WAR Overly POM to delete the old version (1.2.3) to make sure the only version of the JAR in the WAR we are going to run at Yale has only the version of the library we want to use.
Version Numbers and Project Structure
The first Apereo version of CAS was 3.0.0 and now there are CAS 4.0.x versions. CAS 1 and CAS 2 were written at Yale and are no longer meaningful, but this means that the Maven version numbers starting 1.x and 2.x are free and will never be used by Apereo. So Yale internally is reusing the 1.x Version numbers for Yale modules and for Yale modified versions of Apereo modules. Since Yale only periodically updates its CAS version, we skip over a lot of Apereo version numbers:
Apero Version | Yale Version |
---|---|
3.4.x | 1.0.x |
3.5.x | 1.1.x |
4.0.x | 1.2.x |
This becomes important (and confusing) when Yale has to make a change to an Apereo JAR file to fix a problem we cannot ignore and cannot wait for a new Apereo release. In the Maven repositories there will be a vanilla Apereo artifact ending in *-4.0.2.jar and Yale will then add its own artifact named *-1.2.0.jar. If the artifact is one of the optional libraries, then we will simply add the 1.2.0 dependency to the WAR Overlay project, but if this is cas-server-core or anything else in the template WAR built by the cas-server-webapp project then we also have to add the -4.0.2.jar file to the Exclude list of the Maven WAR plugin configuration in the WAR Overlay POM file.
Although there may be additional code in various stages of development, a CAS release at Yale only depends on three Yale projects:
- cas-server-expired-password-change (to prompt the user to change a password that is a year old)
- cas-server-support-CushyTicketRegistry (only the configuration module is currently used to configure ehcache)
- cas-server-yale-webapp (the WAR Overlay with all the Yale configuration and HTML changes)
In CAS 3 development Yale created a source project that included the standard Apereo source along with the three additional Yale projects in a single directory. The advantage of this project structure is that Yale was recompiling all the source and was building at least formal artifacts with the Yale version numbers for all the Apereo code. If Yale ever wanted to change an Apereo module, all it had to do was to make a change in the Apereo source and then change the dependency version number for that artifact in the WAR Overlay from the vanilla 3.5.x to the Yale 1.1.x. It made modifications to Apereo easy, but it created a lot of work when you upgraded to a new CAS release where you ended up changing the project structure of a bunch of Apereo modules you were never really going to change.
So in CAS 4 we change the Yale project structure to a much simpler and cleaner approach. All the vanilla Apereo code is removed initially. If there are modification we need to make to an Apereo module, that will be a bunch of work we can do later on when it is needed. The Yale project then consists of only the three Yale added projects as subdirectories of a parent CAS project, plus a parent POM as the only file in that parent directory.
The Yale parent POM is a modified version of the parent POM in the vanilla Apereo CAS 4.0.2 source directory. First, we change the version number from 4.0.2 to Yale's 1.2.0 because that is the default version of any Yale written or modified code. Then we comment out all the processing that Apereo has added to Maven to do open source project housekeeping. When you build an open source project for general distribution, there are Maven plugins that make sure that your comment files list the open source licenses of all the open source dependency libraries you use, and plugins that complain about syle (missing JavaDoc commments for example), and plugins that do special release packaging. Yale is a customer and our projects only have to run at Yale, so we don't need all those checks.
We should note that there are two distinct meanings to the term "parent" in a Parent POM.
The parent directory is a Maven project with a POM file with a packaging type of "pom". For example, the Apereo parent POM for CAS 4.0.0 begins with the following declaration:
Code Block |
---|
<groupId>org.jasig.cas</groupId>
<artifactId>cas-server</artifactId>
<packaging>pom</packaging>
<version>4.0.0</version> |
This file is processed when anyone does a Maven "mvn install" operation on the parent directory. In this case, the important part of the Parent Directory POM is that it contains "<module>cas-server-core</module>" statements for every subdirectory that builds an artifact. Maven processes each <module> statement in the order they appear in the Parent Directory POM file, so this file can be written so that a JAR file is built and stored in the Maven repository before a subsequent module is compiled that depends on that JAR file.
There is, however, a second meaning of "parent" that relates to the projects in the subdirectories. Each subproject POM file contains a <parent> reference:
Code Block |
---|
<parent>
<artifactId>cas-server</artifactId>
<groupId>org.jasig.cas</groupId>
<version>4.0.0</version>
</parent> |
The <parent> directory contains DepedencyManagement statements, Maven plugin configuration statements, and <parameter> definitions that are shared by all the subprojects that reference it. This ensures that, for example, all the subdirectory projects are complied with the same version of the Apache commons-io JAR library.
It is a common Maven programming convention that the <parent> POM is also the Parent Directory POM. That is the way that Apereo organizes CAS and it is the way that Yale organizes its CAS additions project, but it is not a requirement. Technically, the Parent simply has to be a project of type "<packaging>pom</packaging>" that has already been processed by Maven and has already been stored in the Maven local repository before it is referenced in a <parent> statement of a subproject POM. When the <parent> POM is also the Parent Directory POM and Maven is invoked to build the parent directory, then by definition the Parent Directory POM is processed first and is stored in the Maven Repository before any of the subdirectory projects are built. However, Shibboleth 3 is an example of another approach where the <parent> POM is itself a subdirectory and the Parent Directory POM but is the first <module> statement and is therefore built and stored in the local Maven repository before any of the subprojects that reference it.
The vanilla Apereo CAS source project has its Parent Directory POM that establishes the global defaults and builds all the Apereo artifacts at version 4.0.2. You can recompile them in your sandbox or you can simply let Maven download the artifacts from the Yale Artifactory network server or from the Internet. Then the Yale CAS source project has its modified version of the Apereo Parent Directory POM with all the open source project boilerplate processing commented out and maybe a few version numbers incremented (for example, Yale changes the Java compiler versions from 1.6 to 1.7 for its own coding). The WAR Overlay (cas-server-yale-webapp) project can get the template WAR from an instance previously complied on the sandbox machine and stored in the local Maven repository, or it can get the standard version of the template WAR complied by Apereo and stored on the internet servers. Either way the template WAR is the same file (except for timestamps) and the WAR Overlay processing produces the same result.
In CAS 3.5.x (Yale version 1.1.x) the Yale CAS source project contains the three Yale subprojects, but it also contains a bunch of vanilla Apereo source subprojects. Some of them build Yale versions of Apereo modules (that is, a cas-server-core-1.1.1.jar file), but just because we build a Yale version of the artifact doesn't mean that it is ever used or deployed anywhere. If you look at the WAR Overlay project (cas-server-yale-webapp) you may find that the <dependency> statement in that POM references the vanilla Apereo Version number (3.5.2.1) and so the Yale version of the artifact we have just compiled is stored but ignored. If you are debugging a probem and need to add trace statements to the vanilla Apereo code on the Sandbox, you can temporarily change the Version number in the WAR Overlay to use your modified code, but then after the problem has been resolved you can revert the WAR Overlay POM to its standard settings and go back to using vanilla Apereo code.
Because the CAS 4.0.x (Yale version 1.2.x) project does not normally contain a copy of any Apereo project, making temporary changes for sandbox debugging is a much larger amount of work. You have to build a custom version of the Apereo JAR file, and that means making all the necessary changes to the project and subproject directories to make it work. It is important if you do this to NEVER compile a modified version of the Apereo source and store it in the local Maven repository under the vanilla Apereo version number. Once you have stored your own modified version of cas-server-core-4.0.2.jar then without some custom cleanup on your part it will override the real vanilla version of the module and you will always get the modified version on that Sandbox machine. So if you decide to do some work on a previously vanilla Apereo project, the first thing to do is to change the Version number in the project POM.
Why Not Vanilla?
Apereo code contains some bugs or sloppy coding. Yale has found them and reported them informally, but there has not been much interest in fixing them. Some examples of differences of opinion or bugs:
- The "service=" parameter value on the CAS login has to exactly match the value of the same parameter on the validate, serviceValidate, or samlValidate request. There is a difference of option about how carefully they have to match. The JASIG code matched the entire string excluding JSESSIONID if it was present. Yale believes that the entire query string (everything after the "?") should be excluded, and maybe the match should stop with context (https://servername/context/stuff-to-be-ingnored). This changes the substrings used in the equals() test.
- In the Login WebFlow logic Apereo saves the generated TGTID in a "request" scoped variable between the userid/password form and the CASTGC cookie generation, but when we insert the Duo second login screen all "request" scoped variables are lost. We had to copy it to "flow" scope during the display of the Duo form.
- When you are having a CAS problem, you may want to insert additional logging. For example, if the validate request is failing you may want to print out the exact service= strings being compared and the point at which they differ.
- Apereo defaults all registered services to be able to Proxy, but it seems better if the default is that they cannot Proxy unless you explicitly authorize it. This involves changing the default value for allowedToProxy in AbstractRegisteredService.java from "true" to "false".
- AbstractTicket is a Serializable class, but Apereo source forgot to give it a VersionUID. As a result, every time you deploy a new version of cas-server-core you have to "cold start" CAS by wiping out all the current tickets. You should only have to do that if you actually have changed the Ticket classes, not just because you recompiled the library to change something that has nothing to do with Tickets.
- Yale does not use Single Sign-Out, but code in TicketGrantingTicketImpl added to support that option has a very small but non-zero chance of throwing a ConcurrentAccessException and possibly screwing CAS up. Since we don't need it, Yale might just comment out the statement that causes the problem.
Each of these is a one line change, and only the first is user visible and the last is important for reliablity. If CAS was not open source we would probably just live with the old code, but we have the chance to fix/change it.
Whether we use vanilla Apereo code or make Yale modifications depends on Yale requirements, staffing, and management. There is a cost and commitment to maintaining a modification, but there is also a problem if some application does not work correctly or if some Yale need is not being properly addressed.
Subversion
If you check out a project from Subversion, then you rename a file or copy it to another directory, and then commit the changes, Subversion doesn't realize that the file has been renamed or moved. It sees that the original file name is no longer where it once was, and that a new file name has appeared in the directory, so it treats this as a delete and a create. All the file history is lost.
The command level Subversion client has commands that copy a file and link its old history to its new location. So if it matters, there are some things you should do with Subversion outside the Eclipse environment. This is easy to learn with any Linux Sandbox development environment.
During Sandbox development your complied JAR and WAR artifacts are stored in the local Maven repository on the Sandbox machine. However, the result of Sandbox development is to make and commit source changes to the Subversion version of the project. When you move to DEV (and then TEST and PROD) the Jenkins Build process will check out and compile the project and build new copies of the artifacts on the Jenkins managed machines and store them in the Artifactory network server.
An Eclipse project is built when you check a Maven project out of Subversion into the Eclipse Workspace. Generally a Sandbox instance will contain an Eclipse Workspace directory, it will contain a CAS project, and that project will contain source from some earlier version of CAS development. The first step when you begin work on CAS is to update the source in the workspace project with the latest version from Subversion.
There is a small amount of customization that has to be done manually after the project is checked out of Subversion into an Eclipse Workspace. For example, every Maven J2EE project seems to default to Java 1.6 while Yale CAS modules are written to use Java 1.7. So you have to manually go in and change the project defaults and the generated library section of the Eclipse Build configuration. Updating the contents of an existing Eclipse project preserves this customization, while checking out a complete new Maven project from Subversion means you will have to redo the small amount of post checkout project cleanup.
The Jenkins Build and Install Jobs
...