21
Sat, Dec
3 New Articles

Practical Java: Start Your Java Engine!

Java
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

A little programming allows you to take complete control of your Java environment.

In my previous article, I explained the complexities of the Java environment, especially as it relates to interaction with ILE languages. I introduced the three major components of a Java environment—the Java version, the classpath, and the runtime properties—and explained how to manage them non-programmatically through environment variables and properties files. In this article, I'll provide a program that does all of that, and in so doing I'll also show you a couple of cool techniques that can be used in other situations.

Quick Recap of Java Environments

Remember that the first component of the Java environment is the Java version that you're running. That ties to the actual binary code for the Java Virtual Machine (JVM) and is determined by the JAVA_HOME environment variable, which contains an absolute path to the JVM. You have a default JVM, which depends on the version of the operating system you're running. For my original case, I was running on IBM i 7.3, which defaults to IT4J 8.0 32 bit, but I needed the 64-bit version, and that requirement led to this project.

The next part of a Java environment is the classpath. The classpath acts as the library list for Java and contains a list of locations where prewritten Java code exists. This value is set using the CLASSPATH environment variable.

So far, we've only used environment variables, and if that was all it took, we wouldn't need a dedicated program. However, it's never that easy, so now it's time to address the last piece of the environment, the runtime properties. These properties are usually set on the command line by the script that invokes the JVM, but since we're calling this from ILE RPG, we instead make use of the JNI_CreateJavaVM procedure. The trick here is that the procedure expects ASCII values (more on that in a moment).

Let's Get to the Code!

That's really all the introduction we need, so without further ado, let's see the code. This will be broken into four primary sections: the data definitions, the mainline, and the three subprocedures. It's really that simple.

Data Definitions

ctl-opt actgrp(*new) option(*srcstmt:*nodebugio);              

                                                           

/DEFINE OS400_JVM_12                                            

/copy QSYSINC/QRPGLESRC,JNI                                    

                                                                

dcl-c C_JDK_80_64 '/QOpenSys/QIBM/ProdData/JavaVM/jdk80/64bit';

                                                                

dcl-ds initArgs likeds(JavaVMInitArgs);                        

dcl-ds options likeds(JavaVMOption) occurs(5);                

dcl-s jvm like(JavaVM_p);                                      

dcl-s env like(JNIEnv_P) inz(*null);                          

dcl-s nVMs int(10);                                            

                                                                

dcl-pr system int(10) extproc('system');                      

command pointer value options(*string);                      

end-pr;                                                        

                                                                

dcl-pi *n;                                                    

iFunc char(10) const;                                        

oRC   int(10) options(*nopass);                            

iJVM char(10) const options(*nopass);                      

iOpt1 char(50) const options(*nopass);                      

iOpt2 char(50) const options(*nopass);                      

iOpt3 char(50) const options(*nopass);                      

iOpt4 char(50) const options(*nopass);                      

iOpt5 char(50) const options(*nopass);                      

end-pi;                                                        

This part of the code is very simple. It starts with my normal control specification, in which I create a new activation group for the program. Next are JNI-specific definitions. We haven't really had a chance to touch on that acronym: JNI stands for Java Native Interface and is the industry standard way for non-Java programs to access the Java environment. The IBM i uses this interface to access Java directly from ILE. To keep it brief, this section of code basically sets up a JVM startup parameter that allows up to five runtime properties but that could be expanded easily.

Next, I have the prototype for the system function, which I use to execute IBM i commands, and finally the prototype for this program itself. Let me take a quick review of this:

  • iFunc   char(10) - Key to the CLSPTH file          
  • oRC     int(10) - Return code (0 is success)      
  • iJVM   char(10) - Override Java version (*64)      
  • iOpt1-5 char(50) - Additional JVM startup parameters

