Java tutorial
/* * RHQ Management Platform * Copyright (C) 2005-2011 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.server.sync.test; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.fail; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import javax.persistence.EntityManager; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.stream.XMLStreamException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jmock.Expectations; import org.testng.annotations.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.definition.ConfigurationDefinition; import org.rhq.core.domain.sync.ExporterMessages; import org.rhq.enterprise.server.sync.ExportReader; import org.rhq.enterprise.server.sync.ExportWriter; import org.rhq.enterprise.server.sync.ExportingInputStream; import org.rhq.enterprise.server.sync.NoSingleEntity; import org.rhq.enterprise.server.sync.SynchronizationConstants; import org.rhq.enterprise.server.sync.Synchronizer; import org.rhq.enterprise.server.sync.exporters.AbstractDelegatingExportingIterator; import org.rhq.enterprise.server.sync.exporters.Exporter; import org.rhq.enterprise.server.sync.exporters.ExportingIterator; import org.rhq.enterprise.server.sync.importers.ExportedEntityMatcher; import org.rhq.enterprise.server.sync.importers.Importer; import org.rhq.enterprise.server.sync.validators.ConsistencyValidator; import org.rhq.enterprise.server.sync.validators.EntityValidator; import org.rhq.test.JMockTest; /** * * * @author Lukas Krejci */ @Test public class ExportingInputStreamTest extends JMockTest { private static final Log LOG = LogFactory.getLog(ExportingInputStreamTest.class); private static class ListToStringExporter<T> implements Exporter<NoSingleEntity, T> { List<T> valuesToExport; public static final String NOTE_PREFIX = "Wow, I just exported an item from a list: "; private class Iterator extends AbstractDelegatingExportingIterator<T, T> { public Iterator() { super(valuesToExport.iterator()); } public void export(ExportWriter output) throws XMLStreamException { output.writeCData(getCurrent().toString()); } public String getNotes() { return NOTE_PREFIX + getCurrent(); } protected T convert(T object) { return object; } } public ListToStringExporter(List<T> valuesToExport) { this.valuesToExport = valuesToExport; } public ExportingIterator<T> getExportingIterator() { return new Iterator(); } public String getNotes() { return valuesToExport.toString(); } } private static class DummyImporter<T> implements Importer<NoSingleEntity, T> { @Override public ConfigurationDefinition getImportConfigurationDefinition() { return null; } @Override public void configure(Configuration importConfiguration) { } @Override public ExportedEntityMatcher<NoSingleEntity, T> getExportedEntityMatcher() { return null; } @Override public void update(NoSingleEntity entity, T exportedEntity) throws Exception { } @Override public T unmarshallExportedEntity(ExportReader reader) throws XMLStreamException { return null; } @Override public String finishImport() throws Exception { return null; } @Override public Set<EntityValidator<T>> getEntityValidators() { return Collections.emptySet(); } } private static class ListToStringSynchronizer<T> implements Synchronizer<NoSingleEntity, T> { private List<T> list; public ListToStringSynchronizer(List<T> list) { this.list = list; } @Override public Exporter<NoSingleEntity, T> getExporter() { return new ListToStringExporter<T>(list); } @Override public Importer<NoSingleEntity, T> getImporter() { return new DummyImporter<T>(); } @Override public Set<ConsistencyValidator> getRequiredValidators() { return Collections.emptySet(); } @Override public void initialize(Subject subject, EntityManager entityManager) { } } private static class StringListSynchronizer extends ListToStringSynchronizer<String> { public StringListSynchronizer(List<String> list) { super(list); } } private static class IntegerListSynchronizer extends ListToStringSynchronizer<Integer> { public IntegerListSynchronizer(List<Integer> list) { super(list); } } public void testSucessfulExport() throws Exception { List<String> list1 = Arrays.asList("a", "b", "c"); List<Integer> list2 = Arrays.asList(1, 2, 3); StringListSynchronizer ex1 = new StringListSynchronizer(list1); IntegerListSynchronizer ex2 = new IntegerListSynchronizer(list2); Set<Synchronizer<?, ?>> exporters = this.<Synchronizer<?, ?>>asSet(ex1, ex2); InputStream export = new ExportingInputStream(exporters, new HashMap<String, ExporterMessages>(), 1024, false); // String exportContents = readAll(new InputStreamReader(export, "UTF-8")); // // LOG.info("Export contents:\n" + exportContents); // // export = new ByteArrayInputStream(exportContents.getBytes("UTF-8")); DocumentBuilder bld = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = bld.parse(export); Element root = doc.getDocumentElement(); assertEquals(SynchronizationConstants.CONFIGURATION_EXPORT_ELEMENT, root.getNodeName()); NodeList entities = root.getElementsByTagName(SynchronizationConstants.ENTITIES_EXPORT_ELEMENT); assertEquals(entities.getLength(), 2, "Unexpected number of entities elements"); Element export1 = (Element) entities.item(0); Element export2 = (Element) entities.item(1); assertEquals(export1.getAttribute("id"), StringListSynchronizer.class.getName()); assertEquals(export2.getAttribute("id"), IntegerListSynchronizer.class.getName()); String[] expectedNotes = new String[] { list1.toString(), list2.toString() }; for (int i = 0, elementIndex = 0; i < root.getChildNodes().getLength(); ++i) { Node node = root.getChildNodes().item(i); if (!(node instanceof Element)) { continue; } Element entitiesElement = (Element) node; assertEquals(entitiesElement.getNodeName(), SynchronizationConstants.ENTITIES_EXPORT_ELEMENT); NodeList errorMessages = entitiesElement .getElementsByTagName(SynchronizationConstants.ERROR_MESSAGE_ELEMENT); assertEquals(errorMessages.getLength(), 0, "Unexpected number of error message elements in an entities export."); Node note = getDirectChildByTagName(entitiesElement, SynchronizationConstants.NOTES_ELEMENT); assertNotNull(note, "Couldn't find exporter notes."); String notesText = ((Element) note).getTextContent(); assertEquals(notesText, expectedNotes[elementIndex], "Unexpected notes for entities."); NodeList entityElements = entitiesElement .getElementsByTagName(SynchronizationConstants.ENTITY_EXPORT_ELEMENT); assertEquals(entityElements.getLength(), 3, "Unexpected number of exported entities."); for (int j = 0; j < entityElements.getLength(); ++j) { Element entityElement = (Element) entityElements.item(j); errorMessages = entityElement.getElementsByTagName(SynchronizationConstants.ERROR_MESSAGE_ELEMENT); assertEquals(errorMessages.getLength(), 0, "Unexpected number of error message elements in an entity."); note = getDirectChildByTagName(entityElement, SynchronizationConstants.NOTES_ELEMENT); assertNotNull(note, "Could not find notes for an exported entity."); Node data = getDirectChildByTagName(entityElement, SynchronizationConstants.DATA_ELEMENT); assertNotNull(data, "Could not find data element in the entity."); String dataText = ((Element) data).getTextContent(); notesText = ((Element) note).getTextContent(); assertEquals(notesText, ListToStringExporter.NOTE_PREFIX + dataText, "Unexpected discrepancy between data and notes in the export."); } ++elementIndex; } } @Test(expectedExceptions = IOException.class) public void testExceptionHandling_Exporter_getExportingIterator() throws Exception { final Exporter<?, ?> failingExporter = context.mock(Exporter.class); final Synchronizer<?, ?> syncer = context.mock(Synchronizer.class); context.checking(new Expectations() { { RuntimeException failure = new RuntimeException("Injected failure"); allowing(failingExporter).getExportingIterator(); will(throwException(failure)); allowing(syncer).getRequiredValidators(); will(returnValue(Collections.emptySet())); allowing(syncer).getExporter(); will(returnValue(failingExporter)); } }); Set<Synchronizer<?, ?>> syncers = this.<Synchronizer<?, ?>>asSet(syncer); InputStream export = new ExportingInputStream(syncers, new HashMap<String, ExporterMessages>(), 1024, false); readAll(new InputStreamReader(export, "UTF-8")); //this should never be invoked, because reading the input stream should cause the exporter //to fail... fail("Successfully read the export even though one of the exporters threw an exception when asked for the exported entity iterator."); } @Test(expectedExceptions = IOException.class) public void testExceptionHandling_ExportingIterator_next() throws Exception { final ExportingIterator<?> iterator = context.mock(ExportingIterator.class); final Exporter<?, ?> exporter = context.mock(Exporter.class); final Importer<?, ?> importer = context.mock(Importer.class); final Synchronizer<?, ?> syncer = context.mock(Synchronizer.class); context.checking(new Expectations() { { RuntimeException failure = new RuntimeException("Injected failure"); allowing(iterator).hasNext(); will(returnValue(true)); allowing(iterator).next(); will(onConsecutiveCalls(returnValue("Success"), throwException(failure))); allowing(iterator).export(with(any(ExportWriter.class))); allowing(iterator).getNotes(); allowing(exporter).getExportingIterator(); will(returnValue(iterator)); allowing(exporter).getNotes(); allowing(syncer).getRequiredValidators(); will(returnValue(Collections.emptySet())); allowing(syncer).getExporter(); will(returnValue(exporter)); allowing(syncer).getImporter(); will(returnValue(importer)); allowing(importer).getImportConfigurationDefinition(); } }); Set<Synchronizer<?, ?>> syncers = this.<Synchronizer<?, ?>>asSet(syncer); InputStream export = new ExportingInputStream(syncers, new HashMap<String, ExporterMessages>(), 1024, false); readAll(new InputStreamReader(export, "UTF-8")); //this should never be invoked, because reading the input stream should cause the exporter //to fail... fail("Successfully read the export even though one of the exporters threw an exception when asked for the next exported entity."); } public void testExceptionHandling_ExportingIterator_export() throws Exception { final ExportingIterator<?> iterator = context.mock(ExportingIterator.class); final Exporter<?, ?> exporter = context.mock(Exporter.class); final Importer<?, ?> importer = context.mock(Importer.class); final Synchronizer<?, ?> syncer = context.mock(Synchronizer.class); context.checking(new Expectations() { { RuntimeException failure = new RuntimeException("Injected failure"); allowing(iterator).hasNext(); will(onConsecutiveCalls(returnValue(true), returnValue(true), returnValue(false))); allowing(iterator).next(); allowing(iterator).export(with(any(ExportWriter.class))); will(onConsecutiveCalls(returnValue(null), throwException(failure))); allowing(iterator).getNotes(); allowing(exporter).getExportingIterator(); will(returnValue(iterator)); allowing(exporter).getNotes(); allowing(syncer).getRequiredValidators(); will(returnValue(Collections.emptySet())); allowing(syncer).getExporter(); will(returnValue(exporter)); allowing(syncer).getImporter(); will(returnValue(importer)); allowing(importer).getImportConfigurationDefinition(); } }); Set<Synchronizer<?, ?>> syncers = this.<Synchronizer<?, ?>>asSet(syncer); InputStream export = new ExportingInputStream(syncers, new HashMap<String, ExporterMessages>(), 1024, false); String exportContents = readAll(new InputStreamReader(export, "UTF-8")); LOG.warn("Export contents:\n" + exportContents); export = new ByteArrayInputStream(exportContents.getBytes("UTF-8")); DocumentBuilder bld = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = bld.parse(export); Element root = doc.getDocumentElement(); NodeList entities = root.getElementsByTagName(SynchronizationConstants.ENTITY_EXPORT_ELEMENT); assertEquals(entities.getLength(), 2, "Unexpected number of exported elements"); //get the entity with the error Element failedEntity = (Element) entities.item(1); Node errorMessage = getDirectChildByTagName(failedEntity, SynchronizationConstants.ERROR_MESSAGE_ELEMENT); assertNotNull(errorMessage, "Could not find the error-message element at the entity that failed to export."); } private <T> LinkedHashSet<T> asSet(T... ts) { LinkedHashSet<T> ret = new LinkedHashSet<T>(); for (T t : ts) { ret.add(t); } return ret; } private static String readAll(Reader rdr) throws IOException { try { StringBuilder bld = new StringBuilder(); int c; while ((c = rdr.read()) != -1) { bld.append((char) c); } return bld.toString(); } finally { rdr.close(); } } private static Node getDirectChildByTagName(Node node, String tagName) { for (int i = 0; i < node.getChildNodes().getLength(); ++i) { Node n = node.getChildNodes().item(i); if (n.getNodeName().equals(tagName)) { return n; } } return null; } }