package jeffaschenk.commons.frameworks.cnxidx.resiliency.ldap; import jeffaschenk.commons.frameworks.cnxidx.utility.logging.FrameworkLogger; import jeffaschenk.commons.frameworks.cnxidx.utility.logging.FrameworkLoggerLevel; import jeffaschenk.commons.touchpoint.model.threads.CircularObjectStack; import org.jgroups.Address; import java.io.*; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.channels.SocketChannel; import java.nio.channels.Selector; import java.nio.channels.SelectionKey; import java.util.*; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; /** * Responds to the Clients Incoming HTTP Connections. * * @author jeff.schenk * @version 4.4 $Revision * Developed 2005 */ class WebAdminResponderThread extends Thread { // ******************************* // Common Logging Facility. private static final String CLASSNAME = WebAdminResponderThread.class.getName(); private static final String THREAD_NAME = "ServiceWebResponderThread"; private static final long SELECT_WAIT_DURATION = (1000 * 2); // 2 Seconds. // ******************************** // Read Buffer size. private static final int READ_BUFFER_SIZE = 2048; // ******************************** // Globals private Selector readSelector; private WebAdminConnectionList acceptedConnections; private ByteBuffer[] responseBuffers = new ByteBuffer[2]; private ByteBuffer[] unauthorizedresponseBuffers = new ByteBuffer[2]; private ByteBuffer readBuffer; private Charset charset; private CharsetDecoder charsetDecoder; private LinkedList allow_list = null; // *********************************************** // Thread Objects private IRRChangeLogRestoreServiceControlThread CONTROL_THREAD = null; // Provides interface to Local Cmds. private CircularObjectStack cosin = null; private CircularObjectStack cosout = null; private boolean running = true; // *********************************************** // Predefined matching Patterns which equate to // a String name. private static final String ANY_IPV4ADDR_PATTERN = "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"; private static final String ANY_NAME = "any"; private static final String ALL_NAME = "all"; // *********************************************** // Operation array, Operations available for // Each Resiliency Instance. private static final String SENDECHO_VALUE = "send.echo"; private static final String STARTREADER_VALUE = "start.reader"; private static final String STOPREADER_VALUE = "stop.reader"; private static final String STARTREPLICA_VALUE = "start.replicarestore"; private static final String STOPREPLICA_VALUE = "stop.replicarestore"; private static final String RESETSTAT_VALUE = "reset.statistics"; private static final String SHUTDOWN_INSTANCE_VALUE = "shutdown.instance"; private static final String[] OPERATION_COMMAND_ARRAY = { SENDECHO_VALUE, STARTREADER_VALUE, STOPREADER_VALUE, STARTREPLICA_VALUE, STOPREPLICA_VALUE, RESETSTAT_VALUE, SHUTDOWN_INSTANCE_VALUE }; // End of Operation Array. private static final String[] OPERATION_NAME_ARRAY = { "Send Echo", "Start Reader", "Stop Reader", "Start Replica Restore", "Stop Replica Restore", "Reset Statistics", "Shutdown Instance" }; // End of Operation Array. private static final String SHUTDOWNALL_VALUE = "shutdown-all-instances"; private static final String SHUTDOWNALL_NAME = "Shutdown All Instances"; private static final String CLIENT_NOT_AUTHORIZED = "Client Not Authorized"; // *********************************************** // FRAMEWORK Product Specific Title Information private static final String PRODUCT_COMPONENT_TITLE = "Data Resiliency Administration"; private static final String FRAMEWORK_PAGE_TITLE = "FRAMEWORK " + PRODUCT_COMPONENT_TITLE + " for"; private static final String MEMBER_NAME = "/member/"; private static final String LOCAL_VALUE = "/local"; private static final String LOCAL_NAME = "Local Instance Status"; private static final String LOCAL_REFRESH_VALUE = "/local/refresh"; private static final String LOCAL_REFRESH_NAME = "Auto Refresh of Local Instance Status"; private static final String OPERATION_NAME = "/operation/"; private static final String FRAMEWORK_LOGO = "<img width=311 height=75 src=\042/web/framework.jpg\042>"; // ****************************************** // HTML Specific Status Page Variables. private static final String SPAN_STYLE_BEGIN = "<span style='font-size:10.0pt;font-family:Arial;color:#00000'>"; private static final String SPAN_STYLE_END = "</span>"; private static final String HTMLHEADER_NO_CACHE = "<head>\n" + "<meta http-equiv=\042Pragma\042 content=\042no-cache\042>\n" + "<meta http-equiv=\042expires\042 content=\0420\042>"; private static final String HTMLHEADER = "<head>\n"; private static final String HTMLREFRESH = "<meta http-equiv=\042refresh\042 content=\04215\042>"; private static final String HTMLTITLE = "<title>"; private static final String HTMLTITLE_END = "</title>"; private static final String OPERATION_SELECTION_JAVASCRIPT = "<script type=\042text/javascript\042 language=\042JavaScript\042>" + "function formHandler(form){" + "var URL = document.form.operation.options[document.form.operation.selectedIndex].value;" + "window.location.href = URL; }" + "</script>"; private static final String HTMLHEADEREND_BODYBEGIN = "</head>" + "<body>" + "<div class=HeaderSection>" + FRAMEWORK_LOGO + "<br>" + "<b>" + "<span style='font-size:16.0pt;font-family:Arial;color:#3366FF'>" + PRODUCT_COMPONENT_TITLE + "</span></b><br>" + "</div>\n"; private static final String UNAUTH_HTMLHEADEREND_BODYBEGIN = "</head>" + "<body>" + "<H1>" + CLIENT_NOT_AUTHORIZED + "</H1>" + "<b><span style='font-size:12.0pt;font-family:Arial;color:#3366FF'>" + "Access Restricted by IP Address." + "</span></b>\n"; private static final String HTML_GROUP_MEMBERSHIP_BEGIN = "<div class=MembershipSection>" + "<p><b><u><span style='font-size:16.0pt;font-family:Arial;color:#3366FF'>" + "Group Membership List</span></u></b>\n" + "<br>" + "<span style='font-size:8.0pt;font-family:Arial;color:#000000'>" + "Click on Member Address to obtain remote status." + "</span>"; private static final String HTML_GROUP_MEMBERSHIP_END = "</p></div>\n"; private static final String HTML_OPERATION_FORM_BEGIN = "<form name=\042form\042>\n" + "<select name=\042operation\042 size=1>\n" + "<option value=\042\042>Perform Operation...\n"; private static final String HTML_OPERATION_VALUE = "<option value=\042%1\042>%2\n"; private static final String HTML_OPERATION_FORM_END = "</select>\n" + "<input type=button value=\042Proceed\042 onClick=\042javascript:formHandler(this)\042>" + "</form>\n"; private static final String HTML_LOCAL_INSTANCE_STATUS_BEGIN = "<div class=LocalSection>" + "<p><b><u><span style='font-size:16.0pt;font-family:Arial;color:#3366FF'>" + "Local Instance Status</span></u></b></p>\n"; private static final String HTML_LOCAL_INSTANCE_STATUS_END = "</div>\n"; private static final String HTML_REMOTE_INSTANCE_STATUS_BEGIN = "<div class=RemoteSection>" + "<p><b><u><span style='font-size:16.0pt;font-family:Arial;color:#3366FF'>" + "Remote Peer Instance Status</span></u></b></p>\n"; private static final String HTML_REMOTE_INSTANCE_STATUS_END = "</div>\n"; private static final String HTML_OPERATION_RESPONSE_BEGIN = "<div class=OperationSection>" + "<p><b><u><span style='font-size:16.0pt;font-family:Arial;color:#3366FF'>" + "Operation Response</span></u></b></p>\n"; private static final String HTML_OPERATION_RESPONSE_END = "</div>\n"; private static final String HTMLFOOTER = "<hr>\n" + "</body>\n" + "</html>"; // ******************************************* // Common HTML Markup Constants. protected static final String HR = "<hr>"; protected static final String BEGIN_TABLE = "<TABLE>"; protected static final String END_TABLE = "</TABLE>"; protected static final String BEGIN_ROW = "<tr>"; protected static final String END_ROW = "</tr>"; protected static final String BEGIN_COL = "<td>"; protected static final String END_COL = "</td>"; protected static final String BEGIN_PARA = "<p>"; protected static final String END_PARA = "</p>"; protected static final String BOLD = "<b>"; protected static final String END_BOLD = "</b>"; protected static final String UNDERLINE = "<u>"; protected static final String END_UNDERLINE = "</u>"; protected static final String BEGIN_GROUP_URI = "<a href=\042" + MEMBER_NAME; protected static final String BEGIN_GROUP_URI_ANCHOR_END = "\042>"; protected static final String END_GROUP_URI = "</a>"; protected static final String COMPONENT_BEGIN = "<b><u><span style='font-size:12.0pt;font-family:Arial;color:#3366FF'>"; protected static final String COMPONENT_END = "</span></b></u>"; protected static final String COMPONENT_ONLINE_BEGIN = "<b><u><SPAN style=\042FONT-SIZE: 12pt; COLOR: #3366ff; FONT-FAMILY: Arial\042>"; protected static final String COMPONENT_ONLINE_END = "</SPAN></b></u>" + "  <b><SPAN style=\042FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Arial\042>" + "<FONT style=\042BACKGROUND-COLOR: #33cc00\042 color=#000000>" + "Running" + "</FONT></SPAN></b>"; protected static final String COMPONENT_OFFLINE_BEGIN = "<b><u><SPAN style=\042FONT-SIZE: 12pt; COLOR: #3366ff; FONT-FAMILY: Arial\042>"; protected static final String COMPONENT_OFFLINE_END = "</SPAN></b></u>" + "  <b><SPAN style=\042FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Arial\042>" + "<FONT style=\042BACKGROUND-COLOR: #990000\042 color=#FFFFFF>" + "Stopped" + "</FONT></SPAN></b>"; protected static final String POSITIVE_VALUE = "<b><SPAN style=\042FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Arial\042>" + "<FONT style=\042BACKGROUND-COLOR: #33cc00\042 color=#000000>"; protected static final String POSITIVE_VALUE_END = "</SPAN></b></u>"; protected static final String NEGATIVE_VALUE = "<b><SPAN style=\042FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Arial\042>" + "<FONT style=\042BACKGROUND-COLOR: #990000\042 color=#FFFFFF>"; protected static final String NEGATIVE_VALUE_END = "</SPAN></b></u>"; protected static final String CAUTION_VALUE = "<b><SPAN style=\042FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Arial\042>" + "<FONT style=\042BACKGROUND-COLOR: ##ffff00\042 color=#FFFFFF>"; protected static final String CAUTION_VALUE_END = "</SPAN></b></u>"; protected static final String WARNING_VALUE = "<b><SPAN style=\042FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Arial\042>" + "<FONT style=\042BACKGROUND-COLOR: #ff9900\042 color=#FFFFFF>"; protected static final String WARNING_VALUE_END = "</SPAN></b></u>"; protected static final String NEUTRAL_VALUE = "<b><SPAN style=\042FONT-SIZE: 10pt; COLOR: #000000; FONT-FAMILY: Arial\042>" + "<FONT style=\042BACKGROUND-COLOR: #3333ff\042 color=#000000>"; protected static final String NEUTRAL_VALUE_END = "</SPAN></b></u>"; protected static final String BREAK = "<br>"; protected static final String NOT_AVAILABLE = BOLD + "N/A" + END_BOLD; /** * WebAdminResponder Thread Constructor. * * @param readSelector * @param acceptedConnections * @param cosin * @param cosout * @throws Exception */ public WebAdminResponderThread(Selector readSelector, WebAdminConnectionList acceptedConnections, IRRChangeLogRestoreServiceControlThread CONTROL_THREAD, CircularObjectStack cosin, CircularObjectStack cosout, LinkedList allow_list) throws Exception { super(WebAdminResponderThread.THREAD_NAME); this.readSelector = readSelector; this.acceptedConnections = acceptedConnections; this.readBuffer = ByteBuffer.allocateDirect(READ_BUFFER_SIZE); this.charset = Charset.forName("ISO-8859-1"); this.charsetDecoder = charset.newDecoder(); this.responseBuffers[0] = initializeResponseHeader(); this.unauthorizedresponseBuffers[0] = initializeUNAUTHResponseHeader(); // ********************************* // Set up the Stack to Communicate this.CONTROL_THREAD = CONTROL_THREAD; this.cosin = cosin; this.cosout = cosout; // ********************************* // Save a Reference to Allow List this.allow_list = allow_list; } // End of Constructor. /** * WebAdminResponder Main Loop to Respond to Requests. */ public void run() { final String METHODNAME = "run"; FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.INFO, MessageConstants.WEBADMIN_RESPONDER_THREAD_ESTABLISHED, new String[]{Thread.currentThread().getName()}); // ************************** // Main Responder Thread Loop try { while (running) { registerNewChannels(); // ********************************** // Any Selector Keys ready for a // Response. int keysReady = readSelector.select(SELECT_WAIT_DURATION); // ********************************* // Was I shutdown? // If, break out of loop. if (!running) { break; } // *********************************** // Any Connection Ready? if (keysReady > 0) { acceptPendingRequests(); } } // End of While Loop. } catch (Exception ex) { this.logStackTrace(ex, METHODNAME); } finally { FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.INFO, MessageConstants.WEBADMIN_RESPONDER_THREAD_SHUTDOWN, new String[]{Thread.currentThread().getName()}); } // End of Final Processing for Thread. } // End of Run Method. /** * Register any new Channels for us to Respond. * * @throws Exception */ protected void registerNewChannels() throws Exception { SocketChannel channel; while (null != (channel = acceptedConnections.removeFirst())) { channel.configureBlocking(false); channel.register(readSelector, SelectionKey.OP_READ, new StringBuffer()); } // End of While Loop. } // End of registerNewChannels /** * Accepts any pending incoming requests * * @throws Exception */ protected void acceptPendingRequests() throws Exception { Set readyKeys = readSelector.selectedKeys(); for (Iterator i = readyKeys.iterator(); i.hasNext(); ) { SelectionKey key = (SelectionKey) i.next(); i.remove(); // ****************************** // Now Read and Process Request. readRequest(key); key.cancel(); } // End of For Loop. } // End of acceptPendingRequests Method. /** * Read pending request from current Selector Key and * will perform request. * * @param key * @throws Exception */ protected void readRequest(SelectionKey key) throws Exception { final String METHODNAME = "readRequest"; SocketChannel incomingChannel = (SocketChannel) key.channel(); try { while (incomingChannel.read(readBuffer) > 0) { FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.DEBUG, MessageConstants.WEBADMIN_RESPONDER_THREAD_INCOMING_BYTES_READ, new String[]{Integer.toString(readBuffer.capacity() - readBuffer.remaining())}); readBuffer.flip(); String result = charsetDecoder.decode(readBuffer).toString(); readBuffer.clear(); StringBuffer requestString = (StringBuffer) key.attachment(); requestString.append(result); if (result.endsWith("\n\n") || result.endsWith("\r\n\r\n")) { handleCompletedRequest(requestString.toString(), incomingChannel); break; } } // End of While Loop. } catch (WebAdminRequestException re) { sendError(incomingChannel, re); } catch (IOException ioe) { sendError(incomingChannel, WebAdminRequestException.INTERNAL_SERVER_ERROR); } catch (Exception e) { sendError(incomingChannel, WebAdminRequestException.INTERNAL_SERVER_ERROR); } // End of Exception Processing. } // end of readRequest Method. /** * Handle completed received data from Selector, now perform request * based upon URI. * * @param request * @param channel * @throws WebAdminRequestException * @throws java.io.IOException * @throws Exception */ protected void handleCompletedRequest(String request, SocketChannel channel) throws WebAdminRequestException, IOException, Exception { final String METHODNAME = "handleCompletedRequest"; // ********************************* // Ok, now determine if this // Client is allowed Access. if (this.isClientAllowed(channel.socket().getInetAddress().toString())) { // **************************** // Accept the Connection by // Adding to our Request List. FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.INFO, MessageConstants.WEBADMIN_ACCEPT_CONNECTION, new String[]{channel.socket().getInetAddress().toString()}); } else { // **************************** // Deny the Connection by // ringing out the proper // protocols, otherwise // we will have FIN_WAITs. FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.WARNING, MessageConstants.WEBADMIN_DENIED_CONNECTION, new String[]{channel.socket().getInetAddress().toString()}); sendUnauthorizedPage(channel); channel.close(); return; } // End of Else. // ************************************** // Ok Accept the Connetion and perform // the Request. StringTokenizer tok = new StringTokenizer(request); tok.nextToken(); // skip the method String path = tok.nextToken(); // grab the URI, ignore the rest // *************************************** // Now determine which page or resource to // send or which operation to perform. if ((path == null) || (path.equalsIgnoreCase("")) || (path.equalsIgnoreCase("/")) || (path.equalsIgnoreCase(LOCAL_VALUE))) { sendLocalStatusPage(path, channel, request, false); } else if (path.equalsIgnoreCase(LOCAL_REFRESH_VALUE)) { sendLocalStatusPage(path, channel, request, true); } else if (path.startsWith(MEMBER_NAME)) { sendRemoteStatusPage(path, channel, request, false); } else if (path.startsWith(OPERATION_NAME)) { sendOperationResponsePage(path, channel, request, false); } else { sendResource(path, channel, request); } // ********************************************* // Now Close off the Client Socket after // processing the request, never place in // final block, if error occurs, sendError // method will be invoked. channel.close(); } // End of handleCompletedRequest Method. /** * Initialize HTTP OK Response Header. * * @return ByteBuffer containing encoded header. * @throws Exception */ protected ByteBuffer initializeResponseHeader() throws Exception { // Pre-load a "good" HTTP response as characters. CharBuffer chars = CharBuffer.allocate(128); chars.put("HTTP/1.1 200 OK\n"); chars.put("Connection: close\n"); chars.put("Server: FRAMEWORK RESILIENCY\n"); chars.put("Content-Type: text/html\n"); chars.put("\n"); chars.flip(); // **************************************************** // Translate the Unicode characters into ASCII bytes. ByteBuffer buffer = charset.newEncoder().encode(chars); // ********************** // Return. return buffer; } // End of initializeResponseHeader Method. /** * Initialize HTTP Unauthorized Response Header. * * @return ByteBuffer containing encoded header. * @throws Exception */ protected ByteBuffer initializeUNAUTHResponseHeader() throws Exception { // Pre-load a "UnAuthorized" HTTP response as characters. CharBuffer chars = CharBuffer.allocate(128); chars.put("HTTP/1.1 401 Unauthorized\n"); chars.put("Connection: close\n"); chars.put("Server: FRAMEWORK RESILIENCY\n"); chars.put("Content-Type: text/html\n"); chars.put("\n"); chars.flip(); // **************************************************** // Translate the Unicode characters into ASCII bytes. ByteBuffer buffer = charset.newEncoder().encode(chars); // ********************** // Return. return buffer; } // End of initializeResponseHeader Method. /** * Encode the Response. * * @param response * @return ByteBuffer * @throws Exception */ protected ByteBuffer encodeResponse(String response) throws Exception { CharBuffer chars = CharBuffer.allocate(response.length()); chars.put(response); chars.flip(); // Translate the Unicode characters into ASCII bytes. ByteBuffer buffer = charset.newEncoder().encode(chars); return buffer; } /** * Send Unauthorized Page. */ protected void sendUnauthorizedPage(SocketChannel channel) throws WebAdminRequestException, IOException, Exception { final String METHODNAME = "sendUnauthorizedPage"; FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.WARNING, MessageConstants.WEBADMIN_RESPONDER_THREAD_SENDING_UNAUTH, new String[]{channel.socket().getInetAddress().toString()}); // ************************************* // Formulate Header of Page StringBuffer sb = new StringBuffer(this.buildUnAuthorizedHeader()); // ****************************************** // Finish Up Page. sb.append(WebAdminResponderThread.HTMLFOOTER); // ************************************* // Encode and Rewind our Buffer for // Presentation. this.unauthorizedresponseBuffers[1] = this.encodeResponse(sb.toString()); this.unauthorizedresponseBuffers[1].rewind(); // ************************************** // Present Page to Channel. this.unauthorizedresponseBuffers[0].rewind(); channel.write(this.unauthorizedresponseBuffers); } // End of sendUnauthorizedPage Method. /** * Build Unauthorized Header Page. */ protected String buildUnAuthorizedHeader() { // ************************************* // Formulate Header Page StringBuffer sb = new StringBuffer(); sb.append(WebAdminResponderThread.HTMLHEADER); sb.append(WebAdminResponderThread.HTMLTITLE); sb.append(WebAdminResponderThread.FRAMEWORK_PAGE_TITLE + " " + CLIENT_NOT_AUTHORIZED); sb.append(WebAdminResponderThread.HTMLTITLE_END); // ****************************************** // Show Not Authorized. sb.append(WebAdminResponderThread.UNAUTH_HTMLHEADEREND_BODYBEGIN); // ****************************************** // Return. return sb.toString(); } // End of buildUnAuthorizedHeader Method. /** * Build Common Header for all Pages. * * @param autorefresh */ protected String buildCommonHeader(boolean autorefresh) { // ************************************* // Formulate Header Page StringBuffer sb = new StringBuffer(); if (autorefresh) { sb.append(WebAdminResponderThread.HTMLHEADER_NO_CACHE); } else { sb.append(WebAdminResponderThread.HTMLHEADER); } sb.append(WebAdminResponderThread.HTMLTITLE); sb.append(WebAdminResponderThread.FRAMEWORK_PAGE_TITLE + " " + this.CONTROL_THREAD.my_addr.toString() + ", http:" + this.CONTROL_THREAD.WEBADMIN_PORT); sb.append(WebAdminResponderThread.HTMLTITLE_END); // ****************************************** // Check to see if user request auto refresh? if (autorefresh) { sb.append(WebAdminResponderThread.HTMLREFRESH); } // ****************************************** // Add any JavaScripts We Need to Establish. sb.append(WebAdminResponderThread.OPERATION_SELECTION_JAVASCRIPT); // ****************************************** // Finish Up Header and Begin Body. sb.append(WebAdminResponderThread.HTMLHEADEREND_BODYBEGIN); // ****************************************** // First show our Membership list // and Operation Command List. sb.append(this.generateMemberShipList()); // ****************************************** // Return. return sb.toString(); } // End of buildCommonHeader Method. /** * Send Local Status Page. */ protected void sendLocalStatusPage(String uri, SocketChannel channel, String request, boolean autorefresh) throws WebAdminRequestException, IOException, Exception { final String METHODNAME = "sendLocalStatusPage"; FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.DEBUG, MessageConstants.WEBADMIN_RESPONDER_THREAD_SENDING_URI, new String[]{uri}); // ************************************* // Formulate Header of Page StringBuffer sb = new StringBuffer(this.buildCommonHeader(autorefresh)); // ****************************************** // Obtain Local Status from Components. sb.append(WebAdminResponderThread.HTML_LOCAL_INSTANCE_STATUS_BEGIN); sb.append(this.CONTROL_THREAD.obtainLocalStatus(false)); sb.append(WebAdminResponderThread.HTML_LOCAL_INSTANCE_STATUS_END); // ****************************************** // Finish Up Page. sb.append(WebAdminResponderThread.HTMLFOOTER); // ************************************* // Encode and Rewind our Buffer for // Presentation. this.responseBuffers[1] = this.encodeResponse(sb.toString()); this.responseBuffers[1].rewind(); // ************************************** // Present Page to Channel. this.responseBuffers[0].rewind(); channel.write(this.responseBuffers); } // End of send LocalStatusPage Method. /** * Send Remote Status Page. */ protected void sendRemoteStatusPage(String uri, SocketChannel channel, String request, boolean autorefresh) throws WebAdminRequestException, IOException, Exception { final String METHODNAME = "sendRemoteStatusPage"; FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.DEBUG, MessageConstants.WEBADMIN_RESPONDER_THREAD_SENDING_URI, new String[]{uri}); // ************************************* // Formulate Header of Page StringBuffer sb = new StringBuffer(this.buildCommonHeader(autorefresh)); // ****************************************** // Check if we should obtain Remote Instance // Status. sb.append(this.buildRemoteStatusPage(uri)); // ****************************************** // Finish Up Page. sb.append(WebAdminResponderThread.HTMLFOOTER); // ************************************* // Encode and Rewind our Buffer for // Presentation. this.responseBuffers[1] = this.encodeResponse(sb.toString()); this.responseBuffers[1].rewind(); // ************************************** // Present Page to Channel. this.responseBuffers[0].rewind(); channel.write(this.responseBuffers); } // End of sendRemoteStatusPage Method. /** * Build Status Page of Remote Peers. */ protected StringBuffer buildRemoteStatusPage(String uri) { final String METHODNAME = "buildRemoteStatusPage"; // ****************************************** // TODO - Put this Method Under a Timer! // ****************************************** // Obtain Remote Status from Components. StringBuffer sb = new StringBuffer(); sb.append(WebAdminResponderThread.HTML_REMOTE_INSTANCE_STATUS_BEGIN); // ********************************************** // Now determine based upon the uri which member // I should use. String member_token = null; int member_index = 0; if ((uri != null) && (uri.startsWith(MEMBER_NAME))) { member_token = uri.substring((MEMBER_NAME).length()).trim(); } else { member_token = "0"; } try { member_index = Integer.parseInt(member_token); } catch (NumberFormatException nfe) { member_index = 0; } if (member_index > (this.CONTROL_THREAD.members.size() - 1)) { member_index = 0; } // ************************************************** // Ok Now issue the Command over the Remote Bus. FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.DEBUG, MessageConstants.WEBADMIN_RESPONDER_THREAD_MEMBER_INDEX_SELECTED, new String[]{Integer.toString(member_index), this.CONTROL_THREAD.members.get(member_index).toString()}); this.cosout.push(new StackCommand(StackCommand.CL_STATUS, this.CONTROL_THREAD.my_addr, this.CONTROL_THREAD.members.get(member_index), null)); // *************************************************** // Now Loop TO Obtain Reply on our Local Stack. while (true) { StackCommand instackcommand = null; if (cosin.hasMoreNodes()) { instackcommand = (StackCommand) cosin.getNext(); } // *************************** // Did anything get pulled // from stack? if (instackcommand != null) { // ****************************** // Did our Running State trip to // not based upon a End of Thread // command? If so, never place this // Command on the BUS. if ((instackcommand.getCommandType() == StackCommand.CL_STATUS_REPLY) && (instackcommand.getDestination().equals(this.CONTROL_THREAD.my_addr))) { sb.append((String) instackcommand.getObject()); break; // Break from the While Loop. } // End of Check for End of Thread. else { // ******************************** // We Entered Here due to a Roque // Command. we Could have received // a Command for our parent, so place // back on the queue. cosin.push(instackcommand); } // End of Else. } // End of Nothing in Stack yet to Process. } // End of While Loop. // ************************************************** // Finishup this Section. sb.append(WebAdminResponderThread.HTML_REMOTE_INSTANCE_STATUS_END); // ************************************** // Return. return sb; } // End of buildRemoteStatusPage Method. /** * Send Operation Response. */ protected void sendOperationResponsePage(String uri, SocketChannel channel, String request, boolean autorefresh) throws WebAdminRequestException, IOException, Exception { final String METHODNAME = "sendOperationResponsePage"; FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.DEBUG, MessageConstants.WEBADMIN_RESPONDER_THREAD_SENDING_URI, new String[]{uri}); // ************************************* // Formulate Header of Page StringBuffer sb = new StringBuffer(this.buildCommonHeader(autorefresh)); // ****************************************** // Now Check the incoming uri string path to // determine and trigger the operation to be // performed. sb.append(this.processOperation(uri)); // ****************************************** // Finish Up Page. sb.append(WebAdminResponderThread.HTMLFOOTER); // ************************************* // Encode and Rewind our Buffer for // Presentation. this.responseBuffers[1] = this.encodeResponse(sb.toString()); this.responseBuffers[1].rewind(); // ************************************** // Present Page to Channel. this.responseBuffers[0].rewind(); channel.write(this.responseBuffers); } // End of sendOperationResponsePage Method. /** * Process the Operation and build any additional details * for formulating a response. */ protected String processOperation(String uri) { final String METHODNAME = "processOperation"; // ****************************************** // Initialize. StringBuffer sb = new StringBuffer(); // ************************************************************** // Build the Operation Response Header. sb.append(WebAdminResponderThread.HTML_OPERATION_RESPONSE_BEGIN); // ********************************************** // Now determine based upon the uri what command // should be executed against which member in group. if ((uri == null) || (!uri.startsWith(WebAdminResponderThread.OPERATION_NAME))) { // ************************************************** // Ok Show Invalid Operation URI. FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.WARNING, ErrorConstants.WEBADMIN_RESPONDER_THREAD_INVALID_OPURI, new String[]{uri}); // ************************************************** // Show in HTML Form as Well for the End User. sb.append( WebAdminResponderThread.BEGIN_PARA + WebAdminResponderThread.BOLD + "Invalid Operation URI:[" + WebAdminResponderThread.END_BOLD + uri + WebAdminResponderThread.BOLD + "]" + WebAdminResponderThread.END_BOLD + WebAdminResponderThread.END_PARA); // ************************************************** // Finishup this Section. sb.append(WebAdminResponderThread.HTML_OPERATION_RESPONSE_END); // ************************************************** // return return sb.toString(); } // End of Null or invalid Operation Name. // ************************************************************ // Make a usable operation string stripping out encoded blanks. String operation = uri.substring( WebAdminResponderThread.OPERATION_NAME.length()).replaceAll("%20", " "); // ****************************************************** // Now we seem to have a valid Operation prefix now verify // we have a valid address to send the command. Object cmddestaddr = null; int i = operation.lastIndexOf(" "); if (i > 0) { // ********************************************** // Obtain the Stringified Address. String destaddr = operation.substring(i).trim(); operation = operation.substring(0, i); // ********************************************** // Now Obtain real Destination. for (int r = 0; r < this.CONTROL_THREAD.members.size(); r++) { if (this.CONTROL_THREAD.members.get(r).toString().equalsIgnoreCase(destaddr)) { cmddestaddr = this.CONTROL_THREAD.members.get(r); } } // End of Foor Loop. } // End of If for Destination Address. // *************************************************** // Now Formulate a Stack Command based upon // Operation and Destnation Address if one exists. StackCommand opstackcommand = this.formulateOperationStackCommand(operation, cmddestaddr); // **************************************************** // Now determine if the command was good or not. // If good send it on it's way, if not show issue. if (opstackcommand == null) { // ************************ // Abandon Illegal Request. if (cmddestaddr == null) { sb.append( WebAdminResponderThread.BEGIN_PARA + WebAdminResponderThread.BOLD + "Abandoned Invalid Operation:[" + WebAdminResponderThread.END_BOLD + operation + WebAdminResponderThread.BOLD + "]" + WebAdminResponderThread.END_BOLD + WebAdminResponderThread.END_PARA); } else { sb.append( WebAdminResponderThread.BEGIN_PARA + WebAdminResponderThread.BOLD + "Abandoned Invalid Operation:[" + WebAdminResponderThread.END_BOLD + operation + WebAdminResponderThread.BOLD + "]" + WebAdminResponderThread.END_BOLD + WebAdminResponderThread.BREAK); sb.append( WebAdminResponderThread.BEGIN_PARA + WebAdminResponderThread.BOLD + "Address Destination:[" + WebAdminResponderThread.END_BOLD + cmddestaddr.toString() + WebAdminResponderThread.BOLD + "]" + WebAdminResponderThread.END_BOLD + WebAdminResponderThread.END_PARA); } // End of Inner Else. } else { // *************************** // Execute Operation Request, // by placing command onto // bus. this.cosout.push(opstackcommand); // ****************************** // Now Show Operation was Placed // for execution. if (cmddestaddr == null) { sb.append( WebAdminResponderThread.BEGIN_PARA + WebAdminResponderThread.BOLD + "Acknowledged Operation:[" + WebAdminResponderThread.END_BOLD + operation + WebAdminResponderThread.BOLD + "]" + WebAdminResponderThread.END_BOLD + WebAdminResponderThread.END_PARA); } else { sb.append( WebAdminResponderThread.BEGIN_PARA + WebAdminResponderThread.BOLD + "Acknowledged Operation:[" + WebAdminResponderThread.END_BOLD + operation + WebAdminResponderThread.BOLD + "]" + WebAdminResponderThread.END_BOLD + WebAdminResponderThread.BREAK); sb.append( WebAdminResponderThread.BEGIN_PARA + WebAdminResponderThread.BOLD + "Address Destination:[" + WebAdminResponderThread.END_BOLD + cmddestaddr.toString() + WebAdminResponderThread.BOLD + "]" + WebAdminResponderThread.END_BOLD + WebAdminResponderThread.END_PARA); } // End of Inner Else. } // End of Else // ************************************************** // Finishup this Section. sb.append(WebAdminResponderThread.HTML_OPERATION_RESPONSE_END); // ************************************** // Return. return sb.toString(); } // End of processOperation Method. /** * Formulate a StackCommand from a incoming Operation and optional Address. * * @param operation * @param destination member object. * @return StackCommand Object, if null invalid operation. */ protected StackCommand formulateOperationStackCommand(String operation, Object destination) { // **************************** // Initialize. final String METHODNAME = "formulateOperationStackCommand"; int command = StackCommand.CL_INVALID; Object payload = null; // *********************************** // Log our Method incoming Parameters. if (destination == null) { FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.DEBUG, MessageConstants.WEBADMIN_RESPONDER_THREAD_FORMULATING_OPCMD, new String[]{operation, "none specified"}); } else { FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.DEBUG, MessageConstants.WEBADMIN_RESPONDER_THREAD_FORMULATING_OPCMD, new String[]{operation, destination.toString()}); } // End of Else. // *********************************** // Now Validate and Formulate the // Command to be issued. if (operation == null) { return null; } // *********************************** // Check for a Full Shutdown. if (operation.equalsIgnoreCase(WebAdminResponderThread.SHUTDOWNALL_VALUE)) { return new StackCommand(StackCommand.CL_SHUTDOWN, this.CONTROL_THREAD.my_addr); } // End of Check for Shutdown. // *********************************************** // Now all remaining commands need to have a // Destination Member. if (destination == null) { return null; } // *********************************************** // Check for Send Echo Command. if (operation.equalsIgnoreCase(WebAdminResponderThread.SENDECHO_VALUE)) { command = StackCommand.CL_ECHO; payload = null; // TODO Add something to payload. } // End of Check for Shutdown. // ************************************************* // Check for Valid Commands, which are just commands. else if (operation.equalsIgnoreCase(WebAdminResponderThread.STARTREADER_VALUE)) { command = StackCommand.CL_START_READER; } else if (operation.equalsIgnoreCase(WebAdminResponderThread.STOPREADER_VALUE)) { command = StackCommand.CL_STOP_READER; } else if (operation.equalsIgnoreCase(WebAdminResponderThread.STARTREPLICA_VALUE)) { command = StackCommand.CL_START_REPLICA; } else if (operation.equalsIgnoreCase(WebAdminResponderThread.STOPREPLICA_VALUE)) { command = StackCommand.CL_STOP_REPLICA; } else if (operation.equalsIgnoreCase(WebAdminResponderThread.RESETSTAT_VALUE)) { command = StackCommand.CL_RESET_STATS; } else if (operation.equalsIgnoreCase(WebAdminResponderThread.SHUTDOWN_INSTANCE_VALUE)) { command = StackCommand.CL_SHUTDOWN; } // End of Command Checking. // ***************************** // Return the StackCommand. if (command != StackCommand.CL_INVALID) { return new StackCommand(command, this.CONTROL_THREAD.my_addr, destination, payload); } else { return null; } } // End of formulateOperationStackCommand Private Method. /** * Locate the requested file and send it off. * This is usually for our logo and favicon.ico. */ protected void sendResource(String uri, SocketChannel channel, String request) throws WebAdminRequestException, IOException { final String METHODNAME = "sendResource"; // ************************************ // Create our internal uri and obtain // resource. The resourcename has // to be directed to our web package // to pick anything external up. String resourcename = uri; if (!resourcename.startsWith("/web")) { if (resourcename.startsWith("/")) { resourcename = "web" + resourcename; } else { resourcename = "web" + "/" + resourcename; } } // end of Check to fix URI. else { resourcename = resourcename.substring(1); } // End of Else. FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.DEBUG, MessageConstants.WEBADMIN_RESPONDER_THREAD_SENDING_NAMED_RESOURCE, new String[]{resourcename}); // *********************************** // Now Obtain the Input Stream. InputStream ris = this.getClass().getResourceAsStream(resourcename); if (ris == null) { throw WebAdminRequestException.PAGE_NOT_FOUND; } // ******************************** // Stuff the Bytes into our // Response Buffer. byte[] bytes = this.getBytes(ris); this.responseBuffers[1] = ByteBuffer.allocateDirect(bytes.length); this.responseBuffers[1].order(ByteOrder.nativeOrder()); this.responseBuffers[1].put(bytes); // *************************** // Close off the Input Stream ris.close(); // **************************************** // Perform the Write to the Client Channel. this.responseBuffers[0].rewind(); this.responseBuffers[1].rewind(); channel.write(this.responseBuffers); } // End of sendResource Response. /** * Send HTML Informational Error. * * @param channel * @param error * @throws Exception */ protected void sendError(SocketChannel channel, WebAdminRequestException error) throws Exception { ByteBuffer buffer = null; CharBuffer chars = CharBuffer.allocate(128); chars.put("HTTP/1.0 " + error.getErrorCode() + " OK\n"); chars.put("Connection: close\n"); chars.put("Server: FRAMEWORK RESILIENCY\n"); chars.put("\n"); chars.flip(); // Translate the Unicode characters into ASCII bytes. buffer = charset.newEncoder().encode(chars); buffer.rewind(); // ************************ // Write the Buffer and // Close the Client Channel. channel.write(buffer); channel.close(); } // End of SendError. /** * Shutdown our Child Thread. */ protected void shutdown() { final String METHODNAME = "shutdown"; this.running = false; try { this.readSelector.close(); } catch (IOException ioe) { this.logStackTrace(ioe, METHODNAME); } // End of Exception Processing. } // End of shutdown method. /** * Get InputStream Byte Data. * * @param is InputStream from Channel. * @return byte[] -- Byte Array. * @throws java.io.IOException */ private byte[] getBytes(InputStream is) throws IOException { byte[] buffer = new byte[8192]; ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); int n; baos.reset(); while ((n = is.read(buffer, 0, buffer.length)) != -1) { baos.write(buffer, 0, n); } return baos.toByteArray(); } // End of getBytes Private Method. /** * Determines if the incoming Client is Allowed * access to our WebAdmin Interface. * * @param clientaddress * @return boolean -- indicating if client is allowed or not. */ private boolean isClientAllowed(final String clientaddress) { final String METHODNAME = "isClientAllowed"; // **************************** // Do we have an Allow List? if (allow_list == null) { return false; } // **************************** // Do we have a clientaddress? if ((clientaddress == null) || (clientaddress.trim().equalsIgnoreCase(""))) { return false; } // ********************************* // Synitize the Stringified Address String ca = new String(clientaddress); if (ca.startsWith("/")) { ca = ca.substring(1); } // ****************************** // Now loop through our patterns for (int ae = 0; ae < this.allow_list.size(); ae++) { String allow_entry = (String) this.allow_list.get(ae); // ************************* // Do we have a direct hit? if (allow_entry.equalsIgnoreCase(ca)) { return true; } // ************************************** // Do we have a ANY or OR // Allow? if so force a // pattern, not really ncessary but // it looks better. else if ((allow_entry.equalsIgnoreCase(WebAdminResponderThread.ANY_NAME)) || (allow_entry.equalsIgnoreCase(WebAdminResponderThread.ALL_NAME))) { allow_entry = WebAdminResponderThread.ANY_IPV4ADDR_PATTERN; } try { if (Pattern.matches(allow_entry, ca)) { return true; } } catch (PatternSyntaxException pse) { // ********************************************* // Ignore Exception, it is not a valid pattern. FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.WARNING, ErrorConstants.WEBADMIN_ACCEPTOR_IGNORING_PATTERN_SYNTAX, new String[]{ca, pse.getMessage()}); } // End of Exception Processing. } // End of For Loop. // *************************** // Deny Access. return false; } // End of isClientAllowed Private Method. // *********************************************** // Static Protected Methods used as Helpers to // Build a Web Page. // *********************************************** /** * Build a 2 Column Row */ protected static String build2ColumnRow(String left, String right) { return WebAdminResponderThread.BEGIN_ROW + WebAdminResponderThread.BEGIN_COL + WebAdminResponderThread.BOLD + WebAdminResponderThread.SPAN_STYLE_BEGIN + left + ":" + WebAdminResponderThread.SPAN_STYLE_END + WebAdminResponderThread.END_BOLD + WebAdminResponderThread.END_COL + WebAdminResponderThread.BEGIN_COL + WebAdminResponderThread.SPAN_STYLE_BEGIN + right + WebAdminResponderThread.SPAN_STYLE_END + WebAdminResponderThread.END_COL + WebAdminResponderThread.END_ROW; } // End of build2ColumnRow /** * Build a 3 Column Row * * @param left * @param center * @param right * @return String -- HTML Data */ protected static String build3ColumnRow(String left, String center, String right) { return WebAdminResponderThread.BEGIN_ROW + WebAdminResponderThread.BEGIN_COL + WebAdminResponderThread.BOLD + WebAdminResponderThread.SPAN_STYLE_BEGIN + left + ":" + WebAdminResponderThread.SPAN_STYLE_END + WebAdminResponderThread.END_BOLD + WebAdminResponderThread.END_COL + WebAdminResponderThread.BEGIN_COL + WebAdminResponderThread.SPAN_STYLE_BEGIN + center + WebAdminResponderThread.SPAN_STYLE_END + WebAdminResponderThread.END_COL + WebAdminResponderThread.BEGIN_COL + WebAdminResponderThread.SPAN_STYLE_BEGIN + right + WebAdminResponderThread.SPAN_STYLE_END + WebAdminResponderThread.END_COL + WebAdminResponderThread.END_ROW; } // End of build2ColumnRow /** * Build HTML for Membership List. * * @param vector * @param showlocalreturn * @return String -- HTML Data */ protected static String buildMembershipList(List<Address> vector, boolean showlocalreturn) { StringBuffer sb = new StringBuffer(); sb.append(WebAdminResponderThread.HTML_GROUP_MEMBERSHIP_BEGIN); sb.append(WebAdminResponderThread.BEGIN_TABLE); sb.append(WebAdminResponderThread.SPAN_STYLE_BEGIN); for (int i = 0; i < vector.size(); i++) { sb.append( WebAdminResponderThread.BEGIN_ROW + WebAdminResponderThread.BEGIN_COL + WebAdminResponderThread.BOLD); if (i == 0) { sb.append(WebAdminResponderThread.SPAN_STYLE_BEGIN + "Primary Coordinator:" + WebAdminResponderThread.SPAN_STYLE_END); } else { sb.append(WebAdminResponderThread.SPAN_STYLE_BEGIN + "Secondary PEER:" + WebAdminResponderThread.SPAN_STYLE_END); } // End of Else. sb.append( WebAdminResponderThread.END_BOLD + WebAdminResponderThread.END_COL + WebAdminResponderThread.BEGIN_COL + WebAdminResponderThread.BEGIN_GROUP_URI + Integer.toString(i) + WebAdminResponderThread.BEGIN_GROUP_URI_ANCHOR_END + WebAdminResponderThread.SPAN_STYLE_BEGIN + vector.get(i).toString() + WebAdminResponderThread.SPAN_STYLE_END + WebAdminResponderThread.END_GROUP_URI + WebAdminResponderThread.END_COL + WebAdminResponderThread.END_ROW); } // End of For Loop. sb.append(WebAdminResponderThread.END_TABLE); // ***************************** // Add the Operation Pull Down sb.append(WebAdminResponderThread.HTML_OPERATION_FORM_BEGIN); // ********************************** // Add a Static Local Status Command if (showlocalreturn) { sb.append( WebAdminResponderThread.buildOperationValue( WebAdminResponderThread.LOCAL_VALUE, WebAdminResponderThread.LOCAL_NAME)); sb.append( WebAdminResponderThread.buildOperationValue( WebAdminResponderThread.LOCAL_REFRESH_VALUE, WebAdminResponderThread.LOCAL_REFRESH_NAME)); } // End of if to show local return. // ************************** // Loop to Create Option List for (int i = 0; i < vector.size(); i++) { sb.append( WebAdminResponderThread.buildOperationValues( vector.get(i).toString())); } // End of For Loop to Build Available Operations. // ************************************ // Add a Static SHUTDOWN-ALL Command sb.append( WebAdminResponderThread.buildOperationValue( WebAdminResponderThread.SHUTDOWNALL_VALUE, WebAdminResponderThread.SHUTDOWNALL_NAME)); // ****************************** // Finish off the Pull Down Form // and group membership area. sb.append(WebAdminResponderThread.HTML_OPERATION_FORM_END); sb.append(WebAdminResponderThread.HTML_GROUP_MEMBERSHIP_END); // ************************ // Return String. return sb.toString(); } // End of buildMembershipList /** * Build Operation Pull Down Value. * * @param paddrname * @return String -- HTML Data */ private static String buildOperationValues(final String paddrname) { StringBuffer sb = new StringBuffer(); for (int j = 0; j < WebAdminResponderThread.OPERATION_COMMAND_ARRAY.length; j++) { sb.append( WebAdminResponderThread.buildOperationValue( WebAdminResponderThread.OPERATION_COMMAND_ARRAY[j] + " " + paddrname, WebAdminResponderThread.OPERATION_NAME_ARRAY[j] + " for " + paddrname)); } // End of For Loop. // ************************* // Return the String Buffer return sb.toString(); } // End of buildOperationValue private Method. /** * Build Operation Pull Down Value. * * @param value * @param name * @return String -- HTML Data */ private static String buildOperationValue(final String value, final String name) { if ((value != null) && ((value.equalsIgnoreCase(WebAdminResponderThread.LOCAL_VALUE)) || (value.equalsIgnoreCase(WebAdminResponderThread.LOCAL_REFRESH_VALUE)))) { return WebAdminResponderThread.zapHtml( new String[]{value, name}, WebAdminResponderThread.HTML_OPERATION_VALUE); } else { return WebAdminResponderThread.zapHtml( new String[]{WebAdminResponderThread.OPERATION_NAME + value, name}, WebAdminResponderThread.HTML_OPERATION_VALUE); } // end of Else. } // End of buildOperationValue private Method. /** * Perform Replacement of Parametrized Values in Static HTML. * * @param sarray * @param html * @return String of zapped Html. */ private static String zapHtml(final String[] sarray, final String html) { if ((sarray == null) || (sarray.length == 0)) { return ""; } // ***************************************** // Formulate a new Parameterized HTML Value. StringBuffer sb = new StringBuffer(html); for (int p = 0; p < sarray.length; p++) { int i = sb.indexOf("%" + Integer.toString(p + 1)); if (i > 0) { sb.replace(i, (i + 2), sarray[p]); } // End of Zap. } // End of For Loop. // ******************************* // Return Option HTML return sb.toString(); } // End of zapHtml. /** * Helper Method to Generate The MemberShip List HTML. * * @return String of HTML Formatted List. */ private String generateMemberShipList() { StringBuffer sb = new StringBuffer(); sb.append( WebAdminResponderThread.buildMembershipList( this.CONTROL_THREAD.members, true)); sb.append(WebAdminResponderThread.HR); // *************** // Return return sb.toString(); } // End of private Helper Method. /** * Provides Logging of Stack Traces from Exceptions detail on the Nested Stack Trace * from Severe Runtime Errors. */ private void logStackTrace(final Exception e, final String METHODNAME) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); pw.close(); FrameworkLogger.log(CLASSNAME, METHODNAME, FrameworkLoggerLevel.SEVERE, ErrorConstants.WEBADMIN_RESPONDER_EXCEPTION, new String[]{e.getMessage(), sw.toString()}); } // End of logStackTrace private Method. } ///:~ End of WebAdminResponderThread Class.