iSeries EGL

All things EGL for the iSeries / i5 / Power System

Posts Tagged ‘Tomcat Runtime Environment Variable

How to Read the Environment Variable Assigned to Tomcat

leave a comment »

Introduction

This post illustrates how to read the environment variable (EV) assigned to Tomcat’s JVM at the time Tomcat is started. The variable was assigned within Tomcat’s startup script to indicate Tomcat’s role as being a development server, a test server or a production server. The values were “localhost”, “TEST” or “PROD” respectively. To learn how the EV is assigned to Tomcat’s JVM select this link.

Approach

EGL does not support access to JVM variables as Java does. To solve the problem of reading the value a Java program must be employed to be called from EGL. Shown below is the Java program that reads the variable to be returned to the EGL program. Specifically the line of interest is this one:
runtimeEnvironment = System.getProperty(“runtimeEnvironment”).trim() ;


package com.app.runtime;

public class RuntimeEnvironment {


	private String runtimeEnvironment = null;
	private String clazz = null;
	
	
	/**
	 * Constructor
	 */
	public RuntimeEnvironment() {
	
	}
	
	
	/**
	 * 
	 */
	public String getRuntimeEnvironment() {
		

        //Get the Server property that holds the environment value.  This value will be
        //"localhost" for the developer's PC, "TEST" for the test server, or "PROD" for 
        //the production server.  This value is instrumental to acquiring the correct 
        //property values for the machine/environment in which this application is running.
		try {
			runtimeEnvironment = System.getProperty("runtimeEnvironment").trim() ;
		} catch (Exception ex) {
			System.out.println(" StartupTasks.init()  Server instance not found. " + ex.getMessage() );
		}
		
		if ((runtimeEnvironment == null)) {
			String msg = "Server not configured with runtime environment value. Value = "
					+ runtimeEnvironment
					+ ". (Valid config value: -DruntimeEnvironment='localhost' or TEST or PROD)";
			System.out.println(msg);
			runtimeEnvironment = "localhost";
			System.out.println(" RuntimeEnvironment.getRuntimeEnvironment()  Default environment will be " + runtimeEnvironment  );
		}
		
		
		if (runtimeEnvironment.equals("localhost")
				|| runtimeEnvironment.equals("TEST")
				|| runtimeEnvironment.equals("PROD")) {
		} else {
			String msg = 
					"RuntimeEnvironment.getRuntimeEnvironment(): The server is not configured with correct runtime envrionment value. Value = "
					+ runtimeEnvironment;
			System.out.println(msg);
			msg = "StartupServlet.init(): Examples of valid config values are: 'localhost', 'TEST' or 'PROD'.";
			System.out.println(msg);

		}
		
		System.out.println(clazz+ " **********************************************************************");
		System.out.println(clazz+ " LoggingViewer application startup phase");
		System.out.println(clazz+ "   Reading Server Argument for run-time environment parameter.");
		System.out.println(clazz + "     Server Run-Time Environment  : " + runtimeEnvironment);
		System.out.println(clazz + " ");
		System.out.println("LoggingViewer Runtime Environment is '" + runtimeEnvironment + "'.");
	
		return runtimeEnvironment;
	}
	
}

Fig.1 – The Java class ‘RuntimeEnvironment’

 

The strategy employed by RuntimeEnvironment is straight forward; the method getRuntimeEnvironment() reads the variable runtimeEnvironment the value of which has been assigned to the JVM. (Refer to this post to see how that was done.) In the event that a variable is not found, due mostly to the fact the developer may have elected not to assign it to their test server running on their local machine, the Java method will return “localhost” as a default value. As noted in the link which shows how to assign the values to the server, the values for “TEST” and “PROD” have already been assigned to Tomcat.

Since the Java class will need to be in all EGL projects the class has been packaged into a .jar-file so it can be reused. To use the class in your project, simply add the .jar-file to the “lib” directory under “WebContent”. Then add the .jar-file to the project’s class path. To learn how this is done select this link and read the section titled “Importing the Jar File into Your Project”.

 

Calling the Java method from EGL

This section describes how to create the EGL External Type and the EGL service, both of which are required when calling a Java program from EGL.

The EGL External Type Definition

First, create an external type to describe to EGL the Java class and its methods.


externalType RuntimeEnvironment type JavaObject{PackageName = "com.app.runtime"}
    
    constructor();
    function getRuntimeEnvironment() returns(string);
        
end

Fig.2 – The EGL External type named ‘RuntimeEnvironment’

 
The external type is an EGL part that describes an interface to another program of a different type. In this context it is the bridge between the EGL world and the Java world. The type shown above describes the Java constructor and the method named getRuntimeEnvironment() .

Calling Using a Dedicated Service

Next, create an EGL service that will use the Java class described by the external type, above.

