Friday, December 12, 2014

Prototype Exception Handling

Team. This is the JAVA source with exception handling and the rest of the prototype code. We can test how well our modifications work by throwing an exception in the main method at the end of the try-catch block. I completed both of the steps which I mentioned earlier this week when creating this solution and did not post any intermediate work. I would appreciate any feedback which you might have. Have a great weekend. La-La.

//BEGIN JAVA CAB SOURCE
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cab;

/**
 * The CAB eventually will be the kernel of the CABOOSE general purpose servlet.
 * Implicit Contract Requirements and Guidelines: The CAB contracts with support
 * classes that have rendering methods which return String content. These
 * methods also must have the signature public String methodName(
 * java.util.HashMap<String,String> aMapParameter ) by implicit contract. If
 * they do not, the reflection activities which automatically invoke them will
 * fail since they cannot find the requested method. Class, method, tile
 * replacement, and stencil filename identifiers are all stored in a file called
 * directory.xml which contain a mapping between each item.
 *
 * @author Jody Sharpe
 */
public class CAB {

    //Exception Handling constants
    final String CABOOSE_EXCEPTION_VIEW_MODEL_ERROR = "<html><head><title>CABOOSE ERROR</title><head><body>The exception view model or content was not found.</body></html>";
    final String EXCEPTION_VIEW = "exception";
    //Simulated URL String. We will eventually get all of our input from the
    //the servlet's Request object.
    String request = "?project=caboose&module=skeleton&create_date=10.30.2014";
    //A data structure for holding a description of the page, stencil, and tile
    //directory. It is keyed by the page name. Each value in the structure is a
    //ViewModel. See the description of this inner class for information on its
    //content and structure.
    java.util.HashMap<String, ViewModel> aDirectoryMap = null;

    //private inner class representing a model of a single view which the
    //controller can return.
    private class ViewModel {
        //The stencil filename.

        private String theStencilName = null;
        //A data structure which holds the tile identifier as a key and the name
        //of the class and method for rendering the the tile as a
        //comma seperated value
        private java.util.HashMap<String, String> theTileRendererMappings = null;

        //This method assigns the stencil filename
        void settingTheStencilName(String aStencilName) throws Exception {
            //Opening Contract
            //The stencil name is not a null or empty string.
            if (aStencilName == null || aStencilName.length() == 0) {
                throw new Exception("Stencil name is null or empty");
            }
            theStencilName = aStencilName;
            //Closing Contract (Implicit)
            //The stencil name is a non-empty string.
        }

        //This method assigns the tile renderer mappings
        void settingTheTileRendererMappings(java.util.HashMap<String, String> aSetOfTileRendererMappings) throws Exception {
            //Opening Contract
            //The set of mappings between tiles and renderer methods is not null
            //It might be of size zero if there soes not exist any replacment tiles
            //within the stencil.
            if (aSetOfTileRendererMappings == null) {
                throw new Exception("The mapping between tiles and render methods is null");
            }
            theTileRendererMappings = aSetOfTileRendererMappings;
            //Closing Contract (Implicit)
            //The tile renderer mappings is not null. They might be empty.
        }

        //This method returns the stencil file name
        String gettingTheStencilName() {
            //Invariant Contract (Implicit)
            //The stencil name is a non-empty string.
            return (theStencilName);
        }

        //This method retruns the tile renderer mappings data structure
        java.util.HashMap<String, String> gettingTheTileRendererMappings() {
            //Invariant Contract (Implicit)
            //The tile renderer mappings are not null. They might be empty.
            return (theTileRendererMappings);
        }
    }

