WOW Custom Coding

WOW Custom Coding

WOW Coding with Row Objects

Generate Row Subclasses

Creating Row Objects

Getting and setting Field values on a Row

Validating a Row Prior to Insert, Update or Delete

Showing User Messages

Adding custom logic during Row Insert, Update or Delete

Creating a Row Action:

Running SQL Queries from a Row

Short cut methods for SQL SELECTS from a Row:

Run an SQL Update or Delete:

Controlling if a Row can be Edited/Deleted

Controlling Field Read and Edits

Controlling Field Display, Updates,  and DB Values

Controlling Row Selection:

WOW Coding With RowCollections

Creating RowCollection Actions

Handling an RC Action

Overriding RowCollection at UI Display Time

Controlling Screen Flow Programatically

Going to the previous screen:

Executing an Operation Explicitly

Opening Row Objects for Viewing or Editing:

WOW Coding with Field Objects

Creating a Field Subclass:

Overriding Field Methods:

Sample Code:

Getting Row field values from a Field

Multiple Associations from a Field

Controlling SQL Values for a Field

Sample Code:

Working with Operations

Controlling Workflow using Custom Logic

Custom Coding UI and Logic

Creating User Input or Prompting

Creating the target JSP to Handle Custom Logic:

Prompting with a Custom JSP

Generate Custom Colors for Rows:

Adding User Input via HTML Tags

Running Operations with JavaScript

<TD><INPUT TYPE=BUTTON OnClick="runWOWOperation(14);" VALUE=" Run operation 14 "></TD>

Including An Operation’s Generated Results

Adding Custom CSS to Operations:

Overriding JavaScript Methods:

Working with WOW SQL Connections

Get the current user, operation, application

Resolving Variables

Special Database Metadata SQLs

Startup and ShutDown Processing

Using WOW Development Libraries

Changing WOW Development Libraries Dynamically

Clear Table Cache For Table

ClearCacheForTable Magic Request

ClearCacheForTable static method

WOW Development Environment Modes

Finding Parameters Used in SQL Operation

Configuration Variables


WOW Coding with Row Objects

Web Object Wizard supports a full Java based api for development of custom logic.  

In most cases custom development is not needed, however for complex requirements WOW supports unlimited extensions using Java.  It is assumed the reader has knowledge of Java development.  

This document assumes the reader has Java knowledge.  If not, there are many resources on the web to gain basic knowledge of Java.  Eclipse is the recommended IDE for Java development.  

Many of the example code snippets below assume your classes import the correct java packages.  

Row objects are the most commonly used objects within WOW.  A Row represents a set of fields that map to a database.  

Generate Row Subclasses

When custom Java coding is needed, the FD Manager (Requires WOW 7.04) provides a utility that will generate a Java row subclass based on Field Descriptors.  The Java code contains constants and setters/getters for all field descriptors.  This can save significant development time.  To use, open the FD manager on the table you desire, click “Generate Java...”.  This will download in your browser a .java file.  Import this file into your IDE of choice.  Customize and compile Java as desired and export to Tomcat.    

Creating Row Objects

When needed, creating new Row instances would be used when you desire to insert data into secondary tables.  Typically, a WOW SQL Insert would create the row instance for you.  An example usage would be where an Order is being inserted, you need to programmatically create a ShippingRequest record to handle shipment of the order.  

When a custom function requires you to insert new data into tables, you can create and insert Row instances.  Row instances can be created programmatically as shown here:

Table table = Table.createTable("MY_CONNECTION_NAME", "SCHEMA1", "TABLE1");

Row row = Row.create(table, Row.class, true);        

If done within another Row, you can use the connection alias of the original row without hardcoding the connection alias in code:

Table table = Table.createTable(getSystemAlias(), "SCHEMA1", "TABLE1");

You should have field descriptors created against the table.  Once a Row object is created, you can set its field values as shown below and then ultimately “insert” the row into a table.  

Getting and setting Field values on a Row

If a Row contains a field, its value can be extracted using a variety of methods.  Choose the method that corresponds with the field’s data type.  If the Row does not contain the field, null or the default value is returned.  Below are several examples:

How to read field values from a Row: 

int intVal = row.getValueAsInt("IntFld1");

long longVal = row.getValueAsLong("LongFld1");

String strVal= row.getValueAsString("StringFld1");

// CHAR 1 Field with boolean value (set in FD as boolean),

// with values such as Y,N or T,F

boolean isActive = row.getValueAsBoolean("StringFld2");

Date date = row.getValueAsDate("DateFld1");

Fields in a Row can be set in a similar manner.  If the field does not yet exist in the Row, the field will be added:  (The first parameter is the fieldname, the second is the value to set on the field)

row.setFieldValue("FLD1", object);  // the object could be String, Integer,

// Date, Timestamp, Boolean, BigDecimal,

// etc.

There are also similar methods for the raw data types:

row.setFieldValue("FLD1",0);  // Any long value

row.setFieldValue("FLD1",1.5); // Any double value

row.setFieldValue("FLD1",false);

After a Row and fields have been populated, they must be explicitly inserted or updated as shown below. 

Validating a Row Prior to Insert, Update or Delete

Prior to performing a database insert, update or delete, Row calls its validate method to ensure that the data is valid based on each field’s FD (Field Descriptor).  To override the default validation, you can override the validate method.  Returning a false boolean value from validate to cause the insert or update to fail.  

NOTE:  In validation, it’s important to not throw an exception if you want the validation to work properly.  Instead, you’ll be adding a ValidationException to the Field’s list of exceptions.  Validation can also be accomplished in method insert or update. Do not use the validate method to set any field values since changes will not be honored.  Field value changes should be made in insert or update methods.  

// Sample validate (Salary > 100,000 is not allowed)  

public boolean validate(ExecutingContext ec) throws CMException {

            // flag indicating if row is valid

            boolean valid = true;

       if (getValueAsDouble(“SALARY”) > 100000)

          {return false;}

       return true;

}

// Sample 2  validate method call

