Return-Path: X-Original-To: apmail-ace-commits-archive@www.apache.org Delivered-To: apmail-ace-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 24CD210EA9 for ; Thu, 28 Nov 2013 11:02:04 +0000 (UTC) Received: (qmail 24389 invoked by uid 500); 28 Nov 2013 11:01:41 -0000 Delivered-To: apmail-ace-commits-archive@ace.apache.org Received: (qmail 24270 invoked by uid 500); 28 Nov 2013 11:01:39 -0000 Mailing-List: contact commits-help@ace.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@ace.apache.org Delivered-To: mailing list commits@ace.apache.org Received: (qmail 24194 invoked by uid 99); 28 Nov 2013 11:01:38 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 28 Nov 2013 11:01:38 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 28 Nov 2013 11:01:34 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id C37F623888E4; Thu, 28 Nov 2013 11:01:14 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1546342 - in /ace/trunk/org.apache.ace.agent.controller.itest: ./ .settings/ src/ src/org/ src/org/apache/ src/org/apache/ace/ src/org/apache/ace/agent/ src/org/apache/ace/agent/itest/ test/ Date: Thu, 28 Nov 2013 11:01:14 -0000 To: commits@ace.apache.org From: jawi@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20131128110114.C37F623888E4@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: jawi Date: Thu Nov 28 11:01:13 2013 New Revision: 1546342 URL: http://svn.apache.org/r1546342 Log: ACE-433 - moved custom controller itest to its own project: - this is in preparation for the new way custom controllers should be registered and used. Added: ace/trunk/org.apache.ace.agent.controller.itest/ (with props) ace/trunk/org.apache.ace.agent.controller.itest/.classpath (with props) ace/trunk/org.apache.ace.agent.controller.itest/.gitignore ace/trunk/org.apache.ace.agent.controller.itest/.project (with props) ace/trunk/org.apache.ace.agent.controller.itest/.settings/ ace/trunk/org.apache.ace.agent.controller.itest/.settings/org.eclipse.jdt.core.prefs ace/trunk/org.apache.ace.agent.controller.itest/bnd.bnd ace/trunk/org.apache.ace.agent.controller.itest/build.xml (with props) ace/trunk/org.apache.ace.agent.controller.itest/src/ ace/trunk/org.apache.ace.agent.controller.itest/src/.gitignore ace/trunk/org.apache.ace.agent.controller.itest/src/org/ ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/ ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/ ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/ ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java (with props) ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java (with props) ace/trunk/org.apache.ace.agent.controller.itest/test/ ace/trunk/org.apache.ace.agent.controller.itest/test/.gitignore Propchange: ace/trunk/org.apache.ace.agent.controller.itest/ ------------------------------------------------------------------------------ --- svn:ignore (added) +++ svn:ignore Thu Nov 28 11:01:13 2013 @@ -0,0 +1,8 @@ +bin +bin_test +generated +store +bundle-cache +felix-cache +test-output + Added: ace/trunk/org.apache.ace.agent.controller.itest/.classpath URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/.classpath?rev=1546342&view=auto ============================================================================== Binary file - no diff available. Propchange: ace/trunk/org.apache.ace.agent.controller.itest/.classpath ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: ace/trunk/org.apache.ace.agent.controller.itest/.gitignore URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/.gitignore?rev=1546342&view=auto ============================================================================== --- ace/trunk/org.apache.ace.agent.controller.itest/.gitignore (added) +++ ace/trunk/org.apache.ace.agent.controller.itest/.gitignore Thu Nov 28 11:01:13 2013 @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ Added: ace/trunk/org.apache.ace.agent.controller.itest/.project URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/.project?rev=1546342&view=auto ============================================================================== Binary file - no diff available. Propchange: ace/trunk/org.apache.ace.agent.controller.itest/.project ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: ace/trunk/org.apache.ace.agent.controller.itest/.settings/org.eclipse.jdt.core.prefs URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/.settings/org.eclipse.jdt.core.prefs?rev=1546342&view=auto ============================================================================== --- ace/trunk/org.apache.ace.agent.controller.itest/.settings/org.eclipse.jdt.core.prefs (added) +++ ace/trunk/org.apache.ace.agent.controller.itest/.settings/org.eclipse.jdt.core.prefs Thu Nov 28 11:01:13 2013 @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 Added: ace/trunk/org.apache.ace.agent.controller.itest/bnd.bnd URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/bnd.bnd?rev=1546342&view=auto ============================================================================== --- ace/trunk/org.apache.ace.agent.controller.itest/bnd.bnd (added) +++ ace/trunk/org.apache.ace.agent.controller.itest/bnd.bnd Thu Nov 28 11:01:13 2013 @@ -0,0 +1,85 @@ +Test-Cases: ${classes;CONCRETE;EXTENDS;junit.framework.TestCase} +-runbundles: \ + org.mockito.mockito-all,\ + org.apache.ace.test;version=latest,\ + org.apache.ace.builder;version=latest,\ + org.apache.felix.configadmin,\ + org.apache.felix.dependencymanager,\ + org.apache.felix.dependencymanager.shell,\ + org.apache.felix.gogo.command,\ + org.apache.felix.gogo.runtime,\ + org.apache.felix.gogo.shell,\ + org.apache.felix.http.jetty,\ + org.apache.felix.log,\ + biz.aQute.bnd,\ + osgi.cmpn +Bundle-Activator: org.apache.ace.agent.impl.Activator +Private-Package: \ + org.apache.ace.range,\ + org.apache.ace.agent,\ + org.apache.ace.agent.impl,\ + org.apache.ace.agent.itest,\ + org.apache.ace.agent.updater,\ + org.apache.ace.feedback,\ + org.apache.ace.feedback.util,\ + org.apache.felix.deploymentadmin,\ + org.apache.felix.deploymentadmin.spi,\ + org.osgi.service.event,\ + org.osgi.service.log,\ + org.osgi.util.tracker,\ + aQute.bnd.annotation.component,\ + aQute.bnd.help,\ + aQute.bnd.service.action,\ + aQute.lib.deployer,\ + aQute.lib.osgi.eclipse,\ + aQute.libg.filelock,\ + aQute.bnd.build,\ + aQute.bnd.maven.support,\ + aQute.bnd.settings,\ + aQute.libg.command,\ + aQute.libg.tuple,\ + aQute.lib.osgi,\ + aQute.bnd.annotation,\ + aQute.bnd.annotation.metatype,\ + aQute.bnd.component,\ + aQute.bnd.make,\ + aQute.bnd.make.component,\ + aQute.bnd.make.metatype,\ + aQute.bnd.maven,\ + aQute.bnd.service,\ + aQute.lib.base64,\ + aQute.lib.collections,\ + aQute.lib.filter,\ + aQute.lib.hex,\ + aQute.lib.io,\ + aQute.lib.tag,\ + aQute.libg.cryptography,\ + aQute.libg.generics,\ + aQute.libg.header,\ + aQute.libg.qtokens,\ + aQute.libg.reporter,\ + aQute.libg.sed,\ + aQute.libg.tarjan,\ + aQute.libg.version +-runee: JavaSE-1.6 +-runvm: -ea +-runfw: org.apache.felix.framework +-buildpath: osgi.core;version='[4.2,5)',\ + org.apache.ace.agent;version=latest,\ + org.apache.ace.builder;version=latest,\ + org.apache.ace.test;version=latest,\ + org.apache.felix.dependencymanager,\ + org.apache.felix.http.jetty,\ + biz.aQute.bnd,\ + junit.osgi,\ + org.mockito.mockito-all +-runsystempackages: sun.reflect +-runproperties: \ + org.apache.felix.eventadmin.Timeout=0,\ + org.apache.felix.log.storeDebug=true,\ + org.apache.felix.log.maxSize=1000 +Import-Package: \ + !org.osgi.service.component.annotations,\ + * +Bundle-Version: 1.0.0 +Bundle-Name: Apache ACE Agent Custom Controller itest Added: ace/trunk/org.apache.ace.agent.controller.itest/build.xml URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/build.xml?rev=1546342&view=auto ============================================================================== Binary file - no diff available. Propchange: ace/trunk/org.apache.ace.agent.controller.itest/build.xml ------------------------------------------------------------------------------ svn:mime-type = application/xml Added: ace/trunk/org.apache.ace.agent.controller.itest/src/.gitignore URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/src/.gitignore?rev=1546342&view=auto ============================================================================== (empty) Added: ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java?rev=1546342&view=auto ============================================================================== --- ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java (added) +++ ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java Thu Nov 28 11:01:13 2013 @@ -0,0 +1,161 @@ +package org.apache.ace.agent.itest; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.ace.agent.AgentConstants; +import org.apache.ace.agent.ConfigurationHandler; +import org.apache.ace.builder.DeploymentPackageBuilder; +import org.apache.ace.it.IntegrationTestBase; +import org.osgi.framework.Bundle; +import org.osgi.framework.Version; + +import aQute.lib.osgi.Builder; +import aQute.lib.osgi.Jar; + +public abstract class BaseAgentControllerTest extends IntegrationTestBase { + + protected static class TestBundle { + private final File m_file; + + public TestBundle(String name, Version version, String... headers) throws Exception { + m_file = createBundle(name, version, headers); + } + + public File getFile() { + return m_file; + } + } + + protected static class TestPackage { + private final String m_name; + private final Version m_version; + private final File m_file; + + public TestPackage(String name, Version version, TestBundle... bundles) throws Exception { + m_name = name; + m_version = version; + + File[] files = new File[bundles.length]; + for (int i = 0; i < bundles.length; i++) { + files[i] = bundles[i].getFile(); + } + m_file = createPackage(m_name, m_version, files); + } + + public String getName() { + return m_name; + } + + public File getFile() { + return m_file; + } + + public Version getVersion() { + return m_version; + } + } + + protected static File createBundle(String bsn, Version version, String... headers) throws Exception { + Builder b = new Builder(); + + try { + b.setProperty("Bundle-SymbolicName", bsn); + b.setProperty("Bundle-Version", version.toString()); + for (int i = 0; i < headers.length; i += 2) { + b.setProperty(headers[i], headers[i + 1]); + } + b.setProperty("Include-Resource", "bnd.bnd"); // prevent empty jar bug + + Jar jar = b.build(); + jar.getManifest(); // Not sure whether this is needed... + + File file = File.createTempFile("testbundle", ".jar"); + file.deleteOnExit(); + + jar.write(file); + return file; + } + finally { + b.close(); + } + } + + protected static File createPackage(String name, Version version, File... bundles) throws Exception { + DeploymentPackageBuilder builder = DeploymentPackageBuilder.createDeploymentPackage(name, version.toString()); + + OutputStream fos = null; + try { + for (File bundle : bundles) { + builder.addBundle(bundle.toURI().toURL()); + } + + File file = File.createTempFile("testpackage", ".jar"); + file.deleteOnExit(); + + fos = new FileOutputStream(file); + builder.generate(fos); + + return file; + } + finally { + if (fos != null) { + fos.close(); + } + } + } + + @Override + protected void configureProvisionedServices() throws Exception { + resetAgentBundleState(); + } + + protected void resetAgentBundleState() throws Exception { + Bundle agentBundle = m_bundleContext.getBundle(); + File dataDir = m_bundleContext.getDataFile(""); + + // System.out.println("BaseAgentTest: Stopping agent bundle"); + agentBundle.stop(); + // System.out.println("BaseAgentTest: Cleaning bundle data dir (" + dataDir + ")"); + cleanDir(dataDir); + // System.out.println("BaseAgentTest: Cleaning system properties"); + Set keysBeRemoved = new HashSet(); + for (Object key : System.getProperties().keySet()) { + if (key instanceof String && ((String) key).startsWith(AgentConstants.CONFIG_KEY_NAMESPACE)) { + keysBeRemoved.add((String) key); + } + } + for (String removeKey : keysBeRemoved) { + System.clearProperty(removeKey); + } + // System.out.println("BaseAgentTest: Starting agent bundle"); + agentBundle.start(); + } + + protected void configureAgent(ConfigurationHandler handler, String... configuration) { + Map config = new HashMap(); + for (int i = 0; i < configuration.length; i += 2) { + config.put(configuration[i], configuration[i + 1]); + } + handler.putAll(config); + } + + private void cleanDir(File dir) { + if (!dir.isDirectory()) { + throw new IllegalStateException(); + } + File[] files = dir.listFiles(); + for (File file : files) { + if (file.isDirectory()) { + cleanDir(file); + } + file.delete(); + } + dir.delete(); + } +} Propchange: ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/BaseAgentControllerTest.java ------------------------------------------------------------------------------ svn:eol-style = native Added: ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java?rev=1546342&view=auto ============================================================================== --- ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java (added) +++ ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java Thu Nov 28 11:01:13 2013 @@ -0,0 +1,491 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ace.agent.itest; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.ace.agent.AgentConstants; +import org.apache.ace.agent.AgentControl; +import org.apache.ace.agent.DeploymentHandler; +import org.apache.ace.agent.DownloadHandle; +import org.apache.ace.agent.DownloadHandle.DownloadProgressListener; +import org.apache.ace.agent.DownloadResult; +import org.apache.ace.agent.FeedbackChannel; +import org.apache.ace.agent.FeedbackHandler; +import org.apache.ace.agent.UpdateHandler; +import org.apache.ace.test.constants.TestConstants; +import org.apache.ace.test.utils.NetUtils; +import org.apache.felix.dm.Component; +import org.osgi.framework.Bundle; +import org.osgi.framework.Version; +import org.osgi.service.http.HttpService; + +/** + * Tests that we can create an agent with a completely custom controller, see {@link CustomController} for more + * information about the actual implementation. + * + * @see CustomController + */ +public class CustomAgentControllerTest extends BaseAgentControllerTest { + /** + * Provides a simple implementation of {@link AgentUser} that always acknowledges a download and/or installation. + */ + static class AcknowledgingAgentUser implements AgentUser { + @Override + public boolean downloadAvailableUpdate(UpdateType updateType, String agentId, Version from, Version to) { + // Always proceed with a download... + return true; + } + + @Override + public boolean installAvailableUpdate(UpdateType updateType, String agentId, Version from, Version to) { + // Always proceed with the installation... + return true; + } + } + + /** + * Denotes a "user" of our agent that is monitoring our agent and able to respond to questions. + */ + static interface AgentUser { + /** + * Asks the user whether or not to download an available update. + * + * @param updateType + * the type of update to download, cannot be null; + * @param agentId + * the identification of the agent that has an update available; + * @param from + * the current installed version to upgrade from; + * @param to + * the available version to upgrade to. + * @return true if the update should be downloaded, false otherwise. + */ + boolean downloadAvailableUpdate(UpdateType updateType, String agentId, Version from, Version to); + + /** + * Asks the user whether or not to install an available update, after it has been downloaded. + * + * @param updateType + * the type of update to install, cannot be null; + * @param agentId + * the identification of the agent that has an update available; + * @param from + * the current installed version to upgrade from; + * @param to + * the available version to upgrade to. + * @return true if the update should be installed, false otherwise. + */ + boolean installAvailableUpdate(UpdateType updateType, String agentId, Version from, Version to); + } + + /** + * The actual custom controller as {@link Runnable} task, that simply loops and executes its tasks until notified to + * stop. + * + * @see #run() + */ + class CustomController implements Runnable { + private volatile boolean m_stop = false; + + /** + * Main loop, will sleep for a little and once every 500 ms will do the following: + *
    + *
  1. Synchronize all agent feedback with the server (see {@link #sendFeedbackToServer()});
  2. + *
  3. Check for agent updates (see {@link #checkForUpdate(UpdateType)});
  4. + *
  5. Check for deployment updates (see {@link #checkForUpdate(UpdateType)}).
  6. + *
+ *

+ * Note that this implementation does very little error checking and is rather stubborn when it comes across + * failures: it simply keeps retrying, which, for this use case, is acceptable. + *

+ * + * @see #stop() + * @see #checkForUpdate(UpdateType) + * @see #sendFeedbackToServer() + */ + @Override + public void run() { + while (!m_stop) { + try { + TimeUnit.MILLISECONDS.sleep(500); + } + catch (InterruptedException exception) { + // We're requested to stop... + break; + } + + if (m_stop) { + // Check once more whether we're not stopped while sleeping... + break; + } + + sendFeedbackToServer(); + + checkForUpdate(UpdateType.AGENT); + + checkForUpdate(UpdateType.DEPLOYMENT); + } + } + + /** + * Stops the main loop and allows the {@link #run()} loop to terminate (after it has done all of its work). + */ + public void stop() { + m_stop = true; + } + + /** + * Does the actual check for either the agent or deployment updates, and if available: + *
    + *
  1. asks the "user" whether it should download this update, and if so;
  2. + *
  3. downloads the update to a temporary location;
  4. + *
  5. if the download is complete, it asks the "user" whether it should proceed with installing it, and if so;
  6. + *
  7. installs the agent/deployment update.
  8. + *
+ *

+ * In case an exception occurs during this check, it is logged and the method returns (early). No exceptions are + * propagated. In production code, a little more sophisticated error checking should be performed. + *

+ * + * @param updateType + * the type of update we're performing, cannot be null. + */ + private void checkForUpdate(UpdateType updateType) { + try { + UpdateHandler updateHandler = getUpdateHandler(updateType); + + Version installed = updateHandler.getInstalledVersion(); + Version available = updateHandler.getHighestAvailableVersion(); + if (installed != null && installed.compareTo(available) < 0) { + // Update available, ask the user whether we should download it... + if (!m_agentUser.downloadAvailableUpdate(updateType, getAgentId(), installed, available)) { + // No, we may not download this update now... + return; + } + + System.out.printf("Downloading %s update (from v%s to v%s)...%n", updateType, installed, available); + + DownloadHandle downloadHandle = updateHandler.getDownloadHandle(available, false /* fixPackage */); + + Future future = downloadHandle.start(new DownloadProgressListener() { + @Override + public void progress(long bytesRead) { + System.out.printf("Download progress: %d bytes read...%n", bytesRead); + } + }); + // Block until the download is complete... + DownloadResult result = future.get(); + + // Download is complete, ask the user once more if we're allowed to install the update... + if (m_agentUser.installAvailableUpdate(updateType, getAgentId(), installed, available)) { + System.out.printf("Installing %s update (from v%s to v%s)...%n", updateType, installed, available); + + // We've confirmation that we can install this update... + updateHandler.install(result.getInputStream()); + } + } + } + catch (Exception exception) { + System.out.printf("%s update failed with %s.%n", updateType, exception.getMessage()); + exception.printStackTrace(System.out); + } + } + + /** + * @return the identification of the current agent, as returned by the agent's API. + */ + private String getAgentId() { + return m_control.getAgentId(); + } + + /** + * Returns the update handler for the given {@link UpdateType}. + * + * @param updateType + * the type of update we want an update handler for, cannot be null. + * @return an {@link UpdateHandler} instance, never null. + */ + private UpdateHandler getUpdateHandler(UpdateType updateType) { + UpdateHandler updateHandler; + if (UpdateType.AGENT == updateType) { + updateHandler = m_control.getAgentUpdateHandler(); + } + else { + updateHandler = m_control.getDeploymentHandler(); + } + return updateHandler; + } + + /** + * Synchronizes the agent's feedback with the server by retrieving all feedback channels and sending their + * feedback to the server in turn. + *

+ * In case an exception occurs during this check, it is logged and the method returns (early). No exceptions are + * propagated. In production code, a little more sophisticated error checking should be performed. + *

+ */ + private void sendFeedbackToServer() { + try { + FeedbackHandler feedbackHandler = m_control.getFeedbackHandler(); + Set channelNames = feedbackHandler.getChannelNames(); + for (String channelName : channelNames) { + FeedbackChannel channel = feedbackHandler.getChannel(channelName); + + System.out.printf("Synchronizing feedback of %s with server...%n", channelName); + + channel.sendFeedback(); + } + } + catch (Exception exception) { + System.out.printf("Feedback synchronization failed with %s.%n", exception.getMessage()); + exception.printStackTrace(System.out); + } + } + } + + /** + * Stub servlet that acts as an ACE server for our agent. Does only the bare minimum with respect to a complete + * server. + */ + static class StubDeploymentServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + private final Map m_packages = new HashMap(); + private final String m_agentId; + + public StubDeploymentServlet(String agentId, TestPackage... testPackages) { + m_agentId = agentId; + + for (TestPackage testPackage : testPackages) { + m_packages.put(testPackage.getVersion().toString(), testPackage); + } + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String pathInfo = req.getPathInfo(); + + if (pathInfo.startsWith("/auditlog/query")) { + resp.setContentType("text/plain"); + PrintWriter writer = resp.getWriter(); + writer.println(req.getParameter("tid") + "," + req.getParameter("logid") + ",0-10"); + writer.close(); + } + else if (pathInfo.startsWith("/deployment/")) { + String pathinfoTail = pathInfo.replaceFirst("/deployment/" + m_agentId + "/versions/?", ""); + if (pathinfoTail.equals("")) { + sendVersions(resp); + } + else { + TestPackage dpackage = m_packages.get(pathinfoTail); + if (dpackage == null) { + throw new IllegalStateException("Test error! Should never happen... " + pathinfoTail); + } + sendPackage(dpackage, req, resp); + } + } + else if (pathInfo.startsWith("/agent/")) { + String tail = pathInfo.replaceFirst("/agent/" + m_agentId + "/org.apache.ace.agent/versions/", ""); + if ("".equals(tail)) { + sendVersions(resp); + } + } + else { + resp.setContentLength(0); + resp.setStatus(HttpServletResponse.SC_OK); + } + resp.flushBuffer(); + } + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { + String pathInfo = request.getPathInfo(); + if (pathInfo.startsWith("/auditlog/")) { + InputStream is = request.getInputStream(); + while (is.read() != -1) { + } + is.close(); + } + response.setContentType("text/plain"); + response.flushBuffer(); + } + + @Override + protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/plain"); + response.flushBuffer(); + } + + private void sendPackage(TestPackage dpackage, HttpServletRequest req, HttpServletResponse resp) throws IOException { + InputStream is = null; + OutputStream os = null; + try { + is = new FileInputStream(dpackage.getFile()); + os = resp.getOutputStream(); + + int read; + byte[] buffer = new byte[4096]; + do { + read = is.read(buffer); + if (read >= 0) { + os.write(buffer, 0, read); + } + } + while (read >= 0); + } + finally { + if (is != null) { + is.close(); + } + if (os != null) { + os.close(); + } + } + } + + private void sendVersions(HttpServletResponse resp) throws IOException { + PrintWriter writer = resp.getWriter(); + for (String version : m_packages.keySet()) { + writer.println(version); + } + writer.close(); + + resp.setContentType("text/plain"); + resp.setStatus(200); + resp.flushBuffer(); + } + } + + /** + * Denotes the kind of update. + */ + static enum UpdateType { + AGENT, DEPLOYMENT; + } + + private static final Version V1_0_0 = Version.parseVersion("1.0.0"); + private static final String TEST_BUNDLE_NAME_PREFIX = "test.bundle"; + private static final String AGENT_ID = "defaultTargetID"; + + // Injected by Felix DM... + private volatile HttpService m_http; + private volatile AgentControl m_control; + private volatile AgentUser m_agentUser; + + /** + * Tests that we can provide a custom controller implementation based on the following use-case: + *

+ * The agent should check for updates, and if found, ask the user whether it should proceed to download this update. + * If confirmed, the download of the update is started, and when complete, the user is asked once more whether to + * proceed with the installation of the update. + *

+ * + * @see CustomController + */ + public void testCustomController() throws Exception { + CustomController controller = new CustomController(); + + Thread thread = new Thread(controller); + thread.start(); + + try { + waitForInstalledVersion(V1_0_0); + } + finally { + controller.stop(); + thread.join(); + } + } + + @Override + protected void configureAdditionalServices() throws Exception { + configureAgent(m_control.getConfigurationHandler(), AgentConstants.CONFIG_CONTROLLER_DISABLED, "true"); + + TestBundle bundle1v1 = new TestBundle(TEST_BUNDLE_NAME_PREFIX.concat("1"), V1_0_0); + TestPackage package1 = new TestPackage(AGENT_ID, V1_0_0, bundle1v1); + + StubDeploymentServlet servlet = new StubDeploymentServlet(AGENT_ID, package1); + + String url = String.format("http://localhost:%d/", TestConstants.PORT); + NetUtils.waitForURL(url, 404, 10000); + + m_http.registerServlet("/", servlet, null, null); + + NetUtils.waitForURL(url, 200, 10000); + } + + @Override + protected void configureProvisionedServices() throws Exception { + m_bundleContext.registerService(AgentUser.class.getName(), new AcknowledgingAgentUser(), null); + } + + @Override + protected void doTearDown() throws Exception { + // Remove all provisioned components... + m_dependencyManager.clear(); + + m_http.unregister("/"); + + // Force an uninstall of all remaining test bundles... + for (Bundle bundle : m_bundleContext.getBundles()) { + String bsn = bundle.getSymbolicName(); + if (bsn.startsWith(TEST_BUNDLE_NAME_PREFIX)) { + bundle.uninstall(); + } + } + } + + @Override + protected Component[] getDependencies() { + return new Component[] { + createComponent() + .setImplementation(this) + .add(createServiceDependency().setService(HttpService.class).setRequired(true)) + .add(createServiceDependency().setService(AgentControl.class).setRequired(true)) + .add(createServiceDependency().setService(AgentUser.class).setRequired(true)) + }; + } + + private void waitForInstalledVersion(Version version) throws Exception { + DeploymentHandler deploymentHandler = m_control.getDeploymentHandler(); + + int timeout = 100; + while (!deploymentHandler.getInstalledVersion().equals(version)) { + Thread.sleep(100); + if (timeout-- <= 0) { + fail("Timed out while waiting for deployment " + version); + } + } + } +} Propchange: ace/trunk/org.apache.ace.agent.controller.itest/src/org/apache/ace/agent/itest/CustomAgentControllerTest.java ------------------------------------------------------------------------------ svn:eol-style = native Added: ace/trunk/org.apache.ace.agent.controller.itest/test/.gitignore URL: http://svn.apache.org/viewvc/ace/trunk/org.apache.ace.agent.controller.itest/test/.gitignore?rev=1546342&view=auto ============================================================================== (empty)