Pages

Monday, February 20, 2012

A weekend with Luminis and Tomcat

PROGRAMMING A SERVLET FOR LUMINIS 4.X FOR BANNER BY A NEWBIE PROGRAMMER.

At work we are using Luminis 4.x to allow our students to login into the University Portal.  As part of the migration to Google Application for Education we need to change the students current user id to login into the portal.
In order to accomplish that, we will run a custom sql package that will generate new user ids. The user Id, used by the Luminis portal is stored inside banner inside the table GOBTPAC as GOBTPAC_EXTERNAL_USER column. As soon as the GOBTPAC table is updated, another process needs to be run to import all the changes inside Luminis Platform. In our case we are going to import a xml file with cptool.
Once that GOBTPAC, and Luminis  are updated with the new userId, the students can no longer login into the portal. We thought out two methods to overcome this situation:
  • Change GOBTPAC_EXTERNAL_USER in a cloned database and notify the students of the oncoming change, either by email, portal mail or publish it in an internal website.
  • Go ahead and changed it in Production database, and provide them a way to retrieve the new user ID right at the Luminis Login Page.
 We opted for the second alternative as the solution for our problem as a more friendly one to the students. That way they were not forced to remember anything but their old, already inactive  user Id.

I had done some small programming with PHP reading and writing LDAP fields inside Luminis. That is why my first thought when attacking this issue was to modify the login page of Luminis to include some JavaScript/AJAX procedures and call some PHP page in the background to do the job.
Right away I found a difficulty with that approach, Luminis 4.x webserver is based on Apache-Tomcat 5.5 and as far as I know it does not install PHP support by default. I could have played with integrating the Apache webserver with Tomcat, but I really did not want to spend too much time in setups and configurations that I don't think are supported by Sungard.

Never had programmed  or used Tomcat before, so I did some initial reading and  learned that I had a couple of options here. I could have enabled the CGI module of Tomcat, not a big deal, as it is only requires some comments to be removed from a configuration file (web.xml), and a Luminis restart. Then I could have programmed some perl script to read from the text file I brought from Banner with the mappings of oldusername:newusernames. 

I know that Sungardhe Banner is moving towards a more Java/Javascript/Groovy/Grails based architecture, so I thought that learning how to program a servlet inside the Luminis Tomcat will  be more productive in the long run for the university I work for. And having a long weekend with no much to do seems like a very good investment for the time.

So, I am going to modify the login page and add it another Form  where the students will be entering the old username, the same one he/she has been using so far to get into the portal (this is going to be done in a scheduled weekend, of course). That form will be served by JavaScript functions loaded with the page and that lives inside login.jsp. The JavaScript functions will call the servlet, the servlet will grep the new username from a text file and it will send it back to the browser, where another function will use that information to update the loaded page (login.html). That is the whole picture. Now onto the details:

Login into the WebServer host of Luminis, you have very important environment variable to work with:
  •  $CP_ROOT ---this is the base, where everything luminis related is installed from.
  • $CP_DOC_ROOT--this is the base for the Luminis Web Application
  • $CP_WEBINF  --this is the WEB-INF folder of the Luminis Web Application
  • $CP_WEB_ROOT--Tomcat webserver, configuration, common libraries (servlet-api.jar is found here), logs, conf. It is the regular filestructure of a Tomcat installation.
