All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.roboconf.agent.monitoring.internal.rest.RestHandler Maven / Gradle / Ivy

/**
 * Copyright 2014-2016 Linagora, Université Joseph Fourier, Floralis
 *
 * The present code is developed in the scope of the joint LINAGORA -
 * Université Joseph Fourier - Floralis research program and is designated
 * as a "Result" pursuant to the terms and conditions of the LINAGORA
 * - Université Joseph Fourier - Floralis research program. Each copyright
 * holder of Results enumerated here above fully & independently holds complete
 * ownership of the complete Intellectual Property rights applicable to the whole
 * of said Results, and may freely exploit it in any manner which does not infringe
 * the moral rights of the other copyright holders.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.roboconf.agent.monitoring.internal.rest;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import net.roboconf.agent.monitoring.api.IMonitoringHandler;
import net.roboconf.core.model.beans.Instance;
import net.roboconf.core.utils.Utils;
import net.roboconf.messaging.api.messages.from_agent_to_dm.MsgNotifAutonomic;

/**
 * Handler to check the value returned by a REST call on a URL.
 * @author Pierre-Yves Gibello - Linagora
 */
public class RestHandler implements IMonitoringHandler {

	static final String HANDLER_NAME = "rest";

	private static final String USER_AGENT = "Mozilla/34.0";
	private static final String CHECK = "check";
	private static final String THAT = "that";
	private static final String CONDITION_PATTERN = "(\\w+)\\s+(==|=|>=|>|<=|<)\\s+(\\S+)";
	private static final String WHOLE_PATTERN = CHECK + "\\s+(\\S+)\\s+" + THAT + "\\s+" + CONDITION_PATTERN;

	private final Logger logger = Logger.getLogger(getClass().getName());
	private String applicationName, scopedInstancePath, eventId;
	String url, conditionParameter, conditionOperator, conditionThreshold;



	@Override
	public String getName() {
		return HANDLER_NAME;
	}


	@Override
	public void setAgentId( String applicationName, String scopedInstancePath ) {
		this.applicationName = applicationName;
		this.scopedInstancePath = scopedInstancePath;
	}


	@Override
	public void reset( Instance associatedInstance, String eventId, String rawRulesText ) {
		this.eventId = eventId;

		Matcher m = Pattern.compile( WHOLE_PATTERN, Pattern.CASE_INSENSITIVE ).matcher( rawRulesText );
		if( m .find()) {
			this.url = m.group( 1 );
			this.conditionParameter = m.group( 2 );
			this.conditionOperator = m.group( 3 );
			this.conditionThreshold = m.group( 4 );

		} else {
			this.logger.severe( "Invalid content for the 'rest' handler in the agent's monitoring." );
		}
	}


	@Override
	public MsgNotifAutonomic process() {

		MsgNotifAutonomic result = null;
		String response = null;
		if( this.url != null )
			response = this.url.startsWith("https:") ? httpsQuery() : httpQuery();

		if( response != null )
			response = response.replace('{', ' ').replace('}', ' ').trim();
		else
			response = "";

		HashMap map = new HashMap ();
		for( String s : response.split( "\\n" )) {
			String kv[] = s.split(":");
			if( kv.length == 2 )
				map.put( kv[0].replace("\"", " ").trim(), kv[ 1 ]);
			else
				break;
		}

		if( map.isEmpty()) {
			this.logger.warning( "The REST response could not be parsed." );
			this.logger.finer( "Received response: " + response );

		} else if( evalCondition( map )) {
			result = new MsgNotifAutonomic( this.applicationName, this.scopedInstancePath, this.eventId, response.toString());
		}

		return result;
	}


	/**
	 * Evaluates a condition (eg. "lag>=100") using data in key-pair value map (e.g. <"lag","50">).
	 * @param valueMap The values (key-pairs) on which to evaluate the condition.
	 * @return true if the condition is met, false otherwise
	 */
	boolean evalCondition( Map map ) {

		boolean result = false;
		String value = map.get( this.conditionParameter );
		if( value != null ) {
			try {
				Double doubleValue = Double.parseDouble( value );
				Double thresholdValue = Double.parseDouble( this.conditionThreshold );

				// Do not use arithmetic operators with doubles...
				int comparison = doubleValue.compareTo( thresholdValue );
				if( ">".equals( this.conditionOperator ))
					result = comparison > 0;
				else if( ">=".equals( this.conditionOperator ))
					result = comparison >= 0;
				else if( "<".equals( this.conditionOperator ))
					result = comparison < 0;
				else if( "<=".equals( this.conditionOperator ))
					result = comparison <= 0;
				else
					result = comparison == 0;

			} catch( NumberFormatException e ) {
				if( "==".equals( this.conditionOperator ) || "=".equals( this.conditionOperator ))
					result = Objects.equals( value, this.conditionThreshold );
				else
					this.logger.fine( "Invalid double. " + e.getMessage());
			}
		}

		return result;
	}

	/**
	 * Query a https URL, ignoring certificates.
	 * @return The query response
	 */
	private String httpsQuery() {

		String response = null;
		try {
			// Create a trust manager that does not validate certificate chains
			TrustManager[] trustAllCerts = new TrustManager[] { new LocalX509TrustManager()};

			// Install the all-trusting trust manager
			final SSLContext sc = SSLContext.getInstance("SSL");
			sc.init( null, trustAllCerts, new java.security.SecureRandom());
			HttpsURLConnection.setDefaultSSLSocketFactory( sc.getSocketFactory());

			// Create all-trusting host name verifier
			HostnameVerifier allHostsValid = new LocalHostnameVerifier();
			HttpsURLConnection.setDefaultHostnameVerifier( allHostsValid );

			URL restUrl = new URL( this.url );
			HttpsURLConnection conn = (HttpsURLConnection) restUrl.openConnection();
			response = query( conn );

		} catch( Exception e ) {
			this.logger.severe( "Cannot issue GET on URL " + this.url + ". Monitoring notification is discarded." );
			Utils.logException(this.logger, e);
		}

		return response;
	}

	/**
	 * Query a http URL.
	 * @return The query response
	 */
	private String httpQuery() {

		String response = null;
		try {
			URL restUrl = new URL( this.url );
			HttpURLConnection conn = (HttpURLConnection) restUrl.openConnection();
			response = query( conn );

		} catch( Exception e ) {
			this.logger.severe( "Cannot issue GET on URL " + this.url + ". Monitoring notification is discarded." );
			Utils.logException(this.logger, e);
		}

		return response;
	}


	private String query( HttpURLConnection conn ) throws IOException {

		conn.setRequestMethod( "GET" );
		conn.setRequestProperty( "User-Agent", USER_AGENT );

		ByteArrayOutputStream os = new ByteArrayOutputStream();
		InputStream in = conn.getInputStream();
		Utils.copyStreamSafely( in, os );

		return os.toString("UTF-8");
	}


	/**
	 * @author Pierre-Yves Gibello - Linagora
	 */
	static class LocalX509TrustManager implements X509TrustManager {
		@Override
		public java.security.cert.X509Certificate[] getAcceptedIssuers() {
			return null;
		}

		@Override
		public void checkClientTrusted(X509Certificate[] certs, String authType) {
			// nothing
		}

		@Override
		public void checkServerTrusted(X509Certificate[] certs, String authType) {
			// nothing
		}
	}


	/**
	 * @author Pierre-Yves Gibello - Linagora
	 */
	static class LocalHostnameVerifier implements HostnameVerifier {
		@Override
		public boolean verify(String hostname, SSLSession session) {
			// Never verify
			return false;
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy