/** * Copyright (c) 2010-2016 by the respective copyright holders. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.openhab.binding.frontiersiliconradio.internal; import java.io.IOException; import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class holds the http-connection and session information for controlling the radio. * * @author Rainer Ostendorf * @author paphko * @since 1.7.0 */ public class FrontierSiliconRadioConnection { private static final Logger logger = LoggerFactory.getLogger(FrontierSiliconRadioConnection.class); /** Timeout for HTTP requests. */ private final static int SOCKET_TIMEOUT = 5000; // ms /** Hostname of the radio. */ private final String hostname; /** Port number, usually 80. */ private final int port; /** Access pin, passed upon login as GET parameter. */ private final String pin; /** The session ID we get from the radio after logging in. */ String sessionId; /** http clients, store cookies, so it is kept in connection class. */ HttpClient httpClient = null; /** Flag indicating if we are successfully logged in. */ private boolean isLoggedIn = false; public FrontierSiliconRadioConnection(String hostname, int port, String pin) { this.hostname = hostname; this.port = port; this.pin = pin; } /** * Perform login/establish a new session. Uses the PIN number and when successful saves the assigned sessionID for * future requests. * * @return <code>true</code> if login was successful; <code>false</code> otherwise. */ public boolean doLogin() { isLoggedIn = false; // reset login flag if (httpClient == null) { httpClient = new HttpClient(); } final String url = "http://" + hostname + ":" + port + "/fsapi/CREATE_SESSION?pin=" + pin; logger.trace("opening URL:" + url); final HttpMethod method = new GetMethod(url); method.getParams().setSoTimeout(SOCKET_TIMEOUT); method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)); try { final int statusCode = httpClient.executeMethod(method); if (statusCode != HttpStatus.SC_OK) { logger.warn("Method failed: " + method.getStatusLine()); } final String responseBody = IOUtils.toString(method.getResponseBodyAsStream()); if (!responseBody.isEmpty()) { logger.trace("login response: " + responseBody); } try { final FrontierSiliconRadioApiResult result = new FrontierSiliconRadioApiResult(responseBody); if (result.isStatusOk()) { logger.trace("login successful"); sessionId = result.getSessionId(); isLoggedIn = true; return true; // login successful :-) } } catch (Exception e) { logger.error("Parsing response failed"); } } catch (HttpException he) { logger.error("Fatal protocol violation: {}", he.toString()); } catch (IOException ioe) { logger.error("Fatal transport error: {}", ioe.toString()); } finally { method.releaseConnection(); } return false; // login not successful } /** * Performs a request to the radio with no further parameters. * * Typically used for polling state info. * * @param REST * API requestString, e.g. "GET/netRemote.sys.power" * @return request result */ public FrontierSiliconRadioApiResult doRequest(String requestString) { return doRequest(requestString, null); } /** * Performs a request to the radio with addition parameters. * * Typically used for changing parameters. * * @param REST * API requestString, e.g. "SET/netRemote.sys.power" * @param params * , e.g. "value=1" * @return request result */ public FrontierSiliconRadioApiResult doRequest(String requestString, String params) { // 3 retries upon failure for (int i = 0; i < 3; i++) { if (!isLoggedIn && !doLogin()) { continue; // not logged in and login was not successful - try again! } final String url = "http://" + hostname + ":" + port + "/fsapi/" + requestString + "?pin=" + pin + "&sid=" + sessionId + (params == null || params.trim().length() == 0 ? "" : "&" + params); logger.trace("calling url: '" + url + "'"); final HttpMethod method = new GetMethod(url); method.getParams().setSoTimeout(SOCKET_TIMEOUT); method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)); try { final int statusCode = httpClient.executeMethod(method); if (statusCode != HttpStatus.SC_OK) { logger.warn("Method failed: " + method.getStatusLine()); isLoggedIn = false; method.releaseConnection(); continue; } final String responseBody = IOUtils.toString(method.getResponseBodyAsStream()); if (!responseBody.isEmpty()) { logger.trace("got result: " + responseBody); } else { logger.debug("got empty result"); isLoggedIn = false; method.releaseConnection(); continue; } final FrontierSiliconRadioApiResult result = new FrontierSiliconRadioApiResult(responseBody); if (result.isStatusOk()) { return result; } isLoggedIn = false; method.releaseConnection(); continue; // try again } catch (HttpException he) { logger.error("Fatal protocol violation: {}", he.toString()); isLoggedIn = false; } catch (IOException ioe) { logger.error("Fatal transport error: {}", ioe.toString()); } finally { method.releaseConnection(); } } isLoggedIn = false; // 3 tries failed. log in again next time, maybe our session went invalid (radio restarted?) return null; } }