package eu.dnetlib.dlms;

import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

import pl.edu.icm.driver.factories.DLMSBeansFactory;
import pl.edu.icm.driver.tester.TestDescription;
import pl.edu.icm.driver.tester.runner.Parameterized4TestCase;
import pl.edu.icm.driver.utils.PropertiesKeys;
import eu.dnetlib.dlms.jdbc.InformationObject;
import eu.dnetlib.dlms.jdbc.WSRemoteDataSource;
import eu.dnetlib.dlms.rmi.DLMSConnectionService;
import eu.dnetlib.enabling.tools.JaxwsServiceResolverImpl;
import eu.dnetlib.enabling.tools.StaticServiceLocator;

/**
 * DLMS functional test class.
 * @author mhorst
 *
 */
@TestDescription(
		name="DLMS Service test suite",
		description="Performs all required functionality tests of DLMS service.\n" +
				"Expected properties:\n" +
				"dlms.connection.service.location - localisation of DLMS service\n" +
				"text.pdf.location - remove pdf http location"
)
public class DLMSTest extends Parameterized4TestCase {

	protected static final Logger log = Logger.getLogger(DLMSTest.class);
	
	DLMSConnectionService dlmsConnectionService;
	
	String dlmsConnectionServiceLocation;
	
	DataSource dataSource;
	
	String textPdfLocation = null;
	
	String volumeName = "some volume name";
	String volumeDesc = "some volume description";
	String otherVolumeName = "some other volume name";
	String otherVolumeDesc = "some other volume description";
	String article1Name = "article1 name";
	String article1Desc = "article1 description";
	String article2Name = "article2 name";
	String article2Desc = "article2 description";
	
	/* (non-Javadoc)
	 * @see pl.edu.icm.driver.tester.ParameterizedTest#setParameters(java.util.Map)
	 */
	@Override
	public void setParameters(Map<Object, Object> parameters) {
		dlmsConnectionServiceLocation = (String) parameters.get(PropertiesKeys.SERVICE_LOCATION_DLMS_CONN);
		log.info("TestCase dlmsConnectionServiceLocation: " + dlmsConnectionServiceLocation);
		
		textPdfLocation = (String) parameters.get(PropertiesKeys.TEXT_PDF_LOCATION);
		log.info("TestCase textPdfLocation: " + textPdfLocation);
	}

	@Before
	public void setUp() throws Exception {
		ResourceBundle bundle = ResourceBundle.getBundle("pl.edu.icm.driver.is.auxiliary");
		if (textPdfLocation==null)
			textPdfLocation = bundle.getString(PropertiesKeys.TEXT_PDF_LOCATION);
		
		DLMSBeansFactory factory = new DLMSBeansFactory();
		dlmsConnectionService = factory.getDLMSConnection(dlmsConnectionServiceLocation);
//		setting up dataSource based on dlms connection
		dataSource = new WSRemoteDataSource();
		((WSRemoteDataSource)dataSource).setConnectionServiceLocator(
				new StaticServiceLocator<DLMSConnectionService>(dlmsConnectionService));
		((WSRemoteDataSource)dataSource).setServiceResolver(
				new JaxwsServiceResolverImpl());
	}

	@Test
	@TestDescription(
			planId="dlms.passthrough",
			description="Performs queries using simple statements."
			)
	public void testStatementPassthrough() throws Exception {
		Connection conn = dataSource.getConnection();
		assertNotNull(conn);
//		test string
		String strValue = "blah";
		Statement stmt = conn.createStatement();
		stmt.execute("'" + strValue + "'");
		ResultSet rs = stmt.getResultSet();
		assertTrue(rs.next());
		String strResult = rs.getString(1);
		assertNotNull(strResult);
		assertEquals(strValue, strResult);
		rs.close();
//		test int
		int intValue = 1;
		stmt = conn.createStatement();
		stmt.execute("" + intValue);
		rs = stmt.getResultSet();
		assertTrue(rs.next());
		int intResult = rs.getInt(1);
		assertNotNull(intResult);
		assertEquals(intValue, intResult);
		rs.close();
	}
	