import com.mig.logviewer.externaltypes.RuntimeEnvironment;

// service
service RuntimeService

    function getRuntime() returns(string)
        
        //Instantiate an object of RuntimeEnvironment.  Call it 'runtime'.
        //This uses the external type named 'RuntimeEnvironment'
        runtime RuntimeEnvironment = new RuntimeEnvironment();

        //Call runtime's getRuntimeEnvironment() method to get the runtime value.
        runtimeEnvironment string = runtime.getRuntimeEnvironment();

        return(runtimeEnvironment);

    end

end

Fig.3 – EGL service that will use the Java class

 

Calling the Java method from EGL

Finally, below is the EGL program code that calls the Java method to get the runtime environment variable established for Tomcat’s JVM.

The EGL function presented is part of the startup processing phase of the EGL project’s web-page. It begins by creating a dedicated service object called runtimeService which will is derived from the EGL Service program created above. Next, the EGL code calls the service’s getRuntime()method, which returns the value for Tomcat’s runtime environment variable.


    function start()

        //Initialize the application by getting the runtime environment from the 
        //server and reading property file values.
        runtimeService RuntimeService{@dedicatedService};
        call RuntimeService.process() 
            returning to initServiceReturn 
                        onException ServiceLib.runtimeServiceReturn;    
                        
    end

    function runtimeServiceReturn(runtimeEnvironment String in)
        
        globalDataRecord GlobalDataRecord = SessionLibrary.getGlobalDataRecord();
        globalDataRecord.runtimeEnvironment = runtimeEnvironment ;
        SessionLibrary.setGlobalDataRecord(globalDataRecord);  

    end


Fig.4 – EGL function calling a service program

 

The call back function receives the variable runtimeEnvironment returned by the service call. The value of the variable is then stored in an instance of an EGL basic record called globalDataRecord In this manner the runtimeEnvironment variable can be used throughout the life of the program simply by passing the globalDataRecord as the parameter. Think of it as a container for global values.
 

Summary

This post has shown how to create a Java class to provide a function not available by EGL alone. Specifically, we have seen how to create an EGL External Type, an EGL Service and the code used to call the service to receive values returned.

Tomcat In Development, Test and Production Environments

leave a comment »

Introduction

This post shows how a JVM variable can be employed on Tomcat’s startup command to declare a server instance as either a development server, a test server or a production server. An application can then retrieve this value during its startup processing phase to determine the database to which a connection must be made. To see how EGL can read this JVM variable value select this link.

Variable Indicating Development, Test and Production Servers

The value assigned to the parameter “runtimeEnvironment” indicates the role of the Tomcat server. For the developer’s server, usually running inside the IDE (RDi) the value for this parameter will be “localhost”. For the test and production Tomcat servers running on the iSeries, there are two valid values which are “TEST” and “PROD”. For example this parameter will designate the server as a production server:

-DruntimeEnvironment=”PROD”

All applications should obtain the value of this variable during its startup phase. For Java, this is a startup servlet. For EGL, it will be a service.

Shown below is the command that starts a Tomcat server. It has been modified to set the environment variable and its value, in this case, ‘PROD’.


java -ms64m -Duser.dir="$CATALINA_BASE" -Dcatalina.base="$CATALINA_BASE" -Dcatalina.home="$CATALINA_HOME" -Djava.awt.headless=true -DruntimeEnvironment="PROD" org.apache.catalina.startup.Bootstrap "$@" start

All parameter name/value pairs begin with “-D”.

Why Do This?

The intent is to inform the application about the role of the server, i.e. the developer’s desktop machine in which case the value will default to “localhost” for the developer’s server, “TEST” for the test server and “PROD” for the production server. Once the value is obtained the application’s logic can then determine the proper database connection to use in addition to reading the correct set of property values established for the application. The diagram shown clarifies this.

Fig.1 – Diagram showing roles of three Tomcat servers defined by a JVM variable (Click to enlarge)

Conclusion

This post revealed how a name/value pair can be established for a given Tomcat server instance so that an application may interrogate the value of the named parameter. With the value obtained the application’s logic can then determine the correct database to which a connection must be made. In this way the development staff can be confident their application can be safely run on a test server with the knowledge that it will not update production data. When it comes time to deploy to the production server, the application does not have to be modified in any way and its logic will again connect to the correct production oriented database as well as being able to obtain the correct values from the application’s property file.

Written by iseriesadmin

June 10, 2012 at 9:57 am

Making a DB Connection during runtime

leave a comment »

Using Tomcat’s JVM parameter values to facilitate DB connections

This post will illustrate how a database connection can be established to one of two separate iSeries machines.

Before we get going, for some background you should probably read this post first. Its is about the configuration of the server’s (Tomcat) JVM property value for run-time environment ‘localhost’, ‘TEST’ and ‘PROD’.

 

