001/*
002 * (C) Copyright 2011 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     Julien Carsique
016 *
017 */
018
019package org.nuxeo.launcher.monitoring;
020
021import java.io.BufferedReader;
022import java.io.BufferedWriter;
023import java.io.IOException;
024import java.io.InputStreamReader;
025import java.io.OutputStreamWriter;
026import java.net.HttpURLConnection;
027import java.net.MalformedURLException;
028import java.net.SocketTimeoutException;
029import java.net.URL;
030
031import org.apache.commons.io.IOUtils;
032import org.apache.commons.logging.Log;
033import org.apache.commons.logging.LogFactory;
034import org.nuxeo.launcher.config.ConfigurationGenerator;
035
036/**
037 * HTTP client monitoring Nuxeo server starting status
038 *
039 * @see org.nuxeo.ecm.core.management.statuses.StatusServlet
040 * @since 5.5
041 */
042public class StatusServletClient {
043
044    private static final Log log = LogFactory.getLog(StatusServletClient.class);
045
046    protected static final String URL_PATTERN = "runningstatus";
047
048    protected static final String POST_PARAM = "info";
049
050    protected static final String POST_PARAM_STARTED = "started";
051
052    protected static final String POST_PARAM_SUMMARY = "summary";
053
054    private static final int TIMEOUT = 1000;
055
056    private static final int SUMMARY_TIMEOUT = 2000;
057
058    private URL url;
059
060    private HttpURLConnection server;
061
062    private int timeout;
063
064    private boolean startupFine = false;
065
066    private String key;
067
068    /**
069     * Set secure key used for connection
070     *
071     * @param key any {@link String}
072     */
073    public void setKey(String key) {
074        this.key = key;
075    }
076
077    /**
078     * @param configurationGenerator
079     */
080    public StatusServletClient(ConfigurationGenerator configurationGenerator) {
081        final String servletURL = configurationGenerator.getUserConfig().getProperty(
082                ConfigurationGenerator.PARAM_LOOPBACK_URL)
083                + "/" + URL_PATTERN;
084        try {
085            url = new URL(servletURL);
086        } catch (MalformedURLException e) {
087            log.error("Malformed URL: " + servletURL, e);
088        }
089    }
090
091    /**
092     * @return true if Nuxeo finished starting
093     * @throws SocketTimeoutException
094     */
095    public boolean isStarted() throws SocketTimeoutException {
096        timeout = TIMEOUT;
097        return post(POST_PARAM, POST_PARAM_STARTED);
098    }
099
100    /**
101     * @param postParam
102     * @param postParamStarted
103     * @return
104     * @throws SocketTimeoutException
105     */
106    private boolean post(String postParam, String postParamStarted) throws SocketTimeoutException {
107        return post(postParam, postParamStarted, null);
108    }
109
110    /**
111     * @return true if succeed to connect on StatusServlet
112     * @throws SocketTimeoutException
113     */
114    public boolean init() throws SocketTimeoutException {
115        try {
116            timeout = TIMEOUT;
117            connect("GET");
118        } catch (SocketTimeoutException e) {
119            throw e;
120        } catch (IOException e) {
121            return false;
122        } finally {
123            disconnect();
124        }
125        return true;
126    }
127
128    protected synchronized void disconnect() {
129        if (server != null) {
130            server.disconnect();
131            server = null;
132        }
133        notifyAll();
134    }
135
136    protected synchronized void connect(String method) throws IOException {
137        while (server != null) {
138            try {
139                wait();
140            } catch (InterruptedException e) {
141                // do nothing
142            }
143        }
144        server = (HttpURLConnection) url.openConnection();
145        server.setConnectTimeout(timeout);
146        server.setReadTimeout(timeout);
147        server.setDoInput(true);
148        server.setDoOutput(true);
149        server.setRequestMethod(method);
150        server.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
151        server.connect();
152    }
153
154    /**
155     * @return Nuxeo server startup summary (components loading status)
156     * @throws SocketTimeoutException
157     */
158    public String getStartupSummary() throws SocketTimeoutException {
159        timeout = SUMMARY_TIMEOUT;
160        StringBuilder sb = new StringBuilder();
161        startupFine = post(POST_PARAM, POST_PARAM_SUMMARY, sb);
162        return sb.toString();
163    }
164
165    protected boolean post(String param, String value, StringBuilder response) throws SocketTimeoutException {
166        String post = param + "=" + value;
167        post += "&key=" + key;
168        try {
169            connect("POST");
170            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(server.getOutputStream()));
171            bw.write(post, 0, post.length());
172            bw.close();
173            return getResponse(response);
174        } catch (SocketTimeoutException e) {
175            throw e;
176        } catch (IOException e) {
177            return false;
178        } finally {
179            disconnect();
180        }
181    }
182
183    protected boolean getResponse(StringBuilder response) throws IOException {
184        String line;
185        boolean answer;
186        BufferedReader s = null;
187        try {
188            s = new BufferedReader(new InputStreamReader(server.getInputStream()));
189            // First line is a status (true or false)
190            answer = Boolean.parseBoolean(s.readLine());
191            // Next (if exists) is a response body
192            while ((line = s.readLine()) != null) {
193                if (response != null) {
194                    response.append(line + "\n");
195                }
196            }
197        } catch (IOException e) {
198            throw e;
199        } finally {
200            IOUtils.closeQuietly(s);
201        }
202        return answer;
203    }
204
205    /**
206     * Return detected status of Nuxeo server by last call to {@link #getStartupSummary()}
207     *
208     * @return true if everything is fine; false is there was any error or status is unknown
209     */
210    public boolean isStartupFine() {
211        return startupFine;
212    }
213}