Author: bdelacretaz
Date: Fri Aug 14 09:42:59 2009
New Revision: 804142
URL: http://svn.apache.org/viewvc?rev=804142&view=rev
Log:
SLING-1078 - client must supply digest for InstallableResource that wraps an
InputStream
Added:
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java
- copied, changed from r804107,
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiController.java
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java
- copied, changed from r804107,
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java
Removed:
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiController.java
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java
sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/InputStreamDigestTest.java
Modified:
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/InstallableResource.java
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java
sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java
sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/Utilities.java
Modified:
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/InstallableResource.java
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/InstallableResource.java?rev=804142&r1=804141&r2=804142&view=diff
==============================================================================
---
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/InstallableResource.java
(original)
+++
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/InstallableResource.java
Fri Aug 14 09:42:59 2009
@@ -28,6 +28,7 @@
public class InstallableResource {
private final String url;
private final String extension;
+ private final String digest;
private final InputStream inputStream;
private final Dictionary<String, Object> dictionary;
@@ -37,22 +38,41 @@
this.extension = getExtension(url);
this.inputStream = null;
this.dictionary = null;
+ this.digest = null;
}
- /** Create a data object that wraps an InputStream */
- public InstallableResource(String url, InputStream is) {
+ /** Create a data object that wraps an InputStream
+ * @param url unique URL of the supplied data, must start with the
scheme used
+ * {...@link OsgiInstaller#registerResources} call
+ * @param is the resource contents
+ * @param digest must be supplied by client. Does not need to be an
actual digest
+ * of the contents, but must change if the contents change. Having
this supplied
+ * by the client avoids having to compute real digests to find out
if a resource
+ * has changed, which can be expensive.
+ */
+ public InstallableResource(String url, InputStream is, String digest) {
this.url = url;
this.extension = getExtension(url);
this.inputStream = is;
this.dictionary = null;
+ this.digest = digest;
}
- /** Create a data object that wraps a Dictionary */
+ /** Create a data object that wraps a Dictionary. Digest will be
computed
+ * by the installer in this case, as configuration dictionaries are
+ * usually small so computing a real digest to find out if they changed
+ * is ok.
+ *
+ * @param url unique URL of the supplied data, must start with the scheme
used
+ * {...@link OsgiInstaller#registerResources} call
+ * @param is the resource contents
+ */
public InstallableResource(String url, Dictionary<String, Object> d) {
this.url = url;
this.extension = getExtension(url);
this.inputStream = null;
this.dictionary = d;
+ this.digest = null;
}
@Override
@@ -66,9 +86,9 @@
return (pos < 0 ? "" : url.substring(pos+1));
}
- /** Return this data's URL. It is opaque for the {...@link
OsgiController}
+ /** Return this data's URL. It is opaque for the {...@link
OsgiInstaller}
* but the scheme must be the one used in the
- * {...@link OsgiController#registerResources} call.
+ * {...@link OsgiInstaller#registerResources} call.
*/
public String getURL() {
return url;
@@ -89,4 +109,8 @@
public Dictionary<String, Object> getDictionary() {
return dictionary;
}
+
+ public String getDigest() {
+ return digest;
+ }
}
Copied:
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java
(from r804107,
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiController.java)
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java?p2=sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java&p1=sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiController.java&r1=804107&r2=804142&rev=804142&view=diff
==============================================================================
---
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiController.java
(original)
+++
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/OsgiInstaller.java
Fri Aug 14 09:42:59 2009
@@ -22,21 +22,23 @@
import java.util.Collection;
import java.util.Map;
-/** Controller that installs/updates/removes InstallableData
- * in the OSGi framework. The client can register a number of such
- * resources, and the controller decides based on the resource weights,
- * bundle version numbers, etc. which ones are actually installed.
+/** OSGi Service that installs/updates/removes InstallableData
+ * in the OSGi framework.
+ *
+ * The client can register a number of such resources, and the
+ * installer decides based on the resource weights, bundle version
+ * numbers, etc. which ones are actually installed.
*
* An InstallableResource can be a bundle, a configuration, and later
* we might support deployment packages as well.
*/
-public interface OsgiController {
+public interface OsgiInstaller {
- /** Provide the controller with the complete list of installable
+ /** Provide the installer with the complete list of installable
* resources for a given client.
*
- * Client must call this at startup and/or when the controller
- * service becomes available. The controller stores the list of
+ * Client must call this at startup and/or when the installer
+ * service becomes available. The installer stores the list of
* previously registered/added resources, compares with the new
* list and removes resources that have disappeared.
*
@@ -46,14 +48,14 @@
*/
void registerResources(Collection<InstallableResource> data, String
urlScheme) throws IOException;
- /** Inform the controller that a resource is available for installation.
+ /** Inform the installer that a resource is available for installation.
* also called if the resource has been modified since it was
registered.
*/
void addResource(InstallableResource d) throws IOException;
- /** Inform the controller that a resource is no longer available */
+ /** Inform the installer that a resource is no longer available */
void removeResource(InstallableResource d) throws IOException;
/** Return counters used for statistics, console display, testing, etc.
*/
Map<String, Long> getCounters();
-}
+}
\ No newline at end of file
Modified:
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java?rev=804142&r1=804141&r2=804142&view=diff
==============================================================================
---
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java
(original)
+++
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/Activator.java
Fri Aug 14 09:42:59 2009
@@ -20,7 +20,7 @@
import java.util.Hashtable;
-import org.apache.sling.osgi.installer.OsgiController;
+import org.apache.sling.osgi.installer.OsgiInstaller;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
@@ -43,7 +43,7 @@
private ServiceTracker startLevelTracker;
private ServiceTracker packageAdminTracker;
private ServiceTracker logServiceTracker;
- private OsgiControllerImpl osgiControllerService;
+ private OsgiInstallerImpl osgiControllerService;
private ServiceRegistration osgiControllerServiceReg;
private static long eventsCount;
@@ -71,11 +71,11 @@
// Assume PackageAdmin is available before this bundle is started.
// That's the case when using Felix OSGi, not sure about other
frameworks.
- this.osgiControllerService = new OsgiControllerImpl(context,
+ this.osgiControllerService = new OsgiInstallerImpl(context,
(PackageAdmin)checkNotNull(this.packageAdminTracker.getService(),
"PackageAdmin"),
logServiceTracker);
final String [] serviceInterfaces = {
- OsgiController.class.getName()
+ OsgiInstaller.class.getName()
};
osgiControllerServiceReg =
context.registerService(serviceInterfaces, osgiControllerService, props);
}
Copied:
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java
(from r804107,
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java)
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java?p2=sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java&p1=sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java&r1=804107&r2=804142&rev=804142&view=diff
==============================================================================
---
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiControllerImpl.java
(original)
+++
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/OsgiInstallerImpl.java
Fri Aug 14 09:42:59 2009
@@ -24,7 +24,7 @@
import java.util.Map;
import org.apache.sling.osgi.installer.InstallableResource;
-import org.apache.sling.osgi.installer.OsgiController;
+import org.apache.sling.osgi.installer.OsgiInstaller;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationAdmin;
@@ -32,15 +32,15 @@
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.util.tracker.ServiceTracker;
-/** OsgiController service */
-public class OsgiControllerImpl implements OsgiController,
OsgiControllerContext {
+/** OsgiInstaller service implementation */
+public class OsgiInstallerImpl implements OsgiInstaller, OsgiControllerContext
{
private final BundleContext bundleContext;
private final PackageAdmin packageAdmin;
private final ServiceTracker logServiceTracker;
private Map<String, Long> counters = new HashMap<String, Long>();
- public OsgiControllerImpl(final BundleContext bc,
+ public OsgiInstallerImpl(final BundleContext bc,
final PackageAdmin pa,
final ServiceTracker logServiceTracker)
throws IOException {
@@ -52,7 +52,7 @@
public void deactivate() {
if(getLogService() != null) {
getLogService().log(LogService.LOG_WARNING,
- OsgiController.class.getName()
+ OsgiInstaller.class.getName()
+ " service deactivated - this warning can be ignored if
system is shutting down");
}
}
Modified:
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java?rev=804142&r1=804141&r2=804142&view=diff
==============================================================================
---
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java
(original)
+++
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/RegisteredResource.java
Fri Aug 14 09:42:59 2009
@@ -31,8 +31,12 @@
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Dictionary;
+import java.util.Enumeration;
import java.util.Hashtable;
+import java.util.List;
import java.util.Map;
import java.util.Properties;
@@ -58,6 +62,8 @@
public RegisteredResource(BundleContext ctx, InstallableResource input)
throws IOException {
url = input.getUrl();
+ // TODO if input.url ends with a "config" extension, convert to
dictionary
+
try {
if(input.getDictionary() == null) {
dictionary = null;
@@ -65,12 +71,16 @@
throw new
IllegalArgumentException("input provides no Dictionary and no InputStream:" +
input);
} else {
dataFile = getDataFile(ctx);
- digest =
copyToLocalStorage(input.getInputStream(), dataFile);
+
copyToLocalStorage(input.getInputStream(), dataFile);
+ digest = input.getDigest();
+ if(digest == null || digest.length() ==
0) {
+ throw new IllegalArgumentException(
+ "Digest must be supplied
for an InstallableResource that wraps an InputStream");
+ }
}
} else {
- // TODO Copy dictionary
dataFile = null;
- dictionary = input.getDictionary();
+ dictionary = copy(input.getDictionary());
digest = computeDigest(dictionary);
}
@@ -135,16 +145,14 @@
return new String(bigInt.toString(16));
}
- /** Copy data to local storage and return digest */
- private String copyToLocalStorage(InputStream data, File f) throws
IOException, NoSuchAlgorithmException {
- final MessageDigest d = MessageDigest.getInstance(DIGEST_TYPE);
+ /** Copy data to local storage */
+ private void copyToLocalStorage(InputStream data, File f) throws
IOException, NoSuchAlgorithmException {
final OutputStream os = new BufferedOutputStream(new
FileOutputStream(f));
try {
final byte[] buffer = new byte[16384];
int count = 0;
while( (count = data.read(buffer, 0, buffer.length)) >
0) {
os.write(buffer, 0, count);
- d.update(buffer, 0, count);
}
os.flush();
} finally {
@@ -152,7 +160,6 @@
os.close();
}
}
- return digestToString(d);
}
/** Convert InputStream to Dictionary using our extended properties
format,
@@ -169,4 +176,19 @@
}
return result;
}
+
+ /** Copy given Dictionary, sorting keys */
+ static Dictionary<String, Object> copy(Dictionary<String, Object> d) {
+ final Dictionary<String, Object> result = new Hashtable<String,
Object>();
+ final List<String> keys = new ArrayList<String>();
+ final Enumeration<String> e = d.keys();
+ while(e.hasMoreElements()) {
+ keys.add(e.nextElement());
+ }
+ Collections.sort(keys);
+ for(String key : keys) {
+ result.put(key, d.get(key));
+ }
+ return result;
+ }
}
Modified:
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java?rev=804142&r1=804141&r2=804142&view=diff
==============================================================================
---
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java
(original)
+++
sling/trunk/installer/osgi/installer/src/main/java/org/apache/sling/osgi/installer/impl/tasks/BundleInstallRemoveTask.java
Fri Aug 14 09:42:59 2009
@@ -24,7 +24,7 @@
import java.util.jar.Manifest;
import org.apache.sling.osgi.installer.impl.OsgiControllerContext;
-import org.apache.sling.osgi.installer.impl.OsgiControllerImpl;
+import org.apache.sling.osgi.installer.impl.OsgiInstallerImpl;
import org.apache.sling.osgi.installer.impl.RegisteredResource;
import org.apache.sling.osgi.installer.impl.Storage;
import org.osgi.framework.Bundle;
Modified:
sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java?rev=804142&r1=804141&r2=804142&view=diff
==============================================================================
---
sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java
(original)
+++
sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/RegisteredResourceTest.java
Fri Aug 14 09:42:59 2009
@@ -20,11 +20,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Hashtable;
import org.apache.sling.osgi.installer.InstallableResource;
@@ -49,7 +51,7 @@
}
final TestInputStream t = new TestInputStream(new
ByteArrayInputStream(data.getBytes()));
- final InstallableResource ir = new InstallableResource(data, t);
+ final InstallableResource ir = new InstallableResource(data, t,
"somedigest");
assertEquals("TestInputStream must not be closed before test",
0, t.closeCount);
new LocalFileRegisteredResource(ir);
assertEquals("TestInputStream must be closed by
RegisteredResource", 1, t.closeCount);
@@ -58,9 +60,42 @@
@org.junit.Test public void testLocalFileCopy() throws Exception {
final String data = "This is some data";
final InputStream in = new
ByteArrayInputStream(data.getBytes());
- final LocalFileRegisteredResource r = new
LocalFileRegisteredResource(new InstallableResource(data, in));
+ final LocalFileRegisteredResource r = new
LocalFileRegisteredResource(new InstallableResource(data, in, "somedigest"));
assertTrue("Local file exists", r.getDataFile(null).exists());
assertEquals("Local file length matches our data",
data.getBytes().length, r.getDataFile(null).length());
}
-}
+ @org.junit.Test public void testMissingDigest() throws Exception {
+ final String data = "This is some data";
+ final InputStream in = new ByteArrayInputStream(data.getBytes());
+ try {
+ new LocalFileRegisteredResource(new InstallableResource(data, in,
null));
+ fail("Expected an IllegalArgumentException as digest is null");
+ } catch(IllegalArgumentException asExpected) {
+ }
+ }
+
+ @org.junit.Test public void testDictionaryDigestOutOfOrderData() throws
Exception {
+ final Hashtable<String, Object> d1 = new Hashtable<String, Object>();
+ final Hashtable<String, Object> d2 = new Hashtable<String, Object>();
+
+ final String [] keys = { "foo", "bar", "something" };
+ for(int i=0 ; i < keys.length; i++) {
+ d1.put(keys[i], keys[i] + "." + keys[i]);
+ }
+ for(int i=keys.length - 1 ; i >= 0; i--) {
+ d2.put(keys[i], keys[i] + "." + keys[i]);
+ }
+
+ final RegisteredResource r1 = new RegisteredResource(null, new
InstallableResource("url1", d1));
+ final RegisteredResource r2 = new RegisteredResource(null, new
InstallableResource("url1", d2));
+
+ assertEquals(
+ "Two RegisteredResource with same values but different key
orderings must have the same key",
+ r1.getDigest(),
+ r2.getDigest()
+ );
+
+ // TODO do the same test starting with an InputStream, for configs
+ }
+}
\ No newline at end of file
Modified:
sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/Utilities.java
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/Utilities.java?rev=804142&r1=804141&r2=804142&view=diff
==============================================================================
---
sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/Utilities.java
(original)
+++
sling/trunk/installer/osgi/installer/src/test/java/org/apache/sling/osgi/installer/impl/Utilities.java
Fri Aug 14 09:42:59 2009
@@ -30,7 +30,7 @@
return result;
}
- static void setStorage(OsgiControllerImpl c, Storage s) throws Exception {
+ static void setStorage(OsgiInstallerImpl c, Storage s) throws Exception {
final Field f = c.getClass().getDeclaredField("storage");
f.setAccessible(true);
f.set(c, s);