package org.rhq.core.domain.drift; import static org.apache.commons.io.IOUtils.toInputStream; import java.io.BufferedInputStream; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.PrintWriter; import java.net.URISyntaxException; import java.sql.Blob; import java.util.ArrayList; import java.util.List; import org.apache.commons.io.IOUtils; import org.hibernate.LobHelper; import org.hibernate.Session; import org.testng.annotations.Test; import org.rhq.core.domain.shared.TransactionCallback; import org.rhq.core.domain.test.AbstractEJB3Test; import org.rhq.core.util.MessageDigestGenerator; public class DriftFileTest extends AbstractEJB3Test { static private final MessageDigestGenerator digestGen = new MessageDigestGenerator(MessageDigestGenerator.SHA_256); // Note, this test is more of a general Blob handling test. A real JPADriftFile never has its content updated. // But this is a useful test to just ensure Blob handling is working as expected. @Test(groups = { "driftFile", "drift.ejb" }) public void updateDriftFileData() throws Exception { String content = "driftFile data"; String hashId = digestGen.calcDigestString(content); // Create the initial driftFile final JPADriftFileBits df1 = new JPADriftFileBits(hashId); df1.setDataSize((long) content.length()); Session session = (Session) em.getDelegate(); df1.setData(session.getLobHelper().createBlob(toInputStream(content), content.length())); executeInTransaction(new TransactionCallback() { @Override public void execute() { em.persist(df1); // Make the update final String newContent = "driftFile data updated..."; Session session = (Session) em.getDelegate(); JPADriftFileBits df2 = em.find(JPADriftFileBits.class, df1.getHashId()); df2.setData(session.getLobHelper().createBlob(toInputStream(newContent), newContent.length())); em.merge(df2); try { JPADriftFileBits df3 = em.find(JPADriftFileBits.class, df1.getHashId()); String expected = newContent; String actual = IOUtils.toString(df3.getData()); assertEquals("Failed to update driftFile data", expected, actual); } catch (Exception e) { fail("Failed to load driftFile data: " + e.getMessage()); } } }); } // The purpose of this test is to store a large amount of data and then // load the driftFiles to verify that the driftFile data is not also loaded. // In other words, to ensure LazyLoad semantics are working for Blobs // Because of the amount data involved is very large the test is long // running and should be moved to an integration test suite. @Test(groups = { "driftFile", "drift.ejb" }) public void loadMultipleDriftFilesWithoutLoadingData() throws Exception { executeInTransaction(new TransactionCallback() { @Override public void execute() throws Exception { int numDriftFiles = 3; final List<String> driftFileHashIds = new ArrayList<String>(); for (int i = 0; i < numDriftFiles; ++i) { File dataFile = createDataFile("test_data.txt", 1, (char) ('a' + i)); String hashId = digestGen.calcDigestString(dataFile); final JPADriftFileBits driftFile = new JPADriftFileBits(hashId); driftFile.setDataSize(dataFile.length()); Session session = (Session) em.getDelegate(); driftFile.setData(session.getLobHelper().createBlob( new BufferedInputStream(new FileInputStream(dataFile)), dataFile.length())); dataFile.delete(); em.persist(driftFile); driftFileHashIds.add(driftFile.getHashId()); } final List<Blob> blobs = new ArrayList<Blob>(); final List<JPADriftFileBits> driftFiles = new ArrayList<JPADriftFileBits>(); for (final String hashId : driftFileHashIds) { JPADriftFileBits driftFileBits = em.find(JPADriftFileBits.class, hashId); blobs.add(driftFileBits.getBlob()); driftFiles.add(driftFileBits); } assertEquals("Failed to save or load " + numDriftFiles + " driftFiles", numDriftFiles, driftFiles.size()); } }); } // The purpose of this test is to ensure we won't store two drift files for // the same content. @Test(groups = { "driftFile", "drift.ejb" }) public void loadSameFile() throws Exception { int numDriftFiles = 2; final List<JPADriftFileBits> driftFiles = new ArrayList<JPADriftFileBits>(); final List<String> driftFileHashIds = new ArrayList<String>(); Session session = (Session) em.getDelegate(); LobHelper lobHelper = session.getLobHelper(); for (int driftFileNum = 0; driftFileNum < numDriftFiles; ++driftFileNum) { File dataFile = createDataFile("test_data.txt", 10, 'X'); String hashId = digestGen.calcDigestString(dataFile); final JPADriftFileBits driftFile = new JPADriftFileBits(hashId); driftFile.setDataSize(dataFile.length()); driftFile.setData(lobHelper.createBlob(new BufferedInputStream(new FileInputStream(dataFile)), dataFile.length())); driftFiles.add(driftFile); dataFile.delete(); } try { executeInTransaction(new TransactionCallback() { @Override public void execute() throws Exception { for (JPADriftFileBits driftFile : driftFiles) { try { em.persist(driftFile); driftFileHashIds.add(driftFile.getHashId()); if (driftFileHashIds.size() > 1) { fail("Should not be able to store JPADriftFile with same hashId more than once."); } } catch (Exception e) { // expected for second file if (driftFileHashIds.size() != 1) { fail("Should be able to store JPADriftFile with unique hashId - cause: " + e); } } } final List<Blob> blobs = new ArrayList<Blob>(); final List<JPADriftFileBits> driftFiles = new ArrayList<JPADriftFileBits>(); for (final String hashId : driftFileHashIds) { JPADriftFileBits driftFileBits = em.find(JPADriftFileBits.class, hashId); blobs.add(driftFileBits.getBlob()); driftFiles.add(driftFileBits); } } }); } catch (Exception e) { // expected because the rollback will fail as the transaction has already terminated } assertEquals("Failed to save or load " + driftFiles.size() + " driftFiles", 2, driftFiles.size()); } File workDir() throws URISyntaxException { File dir = new File(new File("target"), "work"); dir.mkdir(); return dir; } /** * Creates a file in {@link #workDir()} that is filled with arbitrary data up to <code>size</code> in megabytes. * * @param name The name of the file to create * @param size The size of the file in megabytes * @return The generated file */ File createDataFile(String name, int size, char fillChar) throws Exception { File file = new File(workDir(), name); long oneMB = 1048576; long sizeInBytes = size * oneMB; int lineSize = 80; // size in bytes long numLines = sizeInBytes / lineSize; PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(file))); for (int i = 0; i < numLines; ++i) { StringBuilder line = new StringBuilder(); for (int j = 1; j < lineSize; ++j) { line.append(fillChar); } writer.println(line.toString()); } writer.close(); return file; } }