/* $HeadURL:: $
* $Id$
*
* Copyright (c) 2009-2010 DuraSpace
* http://duraspace.org
*
* In collaboration with Topaz Inc.
* http://www.topazproject.org
*
* 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.
*/
package org.akubraproject.mux;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.transaction.Transaction;
import org.akubraproject.Blob;
import org.akubraproject.BlobStore;
import org.akubraproject.BlobStoreConnection;
import org.akubraproject.DuplicateBlobException;
import org.akubraproject.MissingBlobException;
import org.akubraproject.UnsupportedIdException;
import org.akubraproject.mem.MemBlobStore;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
/**
* Tests for AbstractMuxConnection.
*
* @author Pradeep Krishnan
*/
public class AbstractMuxConnectionTest {
private MemBlobStore store1;
private MemBlobStore store2;
private AbstractMuxStore store;
private TestConnection con;
private Map<String, String> s1Hint;
private Map<String, String> s2Hint;
/**
* Set things up for all tests.
*/
@BeforeSuite
public void setUp() throws Exception {
store1 = new MemBlobStore(URI.create("urn:store:1"));
store2 = new MemBlobStore(URI.create("urn:store:2"));
store =
new AbstractMuxStore(URI.create("urn:store")) {
@Override
public BlobStoreConnection openConnection(Transaction tx, Map<String, String> hints)
throws UnsupportedOperationException, IOException {
return new TestConnection(this, tx);
}
};
store.setBackingStores(Arrays.asList(store1, store2));
s1Hint = new HashMap<String, String>();
s2Hint = new HashMap<String, String>();
s1Hint.put("store1", "store1");
s2Hint.put("store2", "store2");
con = (TestConnection) store.openConnection(null, null);
}
@AfterSuite
public void tearDown() throws Exception {
}
/**
* Test to see if close() closes all underlying stores.
*/
@Test
public void testClose() throws IOException {
TestConnection con = (TestConnection) store.openConnection(null, null);
assertFalse(con.isClosed());
con.getConnection(store1, null);
con.getConnection(store2, null);
assertEquals(2, con.getCons().size());
Collection<BlobStoreConnection> cons = con.getCons().values();
con.close();
assertTrue(con.isClosed());
for (BlobStoreConnection c : cons)
assertTrue(c.isClosed());
}
/**
* Test for constructor.
*/
@Test
public void testAbstractMuxConnection() {
assertEquals(store, con.getBlobStore());
}
/**
* Tests that getStores() return the backing stores.
*/
@Test
public void testGetStores() {
Set<BlobStore> s = con.getStores(null);
assertEquals(2, s.size());
assertTrue(s.contains(store1));
assertTrue(s.contains(store2));
}
/**
* Tests that the connection cache works as expected.
*/
@Test
public void testGetConnection() throws IOException {
BlobStoreConnection con1 = con.getConnection(store1, null);
BlobStoreConnection con2 = con.getConnection(store2, null);
assertEquals(2, con.getCons().size());
assertEquals(con.getCons().get(store1.getId()), con1);
assertEquals(con.getCons().get(store2.getId()), con2);
assertEquals(con1, con.getConnection(store1, null));
assertEquals(con2, con.getConnection(store2, null));
}
/**
* Cross checks getBlob returned with backing stores.
*/
@Test
public void testGetBlobURIMapOfStringString() throws IOException {
Blob b1 = con.getBlob(null, s1Hint);
Blob b2 = con.getBlob(null, s2Hint);
Blob b3 = store1.openConnection(null, null).getBlob(b1.getId(), s1Hint);
Blob b4 = store2.openConnection(null, null).getBlob(b2.getId(), s2Hint);
b1.openOutputStream(0, true).close();
b2.openOutputStream(0, true).close();
assertTrue(b3.exists());
assertTrue(b4.exists());
b1.delete();
b2.delete();
assertFalse(b3.exists());
assertFalse(b4.exists());
}
/**
* Tests iteration across blob stores.
*/
@Test
public void testListBlobIds() throws IOException {
for (Iterator<URI> it = con.listBlobIds(null); it.hasNext();)
con.getBlob(it.next(), null).delete();
assertFalse(con.listBlobIds(null).hasNext());
Blob b1 = con.getBlob(null, s1Hint);
Blob b2 = con.getBlob(null, s2Hint);
b1.openOutputStream(0, true).close();
b2.openOutputStream(0, true).close();
Set<Blob> blobs = new HashSet<Blob>();
for (Iterator<URI> it = con.listBlobIds(null); it.hasNext();)
blobs.add(con.getBlob(it.next(), null));
assertEquals(2, blobs.size());
assertTrue(blobs.contains(b1));
assertTrue(blobs.contains(b2));
for (Blob b : new Blob[] { b1, b2 }) {
blobs = new HashSet<Blob>();
for (Iterator<URI> it = con.listBlobIds(b.getId().toString()); it.hasNext();)
blobs.add(con.getBlob(it.next(), null));
assertEquals(1, blobs.size());
assertTrue(blobs.contains(b));
}
}
/**
* Tests renames across blob stores.
*/
@Test
public void testMoveTo() throws IOException {
Blob b1 = con.getBlob(null, s1Hint);
Blob b2 = con.getBlob(null, s2Hint);
Blob b3 = store1.openConnection(null, null).getBlob(b1.getId(), s1Hint);
Blob b4 = store2.openConnection(null, null).getBlob(b2.getId(), s2Hint);
b1.openOutputStream(0, true).close();
b2.openOutputStream(0, true).close();
assertTrue(b3.exists());
assertTrue(b4.exists());
b2.delete();
assertFalse(b2.exists());
assertFalse(b4.exists());
assertEquals(b2, b1.moveTo(b2.getId(), s2Hint));
assertFalse(b1.exists());
assertFalse(b3.exists());
assertTrue(b2.exists());
assertTrue(b4.exists());
try {
b1.moveTo(b2.getId(), s2Hint);
fail("Did not get expected MissingBlobException");
} catch (MissingBlobException mbe) {
}
assertFalse(b1.exists());
assertFalse(b3.exists());
assertTrue(b2.exists());
assertTrue(b4.exists());
b1.openOutputStream(0, true).close();
assertTrue(b1.exists());
assertTrue(b3.exists());
try {
b1.moveTo(b2.getId(), s2Hint);
fail("Did not get expected DuplicateBlobException");
} catch (DuplicateBlobException dbe) {
}
assertTrue(b1.exists());
assertTrue(b3.exists());
assertTrue(b2.exists());
assertTrue(b4.exists());
}
private class TestConnection extends AbstractMuxConnection {
private TestConnection(BlobStore store, Transaction txn) {
super(store, txn);
}
@Override
public BlobStore getStore(URI blobId, Map<String, String> hints)
throws IOException, UnsupportedIdException {
if (hints != null) {
if (hints.keySet().containsAll(s1Hint.keySet()))
return store1;
if (hints.keySet().containsAll(s2Hint.keySet()))
return store2;
}
if (blobId != null) {
for (BlobStore s : ((AbstractMuxStore) getBlobStore()).getBackingStores())
if (getConnection(s, null).listBlobIds(blobId.toString()).hasNext()
&& (getConnection(s, null).getBlob(blobId, hints) != null))
return s;
}
for (BlobStore s : ((AbstractMuxStore) getBlobStore()).getBackingStores()) {
try {
if (getConnection(s, null).getBlob(blobId, hints) != null)
return s;
} catch (UnsupportedIdException e) {
// skip this one
}
}
throw new UnsupportedIdException(blobId);
}
public Map<URI, BlobStoreConnection> getCons() {
return cons;
}
}
}