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