The application’s externally defined property values

The application’s configuration data is contained in an external XML file, a common practice in web application development. For this discussion it will suffice to say the application’s configuration data has already been read during the application’s initialization phase conducted when the application is started. During this phase the data is cached into an EGL BasicRecord type i.e. ‘configDataRecord’ which can then be passed to the application’s services as a parameter. In this manner all services can gain access to the application’s configuration data.

Shown below are the entries placed in our XML configuration file that describe connection criteria for MACH1 and MACH2 used for the developer’s (localhost) server, the test (TEST) machine and the production (PROD) machine. (User id’s and passwords are obfuscated.) Library entries, of which there is only one in this example, are separated by spaces. Note the libraries established for each machine for each development environment. In this way, each connection for each machine is assigned to the correct library ensuring the proper set of data is used for each environment. This method ensures that the developer does not have to prefix or qualify SQL statements with embedded library names, which does not translate well when moving the application from a TEST server to a PROD server.


<entry key="localhost.MACH1MachineId">MACH1</entry> <entry key="localhost.MACH1UserID">**********</entry> <entry key="localhost.MACH1Password">**********</entry> <entry key="localhost.MACH1URL">jdbc:as400:MACH1;prompt=false;naming=system;libraries=DEVLIB;transaction isolation=none;</entry> <entry key="localhost.MACH2MachineId">MACH2</entry> <entry key="localhost.MACH2UserID">**********</entry> <entry key="localhost.MACH2Password">**********</entry> <entry key="localhost.MACH2URL">jdbc:as400:MACH2;prompt=false;naming=system;libraries=DEVLIB;transaction isolation=none;</entry> <entry key="TEST.MACH1MachineId">MACH1</entry> <entry key="TEST.MACH1UserID">**********</entry> <entry key="TEST.MACH1Password">**********</entry> <entry key="TEST.MACH1URL">jdbc:as400:MACH1;prompt=false;naming=system;libraries=TESTLIB;transaction isolation=none;</entry> <entry key="TEST.MACH2MachineId">MACH2</entry> <entry key="TEST.MACH2UserID">**********</entry> <entry key="TEST.MACH2Password">**********</entry> <entry key="TEST.MACH2URL">jdbc:as400:MACH2;prompt=false;naming=system;libraries=TESTLIB;transaction isolation=none;</entry> <entry key="PROD.MACH1MachineId">MACH1</entry> <entry key="PROD.MACH1UserID">**********</entry> <entry key="PROD.MACH1Password">**********</entry> <entry key="PROD.MACH1URL">jdbc:as400:MACH1;prompt=false;naming=system;libraries=PRODLIB;transaction isolation=none;</entry> <entry key="PROD.MACH2MachineId">MACH2</entry> <entry key="PROD.MACH2UserID">**********</entry> <entry key="PROD.MACH2Password">**********</entry> <entry key="PROD.MACH2URL">jdbc:as400:MACH2;prompt=false;naming=system;libraries=PRODLIB;transaction isolation=none;</entry>

Fig.1 – The application’s XML configuration file

 

Making the Connection

Shown below is the EGL code that resides in an EGL service program. The variable ‘server’, passed to the function containing this code, is used to govern which set of logic is to be executed. The value determines the values for the variables ‘machineID’, ‘userid’ and ‘password’.

Once these values are obtained the value for ‘runtimeEnvironment’ is used to obtain the correct connection string or URL. For localhost, this value will come from the XML configuration data. For TEST or PROD, the connection URL is obtained from the JNDI-defined connection criteria established in Tomcat’s context.xml file. See this post for information about that.


//Obtain the runtimeEnvrionment variable from the configuration data cache runtimeEnvironment = StrLib.clip(configDataRecord.runtimeEnvironment); //The program defined variable 'server' contains either 'MACH1' or 'MACH2' if (server == configDataRecord.MACH1MachineId) machineID = configDataRecord.MACH1MachineId; userid = configDataRecord.MACH1UserID; password = configDataRecord.MACH1Password; if (runtimeEnvironment == CommonLibrary.CONST_LOCALHOST) connectionString = configDataRecord.MACH1URL; end if (runtimeEnvironment == CommonLibrary.CONST_TEST) connectionString = "jdbc/MACH1"; end if (runtimeEnvironment == CommonLibrary.CONST_PROD) connectionString = "jdbc/MACH1"; end end if (server == configDataRecord.MACH2MachineId) machineID = configDataRecord.MACH2MachineId; userid = configDataRecord.MACH2UserID; password = configDataRecord.MACH2Password; if (runtimeEnvironment == CommonLibrary.CONST_LOCALHOST) connectionString = configDataRecord.MACH2URL; end if (runtimeEnvironment == CommonLibrary.CONST_TEST) connectionString = "jdbc/MACH2"; end if (runtimeEnvironment == CommonLibrary.CONST_PROD) connectionString = "jdbc/MACH2"; end end //Make the connection sqlLib.connect(connectionString, userid, password);

