CAS was created at Yale and Versions 1 and 2 of the CAS code were written at Yale. Version 3 was completely rewritten and has been managed by a group of universities called "JA-SIG". Recently JA-SIG merged with Sakai to and was renamed "Apereo". In Yale CAS documentation, any reference to jasig.org should be understood to now reference apereo.org.
The CAS Project Directory Structure
Any given release of the CAS Server can be downloaded as a zip file from Apereo or it can be checked out from the Git server used by CAS developers as documented at the Apereo Web site. The release source is a Maven project, specifically a "parent" project with subdirectories that contain Maven subprojects. This is a common Maven way to package multiple projects that have to be compiled and built in a particular order.
The outer directory contains the parent or master pom.xml that defines parameters shared by all the subprojects. It also contains a list of <module> statements that identify the subprojects to be built in the order in which they should run.
Each subproject creates a JAR or WAR file. The first project is cas-server-core and it contains 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.
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 to this original template war. Files in this project with the same name replace files in the template project. Other files are added. The section of the pom.xml file that configures the maven-war-plugin also contains a list of files in the template that should be deleted, usually because they are replaced by new files with slightly different names.
Because Yale wrote CAS Versions 1 and 2, Apereo/JASIG numbering starts with Version 3. We know that the Apereo numbering won't go backwards, so any cas.war file with a Version number less than 3 is safe from collision. The Yale WAR file generated by the Overlay project, and all the Yale internal CAS projects are assigned Versions such as 1.1.2. An actual CAS artifact that might be installed into production will be store in Artifactory as https://repository.its.yale.edu/maven2/libs-releases-local/edu/yale/its/cas/cas-server-war/1.1.1/cas-server-war-1.1.1.war.
One or Two Projects?
CAS development at Yale could be separated into one project with vanilla Apereo source and one project with Yale additions and modifications. That keeps things cleanly separated if Yale was never going to make any changes.
However, CAS is not really distributed as a bunch of equal projects. The cas-server-core project is 95% (or more) of the source and all the other projects are relatively small. There are always changes that we make to cas-server-core during development, although some of these changes are just for testing or exploration. In other cases there are CAS bugs, or simply differences of opinion about how CAS should work. Where possible, Yale will take a CAS core source file, generate a Yale named version of the same code, and put it in the Java source section of the WAR Overlay project. Therefore, at least during development the idea of a "pristine" Apereo source is unreasonable.
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.
Once you start to consider changing Apereo code, then it is inconvenient to maintain two separate projects. For one thing, you need two separate Jenkins build jobs, one for each project. Then you have release numbering issues.
The alternative is to have a single project that combines both the Apereo source for a CAS release (example: 3.5.2.1) and the Yale code that has been built and tested for that CAS release (separately designated Version 1.1.x). Most of the time the Apereo source just sits there and is not used and is not compiled because it is not changed. If you need to make a modification, it is immediately available and can be compiled simply by adding or re-enabling the <module> statement for that project in the top level pom.xml file.
If you look at the Yale single project file without reading this explanation, it may be confusing. There are a large number of subdirectories of Apereo source, and one of them (cas-server-core) is huge. Yet many of these Maven projects are ignored by the Build job. Why are they there? If the base Apereo code on which you are working is all preloaded into the Project directory tree, then it is easily available for debugging, testing, and possible modification during development. If you don't need to use it, then just ignore it.
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 create our own top level pom.xml that just has the Maven configuration to compile the code and build the WAR.
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 SVN.
The Jenkins Build and Install Jobs
In order to put CAS into production, you have to conform to the Yale Jenkins process.
First the developer creates code on a desktop sandbox environment and runs basic tests. When the code is working, it is checked into Subversion.
The developer then runs the Jenkins Build job for that project. In "Trunk Build" processing (the default) the Build job first checks out a copy of the current SVN trunk onto a Jenkins managed Build machine. It then runs the top level parent Maven pom.xml job, which in turn runs all the subprojects to compile Java source and build the 1.1.x-SNAPSHOT versions of all the JAR files and of the final WAR files. At the end of the job, these files are all stored in Artifactory. The Trunk Build can be run again and again to replace the SNAPSHOT files until Integration Testing on the DEV machines is successful.
There are Jenkins "Install" jobs for DEV, TEST, and PROD. Each checks out a copy of the installer directory stored in SVN next to but separate from the cas-server source project. The install job also runs a top level (in the installer directory) pom.xml file, although that Maven project just runs an Ant build.xml script to download from Artifactory the specific version of the CAS WAR file built by the previous Build job. The Ant script copies (and typically unzips) this WAR file to the JBoss application deploy directory. As text and XML files are copied, Ant makes some edits "on the fly" to insert parameter values for the names of userids or password used to access databases, AD, or to configure special options in Spring.
After DEV testing is complete, but before Installing to TEST or PROD, the Jenkins Build job is run a second time to Perform a Maven Release. Jenkins checks out the source project from SVN, but this time it changes the version ID in all the project pom.xml files to drop the "-SNAPSHOT" suffix. So if you were working on "1.1.2-SNAPSHOT" this momentarily creates version "1.1.2" files. Those files are checked into SVN as a Tag, and they are also compiled to produce the "1.1.2" version of the WAR file which is stored in Artifactory. This becomes the official "1.1.2" Release of Yale CAS. Then Maven changes all the Version ID strings in the pom.xml files a second time to increment the minor Version number and re-add the suffix, so that when the developer updates the pom.xml files in his Eclipse workspace he begins work on "1.1.3-SNAPSHOT".
Development
Up to this point we have discussed where the files are stored and how the release are built, but nothing has been said about editing files and writing code. You do that on your desktop/laptop computer.
CAS development requires Java, JBoss, Maven, and Eclipse. The last three tools run under Java, and Java is designed to be platform independent, so you can do development under Windows or Mac OSX if you prefer. There are a few hours of setup time getting the right versions of everything set up on your computer (particularly adding the right options to Eclipse). This produces a sandbox machine.
When we talk about the CAS Development Sandbox, however, this is a VM created to run under Oracle's open source VM host called VirtualBox. You can run VirtualBox on Windows or Mac, and in the VM Java, JBoss, Maven, and Eclipse are all set up to work on CAS. This is very helpful for testing, particularly if you need to test communication between CAS machines in a cluster. However, the responsiveness of a VM running on a desktop is not as good as native applications running on the real OS.
This section describes how to set up a sandbox. It can be a guide for updating the CAS Development Sandbox VM when you want to move to Java 8 or 9, JBoss Wildfly, and Eclipse Mars (4.5), or it can explain how to configure current versions of everything on your native desktop OS.
Eclipse
Yale uses Eclipse as the Java IDE. If you prefer a different IDE (Netbeans, IntelliJ, ...) the only absolute requirements are the ability to check projects in and out of SVN, the ability to build Maven projects, and the ability to debug code in JBoss. However, it would be up to you to adapt the following Eclipse instructions.
Start with the Eclipse for J2EE download package of the current release from eclipse.org. This contains Eclipse support to edit Java, XML, and HTML source and to import and automatically configure Maven projects that build JAR or WAR files. Additional capability can be added. Additional features from an already known source (including any eclipse.org features) can be added from the Help - Install New Software menu. Eclipse also has a general source for third party add-on features at Help -Eclipse Marketplace. The Marketplace is easier to use, but you need to carefully read the descriptions to make sure the item you plan to install is the right version for the release of Eclipse you are running.
Eclipse needs:
- Maven support (called "M2E") which has become standard in modern Eclipse releases but we mention it here because it is a complex package with functions that will be describe later in some detail.
- Subversion support ("Subversive") which was developed by a third party named Polarion but was then contributed to eclipse.org. It can be installed from Add Software because it is now owned by the Eclipse project.
- However, while the Subversive code understands basic SVN concepts, the actual code to communicate over the network to the SVN server is still a third party addition from Polarion that you will be prompted to select the first time you try to use any SVN function in Eclipse. Choose the 100% Java library called SVNKit (use the lastest version number in the menu).
- Add JBoss Tools from the Marketplace. Do not select the full JBoss branded replacement for the entire Eclipse program, just add the Tools part and make sure that you choose the one that corresponds to your current Eclipse (Luna for example). You do not have to install all the tools, but it is simpler to simply hit OK and accept the entire package.
- Eclipse has optional AspectJ support, and CAS has some AspectJ components. It used to be necessary to install this option manually, but starting with Luna you will get a popup dialog inviting you to add Eclipse AspecJ support when it encounters it as you import the CAS project.
In addition to Eclipse extensions, Eclipse can be made aware of important external resources.
- In Window - Preferences - Java - Installed JREs you can configure more than one instance of Java installed on your machine. By default Oracle Java tends to run the highest version number, but CAS is distributed to run on 1.6 and at Yale it runs on 1.7. If you happen to have 1.8 installed on your machine for other purposes that is the version Eclipse will discover when you first install it and you should configure other versions that you are going to use to test applications. Install and configure a full JDK because Maven needs it to run.
- Eclipse comes with a current version of Maven 3 built in. Unfortunately, the Yale Jenkins Install jobs run on Maven 2.2.1 and that is not fully compatible with current Maven 3. So you need to unzip a copy of Maven 2.2.1 somewhere on your system and add the location of this directory to Eclipse through Window - Preferences - Maven - Installations.
- Eclipse needs to know where your JBoss server is to start it. This gets to be a bit tricky because the original Eclipse for J2EE code from eclipse.org that you started with has some support for JBoss servers, but the JBoss Tools that you just added has better support. It turns out to be better to let JBoss Tools "discover" the JBoss directory and autoconfigure everything for you. Go to Windows - Preferences - JBoss Tools - JBoss Runtime Detection. Click Add and type the directory one up from the root of the JBoss Server (if JBoss is in c:\appservers\jboss-eap-6.2 then "Add" c:\appservers). Then click Search ... and all the application servers in that directory will be found. If you do not already have JBoss downloaded and installed, "Add" the directory where you want to install it and click Download ... Select the version of JBoss from the list.
Check Out the Maven Project
If you are working with the current CAS release, you check it out from the Yale SVN server. If you are going to start work on a new CAS release you still have to check out the old release, but then you have to also import the new CAS release distribution from Apereo and merge the two.
Open the SVN Repository Exploring "perspective" (page) of Eclipse and define the repository url https://svn.its.yale.edu/repos/cas. This directory contains various cas clients and servers. Find the trunk of the current cas-server project and check it out as a simple directory. Do not use the Eclipse Wizard to create a particular type of Eclipse project. Just create a new generic Eclipse project.
CAS is stored in SVN as a Maven project. This means it has a pom.xml file in its top level directory.
Eclipse has its own project structure. An Eclipse project has a .project and .classpath file and a .settings directory in its top level directory.
M2E is the name of the Eclipse support for Maven. M2E is able to read through the pom.xml file and to generate the .project and .classpath files and the .settings directory that contains what Eclipse needs in order to correctly display and build the Maven project. If in Eclipse Project Explorer you right click on a project and choose Configure from the menu, you can configure that single project to be a Maven project. However, since CAS is a parent project with subprojects, you need to use Import to get all of them.
Return to the J2EE perspective, right click the new cas-server project directory, and choose Import - Maven - Existing Maven Projects. This is the point where the M2E Eclipse support for Maven discovers the parent and subdirectory structure. It reads through the parent POM to find the "modules", then scans the subdirectories for POM files that configure the subprojects. Then it presents a new dialog listing the projects it has found. Generally it has already found and configured the parent project, so only the subprojects need to be checked.
M2E will only display subprojects that were mentioned in a <module> statement of the top level parent pom.xml file. However, you do not need to click the checkboxes to select all of them to be turned into Eclipse projects. You only need to select the projects you are working on. If you leave some out, you can always repeat the Import Existing Maven Projects step and add more.
Now M2E does some serious work, and you have to give it time to do everything. It processes the pom.xml file in a subproject to decide if it builds a JAR or a WAR. It configures the project to compile the Java source. It reads through the dependency list in the POM and downloads to your local .m2 repository file all the JAR libraries on which the CAS project depends. Then it compiles all the source. When it encounters AspectJ stuff it will invite you to add AJDT support to Eclipse.
If you have not properly configured the Yale Artifactory server in your .m2/settings file, then you may get a message about a missing Yale dependency JAR file. Ignore error messages about bad XML syntax. You may also be told that the project is configured for Java 1.6 but there is only a 1.7 runtime available. These are all unimportant issues.
M2E understands Maven projects, but is is distinct from real Maven. Real Maven (lets call it "Batch Maven" because you run it from the command line) is an extensible system of optional plugin modules that can be expanded to provide all sorts of special processing. M2E is an Eclipse component that can read pom.xml files and configure Eclipse to to approximately the same thing that Maven would do to compile the source and build the artifact.
Eclipse can compile Java and AspectJ source. It can resolve references at compile time to JAR files downloaded and stored in the Maven local (.m2) repository. It can merge compiled *.class files with XML and properties files to build the JAR or WAR.
There are some things that M2E and Eclipse cannot do on their own, like compiling WSDL files to generate Java proxy source for remote Web Services. To get those extra steps you have to run Real batch Maven. M2E can run real Maven in batch mode under Eclipse, but you have to do this manually yourself. During the automatic project import step M2E mostly gets the Java and WAR parts right, and fortunately that is all that CAS requires.
M2E knows what it knows, and it knows what it doesn't understand. It reports as an unresolved problem any configuration in any pom.xml file of a Maven plugin that it doesn't fully support. Mostly these are messages like "Plugin execution is not covered by lifecycle configuration ..." This is M2E noting that there is some sort of extra processing that Maven does in batch but that the M2E support cannot exactly duplicate in its Eclipse configuration. For CAS this does not matter, because we will be using Real Batch Maven to generate all the JAR and WAR files that go into JBoss. We only need Eclipse to be configured properly so that the Java IDE functions like autocomplete and autofix and Open Declaration and Open Type Hierarchy work, and M2E gets that part right.
When M2E has completed its Import function, there are now two distinct types of projects for every project directory.
- There is still a Real Batch Maven project represented by the pom.xml. This will run and generate the 100% exactly correct result when you run a "mvn clean install" either at the command line or from within Eclipse.
- There is also an Eclipse project represented by the .project, .classpath, and .settings generated by M2E. This is good enough to compile all the Java source, but it is not able to do all the optional or special processing that was configured in the pom.xml. It produces perfectly acceptable *.class files and puts them in the correct working directory, and there is no reason why Maven would have to recompile that source and create new *.class files of its own. However, Eclipse would not be a reliable source if you tried to use it to build a JAR or WAR file because it would leave out some of the special processing. For that you need to run batch mvn.
Interactive and Batch Modes
We have already discussed why it is important to run the batch "mvn clean install" command to build artifacts. However, there is a second subtle interactive v batch distinction that you need to understand for testing and debugging.
Eclipse has special support to make very simple J2EE development as simple as possible. This is useful for simple applications where the developer spends a long time editing JSP, HTML and CSS files or simple Java source.
Since the start of J2EE, application servers have a "hot deploy" mode of operation where they will notice when the timestamp of a HTML or CSS file changes and they will use the new version of the file as soon as it is stored in the application server deploy directory. However, Eclipse is an IDE and when you save a new copy of an HTML or CSS file, you are putting it in the Eclipse workspace and not over on the c:\appservers\jboss-eap-6.2\standalone\deployments\cas.war directory. It could have an option to immediately copy changed files over to the deployment directory, but it tries instead to do something much more clever.
The J2EE support in Eclipse has a trick for bringing up widely known Java application servers (Tomcat, JBoss) in a special Eclipse managed configuration mode. These servers have always had the ability (required by Linux convention) to put their configuration files over in one directory tree, their libraries in another tree, their log files somewhere else, and their applications wherever the system administrator wants to configure them. Eclipse can run one of these servers overriding its normal configuration directory with one that Eclipse has hacked.
The Eclipse trick is to configure the application server to believe that the WAR file it is dealing with is the Eclipse project in the Eclipse workspace. In some cases Eclipse configures a special Eclipse-Tomcat JAR library with a replacement for the usual Tomcat classes that read files from normal WAR files. Eclipse creates a "virtual" WAR that comes from its workspace. If you are running the application server in this mode, then when you save an HTML file in the Eclipse workspace, it doesn't have to be copied anywhere its. It has been hot deployed to the application server automatically.
This trick cannot work if your application requires any special Maven processing to build the WAR file. The CAS WAR has to be built by Real Batch Maven, and Yale conventions further require last minute parameter substitution with the Install job Ant script. So CAS in general and particularly CAS at Yale cannot use the oversimplified application debugging provided by Eclipse.
You have to run Real Batch Maven under Eclipse to do the same work as the Jenkins Build job, then you need to run it again to do the same work as the Jenkins Install job. That produces a cas.war directory in the JBoss deploy directory on the sandbox machine. Then you want to start JBoss. This is where the JBoss Tools add-on to Eclipse is helpful. It can start and stop, with or without debugging, an ordinary JBoss server installed outside Eclipse. The server runs with its normal configuration from its normal configuration directory. The only change is that you manage it from Eclipse instead of from the command line, and you can set breakpoints with Eclipse debugging without the extra step of attaching Eclipse to a running process.
Running Maven Jobs Under Eclipse
At any time you can right click a POM file and choose "Run as ..." and select a goal (clean, compile, install). Rather than taking all the default configuration parameters, or entering them each time you run Maven, you can build a Run Configuration. Basically, this is a simple job with one step that runs Maven. The Run Configuration panel allows you to select every possible option, including the Java Runtime you want to use to run Maven, and a specific Maven installation so you can run some things under Maven 2 and some under Maven 3, plus the standard Maven options to not run tests or to print verbose messages. Since the Jenkins jobs each run one Maven POM, in the Sandbox you can build two Maven Run Configurations that duplicate the function of each Jenkins job.
First, lets review what the Jenkins jobs do.
- The "Trunk Build" job checks the source project out of subversion. In this case, it is a "parent" project with subdirectories that all get checked out. It then runs a "mvn install" on the parent. The parent POM contains "module" XML elements indicating which subdirectory projects are to be run. Each subdirectory project generates a JAR or WAR artifact. The Jenkins Trunk Build installs these artifacts on the Artifactory server replacing any prior artifact with the same name and version number.
- The Jenkins Install job checks the source of the Install project out of SVN. By Yale convention, each Install project is an Ant build.xml script and a package of parameters in an install.properties file created from the Parameters entered or defaulted by the person running the Jenkins job. Minimally the Install job downloads the artifact from Artifactory and copies it to the JBoss deploy directory, although typically the artifact will be unzipped, variables will be located and replaced by the values in the properties file, and then the file will be rezipped and deployed.
In the sandbox you already have a copy of the CAS source files checked out in your Eclipse project in your workspace, and you can also check out a copy of the Installer project. So there is no need to access SVN. Similarly, in the Sandbox we do not want to mess up Artifactory, so the local Maven repository (which defaults to a subdirectory tree under the .m2 directory in your HOME folder) holds the generated artifacts.
With these changes, a Build project compiles the source (in the Eclipse workspace) and generates artifacts in the .m2 Maven local repostory. The Install project runs the Ant script with a special Sandbox version of the install.properties to copy the artifact from the .m2 local repository to the Sandbox JBoss Deploy directory (with property edits).
There is one last step. The Jenkins install job stops and starts JBoss. In the Sandbox you will manually start and stop the JBoss server using the JBoss AS Eclipse plugin. This plugin generates icons in the Eclipse toolbar to Start, Stop, and Debug JBoss under Eclipse. You Stop JBoss before running the Install job and then start it (typically using the Debug version of start) after the Install completes.
Now to the mechanics of defining a Run Configuration. You can view them by selecting the Run - Run Configurations menu option. Normally you think of using Run configurations to run an application, but once you install M2E there is also a Maven Build section. Here you can choose a particular Maven runtime directory, a set of goals ("clean install" for example), and some options to run Maven in a project directory. The recommendation is that on your sandbox machine you create two Maven Run Configurations. The configuration labelled "CAS Build" runs a Maven "install" or "clean install" goal on the parent POM of the CAS source project. The configuration labelled "CAS Install" runs Maven on the POM of the Installer project (and it has to run Maven 2 because this is still the standard for Installer jobs).
Source Build
CAS is distributed as a cas-server directory with a bunch of subdirectories and a POM file. The subdirectories are JAR and WAR projects. The POM file contains a certain amount of boilerplate for JASIG and the CAS project, plus two things that are important. The parameters and dependency management section ensure that all elements of CAS are compiled or built with the same version of library modules (quartz for timer scheduling, hibernate for object to database mapping, etc.). Then a list of <module> statements indicate which subdirectory projects are to be run, and the order in which they are run, when Maven processes the parent POM. The possible modules include:
- cas-server-core - This is the single really big project that contains most of CAS Server code and build the big JAR library artifact. It is built first and all the other projects depend on it and the classes in the JAR file.
- cas-server-webapp - This is a dummy WAR file that contains the vanilla version of the JSP, CSS, HTML, and the XML files that configure Spring and everything else. The dependencies in this project load the basic required JAR libraries into WEB-INF/lib. This WAR is not intended to install or run on its own. It is the input to the second step WAR overlay where additional library jar files will be added and some of these initial files will be replaced with Yale customizations.
- Additional optional extension subprojects of CAS supporting specific features. For example, the cas-server-support-ldap module is needed if you are going to authenticate Netid passwords to AD using LDAP protocol (you can authenticate to AD using Kerberos, in which case you don't need this module). A JASIG standard distribution includes all possible subprojects and the parent cas-server POM file builds all of them. To save development time, you can comment out the subproject <module> statement in the parent POM of modules you don't use. Disk space is cheap, and deleting the subdirectory of modules you don't use may be confusing.
- Yale CAS extension JAR projects. For example, the cas-server-expired-password-change module is the Yale version of what CAS subsequently added as a feature called "LPPE". Yale continues to use its code because the Yale Netid system involves databases and services beyond simply the Active Directory component and support
- cas-server-yale-webapp - Yale customization is (unless it is impossible) stored in the WAR Overlay project directory. If we need Java code, we put this source here and it is compiled and ends up in WEB-INF/classes. We replace some standard JSP and CSS files to get the Yale look and feel and wording. The XML files that configure Spring and therefore CAS behavior are here. This project must always be the last module listed in the cas-server parent POM so it is built last. The cas-server-webapp project is a dependency, so that is the base WAR file that will be updated. This project replaces JSP, CSS, and XML files in the base WAR with identically named files from this project. It adds new files and library jars (from the dependency list of this project).
Yale will have modified the POM file in the cas-server parent project to comment out "module" references to optional artifacts that we are not using in the current CAS project. It makes no sense to build cas-server-support-radius if you are not using it.
It is possible for Yale to also delete the subproject subdirectory from the instance of cas-server source checked into SVN. If we are not building or using the module, we don't need to maintain it in the project source. However, this makes relatively little difference. In CAS 3.5.2 the entire JASIG source is 1628 files or 10 megabytes of storage, while the optional projects we don't use account for only 210 files or 500K. So deleting the unused optional source modules saves very little time for every Trunk Build checkout and since we don't compile them, there is no processing time cost.
The Road Not Taken
The reference from a child project to its parent uses an artifact name and version, and the parent is found in the local Maven repository rather than the parent directory. So storing the subprojects as subdirectories under the cas-server parent is not a physical requirement. It does make things simpler.
It would have been possible to separate out the JASIG code in one directory of SVN, and then put the Yale code in another directory. This would slightly simplify the documentation because you would know what is Yale and what is JASIG. But that would only be a slight improvement.
We still have to decide how to handle bugs or problems in the JASIG source. When an individual file has been changed in the JASIG source because that is the only place it can be changed, then that file and the project directory that contains it is no longer vanilla JASIG code. At that point there has to be an entry in the Release Notes to show that the file has been changed, and now separate directories do not simplify the documentation.
This also means that there have to be two Jenkins Build jobs, one for the JASIG part of the source and one for the Yale part of the source. Remember, the Jenkins job structure at Yale assumes that a Build job checks out one directory and all its subdirectories.
Therefore, we compared the advantages and disadvantages, flipped a coin, and decided to check into SVN a directory consisting of JASIG source merged with Yale created project subdirectories. We do not claim that this is the right way to do things, but neither is it obviously wrong.
The POM in a vanilla JASIG component subproject will have a Version number that corresponds to CAS releases (3.5.2). The POM in a Yale customization subcomponent will have a Version number that corresponds to the Yale CAS installation "Release" number for Jenkins tracking (1.1.0-SNAPSHOT). The artifact name and version number in the WAR Overlay project must match the artifact name and version number of the Jenkins Install job parameters.
Standalone.xml
In theory, the developer of a project does not have passwords for the production databases, so runtime parameters are provided from a source outside the WAR. A common Yale convention was to build XML files defining each database as a JBoss Datasource and creating a service that stuffed a group of parameters into the JBoss JNDI space where they could be read at runtime using the built in java.naming classes. The Install job used to build these XML files.
However, the JBoss EAP 6.1 rewrite has changed the JBoss best practice. Now both Datasources and JNDI values are stored in the main "subsystem" configuration file. In the sandbox, this is the standalone.xml file in the /config subdirectory of the JBoss server. You need to get a copy of an appropriate configuration file for your JBoss and install it yourself. Production services will be responsible for providing the file in DEV, TEST, and PROD, but you need to notify them if you add something during sandbox development so they can also add it to the versions they manage.
Best Practice
Check out from SVN the current version of the cas-server source. Also check out the Installer project directory. Get a copy of install.properties for the Installer project from someone who has one, and make sure the jboss.deploy.dir points to the location where you put JBoss on your sandbox.
If it is at all possible, make changes only to the WAR Overlay project subdirectory.
M2E generates the .settings directory and the .project and .classpath Eclipse project files, but we generally do not check them into SVN because they may be different depending on which Eclipse optional plugins you have installed and what release of Eclipse you are running. Maven generates the target directory with all the generated class files and artifact structure, but that also should never be checked into SVN. To avoid having these directories even show up when you are synchronizing your edits with the SVN trunk, go to the Eclipse Menu for Window - Preferences - Team - Ignored Resources and Add Pattern for "target", ".settings", ".classpath", and ".project" (omitting the quotes of course).
CAS is vastly over engineered. An effort was made to deliver a product that anyone can configure and customize using only XML files. No Java programming is required. So if you are a Java programmer, you already have an advantage over the target audience.
CAS has a set of key interfaces which are then implemented with one or more Java classes. The choice of which Java class you will use as a AuthenticationManager, AuthenticationHandler, TicketCache, or Web Flow state is made when the fully qualified name of the class is specified in one of the XML bean configuration statements.
Sometimes you need behavior that is just slightly different from the standard JASIG behavior. Make a reasonable effort to see if you can find an alternate implementation class or an alternate configuration of the standard class that has the behavior you want. If not, then instead of modifying the org.jasig.cas code in the JASIG source, see if this is one of the "bean" classes named in the XML. If so, then just make your own copy of the original source in the WAR Overlay project source area and rename its package to a edu.yale.its.tp.cas name. Change it to do what you want, and then change the fully qualified name in the XML to reference the new name of the modified class you have put in the Yale customization project.
Of course, JASIG may change the source of its original class in some future release. However, classes that implement standard interfaces do not really need to pick up every JASIG change. If they fix a bug, then you want to copy over the new code. If they simply add a new feature or change the behavior of something that you are not using, then there is no need to update your modified copy of the original JASIG code. Essentially, the modified code has become 100% Yale stuff no matter who originally authored it and if it continues to do what you need then there is no need to change it in future releases, unless the interface changes.
Most strategic changes in behavior can be coded in Java source complied as part of the WAR Overlay project. What cannot be changed in the WAR Overlay project? If a class is not named in the XML configuration files, but instead is imported into some other Java source file, then you cannot in general replace it with new code in the WEB-INF/classes directory.
This is a consequence of some fine print in the Java implementation of ClassLoaders. Since this is computer logic stuff, I can boil it down to some simple logic, but it has to be expressed as a set of rules:
Every class is loaded by a ClassLoader.
In JBoss, there is one ClassLoader for WEB-INF/classes and a second ClassLoader for WEB-INF/lib. Then there are ClassLoaders for EARs and for the JBoss module JAR files, but they aren't important here.
When the XML references a class by name, JBoss searches first through the classes compiled in the WAR Overlay project and stored in the WEB-INF/classes directory, and then it searches through WEB-INF/lib.So it finds the WAR Overlay source first and uses it.
However, when an already loaded class references another class with an import statement, then Java searches for the new class by first using the ClassLoader that loaded the class that is looking for it. This means that any class loaded from WEB-INF/lib will first search for all of its import names in WEB-INF/lib first, and only later search WEB-INF/classes.
Therefore, you can override XML named classes using source from the WAR Overlay project, but the other classes that are imported into JASIG source files probably have to be edited and changed in their original JASIG project. If you have to make a change, identify the modified JASIG source in the ReleaseNotes, and if it is a bug change submit it to JASIG to be fixed in future releases.
The Debug Cycle
Make changes to the files and save them.
Once you run the CAS Build and CAS Install Run Configuration once, they appear in the recently used Run Configurations from the Run pulldown in the Eclipse toolbar. There is a Run icon (a right pointing triangle on a green circle) and following it there is a downward pointing "pulldown menu" icon that shows the list of recently run configurations.
Once you install JBoss Tools, there is a separate JBoss Run (a green right arrow) farther over to the right on the toolbar.However, during testing you probably do not want to run JBoss in normal mode but instead want to press the JBoss Debug button (the icon of a bug that follows the JBoss Run arrow on the toolbar.
So the normal cycle is to stop the JBoss Server, edit and save the source, the run the CAS Build Maven job, then the CAS Install Maven job, and then restart JBoss (in debug mode).
After The Sandbox
Changed files must eventually be Committed to the SVN trunk.
Do not commit the generated "target" subdirectories to SVN. They contain temporary files that should not be saved.
Once you are ready to proceed to the next phase, run a Jenkins Trunk Build and do a DEV Install.