File Coverage

blib/lib/Java/SJ.pm
Criterion Covered Total %
statement 34 36 94.4
branch n/a
condition n/a
subroutine 12 12 100.0
pod n/a
total 46 48 95.8


line stmt bran cond sub pod time code
1             ###########################################################################
2             #
3             # Java::SJ
4             #
5             # $Id: SJ.pm,v 1.4 2003/07/20 18:52:15 wiggly Exp $
6             #
7             # $Author: wiggly $
8             #
9             # $DateTime$
10             #
11             # $Revision: 1.4 $
12             #
13             ###########################################################################
14              
15             package Java::SJ;
16              
17 1     1   18096 use strict;
  1         3  
  1         34  
18              
19 1     1   5 use Carp;
  1         1  
  1         83  
20 1     1   7 use Cwd;
  1         6  
  1         72  
21 1     1   2717 use Data::Dumper;
  1         15821  
  1         209  
22 1     1   1351 use English;
  1         5805  
  1         8  
23 1     1   671 use File::Path;
  1         2  
  1         72  
24 1     1   1187 use File::Slurp;
  1         17021  
  1         91  
25 1     1   13 use File::Spec;
  1         2  
  1         25  
26 1     1   1439 use File::Temp qw( tempfile );
  1         24312  
  1         80  
27 1     1   5843 use IO::File;
  1         3068  
  1         288  
28 1     1   15 use IO::Handle;
  1         5  
  1         72  
29 1     1   1312 use Java::SJ::Config;
  0            
  0            