	@Test
	@TestDescription(
			planId="dlms.prepared",
			description="Performs queries using prepared statements."
			)
	public void testPreparedStatements() throws Exception {
		Connection conn = dataSource.getConnection();
		assertNotNull(conn);
		String prepStatement = "1";
		PreparedStatement stmt = conn.prepareStatement(prepStatement);
		stmt.setString(1, "someValue");
		assertTrue(stmt.execute());
		ResultSet rs = stmt.getResultSet();
		assertNotNull(rs);
		assertTrue(rs.next());
		int intResult = rs.getInt(1);
		assertEquals(1, intResult);
		rs.close();
//		test with args
		stmt = conn.prepareStatement(" ? ");
		stmt.setInt(1, 1);
		assertTrue(stmt.execute());
		assertNotNull(rs = stmt.getResultSet());
		assertTrue (rs.next());
		intResult = rs.getInt(1);
		assertEquals(1, intResult);
		rs.close();
	}

	@Test
	@TestDescription(
			planId="dlms.atom",
			description="Atom objects created with InputStream tests."
			)
	public void testPayloadAtom() throws Exception {
		Connection conn = dataSource.getConnection();
		try {
//			creating atom type and set
//			may throw an exception if already created
			try {
				execute(new String[] {
						"Type PDFAtomType = Atom(PDF);", 
						"Set Documents = create PDFAtomType;"}, 
						true);
			} catch (Exception e) {
//				TODO check if exception is related to already created type/set
//				if not - raise failure
				log.error(e);
			}
//			fetching PDF via input stream
			PreparedStatement statement = conn.prepareStatement("Object o = new Documents(?);");
			statement.setBinaryStream(1, new URL(textPdfLocation).openStream());
			statement.execute();
//			retrieving payload url 
			statement = conn.prepareStatement("select (payloadURL) from Documents");
			ResultSet rs = statement.executeQuery();
			assertTrue(rs.next());
			String dorotyURL = rs.getString(1);
			assertNotNull(dorotyURL);
//			TODO check using regexp if URL is proper internal doroty link
			System.out.println("payloaded file location: " + dorotyURL);
//			getting payloaded entry for removal
			statement = conn.prepareStatement("select Documents[@payloadURL=?]");
			statement.setString(1, dorotyURL);
			rs = statement.executeQuery();
			assertTrue(rs.next());
			InformationObject infObject = (InformationObject) rs.getObject(1);
			assertNotNull(infObject);
//			removing entry
			statement = conn.prepareStatement("Documents.drop(?)");
			statement.setObject(1, infObject);
			assertTrue(statement.execute());
		} finally {
			cleanupSet("Documents");
			conn.close();
//			TODO remove PDFAtomType and Documents when appriopriate methods provided
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.atom",
			description="Atom objects created with external, not payloaded URL tests."
			)
	public void testExternalURLAtom() throws Exception {
		Connection conn = dataSource.getConnection();
		try {
//			creating atom type and set
//			may throw an exception if already created
			try {
				execute(new String[] {
						"Type PDFAtomType = Atom(PDF);", 
						"Set Documents = create PDFAtomType;"}, 
						true);
			} catch (Exception e) {
//				TODO check if exception is related to already created type/set
//				if not - raise failure
				log.error(e);
			}
//			fetching PDF via input stream
			PreparedStatement statement = conn.prepareStatement("Object o = new Documents(?,?);");
//			statement.setString(1, textPdfLocation);
			statement.setURL(1, new URL(textPdfLocation));
			statement.setBoolean(2, false);
			statement.execute();
//			retrieving payload url 
			statement = conn.prepareStatement("select (payloadURL) from Documents");
			ResultSet rs = statement.executeQuery();
			assertTrue(rs.next());
			String dorotyURL = rs.getString(1);
			assertNotNull(dorotyURL);
			assertEquals(textPdfLocation, dorotyURL);
//			getting payloaded entry for removal
			statement = conn.prepareStatement("select Documents[@payloadURL=?]");
			statement.setString(1, dorotyURL);
			rs = statement.executeQuery();
			assertTrue(rs.next());
			InformationObject infObject = (InformationObject) rs.getObject(1);
			assertNotNull(infObject);
//			removing entry
			statement = conn.prepareStatement("Documents.drop(?)");
			statement.setObject(1, infObject);
			assertTrue(statement.execute());
		} finally {
			conn.close();
			cleanupSet("Documents");
//			TODO remove PDFAtomType and Documents when appriopriate methods provided 
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.atom",
			description="Atom objects created with external, payloaded URL tests."
			)
	public void testExternalPayloadedURLAtom() throws Exception {
		Connection conn = dataSource.getConnection();
		try {
//			creating atom type and set
//			may throw an exception if already created
			try {
				execute(new String[] {
						"Type PDFAtomType = Atom(PDF);", 
						"Set Documents = create PDFAtomType;"}, 
						true);
			} catch (Exception e) {
//				TODO check if exception is related to already created type/set
//				if not - raise failure
				log.error(e);
			}
//			fetching PDF via input stream
			PreparedStatement statement = conn.prepareStatement("Object o = new Documents(?,?);");
//			statement.setString(1, textPdfLocation);
			statement.setURL(1, new URL(textPdfLocation));
			statement.setBoolean(2, true);
			statement.execute();
//			retrieving payload url 
			statement = conn.prepareStatement("select (payloadURL) from Documents");
			ResultSet rs = statement.executeQuery();
			assertTrue(rs.next());
			String dorotyURL = rs.getString(1);
			assertNotNull(dorotyURL);
			assertFalse(textPdfLocation.equals(dorotyURL));
//			TODO check using regexp if URL is proper internal doroty link
			System.out.println("payloaded file location: " + dorotyURL);
//			getting payloaded entry for removal
			statement = conn.prepareStatement("select Documents[@payloadURL=?]");
			statement.setString(1, dorotyURL);
			rs = statement.executeQuery();
			assertTrue(rs.next());
			InformationObject infObject = (InformationObject) rs.getObject(1);
			assertNotNull(infObject);
//			removing entry
			statement = conn.prepareStatement("Documents.drop(?)");
			statement.setObject(1, infObject);
			assertTrue(statement.execute());
		} finally {
			conn.close();
			cleanupSet("Documents");
//			TODO remove PDFAtomType and Documents when appriopriate methods provided
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.store",
			description="Storing objects tests."
			)
	public void testStoringObjects() throws Exception {
		try {
			initializeModel();
			assertTrue(executeSingleOperationWithParams(
					"Object objVolume = new VolumeSet(name=?,description=?);",
					volumeName, volumeDesc));
			assertTrue(executeSingleOperationWithParams(
					"Object objOtherVolume = new VolumeSet(name=?,description=?);",
					otherVolumeName, otherVolumeDesc));
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle1 = new ArticleSet(name=?,description=?);",
					article1Name, article1Desc));
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle2 = new ArticleSet(name=?,description=?);",
					article2Name, article2Desc));
//			getting all stored objects
			InformationObject art1 = getSingleObjectByName(article1Name, "ArticleSet");
			assertNotNull(art1);
			InformationObject art2 = getSingleObjectByName(article2Name, "ArticleSet");
			assertNotNull(art2);
			InformationObject volume = getSingleObjectByName(volumeName, "VolumeSet");
			assertNotNull(volume);
			InformationObject otherVolume = getSingleObjectByName(otherVolumeName, "VolumeSet");
			assertNotNull(otherVolume);
		} finally {
			cleanupModel();
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.rel",
			description="Relations related tests."
			)
	public void testInserInvalidRelations() throws Exception {
		try {
			initializeModel();
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle1 = new ArticleSet(name=?,description=?);",
					article1Name, article1Desc));
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle2 = new ArticleSet(name=?,description=?);",
					article2Name, article2Desc));
			
			InformationObject art1 = getSingleObjectByName(article1Name, "ArticleSet");
			assertNotNull(art1);
			InformationObject art2 = getSingleObjectByName(article2Name, "ArticleSet");
			assertNotNull(art2);
			
