[embjopr-commits] EMBJOPR SVN: r110 - trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit.

embjopr-commits at lists.jboss.org embjopr-commits at lists.jboss.org
Mon Jan 19 13:21:11 EST 2009


Author: ozizka at redhat.com
Date: 2009-01-19 13:21:11 -0500 (Mon, 19 Jan 2009)
New Revision: 110

Added:
   trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/DatasourceTestBase.java
Log:
Added: Datasource test base class - common denominator for AS 4 and AS 5 test subclasses.


Added: trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/DatasourceTestBase.java
===================================================================
--- trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/DatasourceTestBase.java	                        (rev 0)
+++ trunk/jsfunit/src/test/java/org/jboss/jopr/jsfunit/DatasourceTestBase.java	2009-01-19 18:21:11 UTC (rev 110)
@@ -0,0 +1,544 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This 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 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software 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 software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.jboss.jopr.jsfunit;
+
+import org.jboss.jopr.jsfunit.*;
+import com.gargoylesoftware.htmlunit.html.*;
+import java.io.IOException;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.jboss.mx.util.MBeanServerLocator;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+import java.util.Set;
+import java.util.Iterator;
+import org.jdom.Document;
+import org.jdom.Element;
+import org.jdom.input.SAXBuilder;
+import java.io.File;
+import java.util.Map;
+import java.util.HashMap;
+import javax.naming.InitialContext;
+import java.sql.Connection;
+import javax.sql.DataSource;
+import javax.naming.Context;
+import java.sql.SQLException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.management.*;
+
+
+
+
+/**
+ * When complete, this class will contain tests for creating, 
+ * configuring, and deleting various types of datasources. This
+ * test class should be run against JBAS 5.x.
+ * 
+ * @author Farah Juma
+ * @author Ondrej Zizka
+ *
+ */
+public abstract class DatasourceTestBase extends EmbjoprTestCase {
+
+   
+    // Datasource types, as they appear in the left nav
+		protected enum DatasourceType {
+
+			// Some of these values are specific for AS 5.  TODO: Think up some way to split nicely.
+			LOCAL_TX_DATASOURCE("Local TX Datasources", "LocalTxCM", "local-tx-datasource", "default__Local TX Datasource"),
+			NO_TX_DATASOURCE(   "No TX Datasources",    "NoTxCM",    "no-tx-datasource", null),  // TODO: Fill the value.
+			XA_DATASOURCE(      "XA Datasources",       "XATxCM",    "xa-datasource",       "default__XA Datasource");
+
+			protected String label;
+			public String getLabel() { return label; }
+			//public void setLabel(String label) {		this.label = label;	}
+
+			protected final String serviceName;
+			public String getServiceName() {				return serviceName;			}
+
+
+			protected final String xmlElementName;
+			public String getXmlElementName() {				return xmlElementName;			}
+
+			protected final String templateHtmlSelectValue;
+			public String getTemplateHtmlSelectValue() {				return templateHtmlSelectValue;			}
+
+
+
+			private DatasourceType(String label, String serviceName, 
+								String xmlElementName, String htmlSelectValue)
+			{
+				this.label = label;
+				this.serviceName = serviceName;
+				this.xmlElementName = xmlElementName;
+				this.templateHtmlSelectValue = htmlSelectValue;
+			}
+
+		}// DatasourceTypes
+
+
+
+		// --- Datasource Templates --- //
+		/*
+			<select id="resourceCreateForm:selectedTemplate" size="1" name="resourceCreateForm:selectedTemplate">
+				<option value="">Select Template</option>
+				<option value="Oracle Local TX__Datasource">Oracle Local TX (Datasource)</option>
+				<option value="Oracle XA__Datasource">Oracle XA (Datasource)</option>
+				<option value="default__Datasource">default (Datasource)</option>
+			</select>
+		 */
+		protected enum DatasourceTemplate {
+
+			ORACLE_LOCAL_TX("Oracle Local TX__Datasource"),
+			ORACLE_XA("Oracle XA__Datasource"),
+			DEFAULT("default__Datasource");
+
+			protected final String templateHtmlSelectValue;
+
+
+			private DatasourceTemplate(String templateHtmlSelectValue) {
+				this.templateHtmlSelectValue = templateHtmlSelectValue;
+			}
+
+
+
+			/**
+			 * Value of HTML select option for this template.
+			 */
+			public String getTemplateHtmlSelectValue() {
+				return templateHtmlSelectValue;
+			}
+
+		}
+
+
+		
+
+
+		// Datasource properties
+
+		private Map<String, String> datasourceProperties = createDatasourceProperties();
+
+		/**
+		 * @returns a set of properties created upon initialization by overriden createDatasourceProperties().
+		 */
+		public Map<String, String> getDatasourceProperties() { return datasourceProperties; }
+
+
+
+
+
+    /**
+     * Create a new datasource using the given type, template, and properties.
+     * 
+     * @param datasourceType must be the name of a datasource type, as it 
+     * appears in the left nav (eg. "Local TX Datasources")
+     */
+    protected abstract void createDatasource(DatasourceType datasourceType,
+                                  String datasourceTemplate,
+                                  Map<String, String> propertiesMap) throws IOException;
+    
+    /**
+     * Delete the datasource given by datasourceName.
+     */
+    protected abstract void deleteDatasource(String datasourceName) throws IOException;
+    
+    /**
+     * Use JMX to check if the datasource given by datasourceName is deployed.
+     */
+    protected boolean isDatasourceDeployed(String jndiName,  DatasourceType datasourceType) {
+        try {
+
+            String[] dsMBeanServices = {"DataSourceBinding",
+                                        "ManagedConnectionPool",
+                                        "ManagedConnectionFactory",
+                                        datasourceType.getServiceName() };
+
+            // Query the MBean server to check if the datasource is deployed
+            MBeanServer jmxServer = MBeanServerLocator.locateJBoss();
+
+            // Inspect these MBeans and make sure that the their state indicates successful deployment
+						// (e.g. for AS 5, "State" attribute is "DEPLOYED"):
+            // 1) "jboss.jca:name=TestDS,service=DataSourceBinding",type=Component
+            // 2) "jboss.jca:name=TestDS,service=ManagedConnectionPool",type=Component
+            // 3) "jboss.jca:name=TestDS,service=ManagedConnectionFactory",type=Component
+            // 4) The fourth MBean inspected depends on the type of datasource.
+            for(int i = 0; i < dsMBeanServices.length; i++) {
+
+								// Is this necessary? Can't we just query? See InstanceNotFoundException;
+							  // And AFAIK, full MBean name is unique. I vote for minimalistic code.
+                /*ObjectName objName = new ObjectName( this.getMBeanName(jndiName, dsMBeanServices[i]));
+
+                Set dsMBeans = jmxServer.queryNames(objName, null);
+                if (dsMBeans.size() != 1)  return false;
+
+								// Get the first and only one MBean returned.
+                ObjectName deploymentMBean = (ObjectName)dsMBeans.iterator().next(); /**/
+
+								ObjectName deploymentMBean = new ObjectName( this.getMBeanName(jndiName, dsMBeanServices[i]) );
+
+                ///Object state = jmxServer.getAttribute(deploymentMBean, "State");
+                ///if(!("DEPLOYED".equals(state.toString()))) return false;
+								if( !this.isMBeanStateDeployed( deploymentMBean ) )
+									return false;
+
+            }
+
+            return true;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+
+    /**
+     * checkProperties reads the *-ds.xml file corresponding to the datasource 
+     * given by jndiName and compares the property values in this file to the 
+     * given expected values. checkProperties returns true if all the given 
+     * properties are correctly set in the *-ds.xml file and false otherwise.
+     */
+    protected boolean checkProperties(String jndiName,
+                                    DatasourceType datasourceType,
+                                    Map<String, String> expectedValuesMap) {
+        
+        Map<String, String> actualValuesMap = new HashMap<String, String>();
+        
+        
+        try {
+            
+            // Parse the *-ds.xml file; create appropriate file name for AS 4 or 5.
+						File file = new File( this.getDatasourceConfigFile());
+         
+            SAXBuilder builder = new SAXBuilder();
+            Document doc = builder.build(file);
+            
+            Element root = doc.getRootElement();
+            assertTrue(root.getName().equals("datasources"));
+            
+            // Get the datasource element 
+            Element datasource = root.getChild( datasourceType.getXmlElementName() );
+            
+            // Create actualValuesMap by mapping property names to 
+            // property values
+            Iterator itr = (datasource.getChildren()).iterator();
+            while(itr.hasNext()) {
+              Element property = (Element)itr.next();
+              actualValuesMap.put(property.getName(), property.getValue());
+            }
+          
+            // Compare the actual values to the expected ones
+            itr = expectedValuesMap.keySet().iterator();
+            while(itr.hasNext()) {
+                String key = (String)itr.next();
+                if(actualValuesMap.containsKey(key)) {
+                    if(!expectedValuesMap.get(key).equals(actualValuesMap.get(key))) {
+                        return false; // incorrect value
+                    } 
+                } else {
+                    return false; // value was not set
+                }
+            }
+            
+            return true;
+            
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /**
+     * Connect to the database identified by the given JNDI name,
+     * using the given username and password. Return the Connection
+     * object.
+     */
+    public Connection connectDB(String jndiName, String username, 
+                                String password) throws Exception{
+        Context ctx = new InitialContext();
+        DataSource ds = (DataSource)ctx.lookup(jndiName);
+        return ds.getConnection(username, password);
+    }
+    
+    /**
+     * Disconnect from the database.
+     */
+    public void disconnectDB(Connection con) throws SQLException {
+        if(con != null) {
+            con.close();  
+        } 
+    }
+    
+    /**
+     * @return the suite of tests being tested
+		 * @throws UnsupportedOperationException - you have to override this.
+		 *         Can't be abstract, must be static.
+     */
+    public static Test suite()
+    {
+      throw new UnsupportedOperationException("This has to be overriden.");
+    }
+
+
+		
+    
+    /*
+     * --- Some preliminary creation tests ---
+     */
+
+
+		
+    /**
+     * Create a new datasource. Leave some property values that aren't 
+     * required unset.
+     */
+    public void testCreateDatasource() throws IOException {
+
+
+        // The properties we want to configure
+        Map<String, String> propertiesMap = this.getDatasourceProperties();
+
+        createDatasource(DatasourceType.LOCAL_TX_DATASOURCE,
+												 //was: "default__Local TX Datasource",
+												 DatasourceType.LOCAL_TX_DATASOURCE.getTemplateHtmlSelectValue(), // TODO: Redundant - remove.
+												 propertiesMap);
+        client.click("resourceConfigurationForm:saveButton");
+
+        // Check for the appropriate success messages
+        String expectedMessage = "Successfully added new Local TX Datasource";
+        checkClientAndServerMessages(expectedMessage, expectedMessage, false);
+
+        assertTrue(isDatasourceDeployed(propertiesMap.get("jndi-name"),
+                                        DatasourceType.LOCAL_TX_DATASOURCE));
+        assertTrue(checkProperties(propertiesMap.get("jndi-name"),
+                                   DatasourceType.LOCAL_TX_DATASOURCE,
+                                   propertiesMap));
+
+        // TODO: need to verify that appropriate default values were
+        // set for properties that were not specified above
+
+        // Clean up
+        deleteDatasource(propertiesMap.get("jndi-name"));
+        expectedMessage = "Successfully deleted Local TX Datasource '"
+                          + propertiesMap.get("jndi-name") + "'";
+        checkClientAndServerMessages(expectedMessage, expectedMessage, false);
+    }
+
+
+
+
+
+
+		
+    /**
+     * Attempt to create a new datasource but leave at least one required
+     * value unset. An error should occur.
+		 *
+		 * TODO:  Check if works for AS 4 .
+     */
+    public void testCreateDatasourceMissingRequiredValues() throws IOException {
+        
+        // Leave jndi-name and connection-url unset
+        Map<String, String> propertiesMap = new HashMap<String, String>();
+        propertiesMap.put("user-name", "testUser");
+        propertiesMap.put("max-pool-size", "10");
+        propertiesMap.put("prefill", "true");
+        propertiesMap.put("idle-timeout-minutes", "20");
+        propertiesMap.put("set-tx-query-timeout", "true");
+        propertiesMap.put("query-timeout", "1800");
+        
+        createDatasource(DatasourceType.NO_TX_DATASOURCE, 
+								         DatasourceType.NO_TX_DATASOURCE.getTemplateHtmlSelectValue(), //"default__No TX Datasource",
+                         propertiesMap);
+        client.click("resourceConfigurationForm:saveButton");
+        
+        // Check for the appropriate error messages
+        checkClientAndServerMessages("An invalid value was specified for one " 
+                                     + "or more properties",
+                                     "Value is required",
+                                     true);  
+    } 
+    
+    /**
+     * Attempt to create a new datasource but set a property value
+     * beyond its expected range of values. An error should occur.
+		 *
+		 * TODO:  Check if works for AS 4 .
+     */
+    public void testCreateDatasourcePropertyOutOfRange() throws IOException {
+        Map<String, String> propertiesMap = new HashMap<String, String>();
+        propertiesMap.put("jndi-name", "InvalidDS");
+        propertiesMap.put("user-name", "testUser");
+        propertiesMap.put("password", "password");
+        propertiesMap.put("xa-datasource-class", "org.postgresql.xa.PGXADataSource");
+        propertiesMap.put("xa-resource-timeout", "36000");
+
+        // This number is too big
+        propertiesMap.put("max-pool-size", "100000000000000");
+
+        createDatasource(DatasourceType.XA_DATASOURCE,
+												 DatasourceType.XA_DATASOURCE.getTemplateHtmlSelectValue(),
+                         propertiesMap);
+        client.click("resourceConfigurationForm:saveButton");
+
+        // Check for the appropriate error messages
+        checkClientAndServerMessages("An invalid value was specified for one or more properties",
+                                     "Specified attribute is not between the expected values",
+                                     true);
+    }
+    
+    
+    /**
+     * Attempt to create a new datasource but set a property value
+     * to an invalid type. An error should occur.
+		 *
+		 * TODO:  Check if works for AS 4 .
+     */
+    public void testCreateDatasourceInvalidPropertyType() throws IOException {
+        Map<String, String> propertiesMap = new HashMap<String, String>();
+        propertiesMap.put("jndi-name", "InvalidDS");
+        propertiesMap.put("min-pool-size", "10");
+        propertiesMap.put("max-pool-size", "20");
+        propertiesMap.put("driver-class", "org.hsqldb.jdbcDriver");
+        propertiesMap.put("connection-url", "jdbc:hsqldb:.");
+        propertiesMap.put("share-prepared-statements", "false"); 
+        
+        // This property value is supposed to be an integer
+        propertiesMap.put("background-validation-millis", "abcde"); 
+        
+        createDatasource(DatasourceType.LOCAL_TX_DATASOURCE,
+                         DatasourceType.LOCAL_TX_DATASOURCE.getTemplateHtmlSelectValue(), // TODO: Redundant, remove.
+                         propertiesMap);
+        client.click("resourceConfigurationForm:saveButton");
+        
+        // Check for the appropriate error messages
+        checkClientAndServerMessages("An invalid value was specified for one or more properties", 
+                                     "Value is not a valid integer", 
+                                     true);
+    }
+
+
+
+
+
+
+		/*
+		 *   ---  Various support methods. ---
+		 */
+
+
+
+
+		/**
+		 * Creates the default properties for the datasource - shared by AS4 and AS5.
+		 * Overriding method should get result of this method and overwrite it with it's custom properties.
+		 * @returns a set of default properties.
+		 */
+		protected Map<String, String> createDatasourceProperties()
+		{
+		    Map<String, String> propertiesMap = new HashMap();
+
+        // The properties we want to configure
+        propertiesMap.put("jndi-name", "TestDS");
+        propertiesMap.put("user-name", "testUser");
+        propertiesMap.put("password", "password");
+        propertiesMap.put("min-pool-size", "5");
+        propertiesMap.put("driver-class", "org.hsqldb.jdbcDriver");
+        propertiesMap.put("connection-url", "jdbc:hsqldb:."); // Store data current working dir.
+        propertiesMap.put("idle-timeout-minutes", "20");
+				//propertiesMap.put("query-timeout", "180"); // AS 5 only - moved there
+        propertiesMap.put("prepared-statement-cache-size", "2");
+				// Share Prepared Statements - AS 5
+        //propertiesMap.put("share-prepared-statements", "false"); // AS 5
+
+        propertiesMap.put("valid-connection-checker-class-name",
+                          "org.jboss.resource.adapter.jdbc.CheckValidConnectionSQL");
+        //propertiesMap.put("stale-connection-checker-class-name",
+        //                "org.jboss.resource.adapter.jdbc.StaleConnectionChecker"); // AS 5
+        propertiesMap.put("exception-sorter-class-name",
+                          "org.jboss.resource.adapter.jdbc.ExceptionSorter");
+        //propertiesMap.put("allocation-retry", "10000"); // AS 5
+        //propertiesMap.put("allocation-retry-wait-millis", "10000"); // AS 5
+
+        //propertiesMap.put("background-validation-millis", "15000"); // AS 5
+        //propertiesMap.put("prefill", "true");                       // AS 5
+        //propertiesMap.put("use-try-lock", "61000");                 // AS 5
+
+				return propertiesMap;
+
+		}
+
+
+		
+		/**
+		 *
+		 * @returns a name of the Datasource's config file to check the properties in.
+		 */
+		protected abstract String getDatasourceConfigFile();
+
+		protected abstract String getMBeanName( String jndiName, String serviceName );
+
+		/**
+		 * This method is wrapper that handles JMX exceptions at Base level.
+		 * Intended to keep AS-version-specific method, isMBeanStateDeployedImpl(), as small as possible.
+		 * @param deploymentMBean
+		 * @return
+		 * @throws javax.management.JMException
+		 * @throws java.io.IOException
+		 */
+		protected boolean isMBeanStateDeployed( ObjectName deploymentMBean ) {
+
+			try {
+				return this.isMBeanStateDeployedImpl(deploymentMBean);
+			}
+			// This super-exception includes JMX operation failures, including:
+			// AttributeNotFoundException, InstanceNotFoundException, MalformedObjectNameException, ServiceNotFoundException
+			catch (OperationsException ex) {
+				log.log(Level.WARNING, "JMX operation error when retrieving MBean attribute.", ex);
+				return false;
+			}
+			// All other JMX failures...
+			catch (JMException ex) {
+				log.log(Level.WARNING, "JMX error when retrieving MBean attribute.", ex);
+				return false;
+			}
+			catch (IOException ex) {
+				log.log(Level.SEVERE, "I/O error when retrieving MBean attribute.", ex);
+				return false;
+			}
+
+	  }// isMBeanStateDeployed()
+
+		/**
+		 * This method should query the JMX server and decide whether the given MBean displays deployed resource.
+		 * @param deploymentMBean  Name of the MBean to examine. Differs between AS4 and 5.
+		 * @return true  if the MBean indicates that the resource is deployed, false otherwise.
+		 * @throws javax.management.JMException  upon JMX related error, including invalid
+		 *                 or non-existent MBean / attribute name.
+		 * @throws java.io.IOException  upon I/O error.
+		 */
+		protected abstract boolean isMBeanStateDeployedImpl( ObjectName deploymentMBean ) throws JMException, IOException;
+
+
+}
+




More information about the embjopr-commits mailing list