public boolean validate(ExecutingContext ec) throws CMException {

            // flag indicating if row is valid

            boolean valid = true;

            // Spin through fields and validate

            Field field;

            String fieldName;

            for (int i = 0; i < size(); i++) {

                   field = getField(i); // get next field

                   // If you need to validate a derived field,

   // remove check for => field.isPhysical()

                   if (field != null && (field.isPhysical() ||

isValidateDerivedFields())) {

                          fieldName = field.getName(); // get field name

              // clear previous validation exceptions (fixes bug where duplicate

      // validation exceptions show).

                          field.clearValidationExceptions();

             if ("PHONE".equalsIgnoreCase(fieldName)) {

                                  String phone = field.getValueAsString();

                  // Make sure phone number has 10 digits.

                                  if (phone != null && phone.length() > 0) {  

                                         String s = "";

                                         String newNum = "";

                                         for (int a=0; a < phone.length(); a++) {

                                                if (a == phone.length()-1) {

                                                       s = phone.substring(a);

                                                } else {

                                                       s = phone.substring(a, a+1);

                                                }

                                                if ("0123456789".indexOf(s) >= 0) {

                                                       newNum = newNum + s;

                                                }

                                         }

                                         if (newNum.length() != 10) {

                                                field.getValidationExceptions().add(new

          ValidationException(ValidationException.INVALID_VALUE,

          "Invalid phone #. The phone number must contain 10 digits."));

                                                valid = false;

                                         } else {

                                                String formattedNum = newNum.substring(0, 3) + "-" +

   newNum.substring(3, 6) + "-" + newNum.substring(6);

                                                field.setValue(formattedNum);   // Set to consistent format.

                                         }

                                 }

                          }  // If phone #

                          // validate field - Call default validation code.

                          if (!field.validate(ec)) {

                                 valid = false;

                          }

          } // If Physical field

       } // Loop thru Fields

       return valid;

}

Showing User Messages

You may want to show the user feedback or warning messages.  Use the following code:

HttpExecutingContext ec = ExecutingContext.getCurrentHttpExecutingContext();

// To obtain the current HTTPServletRequest

HttpServletRequest request = ec.getRequest();

DataEngineServlet.setUserMessage("My Message Here.", request);

Creating Different Row Subclasses From Same Operation

Assume a single operation select from an “Accounts” table.  Accounts can be “Checking” accounts or “Savings Accounts” in which case you may want to instantiate a different Row subclass since the behavior of these objects may need to be different.  

To accomplish this, override the Row.createRow(...) method and return a Row of your desired type.  [TODO:  Add an example]

Working with Field Objects From Row

There are times where it might make more sense to extract the Field object from the Row and utilize some of it’s methods.  

Extracting the Field object:

Field field = row.getField("FLD1");

Field field2 = row.getFieldWithUsageId(-40); // Get email field from Row

                                                    // The usage ID is set in the FD.

Some usages of the Field Object:

if (field != null) {  // Does the Row contain the field

field.getName();  // Get the field’s name

field.getValue();  // Get the field’s value as an Object

field.getDisplayValue();  // Get value visible to user.

Adding custom logic during Row Insert, Update or Delete

Often during an insert, you may need to set certain field values based on other field values or send an Email or call another program.  You can perform unlimited additional logic by overriding the insert method shown below. This is done in a Row subclass.  

Example of insert:

/**

 * Performs the insert

 */

 public int insert(SQLContext context) throws CMException {

            setFieldValueAsString("FLD","*CURRENT");        // Timestamp field

            setFieldValueAsString("WBFAM",getValueAsString("D_WBFAM")); // Copy derived

                  

       // If needed you can get the SQL Connection that will be used to insert as follows

       // Use Connection to call SP or any other custom logic.  

       Connection connection = context.getConnection();

       return super.insert(context);

 }

Override the update method below to add custom logic when a record is being updated.  NOTE: You can add validation logic into update or insert by throwing a CMException.  

Example of update:

   /**

    * Update method to override.

    */

   public int update(Connection connection, ExecutingContext ec) throws CMException {

            // if joined files, use this to check all values.  

        // Prevents the 1st row from being updated if problem with other join rows.

            if (!(validate(ec))) {

               throw new ValidationException(ValidationException.UPDATE_FAILED,    

             getValidationExceptions().toString());

            }

        // Copy over derived fields in regular fields before update.

             setFieldValueAsString("WBFAM",getValueAsString("D_WBFAM"));

             return super.update(connection, ec);

   }

Example of delete:

  /**

   * Delete corresponding row in 2nd table.

   */

   public int delete(Connection conn, ExecutingContext ec) throws CMException {

            int count = super.delete(conn, ec);

            int id = getValueAsInt("ID1");

            // Remove any matching rows in a 2nd table.

            SQLContext context = new SQLContext(getSystemAlias());

            context.setSQL("delete from MYDB1.TABLE1 where ID2 = " + id);

            context.execute();            

            return count;

   }

Creating a Row Action:

A row “action” is  a button next to each Row to provide custom logic.  Create a Row subclass and implement methods:  getRowHandledActions(....)  and handleAction()...   Below is an example:

package com.qualcomm.codescout;

import java.util.ArrayList;

import java.util.List;

import planetj.database.Row;

import planetj.dataengine.ExecutingContext;

import planetj.exception.CMException;

import com.qualcomm.codescout.pidata.PIPatchData;

import com.qualcomm.codescout.pidata.PatchLoader;

/**

 * @author Paul Holm  Example Row action

 */

public class PIPatchRow extends Row {

    /**

         *

         */

    private static final long serialVersionUID = 1606830380400528417L;

    private String EDIT_ACTION = "Edit Patch";

    /*

     * Return a List of actions that Row performs

     */

    public List getRowHandledActionNames() {

           

            List actions = super.getRowHandledActionNames();

            if (actions == null) {

                    actions = new ArrayList();

            }

            // This adds the action button to the Row!

            actions.add(EDIT_ACTION);

            return actions;

    }

    @Override

    /*

     * When the action button is clicked, this method gets called.  

     */

    public Object handleAction(String action, ExecutingContext ec) throws CMException {

           

            Object result = null;

            if (EDIT_ACTION.equalsIgnoreCase(action)) {

                    // This Row subclass holds all the fields from the MARSSCOUT patches table

                    // Access using these methods below

                    String patchName = getValueAsString("NAME");

                    int piPatchId = getValueAsInt("ID");

                   

                    // open a JSP for editing, this Row will be available to the jsp

                    openFor(Row.MODE_ALTER, ec, "/qualcomm/codescout/editPatch.jsp");

            }

            return this;

    }

}

WOW framework by default will only check the 1st row for ROW actions.  If some ROW actions are only applicable to certain ROWs, you can override the method in your RowCollection subclass: :  

    public boolean isActionsSameForAllRows() {

            // Actions are not the all same, ask each row will rendering table

            return false;

    }

Running SQL Queries from a Row

Within a Row class, you may need to retrieve a RowCollection from the DB.  Below are a couple examples:

Example of running a SQL query within a Row using SQL:

  SQLContext context = new SQLContext(getSystemAlias());

  context.setRowClass(Row.class);

  context.setCheckCache(true);            // Cache the results

  context.setCacheResults(true);  // Cache the results

  context.setSQL("SELECT * from pmweb.seclevels");

  RowCollection rc = DataEngine.getRows(context);

  Map access = new Hashtable();

  if (rc != null && !rc.isEmpty()) {  

                    for (int i=0; i < rc.size(); i++) {

                           Row row = rc.getRow(i);

                           String desc = row.getValueAsString("SLTYPEDESC");

                           int id = row.getValueAsInt("SLID");

                           String key = Integer.toString(id);

                           access.put(key,desc);

                    }

   }

Example of retrieving an operation, running, and extracting the RowCollection:

  ExecutingContext ec = ExecutingContext.getCurrentExecutingContext();

  Application app = ec.getApplication();

  Operation op = app.getOperationById(3000); // Find WOW Operation with ID = 3000

  // If operation has a usage ID assigned

  Operation op2 = app.getOperationByUsageId(5000, ec);

  RowCollection opRc = null;

  if (op != null) {

     opRc = (RowCollection) op.execute(ec);

  }

Short cut methods for SQL SELECTS from a Row:

These code examples would be used inside a Java Row subclass and are short cuts to the above examples and use the same connection that the Row itself came from.  

The method runSQLQuery takes a string that is the SQL to be executed and returns a RowCollection (a set of Rows returned from that SQL). Your SQL can be built dynamically to include field values.

RowCollection rc = runSQLQuery(“SELECT * FROM X.Y where FLDA=9”);

// Now loop thru the results

for (int i = 0; i < rc.size(); i++) {

Row row = rc.getRow(i);

       // Now get field values out of the Row, Replace FLDA with your field names

String desc = row.getValueAsString("FLDA);

int id = row.getValueAsInt("FLDB");

}

   

WOW release 7 will include a method: getRow(String sql) which takes an SQL statement, executes it and returns the 1st row returned.  Example:  

Row myRow = getRow(“SELECT * FROM X.Y where FLDA=9”);

String fldValue = myRow.getValueAsString(“fldName”);  

       

Run an SQL Update or Delete:

The method runSQLStatement can be used to pass in a SQL Update, Insert, Or Delete.  

runSQLStatement(“update lib.table set cosName = “blah”, fld2 = “whatever” where palletNum = “ + getValueAsString(“pallet#”); 

Controlling if a Row can be Edited/Deleted

WOW provides the capability to control Row editability based on users and roles with no programming required.  In complex scenarios, editability may depend on data in other fields.  Row subclasses can override the method public boolean isEditable(ExecutingContext ec).  To control if a Row should have the delete icon appear override method:

public boolean isDeletable(ExecutingContext ec).  To control if a Row should have the copy icon appear override method: public boolean isCopyable(ExecutingContext ec)

Example Java Row subclass:

public class DefectSets extends Row {

        public boolean isEditable(ExecutingContext ec) {

            // Don't allow edits to blocked defect sets    

           int severityId = getValueAsInt("severityID");

           // value 0 is blocked

           if (0 == severityId) {

                    return false;

           }    

            return super.isEditable(ec);

}

public boolean isDeletable(ExecutingContext ec) {        

            if (“locked”.equalsIgnore(getValueAsString(“StatusField”)) {

                return false;

        }

       return true;

}  

}

Example User Interface:  Notice Severity of “Blocked” is not editable.  

Controlling Field Read and Edits

Similar to controlling Row edits and deletes, individual fields readability and editability can be programmatically controlled by overriding these methods on a Row subclass:

Example Java code:  (Do not allow editing of salary field if field isManager is true)  

public boolean isFieldAuthorizedForEdit(Field field, ExecutingContext context) {

   String fieldName  = field.getName();

   if (fieldName.equalsIgnoreCase(“SALARY”) && ( getValueAsBoolean(“isManager”) == true))

           return false;

}

Controlling Field Display, Updates, and DB Values

Field method “getDisplayValue” is called when in read mode to display the value shown to users.

When it EDIT or UPDATE mode, Field.getValue() method is called.  

Similar to controlling Row edits and deletes, individual field values updated to a database can be programmatically controlled by overriding these methods on a Field subclass.

WOW’s framework will detect if a field has been updated and only process an update request on Field instances that have change.  You can force a Field to be updated using the Field subclass methods of setChanged(true);  or overriding “isChanged()”.  

When updating a field, WOW calls a method on a Field subclass “getSQLValue(boolean)”

You can override this method to change the actual value being written to the database if desired.  

Controlling Row Selection

There are three methods on a Row that control the checkbox selection when viewing in a result table.

WOW Coding With RowCollections

A resultset (from a query) is stored as a RowCollection.  In the following sample report there is a header row for each of the 5 fields and 3 Row objects.  Row objects hold all 5 fields for their respective instances.  The 3 Row instances are held in a RowCollection object.  This type of result is created from a WOW SQL operation.  

Creating RowCollection Actions

A RowCollection action is custom logic you may code and attach to a WOW operation.  There are at least 2 ways to create RowCollection Actions (aka RC Actions).  

  1. [Requires WOW 7.0 and up] Include the “action” property group in your WOW operation.  In this case we declare an Action with type “rc” for RowCollection  (A  Row action would be type: row).  The action name is “Auto Fill”.  Also note the Java class specified in the “Row Coll Class.  When the action is clicked this class gets called via the “handleAction” method.  

An example end user application with a RC action is shown below:  (Action: Auto Fill)  

2.  Defining an RC action Code.  

Actions can be defined within a RowCollection subclass as shown below:

public List getRowCollectionHandledActionNames() {

List actions = super.getRowCollectionHandledActionNames();

if (actions == null) {

actions = new ArrayList();

}    

actions.add(“My RC Action”);

return actions;

}

Handling an RC Action

When the end user clicks on an RC action button, your Java RowCollection subclass will get invoked via the handleAction method.  Here is an example:

public Object handleAction(String action, ExecutingContext ec) throws CMException {

// Check the action name

if ("AUTO FILL".equalsIgnoreCase(action)) {

if (this.size() > 0) {

// loop thru all rows in the row collection

for (int i = 0; i < this.size(); i++) {

// Gets access to each row

Row currentRow = this.getRow(i);

Field salary = currentRow.getField("SALARY");

if (salary != null) {

salary.setValue(salary.getValueAsDouble() + 1000);

}

}

}

}

return this;

}

The WOW framework by default will only check the 1st row for ROW actions.  If some ROW actions are only applicable to certain ROWs, you can override the method in your RowCollection subclass:

    public boolean isActionsSameForAllRows() {

            // Actions are not the all same

         // ask each row will rendering table

            return false;

    }

Looping thru individual Rows  

From a RowCollection, you can loop thru all Rows using a loop such as:

// loop thru all rows in the row collection

for (int i = 0; i < this.size(); i++) {

// Gets access to each row

Row currentRow = this.getRow(i);

Field salary = currentRow.getField("SALARY");

if (salary != null) {

salary.setValue(salary.getValueAsDouble() + 1000);

}

}

Overriding RowCollection at UI Display Time

When a RowCollection is returned from an SQL Operation, a method called “prepareForDisplay” is called immediately before the user interface is generated.  This allows developers to alter Row values or perform other custom logic.  

This example shows how a RowCollection can be automatically re-executed or refreshed (to get new values from the database)

package planetj.rowcollection;

import planetj.database.RowCollection;

import planetj.dataengine.ExecutingContext;

import planetj.exception.CMException;

public class RefreshRowCollection extends RowCollection {

    public RowCollection prepareForDisplay(ExecutingContext ec) throws CMException {

            refresh();

        // Add any custom logic here

            return super.prepareForDisplay(ec);

    }

}

A custom RowCollection subclass can be assigned to an operation by setting the RowCollection class of the WOW operation.  (planetj.rowcollection.RefreshRowCollection)

Controlling Screen Flow Programmatically

Standard screen flow is automatic for WOW.  In complex scenarios, you may need to control the flow of screens and operations.  The WOW java API allows for these needs as follows:

Going to the previous screen:

JavaScript and HTML can be added to a WOW operation in the “instructions” or “title” section.  To create a button for the user to return to the previous screen, add the following JavaScript to the operations instructions:  

<a href="javascript:history.go(-1)">Previous Screen</a>

NOTE:  There are limitations to this support.  Typically, you can only go back 3 screens because the client side navigation is not server aware.  Usage of this approach should be minimized.   

For more information on JavaScript visit: http://www.w3schools.com/js/default.asp

Executing an Operation Explicitly

WOW operations have a unique id.  The following code can be used to get an operation and open for execution on the screen:

HttpExecutingContext ec = ExecutingContext.getCurrentHttpExecutingContext();

Operation op = OperationManager.singleton().getOperation(1, "DEFAULT");

if (op != null) {

op.openForExecution(null, ec);

}

NOTE:  In this case an operation with id 1 will be read using the DEFAULT connection which is set up in web.xml.  Substitute your operations id.

The method openForExecution can take a Row and ExecutingContext as parameters.  The 1st parameter (null is example above) can be used to pass in a Row instance useful in cases where the operation is an association.  The row passed in would be used for ??FIELDNAME parameters.  The 2nd parameter is the ExecutingContext which is a WOW class that holds data about the current executing user, application, etc.  

If you need to obtain the ExecutingContext programmatically use the following code:  

HttpExecutingContext ec = ExecutingContext.getCurrentHttpExecutingContext();

Opening Row Objects for Viewing or Editing:  

 

See sections above on how to read a Row object.  Here is sample code that opens a Row object for inserting:

ExecutingContext ec = ExecutingContext.getCurrentExecutingContext();

// Will open WOW’s default insert screen on the row    

row.openFor(Row.MODE_INSERT, ec);

For edit/update:  

ExecutingContext ec = ExecutingContext.getCurrentExecutingContext();

// Will open WOW’s default insert screen on the row    

row.openFor(Row.MODE_UPDATE, ec);

Opening RowCollection:  

 

String sql ="SELECT  a,bc, from x.y where fielda = 99”;

// Uses connection alias for sql server connection

SQLContext sqlc = new SQLContext("SQL_SERVER2008_BLUEPRINT");

sqlc.setSQL(sql);

// runs the SQL against the connection

RowCollection rc = (RowCollection) sqlc.execute();

// open the rows on a browser screen

rc.openFor(MODE_SEARCH, ec);

If you need to obtain the executingContext object programmatically use the following code:  

HttpExecutingContext ec = ExecutingContext.getCurrentHttpExecutingContext();

// To obtain the current HTTPServletRequest

HttpServletRequest request = ec.getRequest();

WOW Coding with Field Objects

Field objects are also a commonly used object within WOW.  A Row is comprised of a set of Fields that map to a database.  In most cases, setting attributes in the corresponding field descriptor for a Field is sufficient for a user’s needs.  But there are times when it makes sense to create a Field subclass to override some of the default Field behavior.

Creating a Field Subclass:

When creating a field subclass, you will need to know which field class to override.  The appropriate field class depends, in large part, on the field’s data type:

Below are the typical field superclasses based on data type:

Additionally, if a Field class has been set for the field, then that field class should be used as the superclass:

Some Examples to Override:

Once the appropriate Field superclass is determined and your class is created, you need to specify the field class in the field’s Field Descriptor (FD):

Overriding Field Methods:

Some typical methods to override are:

Sample Code:

       public boolean validate(ExecutingContext ec) throws CMException {

             boolean valid = super.validate(ec);

             if (getValue() == null) {

               // Add validation exception to current list of exceptions.

               getValidationExceptions().add(new ValidationException(

ValidationException.INVALID_VALUE, "The value cannot be null."));

               valid = false;

             }

             return valid;

       }

Getting Row field values from a Field

A row can have many fields. Within a field instance, you can get the row instance:

getRow();

Once you have a row, you can get any of the rows fields values as shown above.  

Multiple Associations from a Field

In complex scenarios, a single field may have different associations based on the values of the row.  For example, a shipment tracking number may have multiple HTML reference associations to link the tracking number to FedEx or UPS.  To support this, you can override Field’s getAssociationOperation method.  Note, this can also be done at a Row level.  

In this example, if the fields value is FedEx, then return operation with id 21.  

        public Operation getAssociationOperation() throws CMException {

                if ("FedEX".equalsIgnoreCase(getValueAsString())) {

                        return OperationManager.singleton().getOperation(21, "DEFAULT");

                }

              else {....}

}

Controlling SQL Values for a Field

At times you may need to control or alter a Field’s actual value when the field is used in a search or written to a database.  To do this, you override the getSQLObject method on a Field subclass.  Let’s take an example where a customer account number contains leading zeros in the database but you want to allow the user to enter an account number WITHOUT leading zeros.  

         public Object getSQLObject() throws CMException {

                Object val = super.getSQLObject();

                if (val instanceof String) {

                        String value = (String) val;

                     // max size of field in DB                                  

                     int fldSize = getMaxLength();

                     // Add zeros in front of value

                     String leadZeros =  ConvertUtility.insertLeadingZeros(

                            value, fldSize);

                     return leadZeros;

               }

          }

        

There is a prebuilt java class that will prepend % in front of it's value to enable wildcard searches:  Set the following on your target FD field class:  

planetj.database.StringContainsLikeField

WOW Coding with Formatter Objects

Sometimes it is needed to display field values on a page (results page of row details page)  in a different way from how it is stored in the database, in other words …

WOW by default has many default Formatters

In case none of the default wow formatters matches the requirements, a formatter can be created or customized.

To create a custom formatter just implement interface planetj.formatters.IFormatter, and its methods:

Sample Code:

public class PhoneNumberFormatter implements IFormatter {

    @Override

    public String formatValue(Object value, Field field) {

            return "<a href=\"tel:" + value + "\">" + value + "</a>";

    }

    @Override

    public boolean isPrepareFieldValue(Field arg0, ExecutingContext arg1) {

            return false;

    }

}

Working with Operations

Operations are a key component within WOW.  You can read operations using code shown below:  (in this example, we are reading an operation with id = 21)

Operation op = OperationManager.singleton().getOperation(21, "DEFAULT");

Controlling Workflow using Custom Logic

There are times at which you need to control the workflow of your application and the built-in Next Operation feature of WOW does not suffice.  In this case, you can create custom logic to call the Operation.openForExecution(Row, ExecutingContext) method.

Sample Code:

@Override

public Number getNextOperationToRun(Operation currentOp, ExecutingContext context) throws CMException {

        if (skipNextOperation) {

                return null;

        }

        if (!isRuleNested()) {

                Operation op = OperationManager.singleton().getOperation(540,

DBSystem.getMetaDataSystem().getAlias());

                // ask the operation to open itself using this row as the association row source

                op.openForExecution(this, context);

                return null;

        }

        return super.getNextOperationToRun(currentOp, context);

}

Notice how the openForExecution method is being used in the above logic (BOLD for emphasis).  It is being used to open an association operation (op variable), using its current row (this variable) as the association row.  We use a modified version of the code from Working with Operations to get the correct operation as well.

Custom Coding UI and Logic

At times, you may have the need to create your own user input fields and perform custom logic.  Typically, this custom logic will involve 2 parts, a user interface page to prompt the user for input and a custom logic page or Java class.  

Creating User Input or Prompting

You will create a JSP page to prompt the user for input or to allow them to take an action.  A JSP page is just an HTML page with some Java code to provide custom dynamic logic.  In your JSP, you create prompt fields or buttons using industry standard HTML buttons and input tags.  In this example, we’ll show how to create input to prompt the user to change their operating system password.  

Step 1:  

Create a JSP file and include the following Java and HTML:

<table>

<tr>

<td>Userid</td>

<td><input type="text" name="userid" /></td>

</tr>

<tr>

<td>Password</td>

<td><input type="text" name="password" /></td>

</tr>

<%

// Java Code: This will call the JSP within the WOW framework

NavigateToRequest target = new NavigateToRequest("ChgPWD", ITemplate.JSP_BODY, "/chgpwd_logic.jsp", request, response);

%>

<tr><td>

<input type="button" value="Change Pwd" onClick="performMagic('<%= target.getKey() %>')" /></td>

</table>

Study this code carefully.  The initial code simply creates input fields for the userid and password.  This HTML is for entry of userid:  <input type="text" name="userid" /> 

Code inside <% and %> is Java source code.  The Java code creates an object of type “NavigateToRequest”.  The constructor arguments are a request name “ChgPWD”, the location on the page to show the JSP, and then the ACTUAL JSP that gets called to process the user input.  This is the 2nd part or custom logic page.  You can create your OWN jsp page and reference it to handle custom logic.  The last part of the code creates a button that when clicked invokes the target JSP to handle the custom logic.  This is done with: <input type="button" value="Change Pwd" onClick="performMagic('<%= target.getKey() %>')" />

This code creates the normal HTML button.  The “onClick” JavaScript calls a WOW function “performMagic” passing the target JSP’s key attributes that uniquely identifies the target.  So at this point, we are prompting them and have the code to call a target JSP to do the processing.  

Creating the target JSP to Handle Custom Logic:

Create or copy a JSP.  To perform custom logic, you will typically read the user input from the previous page and perform logic on it.  You are free to use ANY Java code to produce your logic.  

To read user input, you would use Java code such as:

<%

String username = request.getParameter("userid");

String oldpassword = request.getParameter("oldpassword");

String password = request.getParameter("password");

%>

The object “request” is a special java supplied object the holds user input.  We use the “getParameter(“html-element-name”); to retrieve user input.  The parameter name MUST match the html element name in the user input JSP.  Once you have the username and password parameter values, you can perform the logic.

Here is custom logic to change the password on IBM’s i5 operating system.  You can create any Java you desire:

<%

// Get a reference to the AS400, change WOW_CONNECTION to your conn alias

AS400 as400 = AS400Manager.singleton().getAS400Connection(“WOW_CONNECTION”);

// Now get a JT400 User object from this AS400

String signedOnUser = HTMLContext.execute("???USERID", null, ec);

as400.changePassword(oldpassword, password);  

// Show a message to user

out.println(“Password changed for user: “ + userid);

%>

Combining these last 2 code segments allows you to get user input and then process it.  

Prompting with a Custom JSP

WOW allows you to customize the operation results by supplying a JSP in the operation.  If you want to allow prompt for parameters via the wow default parameter JSP, follow this section.  

When an operation runs, it will call whatever JSP you have supplied in the operation.  The default_results.jsp is the wow supplied default results JSP.  

Custom JSPs can support prompting by including the default WOW prompting jsp.  Here is a code snippet of how to include the default prompting JSP, where you would put this near the top of your custom JSP.

AbstractContext context = DataEngineManager.getCurrentContext(request);

// If the operation is prompted such as select * from x.y where a = ?

// then include prompting JSP

boolean hasParameters = context != null && context.isContainsDisplayParameters();

 

if (hasParameters) {

        // Use the specified parameters JSP

        String paramsJsp = (op instanceof SQLOperation) ? ((SQLOperation) op).getParametersJspFile() : IDataEngine.DEFAULT_PARAMS_JSP;

%>

<table class="<%= ICSSStyles.RESULTS_SURROUNDING_TABLE_DEFAULT_JSP %>" cellpadding="0" cellspacing="0" border="0" style="margin: 5px;" >

<jsp:include page="<%= paramsJsp %>" flush="true" />

</table>

Generate Custom Colors for Rows

For ease of readability or to add focus to certain rows, you may want to generate rows with a certain background color.  

WOW Row subclasses coded in Java can programmatically control the display attributes for both entire rows and individual fields.  This methodology uses CSS to control the generated HTML by allowing Row and FIeld subclasses to return a String representing the CSS class to generate with the HTML.  

Controlling a Row Display with CSS 

In a Row subclass, override this method:

        public String getRowDisplayAttributes() throws CMException {

            if (getValueAsString(“Status”).equals(“OVERDUE”)) {

                return "pjc-maroon";

            }

            return super.getRowDisplayAttributes();

}

This sample code checks the value of the row field called “Status” and if that field value is “OVERDUE”, then a string is returned "pjc-maroon" that references a WOW supplied CSS class.  WOW supplied colors may include (NOTE: Check your CSS theme for exactness, the WOW Default theme contains a CSS file called colors.css):

"pjc-lightred", "pjc-red", "pjc-green", "pjc-lightblue", "pjc-blue", "pjc-purple", "pjc-yellow"

You can add CSS classes as required.  One of the lesser known tricks with CSS is the fact that you don't have to limit your elements to just one class. If you need to set multiple classes on an element, you add them simply by separating them with a space in your attribute. For example:

<p class="pullquote btmmargin left">...</p>

Controlling a Field Display with CSS

In a Row subclass, override this method:

        public String getFieldStyleClass(Field field, String value, String displayValue,

ExecutingContext ec) {

            if (field.getName().equalsIgnoreCase(“Status”) ) {

                       if (value.equals(“OVERDUE”))

                               return "pjc_red";        

            }

            return super.getFieldStyleClass(field, value, displayValue, ec);

        }

In this example, if the field is called “Status” and the value is “OVERDUE” then the class is returned to render the field as red.  

For additional information on CSS:  http://www.w3schools.com/css/default.asp 

Generate Custom Colors for Rows without Manual Coding

You can easily do this with a custom Row subclass.  There is one already included in WOW that could possible do the trick for you.  Here are the steps to utilize the FieldValueStyleRow.

1) Set the Row class of your operation to planetj.row.FieldValueStyleRow.

2) Edit the FD (Field Descriptor) for the Field you wish to add style to.

        a) Set the Usage Id to -245 (the FieldValueStyleRow looks for a Field with the usage id of -245)

        b) Set the CSS Style Class to what ever you want.  (e.g. mystyle)

3) Add new CSS styles to your theme or the operation.

        a) You can add any new CSS styles to the .css file.

        b) Or, its also possible to just add the necessary scripts to the operations instructions.

