blob: b6e0c6a95906df2a130bc68f19609948fbd3d955 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2021 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.tests.bundles;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import junit.framework.Test;
import junit.framework.TestSuite;
import org.eclipse.core.runtime.Platform;
import org.eclipse.osgi.service.urlconversion.URLConverter;
import org.eclipse.osgi.tests.OSGiTestsActivator;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.hooks.bundle.CollisionHook;
import org.osgi.framework.wiring.BundleWiring;
public class BundleInstallUpdateTests extends AbstractBundleTests {
public static Test suite() {
return new TestSuite(BundleInstallUpdateTests.class);
}
// test installing with location
public void testInstallWithLocation01() {
Bundle test = null;
try {
String location = installer.getBundleLocation("test"); //$NON-NLS-1$
test = OSGiTestsActivator.getContext().installBundle(location);
assertEquals("Wrong BSN", "test1", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
} catch (BundleException e) {
fail("Unexpected failure", e); //$NON-NLS-1$
} finally {
try {
if (test != null)
test.uninstall();
} catch (BundleException e) {
// nothing
}
}
}
// test installing with location and null stream
public void testInstallWithLocation02() {
Bundle test = null;
try {
String location = installer.getBundleLocation("test"); //$NON-NLS-1$
test = OSGiTestsActivator.getContext().installBundle(location, null);
assertEquals("Wrong BSN", "test1", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
} catch (BundleException e) {
fail("Unexpected failure", e); //$NON-NLS-1$
} finally {
try {
if (test != null)
test.uninstall();
} catch (BundleException e) {
// nothing
}
}
}
// test installing with location and non-null stream
public void testInstallWithStream03() {
Bundle test = null;
try {
String location1 = installer.getBundleLocation("test"); //$NON-NLS-1$
String location2 = installer.getBundleLocation("test2"); //$NON-NLS-1$
test = OSGiTestsActivator.getContext().installBundle(location1, new URL(location2).openStream());
assertEquals("Wrong BSN", "test2", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
} catch (Exception e) {
fail("Unexpected failure", e); //$NON-NLS-1$
} finally {
try {
if (test != null)
test.uninstall();
} catch (BundleException e) {
// nothing
}
}
}
// test update with null stream
public void testUpdateNoStream01() {
Bundle test = null;
try {
String location = installer.getBundleLocation("test"); //$NON-NLS-1$
test = OSGiTestsActivator.getContext().installBundle(location);
assertEquals("Wrong BSN", "test1", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
test.update();
assertEquals("Wrong BSN", "test1", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
} catch (BundleException e) {
fail("Unexpected failure", e); //$NON-NLS-1$
} finally {
try {
if (test != null)
test.uninstall();
} catch (BundleException e) {
// nothing
}
}
}
// test update with null stream
public void testUpdateNoStream02() {
Bundle test = null;
try {
String location = installer.getBundleLocation("test"); //$NON-NLS-1$
test = OSGiTestsActivator.getContext().installBundle(location);
assertEquals("Wrong BSN", "test1", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
test.update(null);
assertEquals("Wrong BSN", "test1", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
} catch (BundleException e) {
fail("Unexpected failure", e); //$NON-NLS-1$
} finally {
try {
if (test != null)
test.uninstall();
} catch (BundleException e) {
// nothing
}
}
}
// test update with null stream
public void testUpdateWithStream01() {
Bundle test = null;
try {
String location1 = installer.getBundleLocation("test"); //$NON-NLS-1$
String location2 = installer.getBundleLocation("test2"); //$NON-NLS-1$
test = OSGiTestsActivator.getContext().installBundle(location1);
assertEquals("Wrong BSN", "test1", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
test.update(new URL(location2).openStream());
assertEquals("Wrong BSN", "test2", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
} catch (Exception e) {
fail("Unexpected failure", e); //$NON-NLS-1$
} finally {
try {
if (test != null)
test.uninstall();
} catch (BundleException e) {
// nothing
}
}
}
// test update with null stream
public void testUpdateWithStream02() {
Bundle test = null;
try {
String location1 = installer.getBundleLocation("test"); //$NON-NLS-1$
String location2 = installer.getBundleLocation("test2"); //$NON-NLS-1$
test = OSGiTestsActivator.getContext().installBundle(location1);
Bundle b1 = installer.installBundle("chain.test"); //$NON-NLS-1$
assertEquals("Wrong BSN", "test1", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
test.update(new URL(location2).openStream());
assertEquals("Wrong BSN", "test2", test.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$
// make sure b1 is still last bundle in bundles list
Bundle[] bundles = OSGiTestsActivator.getContext().getBundles();
assertTrue("Wrong bundle at the end: " + bundles[bundles.length - 1], bundles[bundles.length - 1] == b1); //$NON-NLS-1$
Bundle[] tests = installer.getPackageAdmin().getBundles(test.getSymbolicName(), null);
assertNotNull("null tests", tests); //$NON-NLS-1$
assertEquals("Wrong number", 1, tests.length); //$NON-NLS-1$
assertTrue("Wrong bundle: " + tests[0], tests[0] == test); //$NON-NLS-1$
} catch (Exception e) {
fail("Unexpected failure", e); //$NON-NLS-1$
} finally {
try {
if (test != null)
test.uninstall();
} catch (BundleException e) {
// nothing
}
}
}
public void testBug290193() {
Bundle test = null;
try {
URL testBundle = OSGiTestsActivator.getContext().getBundle().getEntry("test_files/security/bundles/signed.jar");
File testFile = OSGiTestsActivator.getContext().getDataFile("test with space/test.jar");
assertTrue(testFile.getParentFile().mkdirs());
readFile(testBundle.openStream(), testFile);
test = OSGiTestsActivator.getContext().installBundle("reference:" + testFile.toURI().toString());
} catch (Exception e) {
fail("Unexpected failure", e); //$NON-NLS-1$
} finally {
try {
if (test != null)
test.uninstall();
} catch (BundleException e) {
// nothing
}
}
}
public static void readFile(InputStream in, File file) throws IOException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
byte buffer[] = new byte[1024];
int count;
while ((count = in.read(buffer, 0, buffer.length)) > 0) {
fos.write(buffer, 0, count);
}
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ee) {
// nothing to do here
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException ee) {
// nothing to do here
}
}
}
}
public void testCollisionHook() throws BundleException, MalformedURLException, IOException {
Bundle test1 = installer.installBundle("test");
installer.installBundle("test2");
try {
test1.update(new URL(installer.getBundleLocation("test2")).openStream());
fail("Expected to fail to update to another bsn/version that causes collision");
} catch (BundleException e) {
// expected;
}
Bundle junk = null;
try {
junk = OSGiTestsActivator.getContext().installBundle("junk", new URL(installer.getBundleLocation("test2")).openStream());
fail("Expected to fail to install duplication bsn/version that causes collision");
} catch (BundleException e) {
// expected;
} finally {
if (junk != null)
junk.uninstall();
junk = null;
}
CollisionHook hook = (operationType, target, collisionCandidates) -> collisionCandidates.clear();
ServiceRegistration reg = OSGiTestsActivator.getContext().registerService(CollisionHook.class, hook, null);
try {
try {
test1.update(new URL(installer.getBundleLocation("test2")).openStream());
} catch (BundleException e) {
fail("Expected to succeed in updating to a duplicate bsn/version", e);
}
try {
junk = OSGiTestsActivator.getContext().installBundle("junk", new URL(installer.getBundleLocation("test2")).openStream());
} catch (BundleException e) {
fail("Expected to succeed to install duplication bsn/version that causes collision", e);
} finally {
if (junk != null)
junk.uninstall();
junk = null;
}
} finally {
reg.unregister();
}
}
public void testInstallWithInterruption() {
Bundle test = null;
Thread.currentThread().interrupt();
try {
test = installer.installBundle("test"); //$NON-NLS-1$
} catch (BundleException e) {
fail("Unexpected failure", e); //$NON-NLS-1$
} finally {
Thread.interrupted();
try {
if (test != null)
test.uninstall();
} catch (BundleException e) {
// nothing
}
}
}
public void testPercentLocation() {
doTestSpecialChars('%', false);
doTestSpecialChars('%', true);
}
public void testSpaceLocation() {
doTestSpecialChars(' ', false);
doTestSpecialChars(' ', true);
}
public void testPlusLocation() {
doTestSpecialChars('+', true);
doTestSpecialChars('+', false);
}
public void testOctothorpLocation() {
doTestSpecialChars('#', true);
// # must be encoded for anything to pass
doTestSpecialChars('#', false, false, false);
}
public void testQuestionMarkLocation() {
if (Platform.getOS().equals(Platform.OS_WIN32)) {
// Skip this test on windows
return;
}
doTestSpecialChars('?', true);
// ? must only be encoded for non-reference installs
doTestSpecialChars('?', false, true, false);
}
private void doTestSpecialChars(char c, boolean encode) {
doTestSpecialChars(c, encode, true, true);
}
private void doTestSpecialChars(char c, boolean encode, boolean refPass, boolean filePass) {
File bundlesDirectory = OSGiTestsActivator.getContext().getDataFile("file_with_" + c + "_char");
bundlesDirectory.mkdirs();
File testBundleJarFile = null;
String testBundleJarFileURL = null;
File testBundleDirFile = null;
String testBundleDirFileURL = null;
try {
testBundleJarFile = SystemBundleTests.createBundle(bundlesDirectory, getName() + 1, false, false);
testBundleJarFileURL = encode ? testBundleJarFile.toURI().toString() : testBundleJarFile.toURL().toString();
testBundleDirFile = SystemBundleTests.createBundle(bundlesDirectory, getName() + 2, false, true);
testBundleDirFileURL = encode ? testBundleDirFile.toURI().toString() : testBundleDirFile.toURL().toString();
} catch (IOException e) {
fail(e.getMessage());
}
String refToJarURL = "reference:" + testBundleJarFileURL;
// Test with reference stream to jar bundle
testInstallSpecialCharBundle(refToJarURL, true, refPass);
// Test with reference URL to jar bundle
testInstallSpecialCharBundle(refToJarURL, false, refPass);
// Test with reference URL to dir bundle
testInstallSpecialCharBundle("reference:" + testBundleDirFileURL, false, refPass);
// Test with jar bundle
testInstallSpecialCharBundle(testBundleJarFileURL, false, filePass);
// Test with dir bundle
testInstallSpecialCharBundle(testBundleDirFileURL, false, filePass);
}
void testInstallSpecialCharBundle(String location, boolean openStream, boolean expectSuccess) {
try {
Bundle b;
if (openStream) {
b = getContext().installBundle(location, new URL(location).openStream());
} else {
b = getContext().installBundle(location);
}
b.start();
b.uninstall();
if (!expectSuccess) {
fail("Should have failed for location: " + location);
}
} catch (Exception e) {
if (expectSuccess) {
fail("Should not have failed for location: " + location + " " + e.getMessage());
}
}
}
public void testPercentCharBundleEntry() throws IOException, BundleException {
doTestSpaceCharsBundleEntry('%');
}
public void testSpaceCharBundleEntry() throws IOException, BundleException {
doTestSpaceCharsBundleEntry(' ');
}
public void testPlusCharBundleEntry() throws IOException, BundleException {
doTestSpaceCharsBundleEntry('+');
}
public void doTestSpaceCharsBundleEntry(char c) throws IOException, BundleException {
String entryName = "file_with_" + c + "_char";
File bundlesDirectory = OSGiTestsActivator.getContext().getDataFile(getName());
bundlesDirectory.mkdirs();
Map<String, String> headers = new HashMap<>();
headers.put(Constants.BUNDLE_MANIFESTVERSION, "2");
headers.put(Constants.BUNDLE_SYMBOLICNAME, getName());
Map<String, String> entry = Collections.singletonMap(entryName, "value");
File testBundleJarFile = SystemBundleTests.createBundle(bundlesDirectory, getName(), headers, entry);
Bundle testBundle = getContext().installBundle(getName(), new FileInputStream(testBundleJarFile));
URL entryURL = testBundle.getEntry(entryName);
assertNotNull("Entry not found.", entryURL);
InputStream is = entryURL.openStream();
is.close();
String encodeEntry = URLEncoder.encode(entryName, "UTF-8");
String urlString = entryURL.toExternalForm();
urlString = urlString.substring(0, urlString.indexOf(entryName)) + encodeEntry;
URL encodedURL = new URL(urlString);
is = encodedURL.openStream();
is.close();
}
public static Method findDeclaredMethod(Class<?> clazz, String method, Class... args) throws NoSuchMethodException {
do {
try {
return clazz.getDeclaredMethod(method, args);
} catch (NoSuchMethodException e) {
clazz = clazz.getSuperclass();
}
} while (clazz != null);
throw new NoSuchMethodException(method);
}
public void testEscapeZipRoot() throws IOException, BundleException, InvalidSyntaxException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String entry1 = "../../escapedZipRoot1.txt";
String entry2 = "dir1/../../../escapedZipRoot2.txt";
String cp1 = "../../cp.jar";
String nativeCode = "../../lib/nativeCode";
File bundlesDirectory = OSGiTestsActivator.getContext().getDataFile(getName());
bundlesDirectory.mkdirs();
Map<String, String> headers = new HashMap<>();
headers.put(Constants.BUNDLE_MANIFESTVERSION, "2");
headers.put(Constants.BUNDLE_SYMBOLICNAME, getName());
headers.put(Constants.BUNDLE_CLASSPATH, "., " + cp1);
headers.put(Constants.BUNDLE_NATIVECODE, nativeCode);
Map<String, String> entries = new HashMap<>();
entries.put(entry1, "value");
entries.put(entry2, "value");
entries.put(cp1, "value");
entries.put(nativeCode, "value");
File testBundleJarFile = SystemBundleTests.createBundle(bundlesDirectory, getName(), headers, entries);
Bundle testBundle = getContext().installBundle(getName(), new FileInputStream(testBundleJarFile));
testBundle.start();
try {
testBundle.loadClass("does.not.exist.Test");
} catch (ClassNotFoundException e) {
// expected
}
Object cl = testBundle.adapt(BundleWiring.class).getClassLoader();
Method findLibrary = findDeclaredMethod(cl.getClass(), "findLibrary", String.class);
findLibrary.setAccessible(true);
assertNull("Found library.", findLibrary.invoke(cl, "nativeCode"));
URLConverter bundleURLConverter = getContext().getService(getContext().getServiceReferences(URLConverter.class, "(protocol=bundleentry)").iterator().next());
URL dir1 = bundleURLConverter.toFileURL(testBundle.getEntry("dir1/"));
File dir1File = new File(dir1.toExternalForm().substring(5));
File dir1EscapedFile2 = new File(dir1File, entry2.substring("dir1".length()));
assertFalse("File escaped zip root: " + dir1EscapedFile2.getCanonicalPath(), dir1EscapedFile2.exists());
URL root = bundleURLConverter.toFileURL(testBundle.getEntry("/"));
File rootFile = new File(root.toExternalForm().substring(5));
File rootEscapedFile1 = new File(rootFile, entry1);
assertFalse("File escaped zip root: " + rootEscapedFile1.getCanonicalPath(), rootEscapedFile1.exists());
File rootEscapedFile2 = new File(rootFile, entry2);
assertFalse("File escaped zip root: " + rootEscapedFile2.getCanonicalPath(), rootEscapedFile2.exists());
File rootEscapedFile3 = new File(rootFile, cp1);
assertFalse("File escaped zip root: " + rootEscapedFile3.getCanonicalPath(), rootEscapedFile3.exists());
}
}