modules/enterprise/gui/agentupdate-war/pom.xml
| 153 +++++
modules/enterprise/gui/agentupdate-war/src/main/java/org/rhq/enterprise/gui/agentupdate/AgentUpdateServlet.java
| 273 +++++++++
modules/enterprise/gui/agentupdate-war/src/main/webapp/index.html
| 5
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
| 6
modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
| 10
modules/enterprise/gui/downloads-war/pom.xml
| 153 +++++
modules/enterprise/gui/downloads-war/src/main/java/org/rhq/enterprise/gui/download/DownloadServlet.java
| 295 ++++++++++
modules/enterprise/gui/pom.xml
| 3
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/agentupdate/AgentUpdateServlet.java
| 271 ---------
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/client/RemoteClientServlet.java
| 196 ------
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/download/DownloadServlet.java
| 293 ---------
modules/enterprise/gui/portal-war/src/main/webapp/WEB-INF/web.xml
| 48 -
modules/enterprise/gui/remote-client-war/pom.xml
| 153 +++++
modules/enterprise/gui/remote-client-war/src/main/java/org/rhq/enterprise/gui/client/RemoteClientServlet.java
| 196 ++++++
modules/enterprise/gui/remote-client-war/src/main/webapp/index.html
| 5
modules/enterprise/server/ear/pom.xml
| 43 +
16 files changed, 1285 insertions(+), 818 deletions(-)
New commits:
commit a91848332427bcf365fccece1dcc6805009e68ad
Author: Jirka Kremser <jkremser(a)redhat.com>
Date: Tue Oct 1 00:58:38 2013 +0200
[BZ 1013489] - It is not possible to download an agent from RHQ server (HTTP Status
404) - Introducing three new simple wars for handling the
"/agentupdate/{download|version}", "/client/{download|version}" and
"/downloads/*". Besides fixing this bug, this commit should help to remove
portal war in the future.
diff --git a/modules/enterprise/gui/agentupdate-war/pom.xml
b/modules/enterprise/gui/agentupdate-war/pom.xml
new file mode 100644
index 0000000..a6a9182
--- /dev/null
+++ b/modules/enterprise/gui/agentupdate-war/pom.xml
@@ -0,0 +1,153 @@
+<?xml version="1.0"?>
+<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-parent</artifactId>
+ <version>4.10.0-SNAPSHOT</version>
+ <relativePath>../../../../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-agentupdate-war</artifactId>
+ <packaging>war</packaging>
+ <name>RHQ Enterprise Agent Update War</name>
+ <description>the RHQ Enterprise Agent Update webapp</description>
+
+ <dependencies>
+ <!-- Internal Deps -->
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-core-domain</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope> <!-- by rhq.ear (as ejb-jar) -->
+ </dependency>
+
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-enterprise-server</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope> <!-- by rhq.ear (as ejb-jar) -->
+ </dependency>
+
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-core-util</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope> <!-- by rhq.ear -->
+ </dependency>
+
+ <!-- Import the Servlet API, we use provided scope as the API is included in
JBoss AS 7 -->
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope> <!-- by JBossAS -->
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <!-- Set the name of the war, used as the context root when the app
+ is deployed -->
+ <finalName>rhq-agentupdate</finalName>
+ <plugins>
+ <plugin>
+ <artifactId>maven-war-plugin</artifactId>
+ <configuration>
+ <failOnMissingWebXml>false</failOnMissingWebXml>
+ <archive>
+ <manifest>
+
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ </manifest>
+ <manifestEntries>
+ <Build-Number>${buildNumber}</Build-Number>
+ </manifestEntries>
+ </archive>
+ <webResources>
+ <resource>
+ <filtering>false</filtering>
+ <directory>${basedir}/src/main/webapp</directory>
+ </resource>
+ </webResources>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>dev</id>
+
+ <properties>
+ <rhq.rootDir>../../../..</rhq.rootDir>
+
<rhq.containerDir>${rhq.rootDir}/${rhq.devContainerServerPath}</rhq.containerDir>
+
<rhq.deploymentName>${project.build.finalName}.war</rhq.deploymentName>
+
<rhq.deploymentDir>${rhq.containerDir}/${rhq.earDeployDir}/${rhq.deploymentName}</rhq.deploymentDir>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>deploy-classes</id>
+ <phase>compile</phase>
+ <configuration>
+ <target>
+ <property name="classes.dir"
location="${rhq.deploymentDir}/WEB-INF/classes" />
+ <echo>*** Copying updated files from target/classes to
${classes.dir}...</echo>
+ <copy todir="${classes.dir}"
verbose="${rhq.verbose}">
+ <fileset dir="target/classes" />
+ </copy>
+ <property name="deployment.dir"
location="${rhq.deploymentDir}" />
+ <echo>*** Copying updated files from
src${file.separator}main${file.separator}webapp${file.separator} to
${deployment.dir}${file.separator}...</echo>
+ <copy todir="${deployment.dir}"
verbose="${rhq.verbose}">
+ <fileset dir="${basedir}/src/main/webapp"
/>
+ </copy>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>deploy</id>
+ <phase>package</phase>
+ <configuration>
+ <target>
+ <property name="deployment.dir"
location="${rhq.deploymentDir}" />
+ <echo>*** Copying updated files from
target${file.separator}${project.build.finalName}${file.separator} to
${deployment.dir}${file.separator}...</echo>
+ <copy todir="${deployment.dir}"
verbose="${rhq.verbose}">
+ <fileset
dir="${basedir}/target/${project.build.finalName}" />
+ </copy>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>undeploy</id>
+ <phase>clean</phase>
+ <configuration>
+ <target>
+ <property name="deployment.dir"
location="${rhq.deploymentDir}" />
+ <echo>*** Deleting
${deployment.dir}${file.separator}...</echo>
+ <delete dir="${deployment.dir}" />
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git
a/modules/enterprise/gui/agentupdate-war/src/main/java/org/rhq/enterprise/gui/agentupdate/AgentUpdateServlet.java
b/modules/enterprise/gui/agentupdate-war/src/main/java/org/rhq/enterprise/gui/agentupdate/AgentUpdateServlet.java
new file mode 100644
index 0000000..a35a9c6
--- /dev/null
+++
b/modules/enterprise/gui/agentupdate-war/src/main/java/org/rhq/enterprise/gui/agentupdate/AgentUpdateServlet.java
@@ -0,0 +1,273 @@
+/*
+ * 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.agentupdate;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
+
+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 org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.rhq.core.domain.cloud.Server.OperationMode;
+import org.rhq.core.domain.common.composite.SystemSetting;
+import org.rhq.core.domain.common.composite.SystemSettings;
+import org.rhq.core.util.exception.ThrowableUtil;
+import org.rhq.core.util.stream.StreamUtil;
+import org.rhq.enterprise.server.core.AgentManagerLocal;
+import org.rhq.enterprise.server.util.LookupUtil;
+
+/**
+ * Serves the agent update binary that is stored in the RHQ Server's download area.
+ * This servlet also provides version information regarding the version of the agent
+ * this servlet serves up as well as versions of agents the RHQ Server supports.
+ */
+@WebServlet(urlPatterns = {"/download", "/version"}, loadOnStartup =
1)
+public class AgentUpdateServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ // the system property that defines how many concurrent downloads we will allow
+ private static String SYSPROP_AGENT_DOWNLOADS_LIMIT =
"rhq.server.agent-downloads-limit";
+
+ // if the system property is not set or invalid, this is the default limit for number
of concurrent downloads
+ private static int DEFAULT_AGENT_DOWNLOADS_LIMIT = 45;
+
+ // the error code that will be returned if the server has been configured to disable
agent updates
+ private static final int ERROR_CODE_AGENT_UPDATE_DISABLED =
HttpServletResponse.SC_FORBIDDEN;
+
+ // the error code that will be returned if the server has too many agents downloading
the agent update binary
+ private static final int ERROR_CODE_TOO_MANY_DOWNLOADS =
HttpServletResponse.SC_SERVICE_UNAVAILABLE;
+
+ private AtomicInteger numActiveDownloads = null;
+
+ private Log log = LogFactory.getLog(this.getClass());
+
+ private AgentManagerLocal agentManager = null;
+
+ private boolean initialized = false;
+
+ @Override
+ public void init() throws ServletException {
+ log.info("Starting the RHQ agent update servlet");
+ numActiveDownloads = new AtomicInteger(0);
+ }
+
+ private synchronized void loadAgentUpdateBinaryInfo() throws ServletException {
+ if (!initialized) {
+ log.info("RHQ agent update servlet is looking up binary file
information...");
+
+ // make sure we have a agent update binary file; log its location
+ try {
+ log.info("Agent Update Binary File: " +
getAgentUpdateBinaryFile());
+ } catch (Throwable t) {
+ log.error("Missing agent update binary file - agents will not be
able to update", t);
+ }
+
+ // make sure we create a version file if we have to by getting the version
file now
+ try {
+ File versionFile = getAgentUpdateVersionFile();
+
+ // log the version info - this also makes sure we can read it back in
+ log.debug(versionFile + ": " + new String(StreamUtil.slurp(new
FileInputStream(versionFile))));
+
+ } catch (Throwable t) {
+ log.error("Cannot determine the agent version information - agents
will not be able to update.", t);
+ }
+
+ initialized = true;
+ }
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
+
+ // lazily initialize the servlet - we do this because when we started deploying
on AS7, our servlets
+ // init() method was being called before the agent SLSB is ready. So we don't
init() this at startup,
+ // rather, we now init this servlet the first time someone requests the agent
update binary file.
+ loadAgentUpdateBinaryInfo();
+
+ // 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.incrementAndGet();
+ getDownload(req, resp);
+ } finally {
+ numActiveDownloads.decrementAndGet();
+ }
+ } 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) {
+ sendErrorAgentUpdateDisabled(resp);
+ return;
+ } else if (limit < numActiveDownloads.get()) {
+ sendErrorTooManyDownloads(resp);
+ return;
+ }
+
+ try {
+ File agentJar = getAgentUpdateBinaryFile();
+ resp.setContentType("application/octet-stream");
+ resp.setHeader("Content-Disposition", "attachment;
filename=" + agentJar.getName());
+ resp.setContentLength((int) agentJar.length());
+ resp.setDateHeader("Last-Modified", agentJar.lastModified());
+
+ FileInputStream agentJarStream = new FileInputStream(agentJar);
+ try {
+ StreamUtil.copy(agentJarStream, resp.getOutputStream(), false);
+ } finally {
+ agentJarStream.close();
+ }
+ } catch (Throwable t) {
+ String clientAddr = getClientAddress(req);
+ log.error("Failed to stream agent jar to remote client [" +
clientAddr + "]: "
+ + ThrowableUtil.getAllMessages(t));
+ disableBrowserCache(resp);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to
stream agent jar");
+ }
+
+ return;
+ }
+
+ private void getVersion(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
+ try {
+ File versionFile = getAgentUpdateVersionFile();
+ 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) {
+ String clientAddr = getClientAddress(req);
+ log.error("Failed to stream version info to remote client [" +
clientAddr + "]: "
+ + ThrowableUtil.getAllMessages(t));
+ disableBrowserCache(resp);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to
stream version info");
+ }
+
+ return;
+ }
+
+ private int getDownloadLimit() {
+ // if the server cloud was configured to disallow updates, return 0
+ SystemSettings systemConfig = LookupUtil.getSystemManager().getSystemSettings(
+ LookupUtil.getSubjectManager().getOverlord());
+ if
(!Boolean.parseBoolean(systemConfig.get(SystemSetting.AGENT_AUTO_UPDATE_ENABLED))) {
+ return 0;
+ }
+
+ String limitStr = System.getProperty(SYSPROP_AGENT_DOWNLOADS_LIMIT);
+ int limit;
+ try {
+ limit = Integer.parseInt(limitStr);
+ } catch (Exception e) {
+ limit = DEFAULT_AGENT_DOWNLOADS_LIMIT;
+ log.warn("Agent downloads limit system property [" +
SYSPROP_AGENT_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_AGENT_UPDATE_DISABLED, "Server Is Down For
Maintenance");
+ }
+
+ private void sendErrorAgentUpdateDisabled(HttpServletResponse resp) throws
IOException {
+ disableBrowserCache(resp);
+ resp.sendError(ERROR_CODE_AGENT_UPDATE_DISABLED, "Agent Updates 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 agent later");
+ }
+
+ private File getAgentUpdateVersionFile() throws Exception {
+ return getAgentManager().getAgentUpdateVersionFile();
+ }
+
+ private File getAgentUpdateBinaryFile() throws Exception {
+ return getAgentManager().getAgentUpdateBinaryFile();
+ }
+
+ private AgentManagerLocal getAgentManager() {
+ if (this.agentManager == null) {
+ this.agentManager = LookupUtil.getAgentManager();
+ }
+ return this.agentManager;
+ }
+
+ private boolean isServerAcceptingRequests() {
+ try {
+ OperationMode mode =
LookupUtil.getServerManager().getServer().getOperationMode();
+ return mode == OperationMode.NORMAL;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private String getClientAddress(HttpServletRequest request) {
+ String ip = request.getHeader("X-Forwarded-For");
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
+ ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+ if (ip == null || ip.length() == 0 ||
"unknown".equalsIgnoreCase(ip)) {
+ ip = String.format("%s (%s)", request.getRemoteHost(),
request.getRemoteAddr());
+ }
+ }
+ return ip;
+ }
+}
diff --git a/modules/enterprise/gui/agentupdate-war/src/main/webapp/index.html
b/modules/enterprise/gui/agentupdate-war/src/main/webapp/index.html
new file mode 100644
index 0000000..5d7a115
--- /dev/null
+++ b/modules/enterprise/gui/agentupdate-war/src/main/webapp/index.html
@@ -0,0 +1,5 @@
+<html>
+ <head>
+ <meta http-equiv="Refresh" content="0; URL=version">
+ </head>
+</html>
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
index 5f3b9b6..ed966c2 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/client/admin/DownloadsView.java
@@ -53,8 +53,6 @@ public class DownloadsView extends EnhancedVLayout {
public static final String VIEW_PATH = AdministrationView.VIEW_ID + "/"
+ AdministrationView.SECTION_CONFIGURATION_VIEW_ID + "/" + VIEW_ID;
- private static final String PORTAL_WAR_CONTEXT = "/portal";
-
private final SystemGWTServiceAsync systemManager =
GWTServiceLookup.getSystemService();
private SectionStack sectionStack;
@@ -145,7 +143,7 @@ public class DownloadsView extends EnhancedVLayout {
StaticTextItem linkItem = new StaticTextItem("agentLink");
linkItem.setTitle(MSG.common_label_link());
- linkItem.setValue("<a href=\"" + PORTAL_WAR_CONTEXT +
"/agentupdate/download\">"
+ linkItem.setValue("<a
href=\"/agentupdate/download\">"
+ MSG.view_admin_downloads_agent_link_value(version, build) +
"</a>");
SpacerItem spacerItem = new SpacerItem("agentSpacer");
@@ -196,7 +194,7 @@ public class DownloadsView extends EnhancedVLayout {
StaticTextItem linkItem = new StaticTextItem("cliLink");
linkItem.setTitle(MSG.common_label_link());
- linkItem.setValue("<a href=\"" + PORTAL_WAR_CONTEXT +
"/client/download\">"
+ linkItem.setValue("<a
href=\"/client/download\">"
+ MSG.view_admin_downloads_cli_link_value(version, build) +
"</a>");
SpacerItem spacerItem = new SpacerItem("clientSpacer");
diff --git
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
index 9792b5f..6c458e3 100644
---
a/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
+++
b/modules/enterprise/gui/coregui/src/main/java/org/rhq/enterprise/gui/coregui/server/gwt/SystemGWTServiceImpl.java
@@ -48,8 +48,6 @@ public class SystemGWTServiceImpl extends AbstractGWTServiceImpl
implements Syst
private static final long serialVersionUID = 1L;
- private static final String PORTAL_WAR_CONTEXT = "/portal";
-
private SystemManagerLocal systemManager = LookupUtil.getSystemManager();
private AgentManagerLocal agentManager = LookupUtil.getAgentManager();
private RemoteClientManagerLocal remoteClientManager =
LookupUtil.getRemoteClientManager();
@@ -138,7 +136,7 @@ public class SystemGWTServiceImpl extends AbstractGWTServiceImpl
implements Syst
HashMap<String, String> map = new HashMap<String,
String>(files.size());
for (File file : files) {
// key is the filename, value is the relative URL to download the file
from the server
- map.put(file.getName(), PORTAL_WAR_CONTEXT +
"/downloads/connectors/" + file.getName());
+ map.put(file.getName(), "/downloads/connectors/" +
file.getName());
}
return map;
} catch (Throwable t) {
@@ -158,7 +156,7 @@ public class SystemGWTServiceImpl extends AbstractGWTServiceImpl
implements Syst
HashMap<String, String> ret = new HashMap<String,
String>(files.size());
for (File file : files) {
- ret.put(file.getName(), PORTAL_WAR_CONTEXT +
"/downloads/cli-alert-scripts/" + file.getName());
+ ret.put(file.getName(), "/downloads/cli-alert-scripts/" +
file.getName());
}
return ret;
}
@@ -178,7 +176,7 @@ public class SystemGWTServiceImpl extends AbstractGWTServiceImpl
implements Syst
HashMap<String, String> ret = new HashMap<String,
String>(files.size());
for (File file : files) {
- ret.put(file.getName(), PORTAL_WAR_CONTEXT +
"/downloads/script-modules/" + file.getName());
+ ret.put(file.getName(), "/downloads/script-modules/" +
file.getName());
}
return ret;
}
@@ -207,7 +205,7 @@ public class SystemGWTServiceImpl extends AbstractGWTServiceImpl
implements Syst
}
File file = files.get(0);
HashMap<String, String> ret = new HashMap<String, String>(1);
- ret.put(file.getName(), PORTAL_WAR_CONTEXT +
"/downloads/bundle-deployer/" + file.getName());
+ ret.put(file.getName(), "/downloads/bundle-deployer/" +
file.getName());
return ret;
} catch (Throwable t) {
throw getExceptionToThrowToClient(t);
diff --git a/modules/enterprise/gui/downloads-war/pom.xml
b/modules/enterprise/gui/downloads-war/pom.xml
new file mode 100644
index 0000000..9d694cb
--- /dev/null
+++ b/modules/enterprise/gui/downloads-war/pom.xml
@@ -0,0 +1,153 @@
+<?xml version="1.0"?>
+<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-parent</artifactId>
+ <version>4.10.0-SNAPSHOT</version>
+ <relativePath>../../../../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-downloads-war</artifactId>
+ <packaging>war</packaging>
+ <name>RHQ Enterprise Downloads War</name>
+ <description>the RHQ Enterprise Downloads webapp</description>
+
+ <dependencies>
+ <!-- Internal Deps -->
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-core-domain</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope> <!-- by rhq.ear (as ejb-jar) -->
+ </dependency>
+
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-enterprise-server</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope> <!-- by rhq.ear (as ejb-jar) -->
+ </dependency>
+
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-core-util</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope> <!-- by rhq.ear -->
+ </dependency>
+
+ <!-- Import the Servlet API, we use provided scope as the API is included in
JBoss AS 7 -->
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope> <!-- by JBossAS -->
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <!-- Set the name of the war, used as the context root when the app
+ is deployed -->
+ <finalName>rhq-downloads</finalName>
+ <plugins>
+ <plugin>
+ <artifactId>maven-war-plugin</artifactId>
+ <configuration>
+ <failOnMissingWebXml>false</failOnMissingWebXml>
+ <archive>
+ <manifest>
+
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ </manifest>
+ <manifestEntries>
+ <Build-Number>${buildNumber}</Build-Number>
+ </manifestEntries>
+ </archive>
+ <webResources>
+ <resource>
+ <filtering>false</filtering>
+ <directory>${basedir}/src/main/webapp</directory>
+ </resource>
+ </webResources>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>dev</id>
+
+ <properties>
+ <rhq.rootDir>../../../..</rhq.rootDir>
+
<rhq.containerDir>${rhq.rootDir}/${rhq.devContainerServerPath}</rhq.containerDir>
+
<rhq.deploymentName>${project.build.finalName}.war</rhq.deploymentName>
+
<rhq.deploymentDir>${rhq.containerDir}/${rhq.earDeployDir}/${rhq.deploymentName}</rhq.deploymentDir>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>deploy-classes</id>
+ <phase>compile</phase>
+ <configuration>
+ <target>
+ <property name="classes.dir"
location="${rhq.deploymentDir}/WEB-INF/classes" />
+ <echo>*** Copying updated files from target/classes to
${classes.dir}...</echo>
+ <copy todir="${classes.dir}"
verbose="${rhq.verbose}">
+ <fileset dir="target/classes" />
+ </copy>
+ <property name="deployment.dir"
location="${rhq.deploymentDir}" />
+ <echo>*** Copying updated files from
src${file.separator}main${file.separator}webapp${file.separator} to
${deployment.dir}${file.separator}...</echo>
+ <copy todir="${deployment.dir}"
verbose="${rhq.verbose}">
+ <fileset dir="${basedir}/src/main/webapp"
/>
+ </copy>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>deploy</id>
+ <phase>package</phase>
+ <configuration>
+ <target>
+ <property name="deployment.dir"
location="${rhq.deploymentDir}" />
+ <echo>*** Copying updated files from
target${file.separator}${project.build.finalName}${file.separator} to
${deployment.dir}${file.separator}...</echo>
+ <copy todir="${deployment.dir}"
verbose="${rhq.verbose}">
+ <fileset
dir="${basedir}/target/${project.build.finalName}" />
+ </copy>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>undeploy</id>
+ <phase>clean</phase>
+ <configuration>
+ <target>
+ <property name="deployment.dir"
location="${rhq.deploymentDir}" />
+ <echo>*** Deleting
${deployment.dir}${file.separator}...</echo>
+ <delete dir="${deployment.dir}" />
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git
a/modules/enterprise/gui/downloads-war/src/main/java/org/rhq/enterprise/gui/download/DownloadServlet.java
b/modules/enterprise/gui/downloads-war/src/main/java/org/rhq/enterprise/gui/download/DownloadServlet.java
new file mode 100644
index 0000000..a6b6818
--- /dev/null
+++
b/modules/enterprise/gui/downloads-war/src/main/java/org/rhq/enterprise/gui/download/DownloadServlet.java
@@ -0,0 +1,295 @@
+/*
+ * 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.download;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+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 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;
+
+/**
+ * Serves the static content found in rhq-downloads.
+ */
+@WebServlet(urlPatterns = {"/*"}, loadOnStartup = 1)
+public class DownloadServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ // the system property that disables/enables file listing - default is to show the
listing
+ private static String SYSPROP_SHOW_DOWNLOADS_LISTING =
"rhq.server.show-downloads-listing";
+
+ private static int numActiveDownloads = 0;
+ private static boolean showDownloadsListing;
+
+ private Log log = LogFactory.getLog(this.getClass());
+
+ @Override
+ public void init() throws ServletException {
+ log.info("Starting the RHQ download servlet...");
+
+ String propValue = System.getProperty(SYSPROP_SHOW_DOWNLOADS_LISTING,
"true");
+ showDownloadsListing = Boolean.parseBoolean(propValue);
+ }
+
+ @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);
+
+ if (isServerAcceptingRequests()) {
+ String requestedDirectory = getRequestedDirectory(req);
+ if (requestedDirectory == null) {
+ numActiveDownloads++;
+ download(req, resp);
+ numActiveDownloads--;
+ } else {
+ if (showDownloadsListing) {
+ outputFileList(requestedDirectory, req, resp);
+ } else {
+ disableBrowserCache(resp);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Listing
disabled");
+ }
+ }
+ } else {
+ sendErrorServerNotAcceptingRequests(resp);
+ }
+ return;
+ }
+
+ /**
+ * Returns the relative path of the requested directory but only if it exists in
+ * one of the root directories. If the requested path is a file or the directory
+ * doesn't exist under a root directory, null is returned.
+ *
+ * @param req
+ * @return relative path of the directory being requested
+ * @throws ServletException
+ */
+ private String getRequestedDirectory(HttpServletRequest req) throws ServletException
{
+
+ String pathInfo = req.getPathInfo();
+
+ File[] rootDownloadsDirs;
+ try {
+ rootDownloadsDirs = getRootDownloadsDirs();
+ } catch (Throwable t) {
+ throw new ServletException(t);
+ }
+
+ if (pathInfo == null || pathInfo.equals("") ||
pathInfo.equals("/")) {
+ return "";
+ }
+
+ for (File rootDownloadsDir : rootDownloadsDirs) {
+ File downloadDir = new File(rootDownloadsDir, pathInfo);
+ if (downloadDir.isDirectory()) {
+ return pathInfo;
+ }
+ }
+
+ // either the path does not exist or its a file, not a directory
+ return null;
+ }
+
+ private void download(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
+ try {
+ if (isForbiddenPath(req.getPathInfo(), resp)) {
+ return;
+ }
+
+ // look for the file in one of the root download directories
+ File fileToDownload = null;
+ File[] downloadDirs = getRootDownloadsDirs();
+ for (File downloadDir : downloadDirs) {
+ File file = new File(downloadDir, req.getPathInfo());
+ if (file.exists()) {
+ fileToDownload = file;
+ break;
+ }
+ }
+
+ if (fileToDownload == null) {
+ disableBrowserCache(resp);
+ resp.sendError(HttpServletResponse.SC_NOT_FOUND, "File does not
exist: " + req.getPathInfo());
+ return;
+ }
+
+ resp.setContentType(getMimeType(fileToDownload));
+ resp.setHeader("Content-Disposition", "attachment;
filename=" + fileToDownload.getName());
+ resp.setContentLength((int) fileToDownload.length());
+ resp.setDateHeader("Last-Modified",
fileToDownload.lastModified());
+
+ FileInputStream stream = new FileInputStream(fileToDownload);
+ try {
+ StreamUtil.copy(stream, resp.getOutputStream(), false);
+ } finally {
+ stream.close();
+ }
+ } catch (Throwable t) {
+ log.error("Failed to stream download content.", t);
+ disableBrowserCache(resp);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to
download content");
+ }
+
+ return;
+ }
+
+ private String getMimeType(File file) {
+ String mimeType = null;
+
+ try {
+ mimeType = getServletContext().getMimeType(file.getName());
+ } catch (Throwable t) {
+ // i'm paranoid, no idea if this will ever happen, but its not fatal so
keep going
+ log.warn("Failed to get mime type for [" + file + "].",
t);
+ }
+
+ if (mimeType == null) {
+ mimeType = "application/octet-stream";
+ }
+ return mimeType;
+ }
+
+ private void outputFileList(String requestedDirectory, HttpServletRequest req,
HttpServletResponse resp)
+ throws ServletException {
+
+ try {
+ if (!isForbiddenPath(requestedDirectory, resp)) {
+ File requestedDirectoryRelativePath = new File(requestedDirectory);
+ String dirName = requestedDirectoryRelativePath.getName();
+ disableBrowserCache(resp);
+ resp.setContentType("text/html");
+ PrintWriter writer = resp.getWriter();
+
writer.println(String.format("<html><head><title>Available
Downloads: %s</title></head>", dirName));
+ writer.println("<body><h1>Available
Downloads</h1>");
+ writer.println(String.format("<font
size=\"+2\"><b><pre>%s</pre></b></font>",
dirName));
+ List<File> files = getDownloadFiles(requestedDirectory);
+ if (files.size() > 0) {
+ String pathInfo = req.getPathInfo();
+ if (!pathInfo.endsWith("/")) {
+ pathInfo += "/";
+ }
+ writer.println("<ul>");
+ for (File file : files) {
+ writer.println("<li><a href=\"" +
req.getContextPath() + req.getServletPath() + pathInfo + file.getName() +
"\">"
+ + file.getName() + "</a></li>");
+ }
+ writer.println("</ul>");
+ } else {
+ writer.println("<h4>NONE</h4>");
+ }
+ writer.println("</body></html>");
+ }
+ } catch (Exception e) {
+ throw new ServletException("Cannot get downloads listing", e);
+ }
+ }
+
+ private boolean isForbiddenPath(String requestedPath, HttpServletResponse resp)
throws IOException {
+ boolean forbidden = true; // assume it is forbidden
+ if (requestedPath.toString().contains("rhq-agent")) {
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Use
/agentupdate/download to obtain the agent");
+ } else if (requestedPath.toString().contains("rhq-client")) {
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Use /client/download
to obtain the client");
+ } else {
+ forbidden = false;
+ }
+ return forbidden;
+ }
+
+ private void sendErrorServerNotAcceptingRequests(HttpServletResponse resp) throws
IOException {
+ disableBrowserCache(resp);
+ resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Server Is Down For
Maintenance");
+ }
+
+ private void disableBrowserCache(HttpServletResponse resp) {
+ resp.setHeader("Cache-Control", "no-cache, no-store");
+ resp.setHeader("Expires", "-1");
+ resp.setHeader("Pragma", "no-cache");
+ }
+
+ private List<File> getDownloadFiles(String requestedDirectory) throws Exception
{
+ // its possible if more than one root dir has the file, we'll get
duplicates.
+ // this should never happen, so ignore this edge case.
+ List<File> returnFiles = new ArrayList<File>();
+ File[] rootDownloadDirs = getRootDownloadsDirs();
+ for (File rootDownloadDir : rootDownloadDirs) {
+ File dir = new File(rootDownloadDir, requestedDirectory);
+ File[] filesArray = dir.listFiles();
+ // this is simple - we only serve up files located in the requested directory
- no content from subdirectories
+ if (filesArray != null) {
+ for (File file : filesArray) {
+ if (file.isFile()) {
+ returnFiles.add(file);
+ }
+ }
+ }
+ }
+
+ return returnFiles;
+ }
+
+ /**
+ * There are two locations for downloads - the static content under the EAR's
rhq-downloads
+ * and dynamic content in the data directory.
+ *
+ * @return the two root locations
+ *
+ * @throws Exception
+ */
+ private File[] getRootDownloadsDirs() throws Exception {
+ File earDir = LookupUtil.getCoreServer().getEarDeploymentDir();
+ File downloadDir = new File(earDir, "rhq-downloads");
+ if (!downloadDir.exists()) {
+ throw new FileNotFoundException("Missing downloads directory at ["
+ downloadDir + "]");
+ }
+
+ File dataDir = LookupUtil.getCoreServer().getJBossServerDataDir();
+ File dataDownloadDir = new File(dataDir, "rhq-downloads");
+
+ return new File[] { dataDownloadDir, downloadDir }; // put the data dir first, I
think that should take precedence
+ }
+
+ private boolean isServerAcceptingRequests() {
+ try {
+ OperationMode mode =
LookupUtil.getServerManager().getServer().getOperationMode();
+ return mode == OperationMode.NORMAL;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+}
diff --git a/modules/enterprise/gui/pom.xml b/modules/enterprise/gui/pom.xml
index 648d913..31f5d8c 100644
--- a/modules/enterprise/gui/pom.xml
+++ b/modules/enterprise/gui/pom.xml
@@ -28,6 +28,9 @@
</activation>
<modules>
<module>portal-war</module>
+ <module>remote-client-war</module>
+ <module>agentupdate-war</module>
+ <module>downloads-war</module>
<module>content_http-war</module>
<module>coregui</module>
<module>rest-war</module>
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/agentupdate/AgentUpdateServlet.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/agentupdate/AgentUpdateServlet.java
deleted file mode 100644
index d115187..0000000
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/agentupdate/AgentUpdateServlet.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * 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.agentupdate;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-import org.rhq.core.domain.cloud.Server.OperationMode;
-import org.rhq.core.domain.common.composite.SystemSetting;
-import org.rhq.core.domain.common.composite.SystemSettings;
-import org.rhq.core.util.exception.ThrowableUtil;
-import org.rhq.core.util.stream.StreamUtil;
-import org.rhq.enterprise.server.core.AgentManagerLocal;
-import org.rhq.enterprise.server.util.LookupUtil;
-
-/**
- * Serves the agent update binary that is stored in the RHQ Server's download area.
- * This servlet also provides version information regarding the version of the agent
- * this servlet serves up as well as versions of agents the RHQ Server supports.
- */
-public class AgentUpdateServlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- // the system property that defines how many concurrent downloads we will allow
- private static String SYSPROP_AGENT_DOWNLOADS_LIMIT =
"rhq.server.agent-downloads-limit";
-
- // if the system property is not set or invalid, this is the default limit for number
of concurrent downloads
- private static int DEFAULT_AGENT_DOWNLOADS_LIMIT = 45;
-
- // the error code that will be returned if the server has been configured to disable
agent updates
- private static final int ERROR_CODE_AGENT_UPDATE_DISABLED =
HttpServletResponse.SC_FORBIDDEN;
-
- // the error code that will be returned if the server has too many agents downloading
the agent update binary
- private static final int ERROR_CODE_TOO_MANY_DOWNLOADS =
HttpServletResponse.SC_SERVICE_UNAVAILABLE;
-
- private AtomicInteger numActiveDownloads = null;
-
- private Log log = LogFactory.getLog(this.getClass());
-
- private AgentManagerLocal agentManager = null;
-
- private boolean initialized = false;
-
- @Override
- public void init() throws ServletException {
- log.info("Starting the RHQ agent update servlet");
- numActiveDownloads = new AtomicInteger(0);
- }
-
- private synchronized void loadAgentUpdateBinaryInfo() throws ServletException {
- if (!initialized) {
- log.info("RHQ agent update servlet is looking up binary file
information...");
-
- // make sure we have a agent update binary file; log its location
- try {
- log.info("Agent Update Binary File: " +
getAgentUpdateBinaryFile());
- } catch (Throwable t) {
- log.error("Missing agent update binary file - agents will not be
able to update", t);
- }
-
- // make sure we create a version file if we have to by getting the version
file now
- try {
- File versionFile = getAgentUpdateVersionFile();
-
- // log the version info - this also makes sure we can read it back in
- log.debug(versionFile + ": " + new String(StreamUtil.slurp(new
FileInputStream(versionFile))));
-
- } catch (Throwable t) {
- log.error("Cannot determine the agent version information - agents
will not be able to update.", t);
- }
-
- initialized = true;
- }
- }
-
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
-
- // lazily initialize the servlet - we do this because when we started deploying
on AS7, our servlets
- // init() method was being called before the agent SLSB is ready. So we don't
init() this at startup,
- // rather, we now init this servlet the first time someone requests the agent
update binary file.
- loadAgentUpdateBinaryInfo();
-
- // 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.incrementAndGet();
- getDownload(req, resp);
- } finally {
- numActiveDownloads.decrementAndGet();
- }
- } 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) {
- sendErrorAgentUpdateDisabled(resp);
- return;
- } else if (limit < numActiveDownloads.get()) {
- sendErrorTooManyDownloads(resp);
- return;
- }
-
- try {
- File agentJar = getAgentUpdateBinaryFile();
- resp.setContentType("application/octet-stream");
- resp.setHeader("Content-Disposition", "attachment;
filename=" + agentJar.getName());
- resp.setContentLength((int) agentJar.length());
- resp.setDateHeader("Last-Modified", agentJar.lastModified());
-
- FileInputStream agentJarStream = new FileInputStream(agentJar);
- try {
- StreamUtil.copy(agentJarStream, resp.getOutputStream(), false);
- } finally {
- agentJarStream.close();
- }
- } catch (Throwable t) {
- String clientAddr = getClientAddress(req);
- log.error("Failed to stream agent jar to remote client [" +
clientAddr + "]: "
- + ThrowableUtil.getAllMessages(t));
- disableBrowserCache(resp);
- resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to
stream agent jar");
- }
-
- return;
- }
-
- private void getVersion(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
- try {
- File versionFile = getAgentUpdateVersionFile();
- 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) {
- String clientAddr = getClientAddress(req);
- log.error("Failed to stream version info to remote client [" +
clientAddr + "]: "
- + ThrowableUtil.getAllMessages(t));
- disableBrowserCache(resp);
- resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to
stream version info");
- }
-
- return;
- }
-
- private int getDownloadLimit() {
- // if the server cloud was configured to disallow updates, return 0
- SystemSettings systemConfig = LookupUtil.getSystemManager().getSystemSettings(
- LookupUtil.getSubjectManager().getOverlord());
- if
(!Boolean.parseBoolean(systemConfig.get(SystemSetting.AGENT_AUTO_UPDATE_ENABLED))) {
- return 0;
- }
-
- String limitStr = System.getProperty(SYSPROP_AGENT_DOWNLOADS_LIMIT);
- int limit;
- try {
- limit = Integer.parseInt(limitStr);
- } catch (Exception e) {
- limit = DEFAULT_AGENT_DOWNLOADS_LIMIT;
- log.warn("Agent downloads limit system property [" +
SYSPROP_AGENT_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_AGENT_UPDATE_DISABLED, "Server Is Down For
Maintenance");
- }
-
- private void sendErrorAgentUpdateDisabled(HttpServletResponse resp) throws
IOException {
- disableBrowserCache(resp);
- resp.sendError(ERROR_CODE_AGENT_UPDATE_DISABLED, "Agent Updates 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 agent later");
- }
-
- private File getAgentUpdateVersionFile() throws Exception {
- return getAgentManager().getAgentUpdateVersionFile();
- }
-
- private File getAgentUpdateBinaryFile() throws Exception {
- return getAgentManager().getAgentUpdateBinaryFile();
- }
-
- private AgentManagerLocal getAgentManager() {
- if (this.agentManager == null) {
- this.agentManager = LookupUtil.getAgentManager();
- }
- return this.agentManager;
- }
-
- private boolean isServerAcceptingRequests() {
- try {
- OperationMode mode =
LookupUtil.getServerManager().getServer().getOperationMode();
- return mode == OperationMode.NORMAL;
- } catch (Exception e) {
- return false;
- }
- }
-
- private String getClientAddress(HttpServletRequest request) {
- String ip = request.getHeader("X-Forwarded-For");
- if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
- ip = request.getHeader("HTTP_X_FORWARDED_FOR");
- if (ip == null || ip.length() == 0 ||
"unknown".equalsIgnoreCase(ip)) {
- ip = String.format("%s (%s)", request.getRemoteHost(),
request.getRemoteAddr());
- }
- }
- return ip;
- }
-}
\ No newline at end of file
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/client/RemoteClientServlet.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/client/RemoteClientServlet.java
deleted file mode 100644
index 9127927..0000000
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/client/RemoteClientServlet.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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 java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-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;
-
-/**
- * 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.
- */
-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;
- }
- }
-
-}
\ No newline at end of file
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/download/DownloadServlet.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/download/DownloadServlet.java
deleted file mode 100644
index 59369a7..0000000
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/download/DownloadServlet.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * 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.download;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-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;
-
-/**
- * Serves the static content found in rhq-downloads.
- */
-public class DownloadServlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- // the system property that disables/enables file listing - default is to show the
listing
- private static String SYSPROP_SHOW_DOWNLOADS_LISTING =
"rhq.server.show-downloads-listing";
-
- private static int numActiveDownloads = 0;
- private static boolean showDownloadsListing;
-
- private Log log = LogFactory.getLog(this.getClass());
-
- @Override
- public void init() throws ServletException {
- log.info("Starting the RHQ download servlet...");
-
- String propValue = System.getProperty(SYSPROP_SHOW_DOWNLOADS_LISTING,
"true");
- showDownloadsListing = Boolean.parseBoolean(propValue);
- }
-
- @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);
-
- if (isServerAcceptingRequests()) {
- String requestedDirectory = getRequestedDirectory(req);
- if (requestedDirectory == null) {
- numActiveDownloads++;
- download(req, resp);
- numActiveDownloads--;
- } else {
- if (showDownloadsListing) {
- outputFileList(requestedDirectory, req, resp);
- } else {
- disableBrowserCache(resp);
- resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Listing
disabled");
- }
- }
- } else {
- sendErrorServerNotAcceptingRequests(resp);
- }
- return;
- }
-
- /**
- * Returns the relative path of the requested directory but only if it exists in
- * one of the root directories. If the requested path is a file or the directory
- * doesn't exist under a root directory, null is returned.
- *
- * @param req
- * @return relative path of the directory being requested
- * @throws ServletException
- */
- private String getRequestedDirectory(HttpServletRequest req) throws ServletException
{
-
- String pathInfo = req.getPathInfo();
-
- File[] rootDownloadsDirs;
- try {
- rootDownloadsDirs = getRootDownloadsDirs();
- } catch (Throwable t) {
- throw new ServletException(t);
- }
-
- if (pathInfo == null || pathInfo.equals("") ||
pathInfo.equals("/")) {
- return "";
- }
-
- for (File rootDownloadsDir : rootDownloadsDirs) {
- File downloadDir = new File(rootDownloadsDir, pathInfo);
- if (downloadDir.isDirectory()) {
- return pathInfo;
- }
- }
-
- // either the path does not exist or its a file, not a directory
- return null;
- }
-
- private void download(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
- try {
- if (isForbiddenPath(req.getPathInfo(), resp)) {
- return;
- }
-
- // look for the file in one of the root download directories
- File fileToDownload = null;
- File[] downloadDirs = getRootDownloadsDirs();
- for (File downloadDir : downloadDirs) {
- File file = new File(downloadDir, req.getPathInfo());
- if (file.exists()) {
- fileToDownload = file;
- break;
- }
- }
-
- if (fileToDownload == null) {
- disableBrowserCache(resp);
- resp.sendError(HttpServletResponse.SC_NOT_FOUND, "File does not
exist: " + req.getPathInfo());
- return;
- }
-
- resp.setContentType(getMimeType(fileToDownload));
- resp.setHeader("Content-Disposition", "attachment;
filename=" + fileToDownload.getName());
- resp.setContentLength((int) fileToDownload.length());
- resp.setDateHeader("Last-Modified",
fileToDownload.lastModified());
-
- FileInputStream stream = new FileInputStream(fileToDownload);
- try {
- StreamUtil.copy(stream, resp.getOutputStream(), false);
- } finally {
- stream.close();
- }
- } catch (Throwable t) {
- log.error("Failed to stream download content.", t);
- disableBrowserCache(resp);
- resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Failed to
download content");
- }
-
- return;
- }
-
- private String getMimeType(File file) {
- String mimeType = null;
-
- try {
- mimeType = getServletContext().getMimeType(file.getName());
- } catch (Throwable t) {
- // i'm paranoid, no idea if this will ever happen, but its not fatal so
keep going
- log.warn("Failed to get mime type for [" + file + "].",
t);
- }
-
- if (mimeType == null) {
- mimeType = "application/octet-stream";
- }
- return mimeType;
- }
-
- private void outputFileList(String requestedDirectory, HttpServletRequest req,
HttpServletResponse resp)
- throws ServletException {
-
- try {
- if (!isForbiddenPath(requestedDirectory, resp)) {
- File requestedDirectoryRelativePath = new File(requestedDirectory);
- String dirName = requestedDirectoryRelativePath.getName();
- disableBrowserCache(resp);
- resp.setContentType("text/html");
- PrintWriter writer = resp.getWriter();
-
writer.println(String.format("<html><head><title>Available
Downloads: %s</title></head>", dirName));
- writer.println("<body><h1>Available
Downloads</h1>");
- writer.println(String.format("<font
size=\"+2\"><b><pre>%s</pre></b></font>",
dirName));
- List<File> files = getDownloadFiles(requestedDirectory);
- if (files.size() > 0) {
- String pathInfo = req.getPathInfo();
- if (!pathInfo.endsWith("/")) {
- pathInfo += "/";
- }
- writer.println("<ul>");
- for (File file : files) {
- writer.println("<li><a href=\"" +
req.getServletPath() + pathInfo + file.getName() + "\">"
- + file.getName() + "</a></li>");
- }
- writer.println("</ul>");
- } else {
- writer.println("<h4>NONE</h4>");
- }
- writer.println("</body></html>");
- }
- } catch (Exception e) {
- throw new ServletException("Cannot get downloads listing", e);
- }
- }
-
- private boolean isForbiddenPath(String requestedPath, HttpServletResponse resp)
throws IOException {
- boolean forbidden = true; // assume it is forbidden
- if (requestedPath.toString().contains("rhq-agent")) {
- resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Use
/agentupdate/download to obtain the agent");
- } else if (requestedPath.toString().contains("rhq-client")) {
- resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Use /client/download
to obtain the client");
- } else {
- forbidden = false;
- }
- return forbidden;
- }
-
- private void sendErrorServerNotAcceptingRequests(HttpServletResponse resp) throws
IOException {
- disableBrowserCache(resp);
- resp.sendError(HttpServletResponse.SC_FORBIDDEN, "Server Is Down For
Maintenance");
- }
-
- private void disableBrowserCache(HttpServletResponse resp) {
- resp.setHeader("Cache-Control", "no-cache, no-store");
- resp.setHeader("Expires", "-1");
- resp.setHeader("Pragma", "no-cache");
- }
-
- private List<File> getDownloadFiles(String requestedDirectory) throws Exception
{
- // its possible if more than one root dir has the file, we'll get
duplicates.
- // this should never happen, so ignore this edge case.
- List<File> returnFiles = new ArrayList<File>();
- File[] rootDownloadDirs = getRootDownloadsDirs();
- for (File rootDownloadDir : rootDownloadDirs) {
- File dir = new File(rootDownloadDir, requestedDirectory);
- File[] filesArray = dir.listFiles();
- // this is simple - we only serve up files located in the requested directory
- no content from subdirectories
- if (filesArray != null) {
- for (File file : filesArray) {
- if (file.isFile()) {
- returnFiles.add(file);
- }
- }
- }
- }
-
- return returnFiles;
- }
-
- /**
- * There are two locations for downloads - the static content under the EAR's
rhq-downloads
- * and dynamic content in the data directory.
- *
- * @return the two root locations
- *
- * @throws Exception
- */
- private File[] getRootDownloadsDirs() throws Exception {
- File earDir = LookupUtil.getCoreServer().getEarDeploymentDir();
- File downloadDir = new File(earDir, "rhq-downloads");
- if (!downloadDir.exists()) {
- throw new FileNotFoundException("Missing downloads directory at ["
+ downloadDir + "]");
- }
-
- File dataDir = LookupUtil.getCoreServer().getJBossServerDataDir();
- File dataDownloadDir = new File(dataDir, "rhq-downloads");
-
- return new File[] { dataDownloadDir, downloadDir }; // put the data dir first, I
think that should take precedence
- }
-
- private boolean isServerAcceptingRequests() {
- try {
- OperationMode mode =
LookupUtil.getServerManager().getServer().getOperationMode();
- return mode == OperationMode.NORMAL;
- } catch (Exception e) {
- return false;
- }
- }
-
-}
\ No newline at end of file
diff --git a/modules/enterprise/gui/portal-war/src/main/webapp/WEB-INF/web.xml
b/modules/enterprise/gui/portal-war/src/main/webapp/WEB-INF/web.xml
index f623679..5aa727d 100644
--- a/modules/enterprise/gui/portal-war/src/main/webapp/WEB-INF/web.xml
+++ b/modules/enterprise/gui/portal-war/src/main/webapp/WEB-INF/web.xml
@@ -314,26 +314,6 @@
<load-on-startup>1</load-on-startup>
</servlet>
- <!-- Servlet that serves the agent update binaries and version information -->
- <servlet>
- <servlet-name>AgentUpdateServlet</servlet-name>
-
<servlet-class>org.rhq.enterprise.gui.agentupdate.AgentUpdateServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
-
- <!-- Servlet that serves the content from rhq-downloads -->
- <servlet>
- <servlet-name>DownloadServlet</servlet-name>
-
<servlet-class>org.rhq.enterprise.gui.download.DownloadServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
-
- <servlet>
- <servlet-name>RemoteClientServlet</servlet-name>
-
<servlet-class>org.rhq.enterprise.gui.client.RemoteClientServlet</servlet-class>
- <load-on-startup>1</load-on-startup>
- </servlet>
-
<!-- standalone servlets used by the JSPs -->
<!-- Struts ActionServlet configuration (with debugging) -->
@@ -363,40 +343,12 @@
<load-on-startup>2</load-on-startup>
</servlet>
- <!-- The endpoints to the agent update servlet -->
- <servlet-mapping>
- <servlet-name>AgentUpdateServlet</servlet-name>
- <url-pattern>/agentupdate/version</url-pattern>
- </servlet-mapping>
- <servlet-mapping>
- <servlet-name>AgentUpdateServlet</servlet-name>
- <url-pattern>/agentupdate/download</url-pattern>
- </servlet-mapping>
-
- <!-- The endpoints to the remote client servlet -->
- <servlet-mapping>
- <servlet-name>RemoteClientServlet</servlet-name>
- <url-pattern>/client/version</url-pattern>
- </servlet-mapping>
- <servlet-mapping>
- <servlet-name>RemoteClientServlet</servlet-name>
- <url-pattern>/client/download</url-pattern>
- </servlet-mapping>
-
<!-- TODO what is this for? -->
<servlet-mapping>
<servlet-name>sessionAccess</servlet-name>
<url-pattern>/sessionAccess</url-pattern>
</servlet-mapping>
- <!-- The download servlet; this URI /downloads will effectively look like its
pointing to rhq-downloads -->
- <servlet-mapping>
- <servlet-name>DownloadServlet</servlet-name>
- <url-pattern>/downloads/*</url-pattern>
- </servlet-mapping>
-
-
-
<!-- mappings for Struts ActionServlet -->
<servlet-mapping>
<servlet-name>action</servlet-name>
diff --git a/modules/enterprise/gui/remote-client-war/pom.xml
b/modules/enterprise/gui/remote-client-war/pom.xml
new file mode 100644
index 0000000..92ec018
--- /dev/null
+++ b/modules/enterprise/gui/remote-client-war/pom.xml
@@ -0,0 +1,153 @@
+<?xml version="1.0"?>
+<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-parent</artifactId>
+ <version>4.10.0-SNAPSHOT</version>
+ <relativePath>../../../../pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-remote-client-war</artifactId>
+ <packaging>war</packaging>
+ <name>RHQ Enterprise Remote Client War</name>
+ <description>the RHQ Enterprise Remote Client webapp</description>
+
+ <dependencies>
+ <!-- Internal Deps -->
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-core-domain</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope> <!-- by rhq.ear (as ejb-jar) -->
+ </dependency>
+
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-enterprise-server</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope> <!-- by rhq.ear (as ejb-jar) -->
+ </dependency>
+
+ <dependency>
+ <groupId>org.rhq</groupId>
+ <artifactId>rhq-core-util</artifactId>
+ <version>${project.version}</version>
+ <scope>provided</scope> <!-- by rhq.ear -->
+ </dependency>
+
+ <!-- Import the Servlet API, we use provided scope as the API is included in
JBoss AS 7 -->
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <scope>provided</scope> <!-- by JBossAS -->
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <!-- Set the name of the war, used as the context root when the app
+ is deployed -->
+ <finalName>rhq-client</finalName>
+ <plugins>
+ <plugin>
+ <artifactId>maven-war-plugin</artifactId>
+ <configuration>
+ <failOnMissingWebXml>false</failOnMissingWebXml>
+ <archive>
+ <manifest>
+
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
+
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+ </manifest>
+ <manifestEntries>
+ <Build-Number>${buildNumber}</Build-Number>
+ </manifestEntries>
+ </archive>
+ <webResources>
+ <resource>
+ <filtering>false</filtering>
+ <directory>${basedir}/src/main/webapp</directory>
+ </resource>
+ </webResources>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <profiles>
+ <profile>
+ <id>dev</id>
+
+ <properties>
+ <rhq.rootDir>../../../..</rhq.rootDir>
+
<rhq.containerDir>${rhq.rootDir}/${rhq.devContainerServerPath}</rhq.containerDir>
+
<rhq.deploymentName>${project.build.finalName}.war</rhq.deploymentName>
+
<rhq.deploymentDir>${rhq.containerDir}/${rhq.earDeployDir}/${rhq.deploymentName}</rhq.deploymentDir>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>deploy-classes</id>
+ <phase>compile</phase>
+ <configuration>
+ <target>
+ <property name="classes.dir"
location="${rhq.deploymentDir}/WEB-INF/classes" />
+ <echo>*** Copying updated files from target/classes to
${classes.dir}...</echo>
+ <copy todir="${classes.dir}"
verbose="${rhq.verbose}">
+ <fileset dir="target/classes" />
+ </copy>
+ <property name="deployment.dir"
location="${rhq.deploymentDir}" />
+ <echo>*** Copying updated files from
src${file.separator}main${file.separator}webapp${file.separator} to
${deployment.dir}${file.separator}...</echo>
+ <copy todir="${deployment.dir}"
verbose="${rhq.verbose}">
+ <fileset dir="${basedir}/src/main/webapp"
/>
+ </copy>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+
+ <execution>
+ <id>deploy</id>
+ <phase>package</phase>
+ <configuration>
+ <target>
+ <property name="deployment.dir"
location="${rhq.deploymentDir}" />
+ <echo>*** Copying updated files from
target${file.separator}${project.build.finalName}${file.separator} to
${deployment.dir}${file.separator}...</echo>
+ <copy todir="${deployment.dir}"
verbose="${rhq.verbose}">
+ <fileset
dir="${basedir}/target/${project.build.finalName}" />
+ </copy>
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ <execution>
+ <id>undeploy</id>
+ <phase>clean</phase>
+ <configuration>
+ <target>
+ <property name="deployment.dir"
location="${rhq.deploymentDir}" />
+ <echo>*** Deleting
${deployment.dir}${file.separator}...</echo>
+ <delete dir="${deployment.dir}" />
+ </target>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git
a/modules/enterprise/gui/remote-client-war/src/main/java/org/rhq/enterprise/gui/client/RemoteClientServlet.java
b/modules/enterprise/gui/remote-client-war/src/main/java/org/rhq/enterprise/gui/client/RemoteClientServlet.java
new file mode 100644
index 0000000..234fc58
--- /dev/null
+++
b/modules/enterprise/gui/remote-client-war/src/main/java/org/rhq/enterprise/gui/client/RemoteClientServlet.java
@@ -0,0 +1,196 @@
+/*
+ * 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;
+ }
+ }
+
+}
diff --git a/modules/enterprise/gui/remote-client-war/src/main/webapp/index.html
b/modules/enterprise/gui/remote-client-war/src/main/webapp/index.html
new file mode 100644
index 0000000..5d7a115
--- /dev/null
+++ b/modules/enterprise/gui/remote-client-war/src/main/webapp/index.html
@@ -0,0 +1,5 @@
+<html>
+ <head>
+ <meta http-equiv="Refresh" content="0; URL=version">
+ </head>
+</html>
diff --git a/modules/enterprise/server/ear/pom.xml
b/modules/enterprise/server/ear/pom.xml
index a2e91a3..f312015 100644
--- a/modules/enterprise/server/ear/pom.xml
+++ b/modules/enterprise/server/ear/pom.xml
@@ -48,6 +48,28 @@
<dependency>
<groupId>${project.groupId}</groupId>
+ <artifactId>rhq-remote-client-war</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-downloads-war</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-agentupdate-war</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </dependency>
+
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
<artifactId>rhq-coregui</artifactId>
<version>${project.version}</version>
<type>war</type>
@@ -263,6 +285,27 @@
<contextRoot>/portal</contextRoot>
</webModule>
+ <webModule>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-remote-client-war</artifactId>
+ <bundleFileName>rhq-client.war</bundleFileName>
+ <contextRoot>/client</contextRoot>
+ </webModule>
+
+ <webModule>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-downloads-war</artifactId>
+
<bundleFileName>rhq-downloads.war</bundleFileName>
+ <contextRoot>/downloads</contextRoot>
+ </webModule>
+
+ <webModule>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>rhq-agentupdate-war</artifactId>
+
<bundleFileName>rhq-agentupdate.war</bundleFileName>
+ <contextRoot>/agentupdate</contextRoot>
+ </webModule>
+
<!-- used to expose content through http -->
<webModule>
<groupId>${project.groupId}</groupId>