    //This method extracts the content of a stencil from the file system based
    //upon a file name. In the future this method will need adapting so it
    //retrieves the proper file in a web server's file system using
    //the servlet context.
    String gettingTheResponseStencil(String aViewFileName) throws Exception {
        //Opening Contract
        //The file name of the view stencil is not a null or empty string.
        if (aViewFileName == null || aViewFileName.length() == 0) {
            throw new Exception("The filename of the view stencil is null or empty");
        }
        java.io.BufferedReader aStencilReader = new java.io.BufferedReader(new java.io.FileReader(aViewFileName));
        String theStencilContent = "", theNextLine = null;
        while ((theNextLine = aStencilReader.readLine()) != null) {
            theStencilContent += theNextLine;
        }
        //Closing Contract
        //The stencil content of the view in not its initial value of an empty string.
        if (theStencilContent.length() == 0) {
            throw new Exception("The stencil content of the view stencil is empty");
        }
        return (theStencilContent);
    }

    //This method extracts the name and value pairs from the input query string.
    //We assume that it is not url encoded. For the generic servlet edition of
    //the cab, this method will not be useful. But it might be of use for a
    //command-line based edition of the cab for report writing or rendering
    //windowing applications.
    java.util.HashMap<String, String> mappingTheRequestNameValuePairs(String aRequest) throws Exception {
        java.util.HashMap<String, String> aMap = new java.util.HashMap<String, String>();
        //Opening Contract
        //If the request query string is null or empty, return an empty aMap
        if (aRequest == null || aRequest.length() == 0) {
            return aMap;
        }
        aRequest = aRequest.replace("?", "");
        String[] theNameValuePairs = aRequest.split("&");
        for (String aNameValuePair : theNameValuePairs) {
            final int theName = 0, theValue = 1;
            String[] aTuple = aNameValuePair.split("=");
            aMap.put(aTuple[theName], aTuple[theValue]);
        }
        //Closing Contract
        //The Map cannot empty on the input of a non-empty request query string
        if (aMap.size() == 0) {
            throw new Exception("The input key-value map is empty. Could not interpret the query string.");
        }
        return aMap;
    }

    //This model populates a view stencil with the content generated by
    //dynamically invoking reendering methods and replacing their associated
    //user-defined tile identifiers ( a.k.a stencil replacement variables ).
    //See the contract guideline in the header comments of this document.
    String preparingTheExceptionResponse(java.util.HashMap<String, String> aRequestMap) {

        String aReplacementString = "";
        String aSupplementalExceptionMessage = "";
        String theStencilContent = CABOOSE_EXCEPTION_VIEW_MODEL_ERROR;
        ViewModel aViewModel = null;
        String[] aTileClassAndMethod = null;

        //Opening Contract (Implicit)
        //The size of the request map is zero or larger

        aViewModel = (ViewModel) aDirectoryMap.get(EXCEPTION_VIEW);

        //Intermediate Contract
        //The view model cannot be null.
        if (aViewModel == null) {
            return (theStencilContent);
        }

        try {
            theStencilContent = gettingTheResponseStencil(aViewModel.gettingTheStencilName());
        } catch (Exception e) {
            return (theStencilContent);           
        }

        //Intermediate Contract (Implicit)
        //The stencil content of the view in not an empty string.

        java.util.HashMap<String, String> someTileRenderMappings = aViewModel.gettingTheTileRendererMappings();

        //Intermediate Contract (Implicit)
        //The tile renderer mappings are not null. They might be empty.

        for (String aTileId : someTileRenderMappings.keySet()) {

            //Intermediate Contract Within Evaluation (.get) (Implicit)
            //The tile class and method is a non-empty string whcih contains a single
            //comma as a CSV string.

            aTileClassAndMethod = ((String) someTileRenderMappings.get(aTileId)).split(",");

            //Intermediate Contract
            //The class and method names for the tile renderer cannot be null or empty.
            if (aTileClassAndMethod[0] == null || aTileClassAndMethod[1] == null || aTileClassAndMethod[0].length() == 0 || aTileClassAndMethod[1].length() == 0) {
                aSupplementalExceptionMessage += "<p>" + "The class or method name for the tile renderer was null or empty."  + "</p>";
            }

            try {
                //Start Reflection Activities
                Class aClass = Class.forName(aTileClassAndMethod[0]);
                Class[] theFullyQualifiedParameterTypeClasses = new Class[1];
                theFullyQualifiedParameterTypeClasses[0] = (new java.util.HashMap<String, String>()).getClass();
                Object[] theParameterObjectList = new Object[1];
                theParameterObjectList[0] = aRequestMap;
                aReplacementString = (String) (aClass.getMethod(aTileClassAndMethod[1], theFullyQualifiedParameterTypeClasses)).invoke((aClass.getConstructor()).newInstance(), theParameterObjectList);
                //End Reflection Activities                        
            } catch (Exception e) {
                aReplacementString = "<p>" + e.getMessage() + "</p>";
            }

            theStencilContent = theStencilContent.replace(aTileId, aReplacementString + aSupplementalExceptionMessage );

        }

        //Closing Contract (Implicit)
        //The class and method for the given name exist among the controller application bundle.     
        return (theStencilContent);
    }

