/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.gui.client; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.core.domain.cloud.Server.OperationMode; import org.rhq.core.util.stream.StreamUtil; import org.rhq.enterprise.server.util.LookupUtil; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.FileInputStream; import java.io.IOException; /** * Serves the remote client binary that is stored in the RHQ Server's download area. * This servlet also provides version information regarding the version of the remote * client this servlet serves up. */ @WebServlet(urlPatterns = {"/download", "/version"}, loadOnStartup = 1) public class RemoteClientServlet extends HttpServlet { private static final long serialVersionUID = 1L; // the system property that defines how many concurrent downloads we will allow private static String SYSPROP_CLIENT_DOWNLOADS_LIMIT = "rhq.server.client-downloads-limit"; // if the system property is not set or invalid, this is the default limit for number of concurrent downloads // There is no reason this be heavily downloaded. private static int DEFAULT_CLIENT_DOWNLOADS_LIMIT = 5; // the error code that will be returned if the server has been configured to disable client updates private static final int ERROR_CODE_CLIENT_UPDATE_DISABLED = HttpServletResponse.SC_FORBIDDEN; // the error code that will be returned if the server has too many clients downloading the binary private static final int ERROR_CODE_TOO_MANY_DOWNLOADS = HttpServletResponse.SC_SERVICE_UNAVAILABLE; private static int numActiveDownloads = 0; private Log log = LogFactory.getLog(this.getClass()); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // seeing odd browser caching issues, even though we set Last-Modified. so force no caching for now disableBrowserCache(resp); String servletPath = req.getServletPath(); if (servletPath != null) { if (isServerAcceptingRequests()) { if (servletPath.endsWith("version")) { getVersion(req, resp); } else if (servletPath.endsWith("download")) { try { numActiveDownloads++; getDownload(req, resp); } finally { numActiveDownloads--; } } else { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid servlet path [" + servletPath + "] - please contact administrator"); } } else { sendErrorServerNotAcceptingRequests(resp); } } else { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid servlet path - please contact administrator"); } return; } private void getDownload(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int limit = getDownloadLimit(); if (limit <= 0) { sendErrorDownloadDisabled(resp); return; } else if (limit < numActiveDownloads) { sendErrorTooManyDownloads(resp); return; } try { File zip = LookupUtil.getRemoteClientManager().getRemoteClientBinaryFile(); if (!zip.exists()) { disableBrowserCache(resp); resp.sendError(HttpServletResponse.SC_NOT_FOUND, "Remote Client binary does not exist: " + zip.getName()); return; } resp.setContentType("application/octet-stream"); resp.setHeader("Content-Disposition", "attachment; filename=" + zip.getName()); resp.setContentLength((int) zip.length()); resp.setDateHeader("Last-Modified", zip.lastModified()); FileInputStream zipStream = new FileInputStream(zip); try { StreamUtil.copy(zipStream, resp.getOutputStream(), false); } finally { zipStream.close(); } } catch (Throwable t) { log.error("Failed to stream remote client zip.", t); disableBrowserCache(resp); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to stream remote client zip"); } return; } private void getVersion(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { File versionFile = LookupUtil.getRemoteClientManager().getRemoteClientVersionFile(); resp.setContentType("text/plain"); resp.setDateHeader("Last-Modified", versionFile.lastModified()); FileInputStream stream = new FileInputStream(versionFile); byte[] versionData = StreamUtil.slurp(stream); resp.getOutputStream().write(versionData); } catch (Throwable t) { log.error("Failed to stream version info.", t); disableBrowserCache(resp); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to stream version info"); } return; } private int getDownloadLimit() { String limitStr = System.getProperty(SYSPROP_CLIENT_DOWNLOADS_LIMIT); int limit; try { limit = Integer.parseInt(limitStr); } catch (Exception e) { limit = DEFAULT_CLIENT_DOWNLOADS_LIMIT; log.warn("Remote Client downloads limit system property [" + SYSPROP_CLIENT_DOWNLOADS_LIMIT + "] is either not set or invalid [" + limitStr + "] - limit will be [" + limit + "]."); } return limit; } private void disableBrowserCache(HttpServletResponse resp) { resp.setHeader("Cache-Control", "no-cache, no-store"); resp.setHeader("Expires", "-1"); resp.setHeader("Pragma", "no-cache"); } private void sendErrorServerNotAcceptingRequests(HttpServletResponse resp) throws IOException { disableBrowserCache(resp); resp.sendError(ERROR_CODE_CLIENT_UPDATE_DISABLED, "Server Is Down For Maintenance"); } private void sendErrorDownloadDisabled(HttpServletResponse resp) throws IOException { disableBrowserCache(resp); resp.sendError(ERROR_CODE_CLIENT_UPDATE_DISABLED, "Client Download Has Been Disabled"); } private void sendErrorTooManyDownloads(HttpServletResponse resp) throws IOException { disableBrowserCache(resp); resp.setHeader("Retry-After", "30"); resp.sendError(ERROR_CODE_TOO_MANY_DOWNLOADS, "Maximum limit exceeded - download client later"); } private boolean isServerAcceptingRequests() { try { OperationMode mode = LookupUtil.getServerManager().getServer().getOperationMode(); return mode == OperationMode.NORMAL; } catch (Exception e) { return false; } } }