This will allow you to either change the Field's style properties or even the whole Row.

Example for 3b:

Lets say I set the style class on the FD to "mystyle", the usage id to -245, and the Row class of my operation to FieldValueStyleRow.  When each Row generates, the Field will have a unique css class set on it as well as the Row.  For example, lets say my Field's value was "" (blank).  The Field's css class will be "mystyle" when HTML is generated for the TD of the TABLE.  This style will also be place on the TR this TD is in.  

In my operation instructions I can add "<style type="text/css">tr.mystyle { background-color: red; }</style>", which would turn the background-color of any Row where the Field's value was blank Red.  Likewise if I specified "<style type="text/css">td.mystyle { background-color: red; }</style>" it would turn the background color of the table cell for Field's with a blank red.

If the Field's value was 'Y', then the style generated would be td.mystyle-Y, so you could include "<style type="text/css">td.mystyle { background-color: red; } td.mystyle-Y {background-color: blue;</style>" to have blank Field's red and Field's with value of Y blue.

Adding User Input via HTML Tags

At times, you may want to allow the user to enter additional parameters that affect custom logic.  Typically this is done in a custom JSP.  The following technique can be used in limited scenarios to get user input WITHOUT the need for a custom JSP.  WOW operations allow you to enter HTML tags into an operations title or instructions.   In the example below, the red highlighted input checkbox was created by adding HTML input tag to the operations instruction.  

