| 1 | /** |
| 2 | * Copyright 2007 ATG DUST Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * |
| 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software |
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | * See the License for the specific language governing permissions and limitations under the License. |
| 13 | */ |
| 14 | package atg.adapter.gsa; |
| 15 | |
| 16 | import java.beans.PropertyEditor; |
| 17 | import java.io.File; |
| 18 | import java.net.URL; |
| 19 | import java.sql.SQLException; |
| 20 | import java.sql.Timestamp; |
| 21 | import java.util.Date; |
| 22 | import java.util.HashMap; |
| 23 | import java.util.Properties; |
| 24 | import java.util.Random; |
| 25 | |
| 26 | import junit.framework.TestCase; |
| 27 | |
| 28 | import org.apache.log4j.Logger; |
| 29 | |
| 30 | import atg.beans.DynamicPropertyDescriptor; |
| 31 | import atg.core.util.StringUtils; |
| 32 | import atg.nucleus.Nucleus; |
| 33 | import atg.nucleus.servlet.NucleusServlet; |
| 34 | import atg.repository.MutableRepositoryItem; |
| 35 | import atg.repository.RepositoryException; |
| 36 | import atg.repository.RepositoryPropertyDescriptor; |
| 37 | import atg.service.idgen.IdGenerator; |
| 38 | import atg.service.idgen.IdGeneratorException; |
| 39 | import atg.test.util.DBUtils; |
| 40 | |
| 41 | /** |
| 42 | * A basic GSA test which is expected to be extended than used directly. |
| 43 | * Has several utility methods. |
| 44 | * @author adwivedi |
| 45 | * |
| 46 | */ |
| 47 | public class GSATest extends TestCase { |
| 48 | |
| 49 | private final transient Random random = new Random(); |
| 50 | |
| 51 | private static Logger log = Logger.getLogger(GSATest.class); |
| 52 | |
| 53 | private HashMap<String, File> mConfigDir = new HashMap<String, File>(); |
| 54 | /** |
| 55 | * |
| 56 | */ |
| 57 | public GSATest() { |
| 58 | super(); |
| 59 | // TODO Auto-generated constructor stub |
| 60 | } |
| 61 | /* |
| 62 | * @see TestCase#setUp() |
| 63 | */ |
| 64 | protected void setUp() throws Exception { |
| 65 | super.setUp(); |
| 66 | } |
| 67 | |
| 68 | /* |
| 69 | * @see TestCase#tearDown() |
| 70 | */ |
| 71 | protected void tearDown() throws Exception { |
| 72 | super.tearDown(); |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Constructor for GSATest. |
| 77 | * @param arg0 |
| 78 | */ |
| 79 | public GSATest(String arg0) { |
| 80 | super(arg0); |
| 81 | } |
| 82 | public File getConfigpath() { |
| 83 | return getConfigpath(null); |
| 84 | } |
| 85 | /** |
| 86 | * Returns the configpath for tests |
| 87 | * |
| 88 | * @return |
| 89 | */ |
| 90 | public File getConfigpath(String pConfigDirectory) { |
| 91 | if (mConfigDir.get(pConfigDirectory) == null) { |
| 92 | String configdirname = "config"; |
| 93 | String packageName = StringUtils.replace(this.getClass().getPackage() |
| 94 | .getName(), '.', "/"); |
| 95 | if (pConfigDirectory != null) |
| 96 | configdirname = pConfigDirectory; |
| 97 | |
| 98 | String configFolder = packageName + "/data/" + configdirname; |
| 99 | |
| 100 | URL dataURL = this.getClass().getClassLoader().getResource(configFolder); |
| 101 | // Mkdir |
| 102 | if (dataURL == null) { |
| 103 | URL root = this.getClass().getClassLoader().getResource(packageName); |
| 104 | |
| 105 | File f = new File(root.getFile()); |
| 106 | File f2 = new File(f, "/data/" + configdirname); |
| 107 | f2.mkdirs(); |
| 108 | dataURL = this.getClass().getClassLoader().getResource(configFolder); |
| 109 | } |
| 110 | |
| 111 | mConfigDir.put(pConfigDirectory ,new File(dataURL.getFile())); |
| 112 | } |
| 113 | System.setProperty("atg.configpath",((File) mConfigDir.get(pConfigDirectory)).getAbsolutePath()); |
| 114 | return (File) mConfigDir.get(pConfigDirectory); |
| 115 | } |
| 116 | |
| 117 | /** |
| 118 | * Create a repository in the given configpath using the given repository definitions (Absolute paths) |
| 119 | * connecting to the db whose properties are specified in pDBProperties @see DBUtils |
| 120 | * Method pMethodName is invoked with the GSARepository passed to it as a parameter. |
| 121 | * @param pConfigPathWhereToCreateTheRepository |
| 122 | * @param definitionFiles |
| 123 | * @param pDBProperties |
| 124 | * @param pMethodName |
| 125 | * @throws Exception |
| 126 | * @throws Exception |
| 127 | */ |
| 128 | protected void setUpAndTest(File pConfigPathWhereToCreateTheRepository, String[] definitionFiles, Properties pDBProperties, String pMethodName) throws Exception{ |
| 129 | String repositoryComponentPath = "/"+getName()+"Repository"; |
| 130 | GSATestUtils.getGSATestUtils().initializeMinimalConfigpath(pConfigPathWhereToCreateTheRepository, |
| 131 | repositoryComponentPath, definitionFiles, pDBProperties, null, null, null, true); |
| 132 | Nucleus n = startNucleus(pConfigPathWhereToCreateTheRepository); |
| 133 | GSARepository r = (GSARepository) n.resolveName(repositoryComponentPath); |
| 134 | try { |
| 135 | getClass().getMethod(pMethodName, new Class[] { GSARepository.class }) |
| 136 | .invoke(this, new Object[] { r }); |
| 137 | } catch (NoSuchMethodError e) { |
| 138 | throw new AssertionError("Please declare a method with name "+pMethodName + " in your class. It must take an atg.adapter.gsa.GSARepository as the only parameter."); |
| 139 | } |
| 140 | finally{ |
| 141 | // if it were null a NPE would have occurred at the earlier dereference |
| 142 | //if(n != null) |
| 143 | n.stopService(); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | |
| 148 | /** |
| 149 | * Createa a file using reasonable defaults. |
| 150 | * Your definition file should exist in the same package as the test and should be |
| 151 | * names <test_name>Repository.xml. Configpath is assumed to be what is returned |
| 152 | * |
| 153 | * @param pMethodName |
| 154 | * @throws Exception |
| 155 | * @throws Exception |
| 156 | */ |
| 157 | protected void setUpAndTest(String pMethodName) throws Exception{ |
| 158 | File configPathWhereToCreateTheRepository = getConfigpath(null); |
| 159 | String packageName = StringUtils.replace(this.getClass().getPackage() |
| 160 | .getName(), '.', "/"); |
| 161 | String fileName = packageName+"/"+getName()+"Repository.xml"; |
| 162 | URL defaultDefinitionFile = getClass().getResource("/"+fileName); |
| 163 | if(defaultDefinitionFile == null )throw new AssertionError("DUDE, I need a file called : "+ fileName +" to start a GSA repository from. "); |
| 164 | String[] definitionFiles = new String[]{fileName}; |
| 165 | Properties DBProperties = DBUtils.getHSQLDBInMemoryDBConnection(); |
| 166 | setUpAndTest(configPathWhereToCreateTheRepository, |
| 167 | definitionFiles, DBProperties,pMethodName); |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Starts Nucleus using the given config directory |
| 172 | * @param configpath |
| 173 | * @return |
| 174 | */ |
| 175 | public static Nucleus startNucleus(File configpath) { |
| 176 | |
| 177 | return startNucleus(configpath.getAbsolutePath()); |
| 178 | } |
| 179 | /** |
| 180 | * Starts Nucleus given an array of configpath entries |
| 181 | * @param configpathStr |
| 182 | * @return |
| 183 | */ |
| 184 | public static Nucleus startNucleus(String configpathStr) { |
| 185 | System.setProperty("atg.dynamo.license.read", "true"); |
| 186 | System.setProperty("atg.license.read", "true"); |
| 187 | NucleusServlet.addNamingFactoriesAndProtocolHandlers(); |
| 188 | return Nucleus.startNucleus(new String[] {configpathStr}); |
| 189 | } |
| 190 | /** |
| 191 | * @param dbName |
| 192 | * @return |
| 193 | * @throws Exception |
| 194 | * @throws SQLException |
| 195 | */ |
| 196 | protected DBUtils initDB(Properties props) throws Exception, SQLException { |
| 197 | DBUtils db = new DBUtils(props.getProperty("URL"),props.getProperty("driver"),props.getProperty("user"),props.getProperty("password")); |
| 198 | return db; |
| 199 | } |
| 200 | |
| 201 | /** |
| 202 | * A Dummy test so smokestack won't report this |
| 203 | * class as a failure. |
| 204 | * It expects that all *Test.class files have |
| 205 | * at least one test. |
| 206 | * |
| 207 | */ |
| 208 | public final void testDummy() { |
| 209 | |
| 210 | } |
| 211 | /** |
| 212 | * @param pGSARepository |
| 213 | * @param descName |
| 214 | * @return |
| 215 | * @throws RepositoryException |
| 216 | */ |
| 217 | protected MutableRepositoryItem createDummyItem(GSARepository pGSARepository, String descName, String pID) throws RepositoryException { |
| 218 | GSAItemDescriptor descriptor = (GSAItemDescriptor) pGSARepository |
| 219 | .getItemDescriptor(descName); |
| 220 | MutableRepositoryItem item = null; |
| 221 | boolean compoundPrimaryKey = descriptor.getPrimaryTable().getIdColumnCount() > 1; |
| 222 | if(pID == null || pID.trim().length() == 0){ |
| 223 | if(compoundPrimaryKey){ |
| 224 | item = pGSARepository.createItem(getNewCompoundId(pGSARepository, descriptor), descName); |
| 225 | } |
| 226 | else |
| 227 | item = pGSARepository.createItem(descName); |
| 228 | } |
| 229 | else{ |
| 230 | item= pGSARepository.createItem(pID, descName); |
| 231 | } |
| 232 | RepositoryPropertyDescriptor[] propDescriptors = (RepositoryPropertyDescriptor[]) descriptor |
| 233 | .getPropertyDescriptors(); |
| 234 | for (int j = 0; j < propDescriptors.length; j++) { |
| 235 | RepositoryPropertyDescriptor propertyDescriptor = propDescriptors[j]; |
| 236 | if (propertyDescriptor.isWritable() |
| 237 | && !propertyDescriptor.isIdProperty() |
| 238 | && propertyDescriptor.isRequired()) { |
| 239 | if (propertyDescriptor.isCollectionOrMap()) { |
| 240 | } else { |
| 241 | |
| 242 | Object dummyPropertyValue = generateDummyValue(propertyDescriptor); |
| 243 | if (dummyPropertyValue != null) { |
| 244 | item.setPropertyValue(propertyDescriptor.getName(), |
| 245 | dummyPropertyValue); |
| 246 | } |
| 247 | } |
| 248 | } |
| 249 | } |
| 250 | return item; |
| 251 | } |
| 252 | /** |
| 253 | * Get a id suitable for creating items of this type. We use out |
| 254 | * <code>Repository</code>'s <code>IdGenerator</code>. |
| 255 | * |
| 256 | * @return a new id, which is unique across all items in this |
| 257 | * repository with this item descriptor. |
| 258 | * @exception RepositoryException if there is trouble creating the id |
| 259 | **/ |
| 260 | protected GSAId getNewCompoundId(GSARepository r, GSAItemDescriptor desc) throws RepositoryException { |
| 261 | // make sure we have a repository |
| 262 | if (r == null) |
| 263 | return null; |
| 264 | |
| 265 | // get the generator to use |
| 266 | IdGenerator gen = r.getIdGenerator(); |
| 267 | if (gen == null) |
| 268 | return null; |
| 269 | |
| 270 | Class<?>[] types = desc.getIdTypes(); |
| 271 | String[] idSpaceNames = desc.getIdSpaceNames(); |
| 272 | Object[] newId = new Object[types.length]; |
| 273 | |
| 274 | if (idSpaceNames.length != types.length) |
| 275 | throw new RepositoryException("No ID SPACES ! "+ desc.getItemDescriptorName()); |
| 276 | |
| 277 | // generate an id in our id space and return it |
| 278 | try |
| 279 | { |
| 280 | for (int i=0;i<types.length;i++) { |
| 281 | if (types[i] == String.class) { |
| 282 | if(i > 0) |
| 283 | newId[i] = "dummyIdPart"; |
| 284 | else |
| 285 | newId[i] = gen.generateStringId(idSpaceNames[i]); |
| 286 | } |
| 287 | else |
| 288 | { |
| 289 | long val = gen.generateLongId(idSpaceNames[i]); |
| 290 | if (types[i] == Long.class) |
| 291 | newId[i] = Long.valueOf(val); |
| 292 | else if (types[i] == Float.class) |
| 293 | newId[i] = Float.valueOf((float) val); |
| 294 | else if (types[i] == Double.class) |
| 295 | newId[i] = Double.valueOf((float) val); |
| 296 | else if (types[i] == java.sql.Timestamp.class) |
| 297 | newId[i] = new java.sql.Timestamp(val); |
| 298 | else if (types[i] == java.util.Date.class) |
| 299 | newId[i] = new java.util.Date(val); |
| 300 | else |
| 301 | newId[i] = Integer.valueOf((int)val); |
| 302 | } |
| 303 | } |
| 304 | } |
| 305 | catch (IdGeneratorException ie) |
| 306 | { |
| 307 | throw new RepositoryException(ie); |
| 308 | } |
| 309 | |
| 310 | return desc.generateGSAId(newId); |
| 311 | } |
| 312 | |
| 313 | @SuppressWarnings("unchecked") |
| 314 | protected Object generateDummyValue(RepositoryPropertyDescriptor propertyDescriptor) { |
| 315 | if (getEnumeratedValues(propertyDescriptor) != null) { |
| 316 | return null;// ignore enums for now. |
| 317 | } |
| 318 | |
| 319 | if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 320 | java.lang.String.class)) { |
| 321 | return generateString(); |
| 322 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 323 | java.lang.Integer.class)) { |
| 324 | return generateInteger(); |
| 325 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 326 | java.lang.Boolean.class)) { |
| 327 | return generateBoolean(); |
| 328 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 329 | java.lang.Byte.class)) { |
| 330 | return generateByte(); |
| 331 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 332 | java.lang.Short.class)) { |
| 333 | return generateShort(); |
| 334 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 335 | java.lang.Long.class)) { |
| 336 | return generateLong(); |
| 337 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 338 | java.lang.Float.class)) { |
| 339 | return generateFloat(); |
| 340 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 341 | java.lang.Double.class)) { |
| 342 | return generateDouble(); |
| 343 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 344 | (new byte[0]).getClass()))//BINARY |
| 345 | { |
| 346 | return null; |
| 347 | // return generateBinary(); |
| 348 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 349 | java.sql.Timestamp.class)) { |
| 350 | return generateTimestamp(); |
| 351 | } else if (propertyDescriptor.getPropertyType().isAssignableFrom( |
| 352 | java.sql.Date.class)) { |
| 353 | return generateDate(); |
| 354 | } |
| 355 | return null; |
| 356 | |
| 357 | } |
| 358 | /** |
| 359 | * |
| 360 | * Returns the set of enumerated values, or null if there are none |
| 361 | **/ |
| 362 | String[] getEnumeratedValues(DynamicPropertyDescriptor pDescriptor) { |
| 363 | if(pDescriptor == null) return null; |
| 364 | PropertyEditor pe = getPropertyEditor(pDescriptor); |
| 365 | String[] ret = (pe == null) ? null : pe.getTags(); |
| 366 | |
| 367 | // make sure it's not just a boolean value |
| 368 | Class<?> type = pDescriptor.getPropertyType(); |
| 369 | if ((type == Boolean.class || type == Boolean.TYPE) |
| 370 | && ret != null |
| 371 | && ret.length == 2 |
| 372 | && (("true".equals(ret[0]) && "false".equals(ret[1])) || ("false" |
| 373 | .equals(ret[0]) && "true".equals(ret[1])))) { |
| 374 | return null; |
| 375 | } else { |
| 376 | return ret; |
| 377 | } |
| 378 | } |
| 379 | /** |
| 380 | * |
| 381 | * Returns an instance of the property editor, null if there is no |
| 382 | * property editor |
| 383 | **/ |
| 384 | PropertyEditor getPropertyEditor(DynamicPropertyDescriptor pDescriptor) { |
| 385 | if(pDescriptor == null ) return null; |
| 386 | Class<?> peclass = pDescriptor.getPropertyEditorClass(); |
| 387 | if (peclass == null) { |
| 388 | return pDescriptor.getUIPropertyEditor(); |
| 389 | } else { |
| 390 | Object peinst = null; |
| 391 | try { |
| 392 | peinst = peclass.newInstance(); |
| 393 | } catch (InstantiationException e) { |
| 394 | log.error("Error: ", e); |
| 395 | } catch (IllegalAccessException e) { |
| 396 | log.error("Error: ", e); |
| 397 | } |
| 398 | if (peinst instanceof PropertyEditor) { |
| 399 | return (PropertyEditor) peinst; |
| 400 | } else { |
| 401 | return null; |
| 402 | } |
| 403 | } |
| 404 | } |
| 405 | /** |
| 406 | * @return |
| 407 | */ |
| 408 | private Object generateDate() { |
| 409 | return new Date(System.currentTimeMillis()); |
| 410 | } |
| 411 | /** |
| 412 | * @return |
| 413 | */ |
| 414 | private Object generateTimestamp() { |
| 415 | return new Timestamp(System.currentTimeMillis()); |
| 416 | } |
| 417 | // /** |
| 418 | // * @return |
| 419 | // */ |
| 420 | // private Object generateBinary() { |
| 421 | // byte[] bytes = new byte[100]; |
| 422 | // Random random = new Random(); |
| 423 | // random.nextBytes(bytes); |
| 424 | // return bytes; |
| 425 | // } |
| 426 | /** |
| 427 | * @return |
| 428 | */ |
| 429 | private Object generateDouble() { |
| 430 | return new Double(random.nextDouble()); |
| 431 | } |
| 432 | /** |
| 433 | * @return |
| 434 | */ |
| 435 | private Object generateInteger() { |
| 436 | return Integer.valueOf(random.nextInt(32768)); |
| 437 | } |
| 438 | /** |
| 439 | * @return |
| 440 | */ |
| 441 | private Object generateFloat() { |
| 442 | return new Float(random.nextFloat()); |
| 443 | } |
| 444 | /** |
| 445 | * @return |
| 446 | */ |
| 447 | private Object generateLong() { |
| 448 | return Long.valueOf(random.nextInt(32278)); |
| 449 | } |
| 450 | /** |
| 451 | * @return |
| 452 | */ |
| 453 | private Object generateShort() { |
| 454 | return Short.valueOf((short) (random.nextInt(100))); |
| 455 | } |
| 456 | /** |
| 457 | * @return |
| 458 | */ |
| 459 | private Object generateByte() { |
| 460 | byte[] bytes = new byte[1]; |
| 461 | random.nextBytes(bytes); |
| 462 | return Byte.valueOf(bytes[0]); |
| 463 | } |
| 464 | /** |
| 465 | * @return |
| 466 | */ |
| 467 | private Object generateBoolean() { |
| 468 | return Boolean.valueOf(random.nextBoolean()); |
| 469 | } |
| 470 | /** |
| 471 | * @return |
| 472 | */ |
| 473 | private Object generateString() { |
| 474 | |
| 475 | return "DUMMY STRING " + generateInteger(); |
| 476 | } |
| 477 | |
| 478 | |
| 479 | } |