    //This model populates a view stencil with the content generated by
    //dynamically invoking reendering methods and replacing their associated
    //user-defined tile identifiers ( a.k.a stencil replacement variables ).
    //See the contract guideline in the header comments of this document.
    String preparingTheSuccessfulResponse(String theViewId, java.util.HashMap<String, String> aRequestMap) throws Exception {

        //Opening Contract
        //The id of the view cannot be null or empty
        if (theViewId == null || theViewId.length() == 0) {
            throw new Exception("The view identifier is null or empty.");
        }

        //Opening Contract (Implicit)
        //The size of the request map is zero or larger

        ViewModel aViewModel = (ViewModel) aDirectoryMap.get(theViewId);

        //Intermediate Contract
        //The view model cannot be null.
        if (aViewModel == null) {
            throw new Exception("The view model was not found.");
        }

        String[] aTileClassAndMethod = null;

        String theStencilContent = gettingTheResponseStencil(aViewModel.gettingTheStencilName());

        //Intermediate Contract (Implicit)
        //The stencil content of the view in not an empty string.

        java.util.HashMap<String, String> someTileRenderMappings = aViewModel.gettingTheTileRendererMappings();

        //Intermediate Contract (Implicit)
        //The tile renderer mappings are not null. They might be empty.

        for (String aTileId : someTileRenderMappings.keySet()) {

            //Intermediate Contract Within Evaluation (.get) (Implicit)
            //The tile class and method is a non-empty string whcih contains a single
            //comma as a CSV string.

            aTileClassAndMethod = ((String) someTileRenderMappings.get(aTileId)).split(",");

            //Intermediate Contract
            //The class and method names for the tile renderer cannot be null or empty.
            if (aTileClassAndMethod[0] == null || aTileClassAndMethod[1] == null || aTileClassAndMethod[0].length() == 0 || aTileClassAndMethod[1].length() == 0) {
                throw new Exception("The class or method name for the tile renderer was null or empty.");
            }

            //Start Reflection Activities
            Class aClass = Class.forName(aTileClassAndMethod[0]);
            Class[] theFullyQualifiedParameterTypeClasses = new Class[1];
            theFullyQualifiedParameterTypeClasses[0] = (new java.util.HashMap<String, String>()).getClass();
            Object[] theParameterObjectList = new Object[1];
            theParameterObjectList[0] = aRequestMap;
            theStencilContent = theStencilContent.replace(aTileId, (String) (aClass.getMethod(aTileClassAndMethod[1], theFullyQualifiedParameterTypeClasses)).invoke((aClass.getConstructor()).newInstance(), theParameterObjectList));
            //End Reflection Activities                        

        }

        //Closing Contract (Implicit)
        //The class and method for the given name exist among the controller application bundle.     
        return (theStencilContent);
    }