Only iFunc is required; that's the key to the CLSPTH file and identifies the Java function we're performing (which would in turn determine which pieces of Java code we'd want to include in our classpath). You'd call the program with only one parameter if you're happy with the system defaults for Java (which are usually fine for 99 percent of the cases) and just want to define the CLASSPATH. This syntax doesn't even start the JVM; you'll do that when you invoke Java.

If you specify any other parameters, then the JVM is started, and the result code is returned in the oRC parameter. Any additional functionality depends on the parameters you pass. The next parameter allows you to change the Java version. This program only has one override, which is selected by specifying either *64 or *64BIT as the third parameter. Specifying anything else (including blank) just uses the default JVM.

Finally, you have up to five additional 50-character parameters. Any additional parameters specified are plugged into the startup parameter structure before the JVM is invoked. The tricky part about this last piece is that the parameters must be converted to ASCII before being passed to the JNI_CreateJavaVM procedure.

Mainline

 

// Always set environment variable CLASSPATH                  

// This will be used by default JVM startup                    

setClassPath();                                                

                                                                

// If JVM parameters are passed, use them                      

if %parms > 1;                                                  

                                                                

// See if a VM is already running; if so, exit with error    

if (JNI_GetCreatedJavaVMs(jvm : 1 : nVMs) = 0) and (nVMs > 0);

   oRC = -99;                                                  

   *inlr = *on;                                                

   return;                                                    

endif;                                                        

                                                                

// Initialize JVM properties                                  

// Ignore any initial settings and overwrite                  

initArgs = *allx'00';                                        

initArgs.version = JNI_VERSION_1_2;                          

                                                                

// First, check for a JVM ID                                  

if %parms > 2;                                                

                                                                

   // If selecting 64-bit, set JAVA_HOME to reflect it        

   if iJVM = '*64' or iJVM = '*64BIT';                        

     setEnvVar( 'JAVA_HOME': C_JDK_80_64);                    

   endif;                                                      

                                                                

   // Now process optional parameters                          

   if %parms > 3;                                              

   addOption(%trim(iOpt1));                                  

     if %parms > 4;                                            

       addOption(%trim(iOpt2));                                

       if %parms > 5;                                          

         addOption(%trim(iOpt3));                              

         if %parms > 6;                                        

           addOption(%trim(iOpt4));                            

           if %parms > 7;                                      

             addOption(%trim(iOpt5));                          

           endif;                                              

         endif;                                                

       endif;                                                  

     endif;                                                    

   endif;                                                      

                                                                

   // Set the address of options (have to occur to first element

   %occur(options) = 1;                                        

   initArgs.options = %addr(options);                          

   // Start the VM and return the result                      

   oRC = JNI_CreateJavaVM (jvm : env : %addr(initArgs));      

                                                                

endif;                                                        

                                                                

endif;                                                          

                                                                

*inlr = *on;                                                    

The only thing that will always be done is the classpath will be set. This is done by a call to the setClasspath procedure, which will follow shortly. If only one parameter is passed, then the program ends here. Otherwise, the next step is to get ready to start the JVM. The first part of that task is to see if there already is a JVM running, since the IBM i architecture limits you to one JVM in a job. Once a JVM is started for a job, you can never start another one. A quick check determines whether a JVM is running and returns an error if so.

The first task is to select the Java version. We do this only if the default JVM won't work. To do it, we have to put a long, rather cumbersome folder path into the JAVA_HOME environment variable. In this program, there is only one alternate JVM, and it is selected by passing in *64 or *64BIT as the second parameter. Finally, we add each additional parameter that is passed using the addOption procedure.

Once all the setup work is complete, we update the initArgs structure and call JNI_CreateJavaVM. We return the result.

Adding an Option

dcl-proc addOption;                                            

dcl-pi *n;                                                    

   iOption varchar(50) const;                                  

end-pi;                                                      

dcl-s wOptionAscii varchar(50) ccsid(819);                    

dcl-s wOptionHex varchar(50) ccsid(*hex);                    

                                                                

// Increment option count                                    

initArgs.noptions += 1;                                      

// Change occur, clear option and allocate string              

%occur(options) = initArgs.noptions;                          

options = *allx'00';                                          

options.optionString = %alloc(%len(iOption) + 1);            

// Convert to ASCII, then hex (so %str won't convert back)    

wOptionAscii = iOption;                                      

wOptionHex = wOptionAscii;                                    

// Copy hex and null-terminate it for Java                    

%str(options.optionString: %len(iOption) + 1) = wOptionHex;  

                                                                

end-proc;                                                      

This is actually one of my favorite parts of the program. I've written many different versions of EBCDIC-to-ASCII conversions (and vice versa), but the time that the compiler team put into string definition really pays off here. Watch how easy this is. First, we pass in an option; this comes in as an EBCDIC varying character string. I have to move it to the next entry in the array. The first part of the procedure changes the occurrence of the data structure. Yes, I probably could have used a data structure array instead of a multi-occurrence data structure, but this came from the original IBM example code and I didn't feel like straying too far from it. Note that I also have to allocate the space for the variable. Again, that's just the way the code was written, but it makes sense when you're talking about an arbitrary number of arbitrary-length strings.

Anyway, it's the next part that is so very cool. By defining the variable wOptionASCII with the keyword CCSID(819), the simple act of setting wOptionASCII equal to iOption automatically converts it from EBCDIC to ASCII! Then I simply use %str to make a zero-terminated copy in the new variable I allocated, and I'm done! Oh, you probably noticed a second move from wOptionASCII to wOptionHex. That's because %str likes to convert things itself. The first time I did this code without the extra move, the value that ended up in options.optionString was actually in EBDIC; %str saw that wOptionASCII was CCSID(819) and (not so) helpfully converted it back to my job CCSID.

Setting the CLASSPATH

                                                                

// Aggregate CLSPTH records into path and set CLASSPATH        

dcl-proc setClassPath;                                          

dcl-s wPath varchar(2000);                                    

exec sql set :wPath =                                        

   (select listagg(trim(CPPATH), ':') within group(order by CPSEQ)

     from CLSPTH where CPFUNC = :iFunc);                      

SetEnvVar( 'CLASSPATH': wPath);                              

end-proc;                                                      

And while the auto-conversion in addOption is indeed the coolest thing in the program, this procedure is also pretty nifty. The file CLSPTH is simple: it has a function (CPFUNC) that is the key, a sequence (CPSEQ), and a 50-character path variable (CPPATH). To format that into the CLASSPATH variable, you have to concatenate all of them with colons in between each entry. This is typically done in a loop in RPG using concatenation, being careful to not include a colon either at the beginning or end of the aggregated string. Well, since we do that about a million times in our programming careers, the fine folks in the SQL world added a way to do that, called LISTAGG.

Without going into detail (you can find the IBM documentation here), the syntax used in the SQL statement above aggregates all the CPPATH values for the specified CPPATH key, in order by the CPSEQ variable, delimited by colons and stuffs that entire value into the wPath variable.

Setting an Environment Variable

                                                                                                                                  

dcl-proc setEnvVar;                                            

dcl-pi *n;                                                    

   iVar varchar(10) const;                                    

   iVal varchar(2000) const;                                  

end-pi;                                                      

system('RMVENVVAR ' + iVar + ' LEVEL(*JOB)');                

system('ADDENVVAR ' + iVar + ' VALUE(''' + iVal + ''') LEVEL(*J

end-proc;                                                      

The last procedure is very simple. It just removes and then sets an environment variable. It’s hardcoded to do this at the *JOB level (as opposed to the *SYSTEM level) and since we're using the system function, any errors are ignored. It's not very robust, but it works.

And That’s All!

That's it for the entire program. You can copy this source code into a SQLRPGLE member and away you go! Let me know if you encounter any issues, but this should give you complete control over your environment. Just to give you an example, this is how I used it in my actual application code.

SYCLSPTH( '*EXCEL': wRC: '*64BIT': '-Xmx4G': '-Xms1G');

The value *EXCEL is the key to the CLSPTH file, and it selects all of the POI JAR files and all of their prerequisites, along with JT400 and a couple of miscellaneous pieces. I needed a 4G heap, so I not only specified that, but since a 4G heap also requires a 64-bit JVM, I overrode that as well.

Enjoy!

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: