Java tutorial
/* * OpenRemote, the Home of the Digital Home. * Copyright 2008-2010, OpenRemote Inc. * * See the contributors.txt file in the distribution for a * full listing of individual contributors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.openremote.android.console.net; import java.io.IOException; import java.io.InputStream; import java.net.ConnectException; import java.net.MalformedURLException; import java.net.SocketTimeoutException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.scheme.Scheme; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.openremote.android.console.Constants; import org.openremote.android.console.Main; import org.openremote.android.console.model.AppSettingsModel; import org.openremote.android.console.model.ViewHelper; import org.openremote.android.console.util.SecurityUtil; import org.openremote.android.console.util.StringUtil; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.util.Log; /** * This is responsible for detecting groupmembers and switching to available controller. * * @author handy 2010-04-29 * */ public class ORControllerServerSwitcher { // Constants ------------------------------------------------------------------------------------ /** * Common log category for fail-over functionality. */ public final static String LOG_CATEGORY = Constants.LOG_CATEGORY + "Failover"; private static final String SERIALIZE_GROUP_MEMBERS_FILE_NAME = "group_members"; public static final int SWITCH_CONTROLLER_SUCCESS = 1; public static final int SWITCH_CONTROLLER_FAIL = 2; // Class Members -------------------------------------------------------------------------------- /** * Detect the groupmembers of current server url */ public static boolean detectGroupMembers(Context context) { Log.i(LOG_CATEGORY, "Detecting group members with current controller server url " + AppSettingsModel.getCurrentServer(context)); HttpParams params = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(params, 5 * 1000); HttpConnectionParams.setSoTimeout(params, 5 * 1000); HttpClient httpClient = new DefaultHttpClient(params); String url = AppSettingsModel.getSecuredServer(context); HttpGet httpGet = new HttpGet(url + "/rest/servers"); if (httpGet == null) { Log.e(LOG_CATEGORY, "Create HttpRequest fail."); return false; } SecurityUtil.addCredentialToHttpRequest(context, httpGet); // TODO : fix the exception handling in this method -- it is ridiculous. try { URL uri = new URL(url); if ("https".equals(uri.getProtocol())) { Scheme sch = new Scheme(uri.getProtocol(), new SelfCertificateSSLSocketFactory(context), uri.getPort()); httpClient.getConnectionManager().getSchemeRegistry().register(sch); } HttpResponse httpResponse = httpClient.execute(httpGet); try { if (httpResponse.getStatusLine().getStatusCode() == Constants.HTTP_SUCCESS) { InputStream data = httpResponse.getEntity().getContent(); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document dom = builder.parse(data); Element root = dom.getDocumentElement(); NodeList nodeList = root.getElementsByTagName("server"); int nodeNums = nodeList.getLength(); List<String> groupMembers = new ArrayList<String>(); for (int i = 0; i < nodeNums; i++) { groupMembers.add(nodeList.item(i).getAttributes().getNamedItem("url").getNodeValue()); } Log.i(LOG_CATEGORY, "Detected groupmembers. Groupmembers are " + groupMembers); return saveGroupMembersToFile(context, groupMembers); } catch (IOException e) { Log.e(LOG_CATEGORY, "The data is from ORConnection is bad", e); } catch (ParserConfigurationException e) { Log.e(LOG_CATEGORY, "Cant build new Document builder", e); } catch (SAXException e) { Log.e(LOG_CATEGORY, "Parse data error", e); } } else { Log.e(LOG_CATEGORY, "detectGroupMembers Parse data error"); } } catch (IllegalStateException e) { Log.e(LOG_CATEGORY, "detectGroupMembers Parse data error", e); } catch (IOException e) { Log.e(LOG_CATEGORY, "detectGroupMembers Parse data error", e); } } catch (MalformedURLException e) { Log.e(LOG_CATEGORY, "Create URL fail:" + url); } catch (ConnectException e) { Log.e(LOG_CATEGORY, "Connection refused: " + AppSettingsModel.getCurrentServer(context), e); } catch (ClientProtocolException e) { Log.e(LOG_CATEGORY, "Can't Detect groupmembers with current controller server " + AppSettingsModel.getCurrentServer(context), e); } catch (SocketTimeoutException e) { Log.e(LOG_CATEGORY, "Can't Detect groupmembers with current controller server " + AppSettingsModel.getCurrentServer(context), e); } catch (IOException e) { Log.e(LOG_CATEGORY, "Can't Detect groupmembers with current controller server " + AppSettingsModel.getCurrentServer(context), e); } catch (IllegalArgumentException e) { Log.e(LOG_CATEGORY, "Host name can be null :" + AppSettingsModel.getCurrentServer(context), e); } return false; } /** * Serialize the groupmembers into file named group_members.xml . * * @param context global Android application context * @param groupMembers controller cluster group members * * @return true if save was successful, false otherwise */ private static boolean saveGroupMembersToFile(Context context, List<String> groupMembers) { // TODO: Use URL class instead of String SharedPreferences.Editor editor = context.getSharedPreferences(SERIALIZE_GROUP_MEMBERS_FILE_NAME, 0).edit(); editor.clear(); editor.commit(); for (int i = 0; i < groupMembers.size(); i++) { editor.putString(i + "", groupMembers.get(i)); } return editor.commit(); } /** * Get the groupmembers from the file group_members.xml . * * @param context global Android application context * * @return list of controller URLs */ @SuppressWarnings("unchecked") public static List<String> findAllGroupMembersFromFile(Context context) { // TODO: Use URL in the API instead of strings List<String> groupMembers = new ArrayList<String>(); Map<String, String> groupMembersMap = (Map<String, String>) context .getSharedPreferences(SERIALIZE_GROUP_MEMBERS_FILE_NAME, 0).getAll(); for (int i = 0; i < groupMembersMap.size(); i++) { groupMembers.add(groupMembersMap.get(i + "")); } return groupMembers; } /** * Get a available controller server url and switch to it. * * @param context global Android application context * * @return TODO */ public static int doSwitch(Context context) { String availableGroupMemberURL = getOneAvailableFromGroupMemberURLs(context); List<String> allGroupMembers = findAllGroupMembersFromFile(context); if (availableGroupMemberURL != null && !"".equals(availableGroupMemberURL)) { Log.i(LOG_CATEGORY, "Got a available controller url from groupmembers" + allGroupMembers); switchControllerWithURL(context, availableGroupMemberURL); } else { Log.i(LOG_CATEGORY, "Didn't get a available controller url from groupmembers " + allGroupMembers + ". Try to detect groupmembers again."); if (!detectGroupMembers(context)) { ViewHelper.showAlertViewWithSetting(context, "Update fail", "There's no controller server available. Leave this problem?"); return SWITCH_CONTROLLER_FAIL; } availableGroupMemberURL = getOneAvailableFromGroupMemberURLs(context); if (availableGroupMemberURL != null && !"".equals(availableGroupMemberURL)) { Log.i(LOG_CATEGORY, "Got a available controller url from groupmembers " + allGroupMembers + " in second groupmembers detection attempt."); switchControllerWithURL(context, availableGroupMemberURL); } else { Log.i(LOG_CATEGORY, "There's no controller server available."); ViewHelper.showAlertViewWithSetting(context, "Update fail", "There's no controller server available. Leave this problem?"); return SWITCH_CONTROLLER_FAIL; } } return SWITCH_CONTROLLER_SUCCESS; } /** * Check all groupmembers' url and get a available one, this function deponds on the WIFI network. * * @param context global Android application context * * @return TODO */ private static String getOneAvailableFromGroupMemberURLs(Context context) { // TODO : Use URL class in API List<String> allGroupMembers = findAllGroupMembersFromFile(context); Log.i(LOG_CATEGORY, "Checking a available controller url from groupmembers " + allGroupMembers); for (String controllerServerURL : allGroupMembers) { HttpResponse response = null; try { response = ORNetworkCheck.verifyControllerURL(context, controllerServerURL); } catch (IOException e) { // TODO : // // In case of a fail-over scenario, don't propagate the exception higher up. // The logic depends on null return values and since there are no tests to back it // up, leaving it untouched for now. // // Logging and keeping the null. // [JPL] Log.i("", "TODO: need to refactor this logic to rely on exception instead of null return values"); Log.i("", "Error was " + e.getMessage(), e); } if (response != null && response.getStatusLine().getStatusCode() == Constants.HTTP_SUCCESS) { if (!AppSettingsModel.isAutoMode(context)) { String selectedControllerServerURL = StringUtil .markControllerServerURLSelected(controllerServerURL); String customServerURLs = AppSettingsModel.getCustomServers(context); if (!customServerURLs.contains(selectedControllerServerURL)) { customServerURLs = StringUtil.removeControllerServerURLSelected(customServerURLs); if (customServerURLs.contains(controllerServerURL)) { customServerURLs = customServerURLs.replaceAll(controllerServerURL, selectedControllerServerURL); } else { customServerURLs = customServerURLs + "," + selectedControllerServerURL; } AppSettingsModel.setCustomServers(context, customServerURLs); } } return controllerServerURL; } } return null; } /** * Switch to the controller identified by the availableGroupMemberURL * * @param context global Android application context * @param availableGroupMemberURL TODO */ private static void switchControllerWithURL(Context context, String availableGroupMemberURL) { if (availableGroupMemberURL.equals(AppSettingsModel.getCurrentServer(context))) { Log.i(LOG_CATEGORY, "The current server is already: " + availableGroupMemberURL + ", should not switch to self."); return; } Main.prepareToastForSwitchingController(); Log.i(LOG_CATEGORY, "ControllerServerSwitcher is switching controller to " + availableGroupMemberURL); AppSettingsModel.setCurrentServer(context, availableGroupMemberURL); Intent intent = new Intent(); intent.setClass(context, Main.class); context.startActivity(intent); } }