//			insert invalid relation
			try {
				executeSingleOperationWithParams(
						"Object badRel = new relatedArticles(?, ?)",
						art1, art2);
				fail("exception should be thrown when inserting invalid relation");
			} catch (Exception e) {
//				ok, TODO verify exception kind
			}
		} finally {
			cleanupModel();
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.rel",
			description="Relations related tests."
			)
	public void testRelations() throws Exception {
		try {
			initializeModel();
			assertTrue(executeSingleOperationWithParams(
					"Object objVolume = new VolumeSet(name=?,description=?);",
					volumeName, volumeDesc));
			assertTrue(executeSingleOperationWithParams(
					"Object objOtherVolume = new VolumeSet(name=?,description=?);",
					otherVolumeName, otherVolumeDesc));
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle1 = new ArticleSet(name=?,description=?);",
					article1Name, article1Desc));
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle2 = new ArticleSet(name=?,description=?);",
					article2Name, article2Desc));
//			getting all stored objects
			InformationObject art1 = getSingleObjectByName(article1Name, "ArticleSet");
			assertNotNull(art1);
			InformationObject art2 = getSingleObjectByName(article2Name, "ArticleSet");
			assertNotNull(art2);
			InformationObject volume = getSingleObjectByName(volumeName, "VolumeSet");
			assertNotNull(volume);
			InformationObject otherVolume = getSingleObjectByName(otherVolumeName, "VolumeSet");
			assertNotNull(otherVolume);
			