Here is the WOW operation:

The HTML input tag will generate a “request parameter” on the server side which can be accessed using Java code as shown below:

public boolean isRowDisplayable(ExecutingContext ec) {

                try {

                        HttpServletRequest req =             ec.getCurrentHttpExecutingContext().getRequest();

                        String value = RequestManager.getParameter("ExcludeMatches", req);

                        if ("ON".equalsIgnoreCase(value)) {

                                Object o = getAssociation("D_EXISTS_IN_SI", ec);

                                if (o != null) {

                                        return false;

                                }

Running Operations with JavaScript

Typically WOW operations are run by selecting a menu item.  At times, you may want to run an operation from another operation.  In a WOW operation’s instructions or title fields, you can add HTML which will create a button and execute an operation.  This can also be done in an HTML code operation.  For example:

<td><input type=”button” onclick="runWOWOperation(14);" value="Run operation 14"></td>

This will create an HTML button using standard HTML.  The onClick event handler will execute an included JavaScript function called runWOWOperation.  This JavaScript method accepts the ID of a WOW operation to be executed.  Change this number to your intended operation to be executed.  Change the Value argument to the text you want to appear on the button.  

You can also use HTML DIV tags and positioning to position your HTML/Javascript.  

The DIV content is shown here:  This HTML can be included in the operations instructions or title.  

<div align="justify" class="graypanel" style="position: absolute; top: 180px; right: 360px; width: 100px; height: 150px;">

<span class="smalltitle">

<a href="#" onclick="runWOWOperation(3864)">Skip Products and Continue</span><br /><br />

<img src="qualcomm/images/continue.png" align="middle" width="75" height="55"/></a>

</div>

Including An Operation’s Generated Results

WOW can include an operation’s results using the following:  ??<WOWOP-1305* 

This uses a SQL replacement text parameter and the * at the end says to include the whole result.  In the example below, operation 1305 will be executed and the results inserted inside the HTML.  

<marquee width="90%" height="400" behavior="scroll" scrollamount="1" direction="up"> ??<WOWOP-1305* </marquee>

Operations can also be included by usageid.  For example:

??WOWOPUSAGID-55           where 55 is the usageid of the operation to use.  

When executing an association operation, developers can also utilize:  ??ROW*

to get the current row and ??ROWCOLLECTION*  to have the entire row collection generated.  

Adding Custom CSS to Operations

WOW generates row tables and row details by attaching CSS classes to HTML elements.  You can view what CSS class names are generated by looking at the HTML source.  Firefox provides some free and effective web tools to automatically see and work with css styles.  You can override WOW’s included CSS by adding CSS style information to your WOW operation in the instructions field.  

Example:  (This tells the browser to use normal wrapping behavior for white space)  

In your operation in the instructions area, add this CSS:

<style type="text/css">

table.pjr {

  white-space: normal;

}

</style>

Another example:

WOW select boxes (HTML) by default render in HTML in their most compact size.  For example notice how each selection box is it’s own size:

To force these to be the same size, you would do the following:

1.  View source and find the CSS class attached to the HTML element you want to alter.  In this example, it is “pjh-s”.

2.  To force the same size, add the following to the operation’s instructions:

<style type="text/css">

.pjh-s{

  width:150px;

  height:250px;

  margin-right:60px;

}

</style>

After adding the CSS override the operation will render as shown below:

Overriding JavaScript Methods

**This technique requires expertise in JavaScript.  

When WOW generates a HTML screen, it generates JavaScript that provides various features such as highlighting a row or clicking on a row.  You can “override” and get control if desired.  To utilize this capability, view the HTML source on your WOW application and examine the javaScript methods.  For example, the following javaScript can be added to a WOW operation in the “instructions”:

<script type="text/javascript">

  trHLightOld = trHLight;

  trHLight = function(obj, className) {

          trHLightOld(obj, className);

          var step =obj.childNodes[2].innerHTML;

          var object = getElementById("rect" + step);

          if(className!=null) {

                  object.style.fill = "rgb(0,0,200)";

          } else {

                object.style.fill = "rgb(200,200,200)";

          }

  }

</script>

The WOW javaScript function is called “trHLight” and it is being “overridden” by adding your own function with that name.   You can use similar techniques to integrate custom JavaScript into your applications.  

Working with WOW SQL Connections

Java connection objects are used to run SQL, call stored procedures and other JDBC related activities.  WOW manages connections via the WOW Builder in the connection screens.  Each connection is defined and identified by an alias.  

To get a connection instance, use the following:  

// Get a new connection to use for SP call

Connection conn =  DatabaseManager.getConnection(“IMS_STAGING”);

After use, you MUST  return a connection back to connection pool:

DatabaseManager.freeConnection(conn);

Get the current user, operation, application

If an application is secured by the operating system or even sql operation, you may want to get the current user.  Here is sample code to do that:  

HttpExecutingContext ec = ExecutingContext.getCurrentHttpExecutingContext();

String signedOnUser = HTMLContext.execute("???USERID", null, ec);

System.out.println(“CURRENT USER IS: “ + signedOnUser);  

 

For operating system authentication, ???USERID is a special character representing the signed on user.  When using a SQL Operation for authentication, all columns from the authentication row are available using similar code.  For example:  (Assuming a column named WORKDEPT existed in the authentication row.)  

String dept = HTMLContext.execute("???WORKDEPT", null, ec);

The following special variables (WOW 7.03) are available for use:

Current operation being executed:

     ??!WOW_OP_ID

Current application being executed:

    ??!WOW_APP_ID

These can be used in a SQL as well:  

   select * from wow_msd_test.sqlops where lvid = ??!WOW_OP_ID

Resolving Variables

In custom code you may want to resolve or retrieve various WOW variables such as ??Field1, ??!GLOBALVAR, ???UserField, etc.  This can be done programmatically with the following code:

HttpExecutingContext ec = ExecutingContext.getCurrentHttpExecutingContext();

String signedOnUser = HTMLContext.execute("??!SomeVariable", null, ec);

System.out.println(“CURRENT USER IS: “ + signedOnUser);  

Replace ??!SomeVariable with your actual variable.

Special Database Metadata SQLs

WOW has limited support for some special SQL queries that return metadata such as a list of collections or libraries and tables and columns.  

These SQLs do not have formal support.   Users are encouraged to test on their local system and use “as is”.  

/*  Get a list of libraries or collections  */

SELECT * FROM WOW_SYS.LIBRARIES                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    

/*  Get a list of Tables prompting on the library */

SELECT * FROM WOW_SYS.TABLES where TABLE_SCHEM = ?                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      

/*  Get a list of columns prompting on library and table */

SELECT * FROM WOW_SYS.COLUMNS WHERE TABLE_SCHEM = ?  AND TABLE_NAME = ?  

SELECT * FROM WOW_SYS.TABLES where TABLE_SCHEM = ? and table_name = ?                

Startup and ShutDown Processing

When the application context  (e.g. Tomcat or Websphere) are started or shutdown, WOW allows custom code to be executed.  

To add a class to be called at startup add a possible values entries such as:

The key should be either _STARTUP or _SHUTDOWN.  The value is a Java class that will get called.  In this class you are free to execute any code.  Custom classes must implement planetj.magic.MagicRequest and override the execute(null, null, null) method.   (See PossibleValuesManager.runInitializeClasses for internal details).  

Using WOW Development Libraries

Currently all applications run from a given user library.  You can have any number of WOW user libraries that hold various WOW based applications.   A problem we've hit is that some applications, such as WSA, need to be able to insert/read information from other WOW user libraries.  To support this, a new place holder, PLANETJ_DEV was added.

 

For example, running SELECT * FROM planetj_dev.sqlops, will select all operations from the current WOW development library.

 

For now, the current development library can be controlled via the URL, parameter _pj_devlib.  For example,

www.planetjavainc.com/wowADDONDEV/run?id=1&_pj_lib=WOWSA&_pj_devlib=PJUSER64

.

This isn't an actual URL, but using it as an example, application id 1 would be the WSA confirmation/management application in the user library WOWSA.  PJUSER64 would be the development library to which WSA uses for reading operations or even inserting new operations.

Changing WOW Development Libraries Dynamically

Method 1:  Create an operation or Execution Group and set the JSP file to:  /dataengine/jsp/dev_lib.jsp

This JSP will display available user libs and allow the user to change the list dynamically.  

Method 2:  Create an operation against the WOW project schema table such as:  

SELECT  PSSCHEMA, PSSTATUS, PSCOMMENTS FROM planetj_sys.PROJECTSCHEMA

Set the Row Class of this operation to:  planetj.dataengine.devlib.DevelopmentSchemaRow

and the Operation Class to: planetj.dataengine.wow.DefaultAliasOperation

Clear Table Cache For Table

WOW caches Row Collections or WOW Operations per SQLContext. If a process or program outside WOW updates a table, WOW is not aware of that change and will not refresh cache. This feature provides a web service (API) to enable outside processes to inform the WOW instance that a table’s data has been changed and therefore WOW should for a table cache refresh.

ClearCacheForTable Magic Request

There is a magic request class to tell WOW to refresh table cache:

planetj.dataengine.cache.ClearCacheForTable

List of parameters needed for this magic request

Sample URL:

http://serverXYZ:8080/wow66/runApp?username=wow@test.com&password=wow&_pj_lib=wow_tmp&alias=FILELEVELDISCOVERY_SONARDB&schem=FILELEVELDISCOVERY&table=FILELEVELDISCOVERY&PJMRCLASS=planetj.dataengine.cache.ClearCacheForTable

ClearCacheForTable static method

There is a static method, so table cache refresh can be done by class parameter:

planetj.dataengine.cache.CacheManager.tableUpdated(String connectionAlias, String tableSchem, String tableName)

WOW Development Environment Modes

WOW application server contexts can be configured in a mode.  Examples of modes are DEVELOPMENT, TEST, PRODUCTION.  To programatically find what mode a server is in use the following:

String currentMode = planetj.wow.WowUtil.getEnvironmentMode();

There are also planetj.wow.WowUtil.isEnvironmentMode(....) methods for testing for a specific environment mode.  These constants are available for testing and denote what is returned:

WowUtil class constants:

public final static String ENVIRONMENT_MODE_PROD = "PROD";

public final static String ENVIRONMENT_MODE_TEST = "TEST";

public final static String ENVIRONMENT_MODE_DEV = "DEV";

Finding Parameters Used in SQL Operation

When WOW runs an operation, the prompted parameters that the user sets are also set on the SQLContext object.  Here is an example of accessing these parameters from an Operation subclass:  

public class ParameterSQLOperation extends SQLOperation {
      // This method is called after an operation executes
        public Object postExecute(Object result, AbstractContext sqlC) throws CMException {

                RowCollection mainRC = (RowCollection) result;

                // get Parameters from the SQL
                ParameterCollection parms = sqlC.getParameterCollection();
                                
                   // loop thru the parameters
                for (int p = 0; p < parms.size(); p++) {
                      Object o =parms.getParameter(p).getValue());
                     System.out.println(o);         
                }        
        }
 }

Configuration Variables

You may want to provide configuration variables that allows your code to provide alternate behavior depending on the configuration values.  For convenience, the WOW Row provides an easy to use method for retrieving configuration values:  

// HOOK to TURN OFF Element filtering if something goes wrong

HttpExecutingContext ec = HttpExecutingContext.getCurrentHttpExecutingContext();

String filterEnabled = (String) getConfigProperty("FILTER_RULE_ELEMENTS", ec);

        

if ("false".equalsIgnoreCase(filterEnabled)) {

return elements; // return the whole thing

}

The method “getConfigProperty” can be used and supplied with a key that identifies the configuration value.  Configuration values can be set up and changed in the WOW Utilities application as shown below:  

 of