Running code from the command line

Version 1

    Overview

    This example allows you to run code which makes calls to the Blackboard APIs from the command line.


    This is the sort of thing you would need to do if you were writing your own command line snapshot utility. Writing a new snapshot utility is an involved and very localised task, so here a much more modest example that anyone should be able to build on - loading details of the specified user. The end result looks like this:

     

    cmdline.png

     

    The impressive thing here is near the bottom of the screenshot - that by running a command on my laptop, I've discovered that a user dfred on the server isidore.dur.ac.uk is called Fred Bloggs. I've established this by calling a UserDbLoader object . The trick is that this code could be running anywhere*, rather than with a normal building block where it is executed in Tomcat on one of your servers running Blackboard.
    * Depending on how locked down your system is of course

     

    The code uses the humble UserDbLoader class, and the method loadByUserName(). Before we can use these though, we need to initialise a whole swarm of Blackboard services to correctly initialise a Blackboard context object - this includes parsing your licence file. When I began looking at this I wasn't sure whether or not I'd be able to manage this. George Kroner pointed me towards a key file [env.sh] and also the somewhat overwhelming service-config-snapshot-jdbc.properties file. I had a look, but ended up posting a question about this in the edugarage forums. Peter Fokkinga, from the University of Groningen in the Netherlands came to my aid and posted details of a custom BlackboardContext class (new: version for 9.x) he'd written to help. Have a look at his code before attempting to run this - note that the code is currently written expecting an ORACLE database connection - Windows users be warned! Following Peter's instructions and using the files from George, I managed to write some code that ran from the command line. Here's what I did.

     

    Java Code

    The code is simple.
    I created a new NetBeans project (Java Application) called LoadUser.

    project.png

    This has two Java classes:

    1. Main.java this runs when the class is called from the command line and processes any command line arguments (in this case the username and Blackboard server we want to use). It then calls a second class:

    2. WorkHorse.java which performs the required task - loading details about the user on the specified server.

     

    The listings for each class are given below:

     

    Main.java

    1 package loaduser;

    2

    3 /**

    4  * This class is called at runtime, processes the command line arguments

    5  * then creates a new WorkHorse object to load the details about the user

    6  * @author Dr Malcolm Murray, Durham University

    7  */

    8 public class Main {

    9

    10     /**

    11      * Demonstration command line utility that uses the Blackboard APIs

    12      * Should be invoked by typing java -jar "LoadUser.jar" [username] [server]

    13      * @param args 2 Strings indicating the username and server from which we want to load their details

    14      */

    15     public static void main(String[] args) {

    16         System.out.println("------------------------------------------------------------");

    17         System.out.println("Loader process starting...");

    18         WorkHorse dobbin = new WorkHorse();

    19         dobbin.processArguments(args);

    20         if(dobbin.isExceptionCaught()){

    21             // halt and warn the user

    22             System.out.println(dobbin.getErrorMessage());

    23             System.out.println("System halting.");

    24             System.out.println("------------------------------------------------------------");

    25             return;

    26         }

    27

    28         // now try and do something useful

    29         System.out.println(dobbin.getUserDetails());

    30

    31         if(!dobbin.isExceptionCaught()){

    32             System.out.println("Loader process completed successfully - system halting.");

    33             System.out.println("------------------------------------------------------------");

    34         }

    35     }

    36

    37 }

     

    Main is a very simple class which basically just creates a new WorkHorse object hands any supplied arguments over to it, then reports back the findings. It doesn't need any special JARS.

     

    WorkHorse.java

    1 package loaduser;

      2

      3 import blackboard.data.user.User;

      4 import blackboard.persist.user.UserDbLoader;

      5 import org.apache.log4j.Logger;

      6 import org.apache.log4j.BasicConfigurator;

      7 import nl.rug.lib.blackboard.BlackboardContext;

      8

      9 /**

    10  * This class actually does the work of initialising the

    11  * BlackboardContext and then trying to load details of

    12  * the specified user. It makes use of Peter Fokkinga's

    13  * custom BlackboardContext class

    14  * @author Dr Malcolm Murray, Durham University

    15  */

    16 public class WorkHorse {

    17     private String userName; /** Name of the user to load details for - note this should exist on the specified server */

    18     private String targetService; /** Name of the blackboard server e.g. bboard.dur.ac.uk */

    19     private boolean exceptionCaught;

    20     private StringBuffer errorLog;

    21

    22     // set up the logging

    23     // Define a static logger variable so that it references the

    24     // Logger instance named "loaduser".

    25     static Logger logger = Logger.getLogger(WorkHorse.class);

    26

    27     /**

    28      * Create a new instance of WorkHorse

    29      * initialising the logger

    30      * needed later by the custom BlackboardContext class

    31      */

    32      public WorkHorse() {

    33          setExceptionCaught(false);

    34          setErrorLog(new StringBuffer(""));

    35          // Set up a simple configuration that logs on the console.

    36          BasicConfigurator.configure();

    37          logger.info("WorkHorse object initiated...");

    38     }

    39

    40      /**

    41      * Convenience method for processing

    42      * the command line arguments supplied by the user

    43      * @param args Array of zero or more Strings - hopefully just one

    44      */

    45     public void processArguments(String[] args) {

    46         if(args == null){

    47             // called without parameters

    48             setExceptionCaught(true);

    49             errorLog.append("Sorry you did not supply valid command line arguments").append(System.getProperty("line.separator"));

    50             errorLog.append("Please indicate the user you wish to load details for and the name of the service, such as:").append(System.getProperty("line.separator"));

    51             errorLog.append("LoadUser bill bboard1.dur.ac.uk").append(System.getProperty("line.separator"));

    52             errorLog.append("or...").append(System.getProperty("line.separator"));

    53             errorLog.append("LoadUser ben bboard2.dur.ac.uk").append(System.getProperty("line.separator"));

    54             return;

    55         }

    56         try {

    57             String uName = trapNulls(args[0]);

    58             setUserName(uName);

    59             String tService = trapNulls(args[1]);

    60             setTargetService(tService);

    61         } catch (java.lang.IndexOutOfBoundsException iE) {

    62             // no argument supplied

    63             setExceptionCaught(true);

    64             errorLog.append("Sorry you did not supply valid command line arguments").append(System.getProperty("line.separator"));

    65             errorLog.append("Please indicate the user you wish to load details for and the name of the service, such as:").append(System.getProperty("line.separator"));

    66             errorLog.append("LoadUser bill bboard1.dur.ac.uk").append(System.getProperty("line.separator"));

    67             errorLog.append("or...").append(System.getProperty("line.separator"));

    68             errorLog.append("LoadUser ben bboard2.dur.ac.uk").append(System.getProperty("line.separator"));

    69             return;

    70         }

    71     }

    72

    73     /**

    74      * Test method to try and use a UserDbLoader at the command line

    75      * @return String (for testing, indicating how we got on)

    76      */

    77     public String getUserDetails(){

    78         StringBuffer sBuffer = new StringBuffer("");

    79         sBuffer.append("------------------------------------------------------------").append(System.getProperty("line.separator"));

    80         sBuffer.append("Attempting to load details of the user '");

    81         sBuffer.append(getUserName());

    82         sBuffer.append("'");

    83         sBuffer.append(System.getProperty("line.separator"));

    84

    85         // ok let's get the Bb stuff - if you are running this on your Bb box you could use this:

    86         // String configFileLocation = "/local/bboard/blackboard/config/service-config-snapshot-jdbc.properties";

    87         // But from the command line we need to point at a special subfolder:

    88         String configFileLocation = "../Stuff/service-config-snapshot-jdbc.properties";

    89

    90         try {

    91             BlackboardContext.init(configFileLocation, getTargetService());

    92             // ------------------------------------------------------------------

    93             // now back to the UserLoader

    94             UserDbLoader uLoader = null;

    95             try {

    96                 uLoader = UserDbLoader.Default.getInstance();

    97                 User user = uLoader.loadByUserName(getUserName());

    98                 sBuffer.append("SUCCESS - loaded the user '").append(getUserName()).append("'").append(System.getProperty("line.separator"));

    99                 sBuffer.append("They are ").append(user.getGivenName()).append(" ").append(user.getFamilyName()).append(System.getProperty("line.separator"));

    100

    101             } catch(blackboard.persist.KeyNotFoundException kE){

    102                 setExceptionCaught(true);

    103                 sBuffer.append("ERROR: No match for the user ").append(getUserName()).append(System.getProperty("line.separator"));

    104                 sBuffer.append(kE.toString()).append(System.getProperty("line.separator"));

    105                 // temporary ugly exposure of full error:

    106                 kE.printStackTrace();

    107                 sBuffer.append("Method halting after error :-)").append(System.getProperty("line.separator"));

    108                 sBuffer.append("------------------------------------------------------------");

    109                 return sBuffer.toString();

    110             }  catch(blackboard.persist.PersistenceException pE){

    111                 setExceptionCaught(true);

    112                 sBuffer.append("ERROR: Failed to get a user loader :-(").append(System.getProperty("line.separator"));

    113                 sBuffer.append(pE.toString()).append(System.getProperty("line.separator"));

    114                 // temporary ugly exposure of full error:

    115                 pE.printStackTrace();

    116                 sBuffer.append("Method halting after error :-)").append(System.getProperty("line.separator"));

    117                 sBuffer.append("------------------------------------------------------------");

    118                 return sBuffer.toString();

    119             } catch (java.lang.NullPointerException nE){

    120                 setExceptionCaught(true);

    121                 sBuffer.append("ERROR: Failed to get a user loader :-(").append(System.getProperty("line.separator"));

    122                 // temporary ugly exposure of full error:

    123                 nE.printStackTrace();

    124                 sBuffer.append("Method halting after error :-)").append(System.getProperty("line.separator"));

    125                 sBuffer.append("------------------------------------------------------------");

    126                 return sBuffer.toString();

    127             }

    128             // ---------------------------------------

    129

    130         } catch (blackboard.base.InitializationException iE){

    131             System.out.println("Sorry there was an error initiating the BlackboardContext object");

    132             iE.printStackTrace();

    133         } finally {

    134             BlackboardContext.shutdown();

    135             logger.info("WorkHorse object finished - out to pasture");

    136         }

    137

    138         sBuffer.append("Method halting normally :-)").append(System.getProperty("line.separator"));

    139         sBuffer.append("------------------------------------------------------------");

    140

    141         return sBuffer.toString();

    142     }

    143

    144

    145      /**

    146       * Convenience method for ensuring no errors

    147       * arise from getting nulls when you expected a String

    148       * @param string the String to test (can be null)

    149       * @return a String of zero or more characters in length

    150       */

    151      private String trapNulls(String string) {

    152         if (string == null) {

    153             return "";

    154         } else {

    155             return string.trim();

    156         }

    157     }

    158

    159      /**

    160       * Indicates whether an exception was thrown and

    161       * caught during method calls in this class.

    162       * Default is false.

    163       * @return true if an exception was caught.

    164       */

    165      public boolean isExceptionCaught() {

    166         return exceptionCaught;

    167     }

    168

    169     /**

    170      * Private setter used within this class

    171      * @param exceptionCaught Set to true if one has

    172      */

    173     private void setExceptionCaught(boolean exceptionCaught) {

    174         this.exceptionCaught = exceptionCaught;

    175     }

    176

    177     /**

    178      * Internal method for getting the ErrorLog.

    179      * Outside the class use @see getErrorMessage

    180      * to display the details to a user.

    181      * @return StringBuffer containing details of any exceptions

    182      */

    183     private StringBuffer getErrorLog() {

    184         return errorLog;

    185     }

    186

    187     /**

    188      * Provides details of the error to display to the user

    189      * @return HTML paragraph object

    190      */

    191     public String getErrorMessage() {

    192         if(getErrorLog().length() == 0){

    193             return "";

    194         } else {

    195             return "<p>" + getErrorLog().toString() + "</p>";

    196         }

    197     }

    198

    199     /**

    200      * Private setter (normally only called

    201      * when the class is initiated).

    202      * @param errorLog

    203      */

    204     private void setErrorLog(StringBuffer errorLog) {

    205         this.errorLog = errorLog;

    206     }

    207

    208     /**

    209      * Returns the username of the User

    210      * we want to load details about

    211      * @return String representation of the username

    212      */

    213     private String getUserName() {

    214         return userName;

    215     }

    216

    217     /**

    218      * Set the username of the User

    219      * we want to load details about

    220      * @param userName String representation of the username

    221      */

    222     private void setUserName(String userName) {

    223         this.userName = userName;

    224     }

    225

    226     /**

    227      * Returns the Blackboard server to query

    228      * @return full name of server e.g. bboard1.dur.ac.uk

    229      */

    230     private String getTargetService() {

    231         return targetService;

    232     }

    233

    234     /**

    235      * Sets the Blackboard server to query

    236      * Note you might want to add more validation to

    237      * this method before inflicting it on a

    238      * production server

    239      * @param targetService - full name of server e.g. bboard1.dur.ac.uk

    240      */

    241     private void setTargetService(String targetService) {

    242         this.targetService = targetService;

    243     }

    244

    245 }

     

    WorkHorse requires the following JARS:

    • bb-platform.jar provides access to the User and UserDbLoader objects and any of the Persistence exceptions thrown
    • log4j-1.2.15.jar (or later) This is used to provide the logging required by Peter's BlackboardContext class. You can download it from http://logging.apache.org/log4j/
    • BlackboardContext.jar - Peter's custom class posted by him to the forum, which I turned into a JAR. It is very well commented and creates nice JavaDocs

     

    Other required files

    In order for this to work, the class WorkHorse has to be able to find all the Blackboard JARs needed (stored in the lib folder) plus all the licence files, etc. which it expects to find in a folder called config.

     

    Determining which JAR files you need

    The JARS are required by the process of initialising a Blackboard Context object. To give you an idea of what this might involve, this process includes a license check to see if you can still run Blackboard (and which bits...). The license is stored as an xml file, so it needs to be able to access the XML parsing library xerces.jar. There are loads of other dependencies like this. Happily the file env.sh (env.bat on a Windows install) provides a definitive list of the JARS needed and gives you pointers to their location. It can be found here on a linux install: /local/bboard/blackboard/apps/snapshot/config/env.sh

    For example my copy of env.sh contains lines like these when it's setting the classpath:

     

    CP=$BBLIB/activation.jar

    CP = $CP:$BBLIB/axis.jar

        ... [bits missed out]

        ...

    CP=$CP:$TOMCATLIB/bin/commons-logging-api.jar

     

    Thus we need to add any of the JAR files listed (such as activation.jar, axis.jar and commons-logging-api.jar) to the lib folder. The text at the start of the file (not shown in my snippet) gives a clue where to find these files on your Blackboard server. The variables $BBLIB and $TOMCATLIB are defined earlier in env.sh (in my case they pointed to /local/bboard/blackboard/systemlib and /local/bboard/blackboard/apps/tomcat respectively, thus activation.jar should be found at local/bboard/blackboard/systemlib/activation.jar, but check your env file for details). Download copies of these JARS to a sensible location accessible to your IDE.

     

    The exact method for linking these JAR files to your project depends on how you write your java code. In NetBeans I created a new Library called multipleJARSforBbContext and added all the JARS I downloaded to it. I then added the Library multipleJARSforBbContext to the NetBeans project and the 33 JARS were all added for me. Thus when I build this project it automatically creates a lib folder, containing these JARs.

     

    Locating the config files

    The config folder is used to provide the information about you needed to initialise your BlackboardContext object.

     

    NOTE:
    It needs to be copied into in the dist folder of your project AFTER you have compiled it, so that config is a subfolder of the directory containing the JAR file LoadUser.jar. This gives a file structure like this:

    folder.png
    Basically you need to copy ALL the files from the config folder on your server into a subfolder of the dist folder called config. As this is rewritten everytime you compile your source code, it makes sense to keep a copy elsewhere too, to save you scp-ing the files across each time you recompile.

     

    It can be found here on a linux install: /local/bboard/blackboard/config

     

    After that you should be in business!

     

    Running the file

    To test your code. having written it and copied the config folder into your dist folder open a command prompt and navigate to the dist folder of your Java code.

     

    Invoke it using the command:

    java -jar "LoadUser.jar" user service

     

    Specifying the username and Blackboard server you want to use (note it will need to be one that is specified in your license file)
    e.g. in my case:java -jar "LoadUser.jar" dfred isidore.dur.ac.uk

    Hopefully you'll be rewarded with a result like this:
    cmdline.png

    - Malcolm