sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From pa...@apache.org
Subject svn commit: r1783162 - in /sling/trunk/installer: core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java it/pom.xml it/src/test/java/org/apache/sling/installer/it/BundleInstallUpgradeExceptionRetryTest.java
Date Wed, 15 Feb 2017 23:17:26 GMT
Author: pauls
Date: Wed Feb 15 23:17:25 2017
New Revision: 1783162

URL: http://svn.apache.org/viewvc?rev=1783162&view=rev
Log:
Make the BundleUpdateTask retry failed bundle updates (SLING-5457).

Added:
    sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/BundleInstallUpgradeExceptionRetryTest.java
Modified:
    sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java
    sling/trunk/installer/it/pom.xml

Modified: sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java?rev=1783162&r1=1783161&r2=1783162&view=diff
==============================================================================
--- sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java
(original)
+++ sling/trunk/installer/core/src/main/java/org/apache/sling/installer/core/impl/tasks/BundleUpdateTask.java
Wed Feb 15 23:17:25 2017
@@ -36,6 +36,11 @@ public class BundleUpdateTask extends Ab
 
     private static final String BUNDLE_UPDATE_ORDER = "50-";
 
+    private static final int MAX_RETRIES = 5;
+    
+    // keep track of retry attempts via temporary attribute stored in taskresource
+    private String ATTR_UPDATE_RETRY = "org.apache.sling.installer.core.impl.tasks.BundleUpdateTask.retrycount";
+
     public BundleUpdateTask(final TaskResourceGroup r,
                             final TaskSupport creator) {
         super(r, creator);
@@ -73,17 +78,17 @@ public class BundleUpdateTask extends Ab
 
         // Do not update if same version, unless snapshot
         boolean snapshot = false;
-    	final Version currentVersion = b.getVersion();
-    	snapshot = BundleInfo.isSnapshot(newVersion);
-    	if (currentVersion.equals(newVersion) && !snapshot) {
-    	    // TODO : Isn't this already checked in the task creator?
-    	    String message = MessageFormat.format("Same version is already installed, and not
a snapshot, ignoring update: {0}", getResource());
-    	    this.getLogger().debug(message);
-    	    this.setFinishedState(ResourceState.INSTALLED, null, message);
-    		return;
-    	}
+        final Version currentVersion = b.getVersion();
+        snapshot = BundleInfo.isSnapshot(newVersion);
+        if (currentVersion.equals(newVersion) && !snapshot) {
+            // TODO : Isn't this already checked in the task creator?
+            String message = MessageFormat.format("Same version is already installed, and
not a snapshot, ignoring update: {0}", getResource());
+            this.getLogger().debug(message);
+            this.setFinishedState(ResourceState.INSTALLED, null, message);
+            return;
+        }
 
-    	try {
+        try {
             // If the bundle is active before the update - restart it once updated, but
             // in sequence, not right now
             final boolean reactivate = this.isBundleActive(b);
@@ -131,11 +136,24 @@ public class BundleUpdateTask extends Ab
             } else {
                 this.setFinishedState(ResourceState.INSTALLED);
             }
-    	} catch (final Exception e) {
-    	    String message = MessageFormat.format("Removing failing update task due to {0} -
unable to retry: {1}", e.getLocalizedMessage(), this);
-            this.getLogger().warn(message, e);
-            this.setFinishedState(ResourceState.IGNORED, null, message);
-    	}
+        } catch (final Exception e) {
+            int retries = 0;
+            Object obj = getResource().getTemporaryAttribute(ATTR_UPDATE_RETRY);
+            if (obj instanceof Integer) {
+                retries = (Integer) obj;
+            }
+            getResource().setTemporaryAttribute(ATTR_UPDATE_RETRY, Integer.valueOf(++retries));
+            if (retries > MAX_RETRIES) {
+                String message = MessageFormat.format("Removing failing update task due to
{0} - unable to retry: {1}",
+                    e.getLocalizedMessage(), this);
+                this.getLogger().error(message, e);
+                this.setFinishedState(ResourceState.IGNORED, null, message);
+            } else {
+                String message = MessageFormat.format("Failing update task due to {0} - will
retry up to {1} more time(s) for {2} later", 
+                    e.getLocalizedMessage(), MAX_RETRIES - (retries - 1) , this);
+                this.getLogger().warn(message, e);
+            }
+        }
     }
 
     @Override

Modified: sling/trunk/installer/it/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/installer/it/pom.xml?rev=1783162&r1=1783161&r2=1783162&view=diff
==============================================================================
--- sling/trunk/installer/it/pom.xml (original)
+++ sling/trunk/installer/it/pom.xml Wed Feb 15 23:17:25 2017
@@ -26,7 +26,7 @@
     </parent>
 
     <artifactId>org.apache.sling.installer.it</artifactId>
-    <version>3.8.1-SNAPSHOT</version>
+    <version>3.8.3-SNAPSHOT</version>
     <packaging>jar</packaging>
 
     <name>Apache Sling Installer Integration Tests</name>

Added: sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/BundleInstallUpgradeExceptionRetryTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/BundleInstallUpgradeExceptionRetryTest.java?rev=1783162&view=auto
==============================================================================
--- sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/BundleInstallUpgradeExceptionRetryTest.java
(added)
+++ sling/trunk/installer/it/src/test/java/org/apache/sling/installer/it/BundleInstallUpgradeExceptionRetryTest.java
Wed Feb 15 23:17:25 2017
@@ -0,0 +1,215 @@
+/*
+ * 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.sling.installer.it;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.framework.Version;
+
+@RunWith(PaxExam.class)
+public class BundleInstallUpgradeExceptionRetryTest extends OsgiInstallerTestBase {
+
+	@org.ops4j.pax.exam.Configuration
+	public Option[] config() {
+		return defaultConfiguration();
+	}
+
+	@Before
+	public void beforeTest() throws Exception {
+		setupInstaller();
+
+		final Object listener = this.startObservingBundleEvents();
+		installer.updateResources(URL_SCHEME, getInstallableResource(createTestBundle(new Version("1.0.0"))),
null);
+
+		this.waitForBundleEvents(symbolicName + " should be installed with version 1.0.0", listener,
+				new BundleEvent(symbolicName, "1.0.0", org.osgi.framework.BundleEvent.STARTED));
+
+		assertBundle("After installing", symbolicName, "1.0.0", Bundle.ACTIVE);
+	}
+
+	static final String symbolicName = "osgi-installer-testbundle";
+
+	@After
+	public void afterTest() throws BundleException {
+		super.tearDown();
+	}
+
+	@Test
+	public void testUpdateFailsWithMoreThanMaxRetrys() throws Exception {
+		// install version 1.1 and fail activation with exception all attempts
+		final Object listener = this.startObservingBundleEvents();
+		final AtomicReference<ServiceRegistration<AtomicInteger>> ref = new AtomicReference<ServiceRegistration<AtomicInteger>>(
+				null);
+		final AtomicInteger counter = new AtomicInteger(5);
+		bundleContext.addBundleListener(new SynchronousBundleListener() {
+
+			@Override
+			public void bundleChanged(org.osgi.framework.BundleEvent event) {
+				if (event.getType() == org.osgi.framework.BundleEvent.STOPPED
+						&& event.getBundle().getSymbolicName().equals(symbolicName)
+						&& event.getBundle().getVersion().equals(new Version("1.0.0"))) {
+					System.out.println(event.getSource() + " " + event.getType());
+					Thread.dumpStack();
+					if (ref.get() == null) {
+						try {
+							event.getBundle().start();
+							ref.set(bundleContext.registerService(AtomicInteger.class, counter, null));
+						} catch (BundleException e) {
+							// TODO Auto-generated catch block
+							e.printStackTrace();
+						}
+					} else {
+						ref.getAndSet(null).unregister();
+						if (counter.get() == 0) {
+							bundleContext.removeBundleListener(this);
+						}
+					}
+				}
+			}
+		});
+
+		installer.updateResources(URL_SCHEME, getInstallableResource(createTestBundle(new Version("2.0.0"))),
null);
+
+		try {
+			long time = 0;
+			while (counter.get() >= 0 && time < 1000) {
+				sleep(100);
+				time += 100;
+			}
+
+			assertBundle("After installing", symbolicName, "1.0.0", Bundle.ACTIVE);
+		} finally {
+			if (ref.get() != null) {
+				ref.get().unregister();
+			}
+		}
+	}
+
+	@Test
+	public void testUpdateSuccedsWithLessThanMaxRetrys() throws Exception {
+		// install version 1.1 and fail activation with exception all attempts
+		final Object listener = this.startObservingBundleEvents();
+		final AtomicReference<ServiceRegistration<AtomicInteger>> ref = new AtomicReference<ServiceRegistration<AtomicInteger>>(
+				null);
+		final AtomicInteger counter = new AtomicInteger(3);
+		bundleContext.addBundleListener(new SynchronousBundleListener() {
+
+			@Override
+			public void bundleChanged(org.osgi.framework.BundleEvent event) {
+				if (event.getType() == org.osgi.framework.BundleEvent.STOPPED
+						&& event.getBundle().getSymbolicName().equals(symbolicName)
+						&& event.getBundle().getVersion().equals(new Version("1.0.0"))) {
+					if (ref.get() == null) {
+						try {
+							event.getBundle().start();
+							ref.set(bundleContext.registerService(AtomicInteger.class, counter, null));
+						} catch (BundleException e) {
+						}
+					} else {
+						ref.getAndSet(null).unregister();
+						if (counter.get() == 0) {
+							bundleContext.removeBundleListener(this);
+						}
+					}
+				}
+			}
+		});
+
+		installer.updateResources(URL_SCHEME, getInstallableResource(createTestBundle(new Version("2.0.0"))),
null);
+
+		try {
+			long time = 0;
+			while (counter.get() >= 0 && time < 1000) {
+				sleep(100);
+				time += 100;
+			}
+
+			assertBundle("After installing", symbolicName, "2.0.0", Bundle.ACTIVE);
+		} finally {
+			if (ref.get() != null) {
+				ref.get().unregister();
+			}
+		}
+	}
+
+	private static File createTestBundle(Version version) throws IOException {
+		String manifest = "Bundle-SymbolicName: " + symbolicName + "\n" + "Bundle-Version: " +
version + "\n"
+				+ "Bundle-ManifestVersion: 2\n" + "Bundle-Activator: " + ThrowingActivator.class.getName()
+ "\n"
+				+ "Import-Package: org.osgi.framework\n" + "Manifest-Version: 1.0\n\n";
+		return createBundle("testbundle", manifest, ThrowingActivator.class);
+	}
+
+	private static File createBundle(String name, String manifest, Class... classes) throws
IOException {
+		File f = File.createTempFile(name, ".jar");
+		f.deleteOnExit();
+
+		Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
+		JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
+
+		for (Class clazz : classes) {
+			String path = clazz.getName().replace('.', '/') + ".class";
+			os.putNextEntry(new ZipEntry(path));
+
+			InputStream is = clazz.getClassLoader().getResourceAsStream(path);
+			byte[] buffer = new byte[8 * 1024];
+			for (int i = is.read(buffer); i != -1; i = is.read(buffer)) {
+				os.write(buffer, 0, i);
+			}
+			is.close();
+			os.closeEntry();
+		}
+		os.close();
+		return f;
+	}
+
+	public static class ThrowingActivator implements BundleActivator {
+		@Override
+		public void start(BundleContext context) throws Exception {
+
+		}
+
+		@Override
+		public void stop(BundleContext context) throws Exception {
+			ServiceReference<AtomicInteger> ref = context.getServiceReference(AtomicInteger.class);
+			if (ref != null && context.getService(ref).getAndDecrement() >= 0) {
+				throw new Exception("Force exception for update");
+			}
+		}
+	}
+}



Mime
View raw message