Fig.2 – Tomcat’s context.xml

 

Problem? What problem?

While everything explained so far is accurate, there is one problem, if it can be called that. It’s more of a developmental artifact than anything else and can be spotted in the configuration file. Everything will still work, but there are entries which have been rendered superfluous. Can you spot the problem? Hint: see the JNDI entries for comparison. Post your thoughts on this in the comments section below this post.

 

Summary

So, there we have it. We’ve seen how Tomcat configuration and the application’s externally defined property values can facilitate connections to a given database for a whole list of different machines. The value of this effort should be self-evident, that is, it does not promote hard-coded connection criteria in the application’s code which, without modifications, would prohibit deployment across multiple run time environments represented by the developer’s machine, the test or QA machine and finally the production machine.

Tomcat on the iSeries. Running Headless.

leave a comment »

First things, first. The server

When running Tomcat on the iSeries a few modifications to its startup script are required. First, the iSeries is headless, meaning it has no keyboard, monitor or mouse attached. (Yes, there are admin consoles, but this isn’t the same thing.)

In our case, Tomcat is running in a UNIX environment therefore the startup script for Tomcat is catalina400.sh. Do not modify the windows version (catalina400.bat)

Within catalina400.sh, Tomcat’s startup command (shown below) must be modified to include an additional parameter beyond the original parameters already supplied to the command.

 

Variable to indicate the server is operating in a ‘Headless’ state

The parameter “-Djava.awt.headless=true” is used to indicate to Java that the system, i.e the iSeries, is operating in a configuration in which the display device, keyboard, or mouse is lacking. See this link for a full explanation

The full command is :


java -ms64m -Duser.dir="$CATALINA_BASE" -Dcatalina.base="$CATALINA_BASE" -Dcatalina.home="$CATALINA_HOME" -Djava.awt.headless=true org.apache.catalina.startup.Bootstrap "$@" start

All parameter name/value pairs begin with “-D”.

Without the parameter, this stack-trace is produced, which is complaining about graphics, which the iSeries does not have. This is the clue indicating the proper course of action which results in the use of the parm.


SEVERE: Critical error during deployment: java.lang.UnsatisfiedLinkError at java.lang.Throwable.(Throwable.java:181) at java.lang.Error.(Error.java:37) at java.lang.LinkageError.(LinkageError.java:26) at java.lang.UnsatisfiedLinkError.(UnsatisfiedLinkError.java:25) at sun.java2d.Disposer.(Disposer.java:42) at javax.imageio.stream.MemoryCacheImageInputStream.(MemoryCacheImageInputStream.java:61) at com.sun.imageio.spi.InputStreamImageInputStreamSpi.createInputStreamInstance(InputStreamImageInputStreamSpi.java:53) at javax.imageio.ImageIO.createImageInputStream(ImageIO.java:331) at org.richfaces.application.InitializationListener$AWTInitializer.initialize(InitializationListener.java:84) at org.richfaces.application.InitializationListener.onStart(InitializationListener.java:122) at org.richfaces.application.InitializationListener.processEvent(InitializationListener.java:152) at javax.faces.event.SystemEvent.processListener(SystemEvent.java:106) at com.sun.faces.application.ApplicationImpl.processListeners(ApplicationImpl.java:2129) at com.sun.faces.application.ApplicationImpl.invokeListenersFor(ApplicationImpl.java:2105) at com.sun.faces.application.ApplicationImpl.publishEvent(ApplicationImpl.java:300) at com.sun.faces.config.ConfigManager.publishPostConfigEvent(ConfigManager.java:600) at com.sun.faces.config.ConfigManager.initialize(ConfigManager.java:369) at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:225) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4205) at org.apache.catalina.core.StandardContext.start(StandardContext.java:4704) at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:799) at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:779) at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:601) at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:830) at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:563) at org.apache.catalina.startup.HostConfig.check(HostConfig.java:1397) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:575) at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:297) at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:836) at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:761) at org.apache.catalina.manager.ManagerServlet.check(ManagerServlet.java:1500) at org.apache.catalina.manager.HTMLManagerServlet.doPost(HTMLManagerServlet.java:252) at javax.servlet.http.HttpServlet.service(HttpServlet.java:637) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.filters.CsrfPreventionFilter.doFilter(CsrfPreventionFilter.java:186) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:563) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298) at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859) at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489) at java.lang.Thread.run(Thread.java:619) . . .

Follow

Get every new post delivered to your Inbox.