//			inserting valid relations
			assertTrue(executeSingleOperationWithParams(
					"Object currentRelArt1 = new relatedArticles(?, ?);",
					volume, art1));
			assertTrue(executeSingleOperationWithParams(
					"Object currentRelArt2 = new relatedArticles(?, ?);",
					volume, art2));
//			adding article to other volume, shouldn't be allowed
			try {
				executeSingleOperationWithParams(
						"Object badRel = new relatedArticles(?, ?)",
						otherVolume, art1);
				fail("exception should be thrown when adding article to more than one volume");
			} catch (Exception e) {
//				ok, TODO verify exception kind
			}
		} finally {
			cleanupModel();
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.rel",
			description="Relations cycles related tests."
			)
	public void testRelationCycles() throws Exception {
		Connection conn = dataSource.getConnection();
		try {
			initializeModel();

			assertTrue(executeSingleOperationWithParams(
					"Object objVolume = new VolumeSet(name=?,description=?);",
					volumeName, volumeDesc));
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle1 = new ArticleSet(name=?,description=?);",
					article1Name, article1Desc));
//			getting all stored objects
			InformationObject art1 = getSingleObjectByName(article1Name, "ArticleSet");
			assertNotNull(art1);
			InformationObject volume = getSingleObjectByName(article2Name, "VolumeSet");
			assertNotNull(volume);

//			inserting valid relations
			assertTrue(executeSingleOperationWithParams(
					"Object currentRelArt1 = new relatedArticles(?, ?);",
					volume, art1));
			
//			test cycles when querying for reachable objects with "//" (cycles between references and finding path in graph);
			assertTrue(executeSingleOperationWithParams(
					"Set beingPartOfVolume = create rel(ArticleSet, VolumeSet, n:1, t:t);", new Object[0]));
			assertTrue(executeSingleOperationWithParams(
					"Object currentRelVol1 = new beingPartOfVolume(?, ?);",
					art1, volume));
			//will it go into infinite loop? should return one volume object
			PreparedStatement prepStatement = conn.prepareStatement("VolumeSet//beingPartOfVolume");
			ResultSet rs = prepStatement.executeQuery();
			assertTrue(rs.next());
			InformationObject infObject = (InformationObject) rs.getObject(1);
			assertNotNull(infObject);
			assertEquals(volume.getId(), infObject.getId());
			assertFalse(rs.next());
			rs.close();
			prepStatement.close();
			
		} finally {
			conn.close();
			cleanupModel();
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.query",
			description="QL related tests."
			)
	public void testQuerying() throws Exception {
		Connection conn = dataSource.getConnection();
		try {
			initializeModel();
//			storing objects
			assertTrue(executeSingleOperationWithParams(
					"Object objVolume = new VolumeSet(name=?,description=?);",
					volumeName, volumeDesc));
			assertTrue(executeSingleOperationWithParams(
					"Object objOtherVolume = new VolumeSet(name=?,description=?);",
					otherVolumeName, otherVolumeDesc));
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle1 = new ArticleSet(name=?,description=?);",
					article1Name, article1Desc));
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle2 = new ArticleSet(name=?,description=?);",
					article2Name, article2Desc));