30              
31             our $VERSION = '0.01';
32              
33             our $LOG = undef;
34              
35             ###########################################################################
36             #
37             # sj - main program for sj script
38             #
39             ###########################################################################
40             sub sj
41             {
42             my $config = new Java::SJ::Config;
43              
44             my $script = shift @ARGV;
45              
46             my $log_file;
47              
48             my $handle;
49              
50             croak "[ERROR] Please specify a script file on the command line.\n"
51             unless defined( $script );
52              
53             # find out the canonical path to the script
54             $script = File::Spec->rel2abs( $script );
55              
56             # open the script and read it into our configuration
57             $handle = new IO::File( "<$script" )
58             or croak "[ERROR] Cannot open file $script, $!\n";
59              
60             $config->load_app_configuration( $handle, 0 )
61             or croak "[ERROR] Could not load configuration\n";
62              
63             $handle->close;
64              
65             # open LOG filehandle
66             $log_file = $config->get_var( 'dir.log' ) . "/sj.log";
67              
68             $LOG = new IO::File $log_file, "w"
69             or croak "[ERROR] Cannot open log file $log_file, $!\n";
70              
71             print $LOG "[INFO] Java::SJ::sj\n";
72             print $LOG "[INFO] PROGRAM $PROGRAM_NAME\n";
73             print $LOG "[INFO] PID $PROCESS_ID\n";
74             print $LOG "[INFO] CWD " . getcwd . "\n";
75             print $LOG "[INFO] ARGV " . join( ' ', @ARGV ) . "\n";
76             print $LOG "[INFO] SCRIPT " . $script . "\n";
77              
78              
79             print $LOG "[DEBUG] configuration\n";
80             print $LOG "[DEBUG]\n";
81             print $LOG Dumper( $config );
82             print $LOG "\n\n";
83              
84             print $LOG "[INFO] Now generate a cached script file in our script dir.\n";
85              
86             my $program_file = $config->get_var( 'dir.script' ) . "/" . $config->get_var( 'app.name' );
87              
88             print $LOG "[INFO] Program file : $program_file\n";
89              
90              
91             # ensure script directory exists
92             my ( $volume, $directories, $file ) = File::Spec->splitpath( $program_file );
93              
94             eval
95             {
96             mkpath( $directories );
97             };
98            
99             if( $@ )
100             {
101             ( $handle, $program_file ) = tempfile( 'sj_script.XXXXXXXX', DIR => $config->get_var( 'dir.tmp' ) );
102             }
103             else
104             {
105             $handle = new IO::File( ">$program_file" )
106             or croak "[ERROR] Cannot open program file $program_file for writing, $!\n";
107             }
108            
109             print $handle "#!$EXECUTABLE_NAME\n";
110             print $handle "use Java::SJ;\n";
111             print $handle "&Java::SJ::run;\n";
112             print $handle "__END__\n";
113             print $handle read_file( $script );
114              
115             $handle->close;
116              
117             # make it executable
118             chmod 0755, $program_file;
119              
120             # execute the script
121             {
122             exec ( $program_file, @ARGV );
123             };
124              
125             croak "[ERROR] Could not exec program $program_file, $!\n";
126             }
127              
128              
129             ###########################################################################
130             #
131             # run
132             #
133             ###########################################################################
134             sub run
135             {
136             my $config = new Java::SJ::Config;
137              
138             my $vm = undef;
139              
140             my @prop = ();
141            
142             my @param = ();
143              
144             my @cp = ();
145              
146             my $command = '';
147              
148             my $log_file;
149              
150             my ( $key, $bcp, $pbcp, $abcp, $cp );
151              
152             $config->load_script_configuration;
153              
154             # open LOG filehandle
155             $log_file = $config->get_var( 'dir.log' ) . "/sj.log";
156              
157             $LOG = new IO::File $log_file, "w"
158             or croak "[ERROR] Cannot open log file $log_file, $!\n";
159              
160             print $LOG "[INFO] Java::SJ::run\n";
161             print $LOG "[INFO] PROGRAM $PROGRAM_NAME\n";
162             print $LOG "[INFO] PID $PROCESS_ID\n";
163             print $LOG "[INFO] CWD " . cwd . "\n";
164             print $LOG "[DEBUG] configuration\n";
165             print $LOG "[DEBUG]\n";
166             print $LOG Dumper( $config );
167             print $LOG "\n\n";
168             print $LOG "[DEBUG] BOOTCLASSPATH : " . $config->{'bootclasspath'}->generate_classpath( $config->get_var( 'dir.lib' ) ) . "\n";
169             print $LOG "[DEBUG] PBOOTCLASSPATH : " . $config->{'prepend_bootclasspath'}->generate_classpath( $config->get_var( 'dir.lib' ) ) . "\n";
170             print $LOG "[DEBUG] ABOOTCLASSPATH : " . $config->{'append_bootclasspath'}->generate_classpath( $config->get_var( 'dir.lib' ) ) . "\n";
171             print $LOG "[DEBUG] CLASSPATH : " . $config->{'classpath'}->generate_classpath( $config->get_var( 'dir.lib' ) ) . "\n";
172             print $LOG "\n\n";
173              
174              
175             print $LOG "[DEBUG] get vm we need to use\n";
176            
177             $vm = $config->{'vm'}{$config->{'vmref'}};
178            
179             print $LOG "[DEBUG] got VM '" . $vm->name . "'\n";
180              
181             print $LOG "[DEBUG] construct environment\n";
182              
183             foreach $key ( keys %{$config->{'env'}} )
184             {
185             print $LOG "[DEBUG] ENV $key -> " . $config->{'env'}{$key} . "\n";
186             $ENV{$key} = $config->{'env'}{$key};
187             }
188              
189             foreach $key ( keys %{$vm->{'env'}} )
190             {
191             print $LOG "[DEBUG] ENV $key -> " . $vm->{'env'}{$key} . "\n";
192             $ENV{$key} = $vm->{'env'}{$key};
193             }
194              
195             # set JAVA_HOME based on VM
196             $ENV{'JAVA_HOME'} = $vm->home;
197              
198             print $LOG "[DEBUG] construct property list\n";
199              
200             foreach $key ( keys %{$config->{'prop'}} )
201             {
202             print $LOG "[DEBUG] PROP $key -> " . $config->{'prop'}{$key} . "\n";
203             push @prop, sprintf( "-D%s=%s", $key, $config->{'prop'}{$key} );
204             }
205              
206             foreach $key ( keys %{$vm->{'prop'}} )
207             {
208             print $LOG "[DEBUG] PROP $key -> " . $vm->{'prop'}{$key} . "\n";
209             push @prop, sprintf( "-D%s=%s", $key, $vm->{'prop'}{$key} );
210             }
211              
212             print $LOG "[DEBUG] property list : " . join( ' ', @prop ) . "\n";
213              
214             print $LOG "[DEBUG] construct parameter list\n";
215              
216             foreach $key ( keys %{$config->{'param'}} )
217             {
218             print $LOG "[DEBUG] PARAM $key -> " . $config->{'param'}{$key} . "\n";
219             push @param, sprintf( "%s%s", $key, $config->{'param'}{$key} );
220             }
221              
222             foreach $key ( keys %{$vm->{'param'}} )
223             {
224             print $LOG "[DEBUG] PARAM $key -> " . $vm->{'param'}{$key} . "\n";
225             push @param, sprintf( "%s%s", $key, $vm->{'param'}{$key} );
226             }
227              
228             print $LOG "[DEBUG] param list : " . join( ' ', ( @param, @ARGV ) ) . "\n";
229              
230             print $LOG "[DEBUG] construct java command line\n";
231              
232              
233             $bcp = $config->{'bootclasspath'}->generate_classpath( $config->get_var( 'dir.lib' ) );
234             $pbcp = $config->{'prepend_bootclasspath'}->generate_classpath( $config->get_var( 'dir.lib' ) );
235             $abcp = $config->{'append_bootclasspath'}->generate_classpath( $config->get_var( 'dir.lib' ) );
236             $cp = $config->{'classpath'}->generate_classpath( $config->get_var( 'dir.lib' ) );
237              
238             if( length( $bcp ) )
239             {
240             push @cp, "-Xbootclasspath:$bcp";
241             }
242              
243             if( length( $pbcp ) )
244             {
245             push @cp, "-Xbootclasspath/p:$pbcp";
246             }
247              
248             if( length( $abcp ) )
249             {
250             push @cp, "-Xbootclasspath/a:$abcp";
251             }
252              
253             if( length( $cp ) )
254             {
255             push @cp, "-classpath $cp";
256             }
257              
258             #$command .= 'echo "';
259             $command .= $vm->home;
260             $command .= '/bin/java';
261             $command .= ' ';
262             $command .= join( ' ', @cp );
263             $command .= ' ';
264             $command .= join( ' ', @prop );
265             $command .= ' ';
266             $command .= $config->get_var( 'app.class' );
267             $command .= ' ';
268             $command .= join( ' ', @param );
269             $command .= ' ';
270             $command .= join( ' ', @ARGV );
271             $command .= ' ';
272             #$command .= '"';
273            
274             print $LOG "[DEBUG] write out PID file if required\n";
275              
276             print $LOG "[DEBUG] command :\n$command\n\n";
277              
278             # execute java
279             exec $command;
280             }
281              
282             ###########################################################################
283             1;
284              
285             =pod
286              
287             =head1 NAME
288              
289             Java::SJ - Highly configurable Java program startup system
290              
291             =head1 SYNOPSIS
292              
293             sj myprogram.sj
294              
295             =head1 DESCRIPTION
296              
297             This module allows you to very easily run Java services that rely on complex
298             configuration at the VM and library level. It also provides an easy way of
299             specifying a sensible 'default' configuration that can be overridden by specific
300             applications should they need to.
301              
302             The system is configured on a machine and application level. The system
303             looks for configuration files in a set of well-known locations, currently
304             these are:
305              
306             =over 4
307              
308             =item * F
309              
310             =item * F<.sj.conf> in users HOME directory
311              
312             =item * F<.sj.conf> in current working directory
313              
314             =back
315              
316             Every application is defined in terms of a similar configuration file. The
317             configuration system has been designed so that it is easy to write a simple
318             and minimal configuration file for a program.
319              
320             Provided the system has a fairly complete configuration associated with it
321             then an application configuration file need only have the class name to be
322             executed.
323              
324             =head1 FEATURES
325              
326             Some of the Goodness(tm) that you get with SJ is as follows:
327              
328             =over 4
329              
330             =item Easy co-existence of multiple Virtual Machines
331              
332             Any number of VMs can be supported and used concurrently on the same
333             machine. Developers don't need to know where the JDK/JRE resides, just a
334             symbolic name for it.
335              
336             =item Easy co-existence of multiple versions of JAR files
337              
338             Any number of different versions of the same JAR file may co-exist. SJ sorts
339             out which ones to use and only places those JAR files that are required in
340             an application's CLASSPATH
341              
342             =item Control over BOOTCLASSPATH variables
343              
344             All three flavours of the bootclasspath can be configured on a system-wide
345             and application specific basis.
346              
347             =item Process control
348              
349             PID files can be automatically generated and placed wherever you wish.
350              
351             =item Cache of executable scripts
352              
353             Application configuration files are cached as executable scripts that can be
354             directly invoked.
355              
356             =back
357              
358              
359             This will probably make more sense as a set of examples, so here goes.
360              
361             =head1 EXAMPLES
362              
363             =head2 Simple Configuration
364              
365            
366            
367            
368            
369             home="/usr/local/IBMJava-1.3.1"
370             default="true"/>
371            
372              
373             The above minimal system configuration simply tells the system where to find
374             a virtual machine called 'ibm' and that it should be used as the default VM
375             unless an application specifically requests another one.
376              
377            
378            
379            
380             myclass
381            
382              
383             The above minimal application configuration simply provides a class file
384             name to run. If both of the above simple configuration files were used then
385             the class 'myclass' would have to be in the system's CLASSPATH environment
386             variable already.
387              
388             =head2 Useful Configuration
389              
390             The above simple configuration is only really useful to test that the system
391             is working. Running a simple class on the command line isn't normally very
392             difficult and so sj doesn't actually add very much to the above case.
393              
394             If we had a system where we wished to test that the same code was compatible
395             with multiple virtual machines and multiple library versions then the
396             following configuration files would enable us to run these programs with
397             different parameters easily.
398              
399            
400            
401            
402            
403            
404            
405            
406            
407            
408              
409            
410            
411              
412            
413            
414            
415            
416            
417            
418            
419              
420            
421             vendor="IBM"
422             version="1.1.8"
423             home="/usr/local/IBMJava-1.1.8"/>
424              
425            
426             vendor="IBM"
427             version="1.4.1"
428             home="/usr/local/IBMJava-1.4.1"/>
429              
430            
431             vendor="Blackdown"
432             version="1.1.8"
433             home="/usr/local/blackdown-1_1_8"/>
434              
435            
436             vendor="Sun Microsystems"
437             version="1.3.1"
438             home="/usr/local/sunjdk_131"
439             default="true"/>
440            
441              
442              
443             The above system configuration contains a lot more information than our
444             initial simple example.
445              
446             =over 4
447              
448             =item Variables
449              
450             There are explicitly declared variables that SJ will use when figuring
451             out where things should be read from/written to.
452              
453             Variables may be defined in terms of other variables, even if they have not
454             been declared yet. The syntax for referring to variables thorughout is the
455             same as Ant, ${variable_name}.
456              
457             =item PID file
458              
459             It states that by default a PID file should be written when running an
460             application. This is most useful for multithreaded server applications where
461             you want to be able to kill or HUP the server without figuring out what the
462             lead process PID is.
463              
464             =item Classpath
465              
466             The classpath definition instructs SJ to look for the latest available
467             versions of xalan, xerces and xml-apis and version 1.0.3 of the commons-cli
468             libraries and add these to the classpath when running any program.
469              
470             SJ will look for the libraries in the path defined by ${dir.lib}.
471              
472             SJ is currently very simplistic about library versioning. If it needs to
473             look for a specific version of a library then it simply looks for the
474             library name and library version number joined by a single hyphen. So in the
475             case of the commons-cli library SJ would look for commons-cli-1.0.3.jar in
476             the ${dir.lib} directory.
477              
478             You may be wondering how SJ figures out which is the 'latest' version of a
479             JAR file if no version is specified. Quite simply it chooses the one whose
480             filename begins with the required name and is lexicographically last in an
481             ordered list of those filenames that match. It's not great but with any
482             half-sensible version numbering scheme it will work.
483              
484             =item Virtual Machines
485              
486             There are four virtual machines defined here. In addition to the home
487             directory for each there is now information regarding the vendor and
488             version. Currently this is for informational purposes only but future
489             versions of SJ should be able to choose a VM based on the version or vendor.
490              
491             =back
492              
493            
494            
495            
496             myclass
497            
498            
499              
500            
501            
502            
503             myclass
504            
505            
506              
507            
508            
509            
510             myclass
511            
512            
513              
514             The above application configurations are exactly the same as the simple
515             version with the addition of a vm reference tag to determine which VM they
516             should be executed under.
517              
518             =head2 High Granularity
519              
520             In addition to being able to specify which VMs and libraries to use you have
521             complete control over the environment that the VM is run under, the
522             properties that are passed the the VM and even default command line options
523             on a per system, VM and application basis.
524              
525            
526            
527            
528            
529            
530            
531            
532            
533            
534              
535            
536            
537              
538            
539            
540            
541              
542            
543            
544            
545            
546              
547            
548            
549              
550            
551            
552            
553            
554            
555            
556            
557              
558            
559             vendor="IBM"
560             version="1.1.8"
561             home="/usr/local/IBMJava-1.1.8">
562            
563            
564            
565              
566            
567             vendor="IBM"
568             version="1.4.1"
569             home="/usr/local/IBMJava-1.4.1"/>
570              
571            
572             vendor="Blackdown"
573             version="1.1.8"
574             home="/usr/local/blackdown-1_1_8">
575            
576            
577            
578              
579            
580             vendor="Sun Microsystems"
581             version="1.3.1"
582             home="/usr/local/sunjdk_131"
583             default="true"/>
584            
585              
586             The above system configuration is identical to our useful configuration
587             except we have now added directives that SJ will use to alter the
588             environment and command line parameters passed to the application and VMs.
589              
590             Using VM specific parameters you can make sure that the correct threading
591             models are used or that memory limuts are enforced unless someone needs to
592             tweak the settings.
593              
594             In an application configuration file it is possible to override previously
595             declared parameters such as the -Xmx directive above for the blackdown VM.
596              
597             For example:
598              
599            
600            
601            
602             myclass
603            
604            
605            
606            
607              
608             The L documentation describes every configuration
609             directive in detail, also have a look in the sample directory for ideas.
610              
611             =head1 TODO
612              
613             Test, test, test.
614              
615             =head1 BUGS
616              
617             None known so far. Please report any and all to Nigel Rantor >
618              
619             =head1 SUPPORT / WARRANTY
620              
621             This module is free software. IT COMES WITHOUT WARRANTY OF ANY KIND.
622              
623             =head1 LICENSE
624              
625             The Java::SJ module is Copyright (c) 2003 Nigel Rantor. England. All rights
626             reserved.
627              
628             You may distribute under the terms of either the GNU General Public License
629             or the Artistic License, as specified in the Perl README file.
630              
631             =head1 AUTHORS
632              
633             Nigel Rantor >
634              
635             =head1 SEE ALSO
636              
637             L.
638              
639             =cut