    //This model extracts the information from the directory.xml file using an
    //XML reader that creates a DOM from which the method creates a user-defined
    //map between view names and ViewModel objects. In the future, we might
    //utilize a SAX XML reader skipping the creation of the DOM and directly
    //create the ViewModel map data structure. This should result in a slight
    //processing speed up although we will only be during it once during a servlet
    //session and storing the utlimate view model map in the servlet's session
    //object. Directoy.xml represents a model mapping for the entire application.
    //Based upon the length of this method ( 50+ lines of code ) it should
    //likely be subdivided and invoke a few private utility methods for
    //completing its work.
    void readingTheDirectoryXMLFile() throws Exception {

        String aViewId = null;
        String aStencil = null;
        String aTileId = null;
        String aTileClass = null;
        String aTileMethod = null;

        aDirectoryMap = new java.util.HashMap<String, ViewModel>();

        javax.xml.parsers.DocumentBuilderFactory theDocumentBuilderFactory =
                javax.xml.parsers.DocumentBuilderFactory.newInstance();
        javax.xml.parsers.DocumentBuilder theDocumentBuilder = theDocumentBuilderFactory.newDocumentBuilder();

        //Intermediate Contract (implicit)
        //The argument for the java.io.File constructor is the canonical name
        //for all CABOOSE bundles and is XML. (It will differ in the final version).
        java.io.File aFile = new java.io.File("./src/cab/directory.xml");

        org.w3c.dom.Document theDocument = theDocumentBuilder.parse(aFile);
        theDocument.getDocumentElement().normalize();
        org.w3c.dom.NodeList theModelNodeList = theDocument.getElementsByTagName("model");

        for (int aModelNodeIndex = 0; aModelNodeIndex < theModelNodeList.getLength(); aModelNodeIndex++) {

            org.w3c.dom.Node aModelNode = theModelNodeList.item(aModelNodeIndex);

            if (aModelNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {

                org.w3c.dom.Element aModelElement = (org.w3c.dom.Element) aModelNode;
                org.w3c.dom.NodeList theViewNodeList =
                        aModelElement.getElementsByTagName("view");

                for (int aViewNodeIndex = 0; aViewNodeIndex < theViewNodeList.getLength(); aViewNodeIndex++) {

                    org.w3c.dom.Node aViewNode = (org.w3c.dom.Node) theViewNodeList.item(aViewNodeIndex);

                    if (aViewNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {

                        org.w3c.dom.Element aViewElement = (org.w3c.dom.Element) aViewNode;
                        ViewModel aViewModel = new ViewModel();

                        aViewId = aViewElement.getAttribute("id");

                        aStencil = aViewElement.getAttribute("stencil");

                        aViewModel.settingTheStencilName(aStencil);
                        java.util.HashMap<String, String> someTileRendererMappings = new java.util.HashMap<String, String>();

                        org.w3c.dom.NodeList theTileNodeList = aViewElement.getElementsByTagName("tile");

                        for (int aTileNodeIndex = 0; aTileNodeIndex < theTileNodeList.getLength(); aTileNodeIndex++) {


                            org.w3c.dom.Node aTileNode = (org.w3c.dom.Node) theTileNodeList.item(aTileNodeIndex);

                            if (aTileNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
                                org.w3c.dom.Element aTileElement = (org.w3c.dom.Element) aTileNode;

                                aTileId = aTileElement.getAttribute("id");
                                aTileClass = aTileElement.getAttribute("class");
                                aTileMethod = aTileElement.getAttribute("method");

                                someTileRendererMappings.put(aTileId, aTileClass + "," + aTileMethod);

                            }

                        }
                        aViewModel.settingTheTileRendererMappings(someTileRendererMappings);
                        aDirectoryMap.put(aViewId, aViewModel);
                    }
                }
            }
        }
        //Closing Contract
        //The directory amp cannot be empty if the CAB returns a view
        if (aDirectoryMap.size() == 0) {
            throw new Exception("The application view directory map is empty.");
        }
    }

    /**
     * @param args the command line arguments This is the main entry point of
     * the application. It builds the request map from the the "temporary" query
     * string. It then build the view model mappings from the directory.xml file
     * by side-effect. Finally it prepares and presents a response. All in less
     * than 0.5 KLOC including the comments. The number of lines of code are not
     * critical in application. The features it provides in the lines it has is
     * most important.
     */
    public static void main(String[] args) {
        // TODO code application logic here
        CAB aCAB = new CAB();
        java.util.HashMap<String, String> aRequestMap = null;
        try {
            aRequestMap = aCAB.mappingTheRequestNameValuePairs(aCAB.request);
            aCAB.readingTheDirectoryXMLFile();           
            System.out.println(aCAB.preparingTheSuccessfulResponse("test_page", aRequestMap));
        } catch (Exception e) {
            //For servlets, we will place this method in the system object.
            aRequestMap.put("exception_message", e.getMessage());
            System.out.println(aCAB.preparingTheExceptionResponse(aRequestMap));
        }
    }
}
//END JAVA CAB SOURCE
//BEGIN DIRECTORY XML
<?xml version="1.0" encoding="UTF-8"?>
<!--
To change this template, choose Tools | Templates
and open the template in the editor.

the model is for the entire application. one view associates
with one page of markup. As attributes this element has an id and a stencil
which is a the filename of the markup. Each view has zero or more replacement
tile child elements associated with it. The tile id attribute appears in the
markup and will be replaced by the String content rendered by the method
uniquely described by the class and method attributes of the tile element.
-->
<model>   
    <view id="test_page" stencil="./src/cab/xstencil.xhtml">
            <tile id="#PROJECT#" class="cab.StencilHandler" method="projectRenderer"/>
            <tile id="#MODULE#" class="cab.StencilHandler" method="moduleRenderer"/>
            <tile id="#CREATE_DATE#" class="cab.StencilHandler" method="createDateRenderer"/>
    </view>   
        <view id="exception" stencil="./src/cab/exception.xhtml">
            <tile id="#EXCEPTION_MESSAGE#" class="cab.StencilHandler" method="exceptionRenderer"/>
    </view>
</model>

//END DIRECTORY XML
//BEGIN STENCIL HANDLER JAVA SOURCE
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cab;

/**
 * An example stencil handler class. For each unique instance of a CABOOSE
 * application, these will be user defined.
 * @author Jody Sharpe
 */
public class StencilHandler {
   
    //Renderer for supplying content about the create date
    public String createDateRenderer( java.util.HashMap<String,String> aRequestMap){
        return( aRequestMap.get("create_date") ); 
    }
    //Renderer for supplying content about the module
    public String moduleRenderer( java.util.HashMap<String,String> aRequestMap){
        return( aRequestMap.get("module") ); 
    }
    //Renderer for supplying content about the project
    public String projectRenderer( java.util.HashMap<String,String> aRequestMap){
        return( aRequestMap.get("project") ); 
    }
    //Renderer for supplying exception content
    public String exceptionRenderer( java.util.HashMap<String,String> aRequestMap){
        return( aRequestMap.get("exception_message") ); 
    }
}
//END STENCIL HANDLER JAVA SOURCE
//BEGIN EXCEPTION XHTML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>CABOOSE Embryo Exception</title>
    </head>
    <body>
        <!--
        I chose that my tile replacement identifiers would
        follow the pattern #ALL_CAPS_JAVA_IDENTIFIER#. This
        is only a convention and not strictly enforced by the
        cab. Obviously, the string used should not collide with
        any other one that might be found in a markup document.
        -->
        <p>#EXCEPTION_MESSAGE#</p>
    </body>
</html>
//END EXCEPTION XHTML
//BEGIN STENCIL XHTML
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>CABOOSE Embryo</title>
    </head>
    <body>
        <!--
        I chose that my tile replacement identifiers would
        follow the pattern #ALL_CAPS_JAVA_IDENTIFIER#. This
        is only a convention and not strictly enforced by the
        cab. Obviously, the string used should not collide with
        any other one that might be found in a markup document.
        -->
        <p>#PROJECT#</p>
        <p>#MODULE#</p>
        <p>#CREATE_DATE#</p>
    </body>
</html>
//END STENCIL XHTML

No comments:

Post a Comment