//			getting all stored objects
			InformationObject art1 = getSingleObjectByName(article1Name, "ArticleSet");
			assertNotNull(art1);
			InformationObject art2 = getSingleObjectByName(article2Name, "ArticleSet");
			assertNotNull(art2);
			InformationObject volume = getSingleObjectByName(volumeName, "VolumeSet");
			assertNotNull(volume);
			InformationObject otherVolume = getSingleObjectByName(otherVolumeName, "VolumeSet");
			assertNotNull(otherVolume);

//			inserting valid relations
			assertTrue(executeSingleOperationWithParams(
					"Object currentRelArt1 = new relatedArticles(?, ?);",
					volume, art1));
			assertTrue(executeSingleOperationWithParams(
					"Object currentRelArt2 = new relatedArticles(?, ?);",
					volume, art2));
			
//			testing queries
//			[target oriented] finding articles from given volume set which contains given name;
			PreparedStatement prepStatement = conn.prepareStatement("VolumeSet/relatedArticles[/*[@name=?]]");
			prepStatement.setString(1, article1Name);
			ResultSet rs = prepStatement.executeQuery();
			assertTrue(rs.next());
			InformationObject infObject = (InformationObject) rs.getObject(1);
			assertNotNull(infObject);
			assertEquals(art1.getId(), infObject.getId());
			assertFalse(rs.next());
			rs.close();
			prepStatement.close();
//			[source oriented] finding volumes which contain articles of specified name;
			prepStatement = conn.prepareStatement("VolumeSet/[relatedArticles[/*[@name=?]]]");
			prepStatement.setString(1, article1Name);
			rs = prepStatement.executeQuery();
			assertTrue(rs.next());
			infObject = (InformationObject) rs.getObject(1);
			assertNotNull(infObject);
			assertEquals(volume.getId(), infObject.getId());
			assertFalse(rs.next());
			rs.close();
			prepStatement.close();
//			//[field oriented] finding names of all articles reachable from given volume set;
			prepStatement = conn.prepareStatement("VolumeSet/relatedArticles/attribute:name");
			rs = prepStatement.executeQuery();
			assertTrue(rs.next());
			String currentArtName = rs.getString(1);
			assertNotNull(currentArtName);
			assertTrue(article1Name.equals(currentArtName) || article2Name.equals(currentArtName));
			assertTrue(rs.next());
			currentArtName = rs.getString(1);
			assertNotNull(currentArtName);
			assertTrue(article1Name.equals(currentArtName) || article2Name.equals(currentArtName));
			assertFalse(rs.next());
			rs.close();
			prepStatement.close();
//			[relation oriented] finding relations objects;
			prepStatement = conn.prepareStatement("VolumeSet/relation::relatedArticles");
			rs = prepStatement.executeQuery();
			assertTrue(rs.next());
			assertNotNull(rs.getObject(1));
			assertTrue(rs.next());
			assertNotNull(rs.getObject(1));
			assertFalse(rs.next());
			rs.close();
			prepStatement.close();
			
		} finally {
			conn.close();
			cleanupModel();
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.import",
			description="Object import related tests."
			)
	public void testImporting() throws Exception {
		Connection conn = dataSource.getConnection();
		try {
			initializeModel();
//			storing article object
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle1 = new ArticleSet(name=?,description=?);",
					article1Name, article1Desc));
//			getting stored article object
			InformationObject art1 = getSingleObjectByName(article1Name, "ArticleSet");
			assertNotNull(art1);
			
