/*************************GO-LICENSE-START********************************* * Copyright 2014 ThoughtWorks, Inc. * * Licensed 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. *************************GO-LICENSE-END***********************************/ package com.thoughtworks.go.plugin.infra.plugininfo; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.net.URISyntaxException; import java.net.URL; import java.util.jar.Attributes; import java.util.jar.Manifest; import com.googlecode.junit.ext.checkers.OSChecker; import com.thoughtworks.go.util.ZipUtil; import com.thoughtworks.go.plugin.activation.DefaultGoPluginActivator; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsNot.not; import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.assertThat; import static org.osgi.framework.Constants.BUNDLE_ACTIVATOR; import static org.osgi.framework.Constants.BUNDLE_CLASSPATH; import static org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME; public class GoPluginOSGiManifestTest { private static OSChecker WINDOWS = new OSChecker(OSChecker.WINDOWS); private static final File TMP_DIR = new File("./tmp"); private File manifestFile; private File bundleLocation; private File bundleDependencyDir; private GoPluginOSGiManifestGenerator goPluginOSGiManifestGenerator; @Before public void setUp() throws Exception { if (WINDOWS.satisfy()) { return; } bundleLocation = createPluginBundle("test-plugin-bundle"); manifestFile = new File(bundleLocation, "META-INF/MANIFEST.MF"); bundleDependencyDir = new File(bundleLocation, "lib"); goPluginOSGiManifestGenerator = new GoPluginOSGiManifest.DefaultGoPluginOSGiManifestCreator(); } @Test public void shouldCreateABundleManifestFromTheGivenPluginDescriptor() throws Exception { if (WINDOWS.satisfy()) { return; } assertThat(valueFor(BUNDLE_SYMBOLICNAME), is(nullValue())); assertThat(valueFor(BUNDLE_CLASSPATH), is(nullValue())); GoPluginDescriptor descriptor = GoPluginDescriptor.usingId("pluginId", "some-plugin.jar", bundleLocation, true); GoPluginOSGiManifest manifest = new GoPluginOSGiManifest(descriptor); manifest.update(); assertThat(valueFor(BUNDLE_SYMBOLICNAME), is("pluginId")); assertThat(valueFor(BUNDLE_ACTIVATOR), is(DefaultGoPluginActivator.class.getCanonicalName())); assertThat(valueFor(BUNDLE_CLASSPATH), is("lib/go-plugin-activator.jar,.,lib/dependency.jar")); assertThat(descriptor.bundleSymbolicName(), is("pluginId")); assertThat(descriptor.bundleClassPath(), is("lib/go-plugin-activator.jar,.,lib/dependency.jar")); assertThat(descriptor.bundleActivator(), is(DefaultGoPluginActivator.class.getCanonicalName())); } @Test public void shouldAddGoPluginActivatorJarToDependenciesOnlyOnceAtTheBeginning() throws Exception { if (WINDOWS.satisfy()) { return; } FileUtils.writeStringToFile(new File(bundleLocation, "lib/go-plugin-activator.jar"), "Some data"); GoPluginDescriptor descriptor = GoPluginDescriptor.usingId("pluginId", "some-plugin.jar", bundleLocation, true); goPluginOSGiManifestGenerator.updateManifestOf(descriptor); String classpathEntry = valueFor(BUNDLE_CLASSPATH); assertThat(classpathEntry, is("lib/go-plugin-activator.jar,.,lib/dependency.jar")); } @Test public void shouldCreateManifestWithProperClassPathForAllDependencyJarsInPluginDependenciesDirectory() throws Exception { if (WINDOWS.satisfy()) { return; } FileUtils.writeStringToFile(new File(bundleLocation, "lib/dependency-1.jar"), "Some data"); FileUtils.writeStringToFile(new File(bundleLocation, "lib/dependency-2.jar"), "Some data"); FileUtils.writeStringToFile(new File(bundleLocation, "lib/dependency-3.jar"), "Some data"); GoPluginDescriptor descriptor = GoPluginDescriptor.usingId("pluginId", "some-plugin.jar", bundleLocation, true); goPluginOSGiManifestGenerator.updateManifestOf(descriptor); assertThat(valueFor(BUNDLE_SYMBOLICNAME), is("pluginId")); String classpathEntry = valueFor(BUNDLE_CLASSPATH); assertThat(classpathEntry, startsWith("lib/go-plugin-activator.jar,.,")); assertThat(classpathEntry, containsString(",lib/dependency.jar")); assertThat(classpathEntry, containsString(",lib/dependency-1.jar")); assertThat(classpathEntry, containsString(",lib/dependency-2.jar")); assertThat(classpathEntry, containsString(",lib/dependency-3.jar")); } @Test public void shouldCreateManifestWithProperClassPathWhenDependencyDirDoesNotExist() throws Exception { if (WINDOWS.satisfy()) { return; } FileUtils.deleteDirectory(bundleDependencyDir); GoPluginDescriptor descriptor = GoPluginDescriptor.usingId("pluginId", "some-plugin.jar", bundleLocation, true); goPluginOSGiManifestGenerator.updateManifestOf(descriptor); assertThat(valueFor(BUNDLE_CLASSPATH), is("lib/go-plugin-activator.jar,.")); } @Test public void shouldMarkThePluginInvalidIfItsManifestAlreadyContainsSymbolicName() throws Exception { if (WINDOWS.satisfy()) { return; } addHeaderToManifest(BUNDLE_SYMBOLICNAME, "Dummy Value"); assertThat(valueFor(BUNDLE_SYMBOLICNAME), is(not(nullValue()))); assertThat(valueFor(BUNDLE_CLASSPATH), is(nullValue())); GoPluginDescriptor descriptor = GoPluginDescriptor.usingId("pluginId", "some-plugin.jar", bundleLocation, true); goPluginOSGiManifestGenerator.updateManifestOf(descriptor); assertThat(valueFor(BUNDLE_SYMBOLICNAME), is("Dummy Value")); assertThat(descriptor.isInvalid(), is(true)); } @Test public void shouldOverrideTheBundleClassPathInTheManifestIfItAlreadyHasIt() throws Exception { if (WINDOWS.satisfy()) { return; } addHeaderToManifest(BUNDLE_CLASSPATH, "Dummy Value"); assertThat(valueFor(BUNDLE_SYMBOLICNAME), is(nullValue())); assertThat(valueFor(BUNDLE_CLASSPATH), is(not(nullValue()))); GoPluginDescriptor descriptor = GoPluginDescriptor.usingId("pluginId", "some-plugin.jar", bundleLocation, true); goPluginOSGiManifestGenerator.updateManifestOf(descriptor); assertThat(valueFor(BUNDLE_CLASSPATH), is("lib/go-plugin-activator.jar,.,lib/dependency.jar")); assertThat(descriptor.isInvalid(), is(false)); } @Test public void shouldOverrideTheBundleActivatorInTheManifestIfItAlreadyHasIt() throws Exception { if (WINDOWS.satisfy()) { return; } addHeaderToManifest(BUNDLE_ACTIVATOR, "Dummy Value"); assertThat(valueFor(BUNDLE_ACTIVATOR), is(not(nullValue()))); GoPluginDescriptor descriptor = GoPluginDescriptor.usingId("pluginId", "some-plugin.jar", bundleLocation, true); goPluginOSGiManifestGenerator.updateManifestOf(descriptor); assertThat(valueFor(BUNDLE_ACTIVATOR), is(DefaultGoPluginActivator.class.getCanonicalName())); assertThat(descriptor.isInvalid(), is(false)); } @Test public void shouldCreateManifestWithProperClassPathWhenDependencyDirIsEmpty() throws Exception { if (WINDOWS.satisfy()) { return; } FileUtils.deleteQuietly(new File(bundleLocation, "lib/dependency.jar")); assertThat(bundleDependencyDir.listFiles().length, is(0)); GoPluginDescriptor descriptor = GoPluginDescriptor.usingId("pluginId", "some-plugin.jar", bundleLocation, true); goPluginOSGiManifestGenerator.updateManifestOf(descriptor); assertThat(valueFor(BUNDLE_CLASSPATH), is("lib/go-plugin-activator.jar,.")); } @Test public void manifestCreatorShouldUpdateTheGoPluginManifest() throws Exception { if (WINDOWS.satisfy()) { return; } assertThat(valueFor(BUNDLE_SYMBOLICNAME), is(nullValue())); assertThat(valueFor(BUNDLE_CLASSPATH), is(nullValue())); GoPluginDescriptor descriptor = GoPluginDescriptor.usingId("pluginId", "some-plugin.jar", bundleLocation, true); goPluginOSGiManifestGenerator.updateManifestOf(descriptor); assertThat(valueFor(BUNDLE_SYMBOLICNAME), is("pluginId")); assertThat(valueFor(BUNDLE_CLASSPATH), is("lib/go-plugin-activator.jar,.,lib/dependency.jar")); } @After public void tearDown() throws Exception { if (WINDOWS.satisfy()) { return; } FileUtils.deleteDirectory(TMP_DIR); } private String valueFor(final String prefix) throws IOException { if (!manifestFile.exists()) { return null; } FileInputStream manifestInputStream = new FileInputStream(manifestFile); Manifest manifest = new Manifest(manifestInputStream); Attributes entries = manifest.getMainAttributes(); return entries.getValue(prefix); } private void addHeaderToManifest(String header, String value) throws IOException { FileInputStream manifestInputStream = new FileInputStream(manifestFile); Manifest manifest = new Manifest(manifestInputStream); Attributes entries = manifest.getMainAttributes(); entries.put(new Attributes.Name(header), value); FileOutputStream manifestOutputStream = new FileOutputStream(manifestFile, false); manifest.write(manifestOutputStream); manifestOutputStream.close(); manifestInputStream.close(); } private File createPluginBundle(String bundleName) throws IOException, URISyntaxException { File destinationPluginBundleLocation = new File(TMP_DIR, bundleName); destinationPluginBundleLocation.mkdirs(); URL resource = getClass().getClassLoader().getResource("defaultFiles/descriptor-aware-test-plugin.jar"); new ZipUtil().unzip(new File(resource.toURI()), destinationPluginBundleLocation); return destinationPluginBundleLocation; } }