Java tutorial
/* * Aloha Open Source SIP Application Server- https://trac.osmosoft.com/Aloha * * Copyright (c) 2008, British Telecommunications plc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 3.0 of the License, or (at your option) any later * version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package com.bt.aloha.dao; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Vector; import java.util.concurrent.ConcurrentMap; import javax.sdp.MediaDescription; import javax.sdp.SessionDescription; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.easymock.EasyMock; import org.junit.Before; import org.junit.Test; import org.springframework.context.ApplicationContext; import com.bt.aloha.collections.database.DatabaseInfoCollectionHousekeepingRowCallBackHandler; import com.bt.aloha.dao.StateInfoDao; import com.bt.aloha.dao.StateInfoDaoImpl; import com.bt.aloha.dialog.state.DialogInfo; import com.bt.aloha.dialog.state.DialogState; import com.bt.aloha.stack.SessionDescriptionHelper; import com.bt.aloha.state.StateInfoBase; import com.bt.aloha.testing.DbTestCase; import com.bt.aloha.testing.SimpleTestInfo; import com.bt.aloha.util.ConcurrentUpdateException; import com.bt.aloha.util.HousekeeperAware; public class StateInfoDaoTest extends DbTestCase { private static Log log = LogFactory.getLog(StateInfoDaoTest.class); private StateInfoDao<DialogInfo> stateInfoDao; private DialogInfo dialogInfo; private DialogInfo dialogInfo2; private String collectionType; @Before public void setUp() { collectionType = "DialogInfo"; createCollectionsTable(); clearCollection(); stateInfoDao = new StateInfoDaoImpl<DialogInfo>(ds); dialogInfo = new DialogInfo("dialogId", "beanId", "1.2.3.4"); dialogInfo2 = new DialogInfo("dialogId2", "beanId", "1.2.3.4"); dialogInfo.setLocalParty("sip:localParty1@abc.de"); dialogInfo2.setLocalParty("sip:localParty2@abc.de"); } // Check if an info object added to collection is stored correctly in database public void testAddInfo() throws Exception { // setup // act stateInfoDao.add(dialogInfo, collectionType); // assert assertEquals("1", query(String.format("select count(*) from StateInfo where object_type='DialogInfo'"))); assertEquals(dialogInfo.getId(), query(String.format("select object_id from StateInfo where object_id='dialogId'"))); } // Check if two info objects added to collection are stored correctly in database @Test public void testAddTwoInfos() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); // act stateInfoDao.add(dialogInfo2, collectionType); // assert assertEquals("2", query(String.format("select count(*) from StateInfo where object_type='DialogInfo'"))); assertEquals(dialogInfo.getId(), query(String.format("select object_id from StateInfo where object_id='dialogId'"))); assertEquals(dialogInfo2.getId(), query(String.format("select object_id from StateInfo where object_id='dialogId2'"))); } // Test that adding null info object results in an exception @Test(expected = IllegalArgumentException.class) public void testAddingNullInfo() throws Exception { // act stateInfoDao.add(null, collectionType); } // Test that adding info object without identifier results in meaningful exception @Test(expected = IllegalArgumentException.class) public void testAddingInfoWithNullId() throws Exception { // setup dialogInfo = new DialogInfo(null, "beanId", "1.2.3.4"); // act stateInfoDao.add(dialogInfo, collectionType); } // test that adding null collection type results in an exception @Test(expected = IllegalArgumentException.class) public void testAddingInfoWithNullCollectionType() throws Exception { // act stateInfoDao.add(dialogInfo, null); } // Test that adding the same info twice results in meaningful exception @Test(expected = IllegalArgumentException.class) public void testAddingInfoTwice() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); // act stateInfoDao.add(dialogInfo, collectionType); } // Test retrieving previously serialized info object @Test public void testGetInfo() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); // act StateInfoBase<DialogInfo> retrievedDialogInfo = stateInfoDao.get(dialogInfo.getId()); // assert assertEquals(dialogInfo.getId(), retrievedDialogInfo.getId()); } // Test retrieving previously serialized info object when there is more than one info in the collection @Test public void testGetWhenTwoInfosAreInCollection() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); stateInfoDao.add(dialogInfo2, collectionType); // act StateInfoBase<DialogInfo> retrievedDialogInfo = stateInfoDao.get(dialogInfo.getId()); // assert assertEquals(dialogInfo.getId(), retrievedDialogInfo.getId()); } // Test that retrieving an info object using null identifier results in an exception @Test(expected = IllegalArgumentException.class) public void testGetWithNullId() throws Exception { // act stateInfoDao.get(null); } // Test that retrieving an info object which doesn't exist results in null being returned @Test public void testGetNonExisting() throws Exception { // act assertNull(stateInfoDao.get("non existing")); } // Test removing an info object from collection @Test public void testRemoveInfo() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); // act stateInfoDao.remove(dialogInfo.getId()); // assert assertEquals("0", query(String.format("select count(*) from StateInfo where object_type='DialogInfo'"))); } // Test removing an info object from collection @Test public void testRemoveWhenTwoInfosInCollection() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); stateInfoDao.add(dialogInfo2, collectionType); // act stateInfoDao.remove(dialogInfo.getId()); // assert assertEquals("1", query(String.format("select count(*) from StateInfo where object_type='DialogInfo'"))); assertEquals(dialogInfo2.getId(), query(String.format("select object_id from StateInfo where object_type='DialogInfo'"))); } // Test that removing an info object using null identifier results in an exception @Test(expected = IllegalArgumentException.class) public void testRemoveWithNullId() throws Exception { // act stateInfoDao.remove(null); } // Test that removing an info object doesn't throw exception @Test public void testRemoveNonExisting() throws Exception { // act try { stateInfoDao.remove("non existing"); } catch (RuntimeException e) { fail("Unexpected exception"); } } // Test replacing an info object in collection @Test public void testReplace() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); dialogInfo.setLocalParty("sip:newLocalParty1@abc.de"); // act stateInfoDao.replace(dialogInfo); // assert assertEquals("sip:newLocalParty1@abc.de", stateInfoDao.get(dialogInfo.getId()).getLocalParty().getURI().toString()); } // Test replacing an info object when there are multiple info objects in collection @Test public void testReplaceWhenTwoInfosInCollection() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); stateInfoDao.add(dialogInfo2, collectionType); dialogInfo.setLocalParty("sip:newLocalParty1@abc.de"); // act stateInfoDao.replace(dialogInfo); // assert assertEquals("sip:newLocalParty1@abc.de", stateInfoDao.get(dialogInfo.getId()).getLocalParty().getURI().toString()); assertEquals("sip:localParty2@abc.de", stateInfoDao.get(dialogInfo2.getId()).getLocalParty().getURI().toString()); } // Test that replacing an info object using null identifier results in an exception @Test(expected = IllegalArgumentException.class) public void testReplaceWithNullId() throws Exception { // act stateInfoDao.replace(null); } // Test concurrent modification @Test(expected = ConcurrentUpdateException.class) public void testReplaceWithConcurrentModification() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); DialogInfo ver1 = stateInfoDao.get(dialogInfo.getId()); DialogInfo ver2 = stateInfoDao.get(dialogInfo.getId()); stateInfoDao.replace(ver2); // act stateInfoDao.replace(ver1); } // Test size getter @Test public void testSize() throws Exception { // setup/act/assert assertEquals(0, stateInfoDao.size(collectionType)); stateInfoDao.add(dialogInfo, collectionType); assertEquals(1, stateInfoDao.size(collectionType)); stateInfoDao.add(dialogInfo2, collectionType); assertEquals(2, stateInfoDao.size(collectionType)); } // test the getAll method @Test public void testGetAll() throws Exception { // setup stateInfoDao.add(dialogInfo, collectionType); stateInfoDao.add(dialogInfo2, collectionType); // act ConcurrentMap<String, DialogInfo> result = stateInfoDao.getAll(collectionType); // assert assertEquals(2, result.size()); assertNotNull(result.get(dialogInfo.getId())); assertNotNull(result.get(dialogInfo2.getId())); } /** * this test checks that adding an element to a collection correctly stores * the data in the DB. we check this by going directly to the db and * deserialize the data in the selected row. */ @Test public void testPersistObjectInCollection() throws Exception { StateInfoDao<SimpleTestInfo> simpleStateInfoDao = new StateInfoDaoImpl<SimpleTestInfo>(ds); SimpleTestInfo myInfo1 = new SimpleTestInfo("id1", "one", "two"); // act simpleStateInfoDao.add(myInfo1, "SimpleTestInfo"); // assert List<SimpleTestInfo> l = retrieveSimpleTestInfo(); assertEquals(1, l.size()); SimpleTestInfo retrievedInfo = l.get(0); assertEquals("id1", retrievedInfo.getId()); assertEquals("one", retrievedInfo.getF1()); } /** * this test checks that getting an element from the db into a collection * correctly loads the db. we check this by setting some known data in the * db and the getting it out of the collection */ @Test public void testRetrieveObjectFromDatabase() { // set SimpleTestInfo stored = insertSimpleTestInfo("idX", "f1X", "f2X"); StateInfoDao<SimpleTestInfo> simpleStateInfoDao = new StateInfoDaoImpl<SimpleTestInfo>(ds); // act SimpleTestInfo sti = simpleStateInfoDao.get("idX"); // assert assertEquals("idX", stored.getId()); assertEquals("idX", sti.getId()); assertEquals("f1X", sti.getF1()); } private List<SimpleTestInfo> retrieveSimpleTestInfo() { PreparedStatement statement = null; ResultSet rs = null; try { List<SimpleTestInfo> l = new ArrayList<SimpleTestInfo>(); statement = connection.prepareStatement("select * from StateInfo where object_type='SimpleTestInfo'"); rs = statement.executeQuery(); while (rs.next()) { byte[] bytes = rs.getBytes("object_value"); ObjectInputStream oip = new ObjectInputStream(new ByteArrayInputStream(bytes)); SimpleTestInfo info = (SimpleTestInfo) oip.readObject(); l.add(info); } return l; } catch (SQLException e) { throw new IllegalStateException("Unable to retrieve from SimpleTestInfo"); } catch (IOException e) { throw new IllegalStateException("Unable to create ObjectInputStream"); } catch (ClassNotFoundException e) { throw new IllegalStateException("Unable to read object"); } finally { if (rs != null) try { rs.close(); } catch (SQLException e) { } if (statement != null) { try { statement.close(); } catch (SQLException e) { } } } } private SimpleTestInfo insertSimpleTestInfo(String id, String f1, String f2) { SimpleTestInfo sti = new SimpleTestInfo(id, f1, f2); byte[] stiBytes = null; try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(sti); oos.flush(); oos.close(); stiBytes = bos.toByteArray(); } catch (Exception e) { throw new RuntimeException("Unable to serialize SimpleTestInfo", e); } PreparedStatement s = null; try { s = connection.prepareStatement("insert into StateInfo" + "(object_id, object_type, object_version, last_use_time, is_dead, force_housekeep, object_value) values(?, 'Collection', ?, ?, ?, 0, ?)"); s.setString(1, id); s.setString(2, "1"); s.setLong(3, new java.util.Date().getTime()); s.setInt(4, 0); s.setBytes(5, stiBytes); log.debug("Inserted row in Collection " + "for current SimpleTestInfo"); s.execute(); connection.commit(); } catch (SQLException e) { try { if (connection != null) connection.rollback(); } catch (SQLException e1) { throw new RuntimeException("Unable to rollback operation on SimpleTestInfo", e); } throw new RuntimeException("Unable to execute db operation on SimpleTestInfo. op rolledback", e); } finally { if (s != null) try { s.close(); } catch (SQLException e) { log.warn("Unable to close prepared statement", e); } } return sti; } // test that session description gets serialized and deserialized and the media descriptions still exist. @Test public void testSessionDescriptionSerializeAndDeserialize() throws Exception { // setup DialogInfo di1 = new DialogInfo("id", "bean", "1.2.3.4."); SessionDescription sessionDescription = SessionDescriptionHelper.createSessionDescription("127.0.0.1", "test"); Vector<MediaDescription> mediaDescriptions = new Vector<MediaDescription>(); MediaDescription holdMediaDescription = SessionDescriptionHelper.generateHoldMediaDescription(); mediaDescriptions.add(holdMediaDescription); sessionDescription.setMediaDescriptions(mediaDescriptions); di1.setSessionDescription(sessionDescription); di1.setRemoteOfferMediaDescription(holdMediaDescription); // act stateInfoDao.add(di1, collectionType); // assert DialogInfo di2 = stateInfoDao.get(di1.getId()); assertNotNull(di2.getSessionDescription()); assertNotNull(di2.getSessionDescription().getMediaDescriptions(true)); assertTrue(di2.getSessionDescription().getMediaDescriptions(false).get(0).toString().contains("0.0.0.0")); assertNotNull(di2.getRemoteOfferMediaDescription()); assertEquals(di1.getRemoteOfferMediaDescription().toString(), di2.getRemoteOfferMediaDescription().toString()); } // test that session description gets serialized and deserialized and the media descriptions still exist on a replace in the collection. @Test public void testSessionDescriptionSerializeAndDeserializeOnReplace() throws Exception { // setup DialogInfo di1 = new DialogInfo("id", "bean", "1.2.3.4."); stateInfoDao.add(di1, collectionType); SessionDescription sessionDescription = SessionDescriptionHelper.createSessionDescription("127.0.0.1", "test"); Vector<MediaDescription> mediaDescriptions = new Vector<MediaDescription>(); MediaDescription holdMediaDescription = SessionDescriptionHelper.generateHoldMediaDescription(); mediaDescriptions.add(holdMediaDescription); sessionDescription.setMediaDescriptions(mediaDescriptions); di1.setSessionDescription(sessionDescription); di1.setRemoteOfferMediaDescription(holdMediaDescription); // act stateInfoDao.replace(di1); // assert DialogInfo di2 = stateInfoDao.get(di1.getId()); assertNotNull(di2.getSessionDescription()); assertNotNull(di2.getSessionDescription().getMediaDescriptions(true)); assertTrue(di2.getSessionDescription().getMediaDescriptions(false).get(0).toString().contains("0.0.0.0")); assertNotNull(di2.getRemoteOfferMediaDescription()); assertEquals(di1.getRemoteOfferMediaDescription().toString(), di2.getRemoteOfferMediaDescription().toString()); } // setup the table to have rows that have expired but are not marked for termination // force housekeeping on them and then assert they are removed on the next pass @Test public void testHouseKeeperForced() throws Exception { // setup - call dialogs dialogInfo.setDialogState(DialogState.Terminated); dialogInfo2.setDialogState(DialogState.Terminated); DialogInfo dialogInfo3 = new DialogInfo("id3", "bean3", "1.2.3.4"); dialogInfo3.setDialogState(DialogState.Created); stateInfoDao.add(dialogInfo, collectionType); stateInfoDao.add(dialogInfo2, collectionType); stateInfoDao.add(dialogInfo3, collectionType); // setup - housekeeper bean HousekeeperAware outboundCallLegBean = EasyMock.createMock(HousekeeperAware.class); outboundCallLegBean.killHousekeeperCandidate(dialogInfo3.getId()); EasyMock.replay(outboundCallLegBean); // set up - row call back handler DatabaseInfoCollectionHousekeepingRowCallBackHandler handler = new DatabaseInfoCollectionHousekeepingRowCallBackHandler(); ApplicationContext applicationContext = EasyMock.createMock(ApplicationContext.class); EasyMock.expect(applicationContext.getBean("bean3")).andReturn(outboundCallLegBean); EasyMock.replay(applicationContext); handler.setApplicationContext(applicationContext); // setup - collection checks assertEquals(3, stateInfoDao.size(collectionType)); stateInfoDao.housekeep(collectionType, 2000, handler); assertEquals(3, stateInfoDao.size(collectionType)); // act // wait for housekeeper to remove the old dialogInfos Thread.sleep(3000); stateInfoDao.housekeep(collectionType, 2000, handler); assertEquals(1, stateInfoDao.size(collectionType)); stateInfoDao.housekeep(collectionType, 2000, handler); // assert EasyMock.verify(applicationContext); EasyMock.verify(outboundCallLegBean); assertEquals(0, stateInfoDao.size(collectionType)); } }