Java tutorial
/** * Helios, OpenSource Monitoring * Brought to you by the Helios Development Group * * Copyright 2007, Helios Development Group and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * */ package org.helios.collector.url; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.protocol.Protocol; import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import org.apache.commons.ssl.KeyMaterial; import org.helios.collector.core.CollectionResult; import org.helios.collector.core.CollectorException; import org.helios.collector.core.SocketAbstractCollector; import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedResource; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.security.GeneralSecurityException; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; /** * * <p>Title: URLCollector </p> * <p>Description: Checks URL end points and traces availability and other statistics.</p> * <p>Company: Helios Development Group</p> * @author Sandeep Malhotra (smalhotra@heliosdev.org) */ @ManagedResource public class URLCollector extends SocketAbstractCollector { /** Endpoint URL that collector needs to monitor */ protected URL url = null; /** Extracted value for host from endpoint URL */ private String host = null; /** Endpooint port */ private int port = 80; /** Timeout in milliseconds for initial HTTP/S connection*/ protected int timeout = 5000; /** String pattern to be matched match in response to determine endpoint availability*/ protected String successContentMatch = null; /** * String pattern to be matched in response to determine endpoint availability. * If both success and failure patterns are provided, and they both match then * result of a failure match would determine the overall availability. * * some examples: * success pattern = matched, failure pattern: not specified - Availability = true * success pattern = not specified, failure pattern: matched - Availability = false * success pattern = matched, failure pattern = matched - Availability = false */ protected String failureContentMatch = null; /** Success Pattern */ protected Pattern successContentPattern = null; /** Failure Pattern */ protected Pattern failureContentPattern = null; /** Flag to indicate availability of the endpoint */ protected boolean available = false; /** Possible Auth_Types for an endpoint */ protected enum AUTH_TYPE { NONE, BASIC, CLIENT_CERT } /** Auth_Type for the current endpoint */ protected AUTH_TYPE authType = AUTH_TYPE.NONE; /** User Name for BASIC Auth_Type */ protected String userName = null; /** Password for BASIC Auth_Type */ protected String password = null; /** KeyStore file location for CLIENT-CERT Auth_Type */ protected String keyStoreLocation = null; /** Passphrase for keystore file for CLIENT-CERT Auth_Type */ protected String keyStorePassphrase = null; /** Http CLient object */ protected HttpClient httpClient = null; /** Reference to HTTP GET method */ protected GetMethod getMethod = null; /** Reference to HTTP POST method */ protected PostMethod postMethod = null; /** Default protocol for SSL endpoints */ protected static final String HTTPS_PROTOCOL = "https"; /** Default port for SSL endpoints */ protected static final int DEFAULT_SSL_PORT = 443; /** Internal counter used to create custom HTTPS protocol for CLIENT-CERT endpoints*/ private static AtomicInteger uniqueCounter = new AtomicInteger(0); /** Custom prefix for SSL CLIENT-CERT endpoints */ protected String myProtocolPrefix = null; /** Whether it's SOAP or REST style */ protected String wsStyle = "REST"; /** Flag to indicate whether the current endpoint is web service or not*/ protected boolean isWebServiceEndpoint = false; /** URL collector version */ private static final String URL_COLLECTOR_VERSION = "0.1"; private static boolean isSSLFactoryInitialized = false; private static final int BYTES_TO_READ = 3000; /** * This static block re-registers HTTPS protocol with EasySSLProtocolSocketFactory to * trust web sites that presents self-signed certificates. */ static { try { EasySSLProtocolSocketFactory easySSLPSFactory = new EasySSLProtocolSocketFactory(); Protocol httpsProtocol = new Protocol(HTTPS_PROTOCOL, (ProtocolSocketFactory) easySSLPSFactory, DEFAULT_SSL_PORT); Protocol.registerProtocol(HTTPS_PROTOCOL, httpsProtocol); isSSLFactoryInitialized = true; } catch (Exception ex) { isSSLFactoryInitialized = false; } } /** * Only constructor for URLCollector class */ public URLCollector(String url) { super(); setUrl(url); } /** * Implementation of abstract method in Base class (AbstractCollector) for tasks * that needs to be done before this collector is started. */ public void startCollector() throws CollectorException { if (!isSSLFactoryInitialized) { throw new CollectorException( "An error occured while initializing EasySSLProtocolSocketFactory: " + this.getBeanName()); } if (this.url != null && getHost() != null && getPort() > 0) { httpClient = new HttpClient(); if (getPortTunnel() != null) { StringBuilder newUrl = new StringBuilder(url.getProtocol() + "://" + getPortTunnel().getLocalHostName() + ":" + getPortTunnel().getLocalPort()); newUrl.append(url.getPath() == null ? "" : url.getPath()); newUrl.append(url.getQuery() == null ? "" : "?" + url.getQuery()); info("$$$$$$$$$$$$ Port tunnel is active so new URL is: " + newUrl); try { this.url = new URL(newUrl.toString()); } catch (MalformedURLException muex) { throw new CollectorException( "An error occured while recreating new URL based on port tunnel provided: " + newUrl, muex); } } initializeHttpMethod(this.url.toString()); /** * check whether call to initializeHttpMethod resulted in any issue. * If yes, then that method would have set the CollectorState to * START_FAILED, so just return without any further processing. */ if (getState() == CollectorState.START_FAILED) { throw new CollectorException( "Endpoint style is either missing or invalid for web service collector bean: " + this.getBeanName()); } httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(timeout); } else { error("Invalid URL string provided for collector bean: " + this.getBeanName()); throw new CollectorException("Invalid URL string provided for collector bean: " + this.getBeanName()); } if (authType == AUTH_TYPE.BASIC) { if (userName != null && !(userName.trim().length() == 0) && password != null && !(password.trim().length() == 0)) { Credentials credentials = new UsernamePasswordCredentials(userName, password); httpClient.getState().setCredentials(new AuthScope(host, port), credentials); //httpClient.getParams().setAuthenticationPreemptive(true); if (getMethod != null) { getMethod.setDoAuthentication(true); } else { postMethod.setDoAuthentication(true); } } else { error("Check username and password provided for collector bean: " + this.getBeanName()); throw new CollectorException( "Check username and password provided for collector bean: " + this.getBeanName()); } } else if (authType == AUTH_TYPE.CLIENT_CERT) { if (keyStoreLocation != null && !(keyStoreLocation.trim().length() == 0) && keyStorePassphrase != null && !(keyStorePassphrase.trim().length() == 0)) { try { registerProtocolCertificate(); httpClient.getHostConfiguration().setHost(host, port, Protocol.getProtocol(myProtocolPrefix)); //trace("URL with custom protocol is: "+this.url.toString().replace(HTTPS_PROTOCOL, myProtocolPrefix)); initializeHttpMethod(this.url.toString().replace(HTTPS_PROTOCOL, myProtocolPrefix)); /** * check whether call to initializeHttpMethod resulted in any issue. * If yes, then that method would have set the CollectorState to * START_FAILED, so just return without any further processing. */ if (getState() == CollectorState.START_FAILED) { throw new CollectorException( "Endpoint style is either missing or invalid for web service collector bean: " + this.getBeanName()); } } catch (Exception ex) { error("Unable to register secure protocol for URL [ " + this.url + " ] of bean: " + this.getBeanName()); throw new CollectorException("Unable to register secure protocol for URL [ " + this.url + " ] of bean: " + this.getBeanName(), ex); } } else { error("KeyStoreLocation and/or KeyStorePassphrase is missing for a secure URL of bean: " + this.getBeanName()); throw new CollectorException( "KeyStoreLocation and/or KeyStorePassphrase is missing for a secure URL of bean: " + this.getBeanName()); } } trace("Object [ " + getObjectName() + " ]" + getState()); } private void initializeHttpMethod(String url) throws CollectorException { try { if (!isWebServiceEndpoint) { getMethod = new GetMethod(url); } else { if (wsStyle.equalsIgnoreCase("REST")) { getMethod = new GetMethod(url); } else if (wsStyle.equalsIgnoreCase("SOAP")) { postMethod = new PostMethod(url); } else { error("Endpoint style is either missing or invalid for web service collector bean: " + this.getBeanName()); this.state = CollectorState.START_FAILED; } } } catch (IllegalArgumentException iaex) { error("Invalid URI passed for collector bean: " + this.getBeanName() + " - " + url); throw new CollectorException( "Invalid URI passed for collector bean: " + this.getBeanName() + " - " + url, iaex); } catch (IllegalStateException isex) { error("Unrecognized protocol for URI passed for collector bean: " + this.getBeanName() + " - " + url); throw new CollectorException( "Unrecognized protocol for URI passed for collector bean: " + this.getBeanName() + " - " + url, isex); } } /** * This method does the following: * 1. Creates a new and unique protocol for each SSL URL that is secured by client certificate * 2. Bind keyStore related information to this protocol * 3. Registers it with HTTP Protocol object * 4. Stores the local reference for this custom protocol for use during furture collect calls * * @throws Exception */ public void registerProtocolCertificate() throws Exception { EasySSLProtocolSocketFactory easySSLPSFactory = new EasySSLProtocolSocketFactory(); easySSLPSFactory.setKeyMaterial(createKeyMaterial()); myProtocolPrefix = (HTTPS_PROTOCOL + uniqueCounter.incrementAndGet()); Protocol httpsProtocol = new Protocol(myProtocolPrefix, (ProtocolSocketFactory) easySSLPSFactory, port); Protocol.registerProtocol(myProtocolPrefix, httpsProtocol); trace("Protocol [ " + myProtocolPrefix + " ] registered for the first time"); } /** * Load keystore for CLIENT-CERT protected endpoints * * @return * @throws GeneralSecurityException * @throws Exception */ private KeyMaterial createKeyMaterial() throws GeneralSecurityException, Exception { KeyMaterial km = null; char[] password = keyStorePassphrase.toCharArray(); File f = new File(keyStoreLocation); if (f.exists()) { try { km = new KeyMaterial(keyStoreLocation, password); trace("Keystore location is: " + keyStoreLocation + ""); } catch (GeneralSecurityException gse) { if (logErrors) { error("Exception occured while loading keystore from the following location: " + keyStoreLocation, gse); throw gse; } } } else { error("Unable to load Keystore from the following location: " + keyStoreLocation); throw new CollectorException( "Unable to load Keystore from the following location: " + keyStoreLocation); } return km; } /** * @return the userName */ @ManagedAttribute public String getUserName() { return userName; } /** * @param userName the userName to set */ public void setUserName(String userName) { this.userName = userName; } /** * @return the password */ public String getPassword() { return password; } /** * @param password the password to set */ public void setPassword(String password) { this.password = password; } /** * @return the url */ @ManagedAttribute public String getUrl() { return this.url.toString(); } /** * @param url to set */ public void setUrl(String url) { try { this.url = new URL(url); host = this.url.getHost(); port = this.url.getPort() == -1 ? this.url.getDefaultPort() : this.url.getPort(); } catch (MalformedURLException muex) { error("Incorrect URL format provided to monitor: [ " + this.url + " ]", muex); } } /** * @return the timeout */ @ManagedAttribute public int getTimeout() { return timeout; } /** * @param timeout the timeout to set */ public void setTimeout(int timeout) { this.timeout = timeout; } /** * @return the successContentMatch */ @ManagedAttribute public String getSuccessContentMatch() { return successContentMatch; } /** * @param successContentMatch the successContentMatch to set */ public void setSuccessContentMatch(String successContentMatch) { this.successContentMatch = successContentMatch; if (successContentMatch != null && successContentMatch.length() > 0) { successContentPattern = Pattern.compile(successContentMatch.trim()); } } /** * @return the failureContentMatch */ @ManagedAttribute public String getFailureContentMatch() { return failureContentMatch; } /** * @param failureContentMatch the failureContentMatch to set */ public void setFailureContentMatch(String failureContentMatch) { this.failureContentMatch = failureContentMatch; if (failureContentMatch != null && failureContentMatch.length() > 0) { failureContentPattern = Pattern.compile(failureContentMatch.trim()); } } /** * @return the available */ @ManagedAttribute public boolean getAvailable() { return available; } /** * @param available the available to set */ public void setAvailable(boolean available) { this.available = available; } /** * @return String version of Helios URLCollector */ @ManagedAttribute public String getCollectorVersion() { return "URLCollector v. " + URL_COLLECTOR_VERSION; } /** * @return the authType */ @ManagedAttribute public AUTH_TYPE getAuthType() { return authType; } /** * @param authType the authType to set */ public void setAuthType(AUTH_TYPE authType) { this.authType = authType; } /** * @return the host */ @ManagedAttribute public String getHost() { return host; } /** * @return the port */ @ManagedAttribute public int getPort() { return port; } /** * @return the keyStoreLocation */ @ManagedAttribute public String getKeyStoreLocation() { return keyStoreLocation; } /** * @param keyStoreLocation the keyStoreLocation to set */ public void setKeyStoreLocation(String keyStoreLocation) { this.keyStoreLocation = keyStoreLocation; } /** * @return the keyStorePassphrase */ @ManagedAttribute public String getKeyStorePassphrase() { return keyStorePassphrase; } /** * @param keyStorePassphrase the keyStorePassphrase to set */ public void setKeyStorePassphrase(String keyStorePassphrase) { this.keyStorePassphrase = keyStorePassphrase; } /** * @return the uniqueCounter */ public int getUniqueCounter() { return uniqueCounter.get(); } /** * @return the myProtocolPrefix */ @ManagedAttribute public String getMyProtocolPrefix() { return myProtocolPrefix; } /** * Implementation of abstract collectCallback method from base class (AbstractCollector) * @return CollectionResult Results of the scheduled URL Monitor */ public CollectionResult collectCallback() { int availability = 0; int httpResponseCode = -1; int contentSize = -1; int successContentMatched = 0; int failureContentMatched = 0; BufferedReader reader = null; CollectionResult result = new CollectionResult(); try { if (httpClient != null) { if (!isWebServiceEndpoint) { httpResponseCode = httpClient.executeMethod(getMethod); trace("HTTP Response Code returned by URL [" + this.url.toString() + "] is: " + httpResponseCode); reader = new BufferedReader(new InputStreamReader(getMethod.getResponseBodyAsStream())); } else { if (wsStyle.equalsIgnoreCase("SOAP")) { httpResponseCode = httpClient.executeMethod(postMethod); reader = new BufferedReader(new InputStreamReader(postMethod.getResponseBodyAsStream())); } else { httpResponseCode = httpClient.executeMethod(getMethod); reader = new BufferedReader(new InputStreamReader(getMethod.getResponseBodyAsStream())); } } if (httpResponseCode != HttpStatus.SC_OK) { availability = 0; throw new CollectorException("HTTP Response Code returned by URL [" + this.url.toString() + "] is: " + httpResponseCode); } else { // Response code is 200 availability = 1; //check for any success or failure patterns if (successContentPattern != null || failureContentPattern != null) { long startT = System.currentTimeMillis(); char[] holder = new char[BYTES_TO_READ]; String firstBucket = ""; String secondBucket = ""; try { int bytesRead = reader.read(holder, 0, BYTES_TO_READ); while (bytesRead != -1) { secondBucket = new String(holder); if (successContentPattern != null && successContentMatched == 0) successContentMatched = successContentPattern .matcher(firstBucket + secondBucket).find() == true ? 1 : 0; if (failureContentPattern != null && failureContentMatched == 0) failureContentMatched = failureContentPattern .matcher(firstBucket + secondBucket).find() == true ? 1 : 0; contentSize += bytesRead; firstBucket = secondBucket; holder = new char[BYTES_TO_READ]; bytesRead = reader.read(holder, 0, BYTES_TO_READ); } //tracer.traceSticky(contentSize, "Content Size", getTracingNameSpace()); tracer.traceGauge(contentSize, "Content Size", getTracingNameSpace()); debug("Time taken for success/failure pattern matcher: " + (System.currentTimeMillis() - startT)); } catch (IOException iox) { debug("An error occured while matching the success/failure pattern..." + iox.getMessage()); } if (failureContentMatched == 1) availability = 0; else if (successContentPattern != null && successContentMatched == 0) availability = 0; } result.setResultForLastCollection(CollectionResult.Result.SUCCESSFUL); } } else { // Either HTTPClient or GetMethod is not initialized properly availability = 0; throw new CollectorException( "Invalid state of HttpClient or GetMethod for location [ " + getUrl() + " ]"); } } catch (Exception ex) { if (logErrors) { error(ex.getMessage(), ex); } result.setResultForLastCollection(CollectionResult.Result.FAILURE); result.setAnyException(ex); return result; } finally { try { if (reader != null) { reader.close(); } tracer.traceGauge(availability, defaultAvailabilityLabel, getTracingNameSpace()); tracer.traceGauge(httpResponseCode, "ResponseCode", getTracingNameSpace()); if (successContentPattern != null) { tracer.traceGauge(successContentMatched, "SuccessContentMatch", getTracingNameSpace()); } if (failureContentPattern != null) { tracer.traceGauge(failureContentMatched, "FailureContentMatch", getTracingNameSpace()); } } catch (Exception ex) { reader = null; } } return result; } /** * Parses response returned by endpoint * @param reader * @return */ // public StringBuilder parseContent(BufferedReader reader){ // if(reader==null){ // return null; // } // StringBuilder tempBuilder = new StringBuilder(); // try{ // String oneLine = reader.readLine(); // while(oneLine!=null){ // trace(oneLine+"\n"); // tempBuilder.append(oneLine); // oneLine=reader.readLine(); // } // }catch(IOException iox){ // tempBuilder=null; // } // return tempBuilder; // } /** * Unregisters any custom Protocol set for this instance */ public void stopCollector() { if (myProtocolPrefix != null) { Protocol.unregisterProtocol(myProtocolPrefix); } if (getMethod != null) { getMethod.releaseConnection(); } else if (postMethod != null) { postMethod.releaseConnection(); } } /** * @return the wsStyle */ @ManagedAttribute public String getWsStyle() { return wsStyle; } /** * Constructs a <code>StringBuilder</code> with all attributes * in name = value format. * * @return a <code>String</code> representation * of this object. */ public String toString() { final String TAB = " "; StringBuilder retValue = new StringBuilder(""); retValue.append("url = " + this.url + TAB); retValue.append("host = " + this.host + TAB); retValue.append("port = " + this.port + TAB); retValue.append("timeout = " + this.timeout + TAB); retValue.append("successContentMatch = " + this.successContentMatch + TAB); retValue.append("failureContentMatch = " + this.failureContentMatch + TAB); retValue.append("available = " + this.available + TAB); retValue.append("authType = " + this.authType + TAB); retValue.append("userName = " + this.userName + TAB); retValue.append("password = " + this.password + TAB); retValue.append("keyStoreLocation = " + this.keyStoreLocation + TAB); retValue.append("keyStorePassphrase = " + this.keyStorePassphrase + TAB); retValue.append("httpClient = " + this.httpClient + TAB); retValue.append("myProtocolPrefix = " + this.myProtocolPrefix + TAB); retValue.append("wsStyle = " + this.wsStyle + TAB); retValue.append("isWebServiceEndpoint = " + this.isWebServiceEndpoint + TAB); retValue.append(" )"); return retValue.toString(); } }