blob: 22882e027024939c343148862d8148425bd5ec47 [file] [log] [blame]
/*
* 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.obr.storage.file;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Random;
import java.util.jar.Attributes;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import org.apache.ace.obr.metadata.MetadataGenerator;
import org.apache.ace.obr.storage.OBRFileStoreConstants;
import org.apache.ace.test.utils.FileUtils;
import org.apache.ace.test.utils.TestUtils;
import org.osgi.framework.Constants;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.log.LogService;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class BundleFileStoreTest {
private BundleFileStore m_bundleStore;
private MockMetadataGenerator m_metadata;
private File m_directory;
private File m_bundleSubstitute1;
private File m_bundleSubstitute1Larger;
private File m_bundleSubstitute2;
private File m_bundleSubstitute3;
private File m_bundleRepositoryFile;
@BeforeMethod(alwaysRun = true)
protected void setUp() throws Exception {
m_bundleStore = new BundleFileStore();
m_directory = FileUtils.createTempFile(null);
m_directory.mkdir();
Dictionary<String, Object> props = new Hashtable<>();
props.put(OBRFileStoreConstants.FILE_LOCATION_KEY, m_directory.getAbsolutePath());
m_bundleStore.updated(props);
// set a null object on for log
TestUtils.configureObject(m_bundleStore, LogService.class);
// create a mock MetadataGenerator
m_metadata = new MockMetadataGenerator();
TestUtils.configureObject(m_bundleStore, MetadataGenerator.class, m_metadata);
// create some bundles to work with
m_bundleSubstitute1 = createFileWithContent(m_directory.getAbsoluteFile(), "bundleSub1.jar", 1000);
m_bundleSubstitute2 = createFileWithContent(m_directory.getAbsoluteFile(), "bundleSub2.jar", 2000);
m_bundleSubstitute3 = createFileWithContent(m_directory.getAbsoluteFile(), "bundleSub3.jar", 3000);
m_bundleRepositoryFile = createFileWithContent(m_directory.getAbsoluteFile(), "index.xml", 1000);
}
@AfterMethod(alwaysRun = true)
public void tearDown() throws Exception {
m_bundleSubstitute1.delete();
m_bundleSubstitute2.delete();
m_bundleSubstitute3.delete();
m_bundleRepositoryFile.delete();
m_directory.delete();
}
/**
* Test whether the metadata is generated when getting a bundle from the repository.
*/
@Test()
public void getBundle() throws Exception {
m_bundleStore.get(m_bundleSubstitute1.getName());
assert !m_metadata.generated() : "During getting a bundle, the metadata should not be regenerated.";
}
/**
* Test that the bundle store reutrns null for non-existing files.
*/
@Test()
public void getNonExistingBundle() throws Exception {
assert m_bundleStore.get("blaat") == null : "Getting an non-existing file did not result in null?";
}
/**
* Test whether retrieving the index.xml results in a call to the (mock) metadata generator, and the original file
* should correspond with the retrieved file.
*/
@Test()
public void getRepositoryFile() throws Exception {
InputStream newInputStream = m_bundleStore.get("index.xml");
assert m_metadata.generated() : "During getting the repository file, the metadata should be regenerated.";
byte[] orgContentBuffer = new byte[1000];
newInputStream.read(orgContentBuffer);
FileInputStream orgInputStream = new FileInputStream(m_bundleRepositoryFile);
byte[] newContentBuffer = new byte[1000];
orgInputStream.read(newContentBuffer);
orgInputStream.close();
assert Arrays.equals(orgContentBuffer, newContentBuffer) : "The original index.xml content should equal the newly retrieved content.";
}
/**
* Test whether the BundleStore notices the set of bundles has changed (bundle updated), and makes a call to the
* (mock) metadata generator.
*/
@Test()
public void updateBundle() throws Exception {
m_bundleStore.get("index.xml");
assert m_metadata.numberOfCalls() == 1 : "The MetadataGenerator should be called once";
m_bundleSubstitute1Larger = createFileWithContent(m_directory.getAbsoluteFile(), "bundleSub1.jar", 2000);
m_bundleStore.get("index.xml");
assert m_metadata.numberOfCalls() == 2 : "The MetadataGenerator should be called twice";
// test specific tear down
m_bundleSubstitute1Larger.delete();
}
/**
* Test whether the BundleStore notices the set of bundles has changed (bundle added), and makes a call to the
* (mock) metadata generator. Also a call should be made when a bundle is replaced by another one (number of bundles
* stay the same, but one bundle is replaced by another).
*/
@Test()
public void addBundle() throws Exception {
m_bundleStore.get("index.xml");
assert m_metadata.numberOfCalls() == 1 : "The MetadataGenerator should be called once";
File bundleSubstituteX = createFileWithContent(m_directory.getAbsoluteFile(), "bundleSubX.jar", 2000);
m_bundleStore.get("index.xml");
assert m_metadata.numberOfCalls() == 2 : "The MetadataGenerator should be called twice";
bundleSubstituteX.delete();
File bundleSubstituteY = createFileWithContent(m_directory.getAbsoluteFile(), "bundleSubY.jar", 2000);
m_bundleStore.get("index.xml");
assert m_metadata.numberOfCalls() == 3 : "The MetadataGenerator should be called three times";
// test specific tear down
bundleSubstituteY.delete();
}
/**
* Test whether the BundleStore notices the set of bundles has not changed, and thus will not make a call to the
* (mock) metadata generator.
*/
@Test()
public void replaceWithSameBundle() throws Exception {
m_bundleStore.get("bundleSub1.jar");
assert m_metadata.numberOfCalls() == 0 : "The MetadataGenerator should not be called";
FileInputStream inputStream = new FileInputStream(m_bundleSubstitute1);
byte[] buffer = new byte[1000];
inputStream.read(buffer);
inputStream.close();
m_bundleSubstitute1.delete();
File newFile = new File(m_directory, "bundleSub1.jar");
FileOutputStream outputStream = new FileOutputStream(newFile);
outputStream.write(buffer);
outputStream.close();
m_bundleStore.get("bundleSub1.jar");
assert m_metadata.numberOfCalls() == 0 : "The MetadataGenerator should still not be called";
}
/**
* Test whether changing the directory where the bundles are stored, does not result in a call to the (mock)
* metadata generator, as the metadata will only be regenerated after getting a file.
*/
@Test()
public void updateConfigurationWithValidConfiguration() throws Exception {
File subDir = new File(m_directory.getAbsolutePath(), "changedDirectory");
subDir.mkdir();
Dictionary<String, Object> props = new Hashtable<>();
props.put(OBRFileStoreConstants.FILE_LOCATION_KEY, subDir.getAbsolutePath());
try {
m_bundleStore.updated(props);
}
catch (ConfigurationException e) {
assert false : "Reconfiguring directory failed, directory was '" + m_directory + "' but should be '" + subDir + "'";
}
assert !m_metadata.generated() : "After changing the directory, the metadata should not be regenerated.";
// test specific tear down
subDir.delete();
}
/**
* Test whether changing the directory where the bundles are stored to something that is not a directory, this
* should fail.
*/
@Test()
public void updateConfigurationWithIsNotDirectory() throws Exception {
boolean exceptionThrown = false;
File file = new File(m_directory.getAbsolutePath(), "file");
file.createNewFile();
Dictionary<String, Object> props = new Hashtable<>();
props.put(OBRFileStoreConstants.FILE_LOCATION_KEY, file.getAbsolutePath());
try {
m_bundleStore.updated(props);
}
catch (ConfigurationException e) {
// exception should be thrown as attempting to configure with File that is no directory
exceptionThrown = true;
}
assert exceptionThrown : "Reconfiguring directory succeeded, but should fail as it is no directory";
// test specific tear down
file.delete();
}
@Test()
public void putBundle() throws Exception {
File bundle = createTmpResource("foo.bar", "1.0.0");
String filePath = m_bundleStore.put(new FileInputStream(bundle), null, false);
assert filePath.equals("foo/foo.bar-1.0.0.jar") : "Path should be 'foo/foo.bar-1.0.0.jar', was " + filePath;
File file = new File(m_directory, filePath);
assert file.exists();
}
@Test()
public void putBundleSameDuplicate() throws Exception {
File bundle = createTmpResource("foo.bar", "1.0.0");
String filePath = m_bundleStore.put(new FileInputStream(bundle), null, false);
assert filePath != null;
String filePath2 = m_bundleStore.put(new FileInputStream(bundle), null, false);
assert filePath2 != null;
assert filePath2.equals(filePath);
}
@Test()
public void putBundleDifferentDuplicate() throws Exception {
File bundle = createTmpResource("foo.bar", "1.0.0", new byte[] { 1 });
File bundle2 = createTmpResource("foo.bar", "1.0.0", new byte[] { 2 });
String filePath = m_bundleStore.put(new FileInputStream(bundle), null, false);
assert filePath != null;
String filePath2 = m_bundleStore.put(new FileInputStream(bundle2), null, false);
assert filePath2 == null;
}
@Test(expectedExceptions = { IOException.class }, expectedExceptionsMessageRegExp = "Not a valid bundle and no filename found.*")
public void putBundleFail() throws Exception {
File bundle = createTmpResource(null, "1.0.0");
String filePath = m_bundleStore.put(new FileInputStream(bundle), null, false);
assert filePath.equals("foo/bar/foo.bar-1.0.0.jar") : "Path should be 'foo/bar/foo.bar-1.0.0.jar', was " + filePath;
File file = new File(m_directory, filePath);
assert file.exists();
}
@Test()
public void putRemoveArtifact() throws Exception {
File bundle = createTmpResource(null, null);
String filePath = m_bundleStore.put(new FileInputStream(bundle), "foo.bar-2.3.7.test1.xxx", false);
assert filePath.equals("foo/foo.bar-2.3.7.test1.xxx");
File file = new File(m_directory, filePath);
assert file.exists();
}
@Test()
public void putArtifactDefaultVersion() throws Exception {
File bundle = createTmpResource(null, null);
String filePath = m_bundleStore.put(new FileInputStream(bundle), "foo.bar.xxx", false);
assert filePath.equals("foo/foo.bar.xxx");
File file = new File(m_directory, filePath);
assert file.exists();
}
@Test()
public void putArtifactMavenVersion() throws Exception {
File bundle = createTmpResource(null, null);
String filePath = m_bundleStore.put(new FileInputStream(bundle), "foo.bar-2.3.7-test1.xxx", false);
assert filePath.equals("foo/foo.bar-2.3.7-test1.xxx");
File file = new File(m_directory, filePath);
assert file.exists();
}
@Test(expectedExceptions = { IOException.class }, expectedExceptionsMessageRegExp = "Not a valid bundle and no filename found.*")
public void putArtifactFail1() throws Exception {
File bundle = createTmpResource(null, null);
m_bundleStore.put(new FileInputStream(bundle), null, false);
}
@Test(expectedExceptions = { IOException.class }, expectedExceptionsMessageRegExp = "Not a valid bundle and no filename found.*")
public void putArtifactFail2() throws Exception {
File bundle = createTmpResource(null, null);
m_bundleStore.put(new FileInputStream(bundle), "", false);
}
@Test()
public void removeBundle() throws Exception {
File bundle = createTmpResource("foo.bar", "1.0.0");
String filePath = m_bundleStore.put(new FileInputStream(bundle), null, false);
File file = new File(m_directory, filePath);
assert file.exists();
assert m_bundleStore.remove(filePath);
assert !file.exists();
}
@Test()
public void removeBundleFaill() throws Exception {
File file = new File(m_directory, "no/such/file");
assert !file.exists();
assert !m_bundleStore.remove("no/such/file");
}
@Test()
public void removeArtifact() throws Exception {
File bundle = createTmpResource(null, null);
String filePath = m_bundleStore.put(new FileInputStream(bundle), "foo.bar-2.3.7.test1.xxx", false);
assert filePath.equals("foo/foo.bar-2.3.7.test1.xxx");
File file = new File(m_directory, filePath);
assert file.exists();
assert m_bundleStore.remove("foo/foo.bar-2.3.7.test1.xxx");
assert !file.exists();
}
/**
* Test whether not configuring the directory (so retrieving the directory returns null), results in a
* ConfigurationException. Updating with null as dictionary should only clean up things, and nothing else.
*/
@Test()
public void updateConfigurationWithNull() throws Exception {
boolean exceptionThrown = false;
Dictionary<String, Object> props = new Hashtable<>();
try {
m_bundleStore.updated(props);
}
catch (ConfigurationException e) {
exceptionThrown = true;
}
assert exceptionThrown : "Reconfiguring directory succeeded but should fail, as property is supposed to be missing";
assert !m_metadata.generated() : "After changing the directory, the metadata should not be regenerated.";
exceptionThrown = false;
try {
m_bundleStore.updated(null);
}
catch (ConfigurationException e) {
exceptionThrown = true;
}
assert !exceptionThrown : "Reconfiguring succeeded as the bundle should only do the clean up, and not throw exception";
assert !m_metadata.generated() : "After changing the directory, the metadata should not be regenerated.";
}
/**
* Test whether not configuring the directory (so retrieving the directory returns null), results in a
* ConfigurationException.
*/
@Test()
public void updateConfigurationWithSameDirectory() throws Exception {
Dictionary<String, Object> props = new Hashtable<>();
props.put(OBRFileStoreConstants.FILE_LOCATION_KEY, m_directory.getAbsolutePath());
try {
m_bundleStore.updated(props);
}
catch (ConfigurationException e) {
assert false : "Nothing should happen, as the directory did not change";
}
assert !m_metadata.generated() : "After changing the directory, the metadata should not be regenerated.";
}
private File createTmpResource(String symbolicName, String version) throws IOException {
return createTmpResource(symbolicName, version, null);
}
private File createTmpResource(String symbolicName, String version, byte[] data) throws IOException {
File tmpFile = File.createTempFile("tmpbundle-", "jar");
tmpFile.deleteOnExit();
Manifest manifest = new Manifest();
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
if (symbolicName != null) {
manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, symbolicName);
}
if (version != null) {
manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, version);
}
JarOutputStream target = new JarOutputStream(new FileOutputStream(tmpFile), manifest);
if (data != null) {
target.putNextEntry(new ZipEntry("data"));
target.write(data, 0, data.length);
}
target.close();
return tmpFile;
}
private File createFileWithContent(File baseDir, String filename, int size) throws IOException {
OutputStream fileOut = null;
File file = new File(baseDir, filename);
try {
fileOut = new FileOutputStream(file);
byte[] byteArray = new byte[size];
Random randomContentCreator = new Random();
randomContentCreator.nextBytes(byteArray);
fileOut.write(byteArray);
return file;
}
finally {
try {
if (fileOut != null) {
fileOut.close();
}
}
catch (IOException e) {
throw e;
}
}
}
}