Most of the work is done in $CP_DOC_ROOT:
Here you will find two important directories:
  • site              (this one contains the login.html  page)
  • WEB-INF  (this one is the one referred by $CP_WEBINF
  • jsp/portal    (here it is the login.jsp that needs also to be edited)
 -First the servlet:
This is a java program that imports a set of classes that are used by the Servlet container (tomcat) to allow communication with the java class from the web. Maybe that is not a 100% true technical definition, but it is the way I interpret it. Please correct me if I am wrong. Thanks!
Basically you use your regular java knowledge to do what you want to do, and the program output could be fed back to the browse, how cool is that?
So, my java program will have an input variable, the old username, and one only line of output,"notfound" or the new user id. To do that, it reads the old username and search a static file that contains the mapping of usernames with the following layout:
                        oldusername:newusername

Place your java code inside $CP_WEBINF/classes  (you don't need to have the source here, but I wil compile in this same folder, you can move the source later to where you desire)
  This is a snippet of the java program, getId.java:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.InputStream;
import java.io.InputStreamReader;

public class getUserId  extends HttpServlet {
  public void doGet(HttpServletRequest  request,                HttpServletResponse response)
  throws IOException, ServletException, IOException
    {
   String argument_name = "olduserid";
   String argument_value= request.getParameter(argument_name);
   //the above line allows you to read the value of the 
   //variable sent by the browser

  <rest of the code that search for argument value inside a ext file...regular java programming>
   //Now to produce the ouput that will be read by the browser
   response.setContentType("text/plain");
   response.setCharacterEncoding("UTF-8");
   response.getWriter().write(newusername);
   }
}


Save and compile the java program:
$javac -classpath $CP_WEB_ROOT/common/lib/servlet-api.jar getId.java

The above will generate a file getId.class, and because it was compiled inside $WEB_INF/classes is already in the right place.
Now this servlet, needs to be registered in the Tomcat as a valid servlet. That is done in $CP_WEBINF/web.xml. I added this two new sections to the web.xml of the luminis webapps:

 <servlet>
        <servlet-name>getId</servlet-name>
        <servlet-class>getId</servlet-class>
    </servlet>
 <servlet-mapping>
        <servlet-name> getId </servlet-name>
        <url-pattern> /getId/* </url-pattern>
    </servlet-mapping>

And restart Luminis:  $stopcp -a && startcp  (I haven't spend time finding out how to restart the tomcat server alone to save time...I should do it and post it here when I find out)

Once that Tomcat restarts you can try your servlet without more programming.  Go to the browser and type:
https://yourserver.yourdomain/getId?olduserid=p00215350    (https or http depending on your particular case).  Here p00215350 is a valid username that exists in the text file.  You should receive the new username in a new browser page. You might also try an invalid username and you should receive a message accordingly. In my case "not found".

So,having  a working servlet already, the rest is downhill. Modify login.html and login.jsp. As said before, login.html is found inside $CP_DOC_ROOT/site.  A regular html form was added to the page:


 <form name="newuserid" action="https://yourserver.yourdomain/cp/home/login" onSubmit="getthenewID(); return false;" method="post">
UserID Actual: <input type="text" name="currentuserid" />
<input type="submit" value="Get my New ID" />
</form>


 Above there are very important names you have to be careful when modifying login.jsp. The name of the form, must be different from the forms already in the page ( cplogin and userid),  and the name of the field where the student type his/her old user id, "currentuserid". Those names will be used inside login.jsp.

Inside login.jsp (location $CP_DOC_ROOT/jsp/portal). I added the variable  at the begining of the page. I am not a javascript programmer, but I suppose that they might call that a global scope variable. That's what I want it, a variable that exists during the session and that is declared when the login.html page was loaded into the browser. So I added at the begining of the login.jsp, right after the <script language="javascript 1.1"> directive:


 var xmlhttp = new getXMLObject();       //xmlhttp holds the ajax object
Add the following two functions to  the login.jsp:

function getXMLObject()  //XML OBJECT
{
   var xmlHttp = false;
   try {
     xmlHttp = new ActiveXObject("Msxml2.XMLHTTP")  // For Old Microsoft Browsers
   }
   catch (e) {
     try {
       xmlHttp = new ActiveXObject("Microsoft.XMLHTTP")  // For Microsoft IE 6.0+
     }
     catch (e2) {
       xmlHttp = false   // No Browser accepts the XMLHTTP Object then false
     }
   }
   if (!xmlHttp && typeof XMLHttpRequest != 'undefined') {
     xmlHttp = new XMLHttpRequest();        //For Mozilla, Opera Browsers
   }
   return xmlHttp;  // Mandatory Statement returning the ajax object created
}

This function creates the variable xmlhttp as a XML object that it is used in our script to communicate with the servlet requesting it for the new user id (AJAX).

Add the function getthenewID(). This one is called when the students click on the button, "Get my New ID"  on the page. When called this function sends the request to the servlet getId using the GET method,which sends the variable name and value on the URL of the request (the same way we manually tried the servlet above). The function sends the request and returns, it does not wait, it just tell the browser the name of the function that will take care of the reponse from the server.
And this is the beauty of AJAX. When the servlet finishes its work (at the server side), and send its response back to the browser, the browser does not open a new page, not even refresh the current page but whoever function that handles the response will be called by the browser. In this case the function is handleServerResponse, which is listed here:


function getthenewID() {
  var newuser = ""; 
  var olduser = document.forms['newuserid'].elements['currentuserid'].value;
  if(xmlhttp) {
    xmlhttp.open("GET","https://yourserver.yourid/getId?olduserid="+olduser,true); //getId: servlet name
    xmlhttp.onreadystatechange  = handleServerResponse;
    xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xmlhttp.send();
  }
}

Things to consider, the old user id value is taken from the document, that's why you have to be careful with the name of the form that was created in login.html. 
Now the handleServerResponse function:

function handleServerResponse() {                                                
var newusername="";                                                              
   if (xmlhttp.readyState == 4) {                                                
     if(xmlhttp.status == 200) {                                                 
       newusername= xmlhttp.responseText; //Update the HTML Form element         
        if (newusername == "notfound" ) {                                        
        alert("Please provide your Current Luminis ID");                         
        document.forms['newuserid'].elements['currentuseridl'].value="";         
        document.forms['newuserid'].elements['currentuserid'].focus();           
        }                                                                        
        else {                                                                   
        document.forms['userid'].elements['user'].value = newusername;           
        document.forms['cplogin'].elements['pass'].focus();                      
        }                                                                        
     }                                                                           
     else                                                                         {                                                                                
        alert("Unknown Error. Please, reload your page and try again");          
     }
   }
}

The way handleServerResponse is written, when the student enter the current user Id and hit the Get my New ID button, his/her new user id will be filled in the Luminis User ID and the focus is passed already to the password field, so there is no need to copy/paste or memorize the new userid, if the user id is invalid an alarm window pops up notifying the user.
Pretty much this is everything that was touched.  I have extensively used Google for this research and I have an immense gratitude to everybody that has posted something I ended up using or not. I have copy and paste many fragments, from many ones, Thank you very much,





2 comments:

  1. Maybe it's the icky old way of doing Java web development, but when it comes to L*****s, I just develop JSPs. See http://www.lumdev.net/node/3894 for an example. The benefits are 1) the container (Tomcat) does not need to be restarted and 2) you don't have to figure out a working CLASSPATH.

    ReplyDelete
  2. Thanks, I found this as a google search for editing the login.html file in luminis. I was missing a forced update to login.jsp so it could pick up the changes.

    ReplyDelete