//			testing importing object from different set
			assertTrue(executeSingleOperationWithParams(
					"VolumeSet.import(?);", 
					art1));
			PreparedStatement prepStatement = conn.prepareStatement("VolumeSet[@name=?]");
			prepStatement.setString(1, article1Name);
			ResultSet rs = prepStatement.executeQuery();
			assertTrue(rs.next());
			InformationObject infObject = (InformationObject) rs.getObject(1);
			assertNotNull(infObject);
			assertFalse(rs.next());
		} finally {
			conn.close();
			cleanupModel();
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.import",
			description="Object import related tests."
			)
	public void testCasting() throws Exception {
		Connection conn = dataSource.getConnection();
		try {
			initializeModel();
//			storing article object
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle1 = new ArticleSet(name=?,description=?);",
					article1Name, article1Desc));
//			getting stored article object
			InformationObject art1 = getSingleObjectByName(article1Name, "ArticleSet");
			assertNotNull(art1);
			
//			casting tests
			try {
				execute(new String[] {
						"Type SimpleVolumeTypeBis = struct([name:string;description:string]);", 
						"Set VolumeSetBis = create SimpleVolumeTypeBis;"}, 
						true);
			} catch (Exception e) {
//				TODO check if exception is related to already created type/set
//				if not - raise failure
				log.error(e);
			}
			String volumeBisName = "volumeBis";
			assertTrue(executeSingleOperationWithParams(
					"Object volumeObjBis = new VolumeSetBis(name=?,description=?);",
					volumeBisName, "volume bis description"));
			InformationObject volBis = getSingleObjectByName(volumeBisName, "VolumeSetBis");
			assertNotNull(volBis);
			assertTrue(executeSingleOperationWithParams(
					"VolumeSet.cast(?);",
					volBis));
			PreparedStatement prepStatement = conn.prepareStatement("VolumeSet[@name=?]");
			prepStatement.setString(1, volumeBisName);
			ResultSet rs = prepStatement.executeQuery();
			assertTrue(rs.next());
			InformationObject infObject = (InformationObject) rs.getObject(1);
			assertNotNull(infObject);
			assertFalse(rs.next());
		} finally {
			conn.close();
			cleanupSet("VolumeSetBis");
			cleanupModel();
		}
	}
	
	@Test
	@TestDescription(
			planId="dlms.del",
			description="Data deletion related tests."
			)
	public void testRemoval() throws Exception {
		Connection conn = dataSource.getConnection();
		try {
			initializeModel();
//			storing article object and volume
			assertTrue(executeSingleOperationWithParams(
					"Object objVolume = new VolumeSet(name=?,description=?);",
					volumeName, volumeDesc));
			assertTrue(executeSingleOperationWithParams(
					"Object objArticle1 = new ArticleSet(name=?,description=?);",
					article1Name, article1Desc));
//			getting stored article object
			InformationObject art1 = getSingleObjectByName(article1Name, "ArticleSet");
			assertNotNull(art1);
			InformationObject volume = getSingleObjectByName(volumeName, "VolumeSet");
			assertNotNull(volume);
			
			assertTrue(executeSingleOperationWithParams(
					"Object currentRelArt1 = new relatedArticles(?, ?);",
					volume, art1));
			
//			test removal objects from set
			assertTrue(executeSingleOperationWithParams(
					"ArticleSet.remove(?);", 
					art1));
			
//			verify results of removal
			PreparedStatement prepStatement = conn.prepareStatement("ArticleSet[@name=?]");
			prepStatement.setString(1, article1Name);
			ResultSet rs = prepStatement.executeQuery();
			assertFalse(rs.next());
//			it should be removed from relation as well
			prepStatement = conn.prepareStatement("VolumeSet/relatedArticles[/*[@name=?]]");
			prepStatement.setString(1, article1Name);
			rs = prepStatement.executeQuery();
			assertFalse(rs.next());
		} finally {
			conn.close();
			cleanupModel();
		}
	}
	
	/**
	 * Initializes model for tests.
	 * @throws Exception
	 */
	protected void initializeModel() throws Exception {
//		defining types and sets
//		may throw an exception if already created
		try {
			execute(new String[] {
					"Type GenericNameDescType = struct([name:string;description:string]);", 
					"Type SimpleVolumeType = GenericNameDescType;",
					"Type SimpleArticleType = GenericNameDescType;",
					"Set VolumeSet = create SimpleVolumeType;",
					"Set ArticleSet = create SimpleVolumeType;",
					"Set relatedArticles = create rel(VolumeSet, ArticleSet, 1:n, t:t);"}, 
					true);
		} catch (Exception e) {
//			TODO check if exception is related to already created type/set
//			if not - raise failure
			log.error(e);
		}
	}
	
	protected void cleanupModel() throws Exception {
		cleanupSet("ArticleSet");
		cleanupSet("VolumeSet");
		
//		TODO drop types and sets when DELETE supported in dlms
	}
	
	protected InformationObject getSingleObjectByName(String objName, String setName) 
		throws Exception {
		Connection conn = dataSource.getConnection();
		PreparedStatement statement = null;
		ResultSet rs = null;
		try {
			statement = conn.prepareStatement(
					"select " + setName + "[@name=?]");
			statement.setString(1, objName);
			rs = statement.executeQuery();
			rs.next();
			return (InformationObject) rs.getObject(1);
		} finally {
			if (rs!=null){
				rs.close();
			}
			if (statement!=null){
				statement.close();
			}
			conn.close();
		}
	}
	
	/**
	 * Executes single operation with string params.
	 * If object is instance of String PreparedStatement#setString() is used.
	 * @param operation
	 * @param params
	 * @return returns execute() result
	 */
	protected boolean executeSingleOperationWithParams(String operation, Object... params) 
		throws Exception {
		Connection conn = dataSource.getConnection();
		PreparedStatement statement = null;
		try {
			statement = conn.prepareStatement(operation);
			if (params!=null && params.length>0) {
				for (int i=0; i<params.length; i++) {
					if (params[i] instanceof String) {
						statement.setString(i+1, (String) params[i]);
					} else {
						statement.setObject(i+1, params[i]);
					}
					
				}
			}
			return statement.execute();	
		} finally {
			if (statement!=null){
				statement.close();
			}
			conn.close();
		}
		
	}
	
	/**
	 * Executes given operations against dmls using transaction if flag set.
	 * @param operations
	 * @param inTransaction
	 * @throws Exception
	 */
	protected void execute(String[] operations, boolean inTransaction) throws Exception {
		if (inTransaction) {
			StringBuffer transaction = new StringBuffer();
			transaction.append('{');
			for (String currentOp : operations) {
				if (!currentOp.trim().endsWith(";")) {
					transaction.append(currentOp.trim() + ';');
				} else {
					transaction.append(currentOp);
				}
				transaction.append('\n');
			}
			transaction.append('}');
			Connection conn = dataSource.getConnection();
			Statement statement = conn.createStatement();
			statement.execute(transaction.toString());
			statement.close();
			conn.close();
		} else {
			Connection conn = dataSource.getConnection();
			Statement statement = conn.createStatement();
			for (String currentOp : operations) {
				statement.execute(currentOp);
			}
			statement.close();
			conn.close();
		}
	}
	
	/**
	 * Cleaning up set by deleting all stored objects.
	 * Relations are removed automatically by underlaying dlms.
	 * @param setName
	 * @throws Exception
	 */
	protected void cleanupSet(String setName) throws Exception {
		Connection conn = dataSource.getConnection();
		Statement statement = null;
		ResultSet rs = null;
		List<InformationObject> toBeDeleted = new ArrayList<InformationObject>(); 
		try {
//			getting all stored objects
			statement = conn.createStatement();
			rs = statement.executeQuery("select " + setName);
			while (rs.next()) {
//				will it work when deleting while iterating RS?
				toBeDeleted.add((InformationObject) rs.getObject(1));
			}
			PreparedStatement prepStatement = conn.prepareStatement(setName + ".drop(?)");
			try {
				for (InformationObject currentInfoObj : toBeDeleted) {
					prepStatement.setObject(1, currentInfoObj);
					prepStatement.execute();
				}
			} finally {
				prepStatement.close();
			}
		} finally {
			if (rs!=null){
				rs.close();
			}
			if (statement!=null){
				statement.close();
			}
			conn.close();
		}
	}
}