EMMA Coverage Report (generated Tue Nov 24 15:49:41 EST 2009)
[all classes][atg.test]

COVERAGE SUMMARY FOR SOURCE FILE [AtgDustCase.java]

nameclass, %method, %block, %line, %
AtgDustCase.java100% (1/1)93%  (14/15)45%  (478/1055)57%  (96.4/169)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class AtgDustCase100% (1/1)93%  (14/15)45%  (478/1055)57%  (96.4/169)
manageConfigurationFiles (Properties): void 0%   (0/1)0%   (0/270)0%   (0/39)
forceGlobalScopeOnAllConfigs (String): void 100% (1/1)24%  (34/141)31%  (4/13)
startNucleus (File): void 100% (1/1)38%  (71/186)59%  (14.1/24)
setConfigurationLocation (String): void 100% (1/1)45%  (10/22)75%  (3/4)
<static initializer> 100% (1/1)54%  (53/98)61%  (11.5/19)
preCopyingOfConfigurationFiles (String [], String []): void 100% (1/1)77%  (75/97)64%  (9/14)
copyConfigurationFiles (String [], String, String []): void 100% (1/1)88%  (42/48)88%  (8.8/10)
AtgDustCase (): void 100% (1/1)100% (13/13)100% (3/3)
createPropertyFile (String, String, Class): void 100% (1/1)100% (16/16)100% (3/3)
enableLoggingOnGenericService (Object): Object 100% (1/1)100% (34/34)100% (8/8)
prepareRepository (String, Properties, boolean, boolean, String []): void 100% (1/1)100% (62/62)100% (13/13)
prepareRepository (String, String []): void 100% (1/1)100% (32/32)100% (7/7)
resolveNucleusComponent (String): Object 100% (1/1)100% (11/11)100% (2/2)
setDebug (boolean): void 100% (1/1)100% (4/4)100% (2/2)
tearDown (): void 100% (1/1)100% (21/21)100% (8/8)

1package atg.test;
2 
3import java.io.File;
4import java.io.IOException;
5import java.lang.reflect.Method;
6import java.sql.SQLException;
7import java.util.ArrayList;
8import java.util.Arrays;
9import java.util.HashMap;
10import java.util.Iterator;
11import java.util.List;
12import java.util.Map;
13import java.util.Properties;
14import java.util.Map.Entry;
15 
16import junit.framework.TestCase;
17 
18import org.apache.commons.io.FileUtils;
19import org.apache.log4j.Logger;
20 
21import atg.nucleus.GenericService;
22import atg.nucleus.Nucleus;
23import atg.nucleus.logging.ClassLoggingFactory;
24import atg.nucleus.logging.ConsoleLogListener;
25import atg.test.configuration.BasicConfiguration;
26import atg.test.configuration.RepositoryConfiguration;
27import atg.test.util.FileUtil;
28import atg.test.util.RepositoryManager;
29 
30/**
31 * Replacement base class for {@link AtgDustTestCase}. Extend this class and use
32 * the following 'pattern' whenever you want to junit test some atg components:
33 * <ul>
34 * <li><b>Copy</b> all needed configuration and repository mapping files to a
35 * staging location outside of your source tree using<b>
36 * {@link AtgDustCase#copyConfigurationFiles(String[], String, String...)}</b>.
37 * The staging directory will automatically be used as the configuration
38 * directory. Copying all needed priorities to a location outside of the source
39 * tree is the preferred method, because this frameworks creates properties on
40 * the fly and that could pollute your current source tree.</li>
41 * <!--
42 * <li><b>
43 * 
44 * <i>Or: </i></b>tell {@link AtgDustCase} class where the configuration
45 * location is by using <b>{@link AtgDustCase#setConfigurationLocation(String)}
46 * </b>, but be aware that the location will also be used for properties file
47 * generation.</li>
48 * -->
49 * </ul>
50 * 
51 * <!-- p> <b>Rule of thumb:</b> When running repository tests, copy everything
52 * outside of your source tree (or when you use maven, use the target directory
53 * ). If you run basic component/formhandler tests, pointing it to your existing
54 * configuration directory might be sufficient.
55 * 
56 * </p-->
57 * 
58 * Repository based tests are depended on one of the two steps previously
59 * described plus:
60 * <ul>
61 * <li><b>{@link AtgDustCase#prepareRepository(String, String...)}</b> for
62 * testing against an default in-memory hsql database or <b>
63 * {@link AtgDustCase#prepareRepository(String, Properties, boolean, String...)}
64 * </b> for testing against an existing database.</li>
65 * </ul>
66 * 
67 * If you need to generate some components "on the fly":
68 * <ul>
69 * <li><b>{@link AtgDustCase#createPropertyFile(String, String, Class)}</b></li>
70 * </ul>
71 * 
72 * <p>
73 * Example usage can be found in test.SongsRepositoryTest.
74 * </p>
75 * 
76 * <p>
77 * This class overrides Junit 3 and not Junit 4 because currently Junit 4 has
78 * some test runner/eclipse related bugs which makes it impossible for me to use
79 * it.
80 * </p>
81 * 
82 * @author robert
83 */
84@SuppressWarnings("unchecked")
85public class AtgDustCase extends TestCase {
86 
87        private static final Logger log = Logger.getLogger(AtgDustCase.class);
88        private RepositoryManager repositoryManager = new RepositoryManager();
89        private final BasicConfiguration basicConfiguration = new BasicConfiguration();
90        private File configurationLocation;
91        private Nucleus nucleus;
92        private boolean isDebug;
93        private String atgConfigPath;
94        private String environment;
95        private String localConfig;
96        private List<String> configDstsDir;
97        private static Map<String, Long> CONFIG_FILES_TIMESTAMPS,
98                        CONFIG_FILES_GLOBAL_FORCE = null;
99        private static Class<?> perflib;
100 
101        public static final File TIMESTAMP_SER = new File(System
102                        .getProperty("java.io.tmpdir")
103                        + File.separator + "atg-dust-tstamp-rh.ser"),
104                        GLOBAL_FORCE_SER = new File(System.getProperty("java.io.tmpdir")
105                                        + File.separator + "atg-dust-gforce-rh.ser");
106        private static long SERIAL_TTL = 43200000L;
107 
108        /**
109         * Every *.properties file copied using this method will have it's scope (if
110         * one is available) set to global.
111         * 
112         * @param srcDirs
113         *            One or more directories containing needed configuration files.
114         * @param dstDir
115         *            where to copy the above files to. This will also be the
116         *            configuration location.
117         * @param excludes
118         *            One or more directories not to include during the copy
119         *            process. Use this one to speeds up the test cycle
120         *            considerably. You can also call it with an empty
121         *            {@link String[]} or <code>null</code> if nothing should be
122         *            excluded
123         * @throws IOException
124         *             Whenever some file related error's occur.
125         */
126        protected final void copyConfigurationFiles(final String[] srcDirs,
127                        final String dstDir, final String... excludes) throws IOException {
128 
129                setConfigurationLocation(dstDir);
130 
131                if (log.isDebugEnabled()) {
132                        log.debug("Copying configuration files and "
133                                        + "forcing global scope on all configs");
134                }
135                preCopyingOfConfigurationFiles(srcDirs, excludes);
136 
137                for (final String srcs : srcDirs) {
138                        FileUtil.copyDirectory(srcs, dstDir, Arrays
139                                        .asList(excludes == null ? new String[] {} : excludes));
140                }
141 
142                forceGlobalScopeOnAllConfigs(dstDir);
143 
144                if (FileUtil.isDirty()) {
145                        FileUtil.serialize(GLOBAL_FORCE_SER, FileUtil
146                                        .getConfigFilesTimestamps());
147                }
148 
149        }
150 
151        /**
152         * Donated by Remi Dupuis
153         * 
154         * @param properties
155         * @throws IOException
156         */
157        protected final void manageConfigurationFiles(Properties properties)
158                        throws IOException {
159 
160                String atgConfigPath = properties.getProperty("atgConfigsJars")
161                                .replace("/", File.separator);
162                String[] configs = properties.getProperty("configs").split(",");
163                String environment = properties.getProperty("environment");
164                String localConfig = properties.getProperty("localConfig");
165                String[] excludes = properties.getProperty("excludes").split(",");
166                String rootConfigDir = properties.getProperty("rootConfigDir").replace(
167                                "/", File.separator);
168                int i = 0;
169                for (String conf : configs) {
170                        String src = conf.split(" to ")[0];
171                        String dst = conf.split(" to ")[1];
172                        configs[i] = (rootConfigDir + "/" + src.trim() + " to "
173                                        + rootConfigDir + "/" + dst.trim()).replace("/",
174                                        File.separator);
175                        i++;
176                }
177                i = 0;
178                for (String dir : excludes) {
179                        excludes[i] = dir.trim();
180                        i++;
181                }
182                final List<String> srcsAsList = new ArrayList<String>();
183                final List<String> distsAsList = new ArrayList<String>();
184 
185                for (String config : configs) {
186                        srcsAsList.add(config.split(" to ")[0]);
187                        distsAsList.add(config.split(" to ")[1]);
188                }
189 
190                this.atgConfigPath = atgConfigPath;
191                this.environment = environment;
192                this.localConfig = localConfig;
193                // The Last dstdir is used for Configuration location
194                setConfigurationLocation(distsAsList.get(distsAsList.size() - 1));
195 
196                if (log.isDebugEnabled()) {
197                        log.debug("Copying configuration files and "
198                                        + "forcing global scope on all configs");
199                }
200                preCopyingOfConfigurationFiles(srcsAsList.toArray(new String[] {}),
201                                excludes);
202 
203                log.info("Copying configuration files and "
204                                + "forcing global scope on all configs");
205                // copy all files to it's destination
206                for (String config : configs) {
207                        FileUtil.copyDirectory(config.split(" to ")[0], config
208                                        .split(" to ")[1], Arrays
209                                        .asList(excludes == null ? new String[] {} : excludes));
210                        log.debug(config);
211                        log.debug(config.split(" to ")[0]);
212                        log.debug(config.split(" to ")[1]);
213                }
214 
215                // forcing global scope on all configurations
216                for (String config : configs) {
217                        String dstDir = config.split(" to ")[1];
218                        // forcing global scope on all property files
219                        forceGlobalScopeOnAllConfigs(dstDir);
220                }
221                this.configDstsDir = distsAsList;
222 
223        }
224 
225        /**
226         * @param configurationStagingLocation
227         *            The location where the property file should be created. This
228         *            will also set the {@link AtgDustCase#configurationLocation}.
229         * 
230         * @param nucleusComponentPath
231         *            Nucleus component path (e.g /Some/Service/Impl).
232         * 
233         * @param clazz
234         *            The {@link Class} implementing the nucleus component specified
235         *            in previous argument.
236         * 
237         * @throws IOException
238         *             If we have some File related errors
239         */
240        protected final void createPropertyFile(
241                        final String configurationStagingLocation,
242                        final String nucleusComponentPath, final Class<?> clazz)
243                        throws IOException {
244                this.configurationLocation = new File(configurationStagingLocation);
245                FileUtil.createPropertyFile(nucleusComponentPath,
246                                configurationLocation, clazz.getClass(),
247                                new HashMap<String, String>());
248        }
249 
250        /**
251         * Prepares a test against an default in-memory hsql database.
252         * 
253         * @param repoPath
254         *            the nucleus component path of the repository to be tested.
255         * 
256         * @param definitionFiles
257         *            one or more repository definition files.
258         * @throws IOException
259         *             The moment we have some properties/configuration related
260         *             error
261         * @throws SQLException
262         *             Whenever there is a database related error
263         * 
264         */
265        protected final void prepareRepository(final String repoPath,
266                        final String... definitionFiles) throws SQLException, IOException {
267 
268                final Properties properties = new Properties();
269                properties.put("driver", "org.hsqldb.jdbcDriver");
270                properties.put("url", "jdbc:hsqldb:mem:testDb");
271                properties.put("user", "sa");
272                properties.put("password", "");
273 
274                prepareRepository(repoPath, properties, true, true, definitionFiles);
275 
276        }
277 
278        /**
279         * Prepares a test against an existing database.
280         * 
281         * @param repositoryPath
282         *            The the repository to be tested, specified as nucleus
283         *            component path.
284         * @param connectionProperties
285         *            A {@link Properties} instance with the following values (in
286         *            this example the properties are geared towards an mysql
287         *            database):
288         * 
289         *            <pre>
290         * final Properties properties = new Properties();
291         * properties.put(&quot;driver&quot;, &quot;com.mysql.jdbc.Driver&quot;);
292         * properties.put(&quot;url&quot;, &quot;jdbc:mysql://localhost:3306/someDb&quot;);
293         * properties.put(&quot;user&quot;, &quot;someUserName&quot;);
294         * properties.put(&quot;password&quot;, &quot;somePassword&quot;);
295         * </pre>
296         * 
297         * 
298         * @param dropTables
299         *            If <code>true</code> then existing tables will be dropped and
300         *            re-created, if set to <code>false</code> the existing tables
301         *            will be used.
302         * 
303         * @param createTables
304         *            if set to <code>true</code> all non existing tables needed for
305         *            the current test run will be created, if set to
306         *            <code>false</code> this class expects all needed tables for
307         *            this test run to be already created
308         * 
309         * @param definitionFiles
310         *            One or more needed repository definition files.
311         * @throws IOException
312         *             The moment we have some properties/configuration related
313         *             error
314         * @throws SQLException
315         *             Whenever there is a database related error
316         * 
317         */
318        protected final void prepareRepository(final String repositoryPath,
319                        final Properties connectionProperties, final boolean dropTables,
320                        final boolean createTables, final String... definitionFiles)
321                        throws SQLException, IOException {
322 
323                final Map<String, String> connectionSettings = new HashMap<String, String>();
324 
325                for (final Iterator<Entry<Object, Object>> it = connectionProperties
326                                .entrySet().iterator(); it.hasNext();) {
327                        final Entry<Object, Object> entry = it.next();
328                        connectionSettings.put((String) entry.getKey(), (String) entry
329                                        .getValue());
330 
331                }
332                final RepositoryConfiguration repositoryConfiguration = new RepositoryConfiguration();
333 
334                repositoryConfiguration.setDebug(isDebug);
335                repositoryConfiguration
336                                .createPropertiesByConfigurationLocation(configurationLocation);
337                repositoryConfiguration.createFakeXADataSource(configurationLocation,
338                                connectionSettings);
339                repositoryConfiguration.createRepositoryConfiguration(
340                                configurationLocation, repositoryPath, dropTables,
341                                createTables, definitionFiles);
342 
343                repositoryManager.initializeMinimalRepositoryConfiguration(
344                                configurationLocation, repositoryPath, connectionSettings,
345                                dropTables, isDebug, definitionFiles);
346        }
347 
348        /**
349         * Method for retrieving a fully injected atg component
350         * 
351         * @param nucleusComponentPath
352         *            Path to a nucleus component (e.g. /Some/Service/Impl).
353         * @return Fully injected instance of the component registered under
354         *         previous argument or <code>null</code> if there is an error.
355         * @throws IOException
356         */
357        protected Object resolveNucleusComponent(final String nucleusComponentPath)
358                        throws IOException {
359                startNucleus(configurationLocation);
360                return enableLoggingOnGenericService(nucleus
361                                .resolveName(nucleusComponentPath));
362        }
363 
364        /**
365         * Call this method to set the configuration location.
366         * 
367         * @param configurationLocation
368         *            The configuration location to set. Most of the time this
369         *            location is a directory containing all repository definition
370         *            files and component property files which are needed for the
371         *            test.
372         */
373        protected final void setConfigurationLocation(
374                        final String configurationLocation) {
375                this.configurationLocation = new File(configurationLocation);
376                if (log.isDebugEnabled()) {
377                        log.debug("Using configuration location: "
378                                        + this.configurationLocation.getPath());
379                }
380        }
381 
382        /**
383         * Always make sure to call this because it will do necessary clean up
384         * actions (shutting down in-memory database (if it was used) and the
385         * nucleus) so he next test can run safely.
386         */
387        @Override
388        protected void tearDown() throws Exception {
389                super.tearDown();
390                if (repositoryManager != null) {
391                        repositoryManager.shutdownInMemoryDbAndCloseConnections();
392                }
393                if (nucleus != null) {
394                        nucleus.doStopService();
395                        nucleus.stopService();
396                        nucleus.destroy();
397                }
398        }
399 
400        /**
401         * Enables or disables the debug level of nucleus components.
402         * 
403         * @param isDebug
404         *            Setting this to <code>true</code> will enable debug on all
405         *            (currently only on repository related) components, setting it
406         *            to <code>false</code> turn's the debug off again.
407         */
408        protected void setDebug(boolean isDebug) {
409                this.isDebug = isDebug;
410        }
411 
412        /**
413         * 
414         * @param configpath
415         * @return
416         * @throws IOException
417         */
418        private void startNucleus(final File configpath) throws IOException {
419                if (nucleus == null || !nucleus.isRunning()) {
420                        ClassLoggingFactory.getFactory();
421                        basicConfiguration.setDebug(isDebug);
422                        basicConfiguration
423                                        .createPropertiesByConfigurationLocation(configpath);
424                        System.setProperty("atg.dynamo.license.read", "true");
425                        System.setProperty("atg.license.read", "true");
426                        // TODO: Can I safely keep this one disabled?
427                        // NucleusServlet.addNamingFactoriesAndProtocolHandlers();
428 
429                        if (environment != null && !environment.equals("")) {
430                                for (String property : environment.split(";")) {
431                                        String[] keyvalue = property.split("=");
432                                        System.setProperty(keyvalue[0], keyvalue[1]);
433                                        log.info(keyvalue[0] + "=" + keyvalue[1]);
434                                }
435                        }
436 
437                        String fullConfigPath = "";
438                        if (atgConfigPath != null && !atgConfigPath.equals("")) {
439                                fullConfigPath = atgConfigPath + ";" + fullConfigPath;
440                        }
441                        if (configDstsDir != null && configDstsDir.size() > 0) {
442                                for (String dst : configDstsDir) {
443                                        fullConfigPath = fullConfigPath + dst + ";";
444                                }
445                        } else
446                                fullConfigPath = configpath.getAbsolutePath();
447                        if (atgConfigPath != null && !atgConfigPath.equals(""))
448                                fullConfigPath = fullConfigPath
449                                                + localConfig.replace("/", File.separator);
450 
451                        log.info("The full config path used to start nucleus: "
452                                        + fullConfigPath);
453                        System.setProperty("atg.configpath", new File(fullConfigPath)
454                                        .getAbsolutePath());
455                        nucleus = Nucleus.startNucleus(new String[] { fullConfigPath });
456 
457                }
458        }
459 
460        /**
461         * Will enable logging on the object/service that was passed in (as a method
462         * argument) if it's an instance of {@link GenericService}. This method is
463         * automatically called from
464         * {@link AtgDustCase#resolveNucleusComponent(String)}. Debug level is
465         * enabled the moment {@link AtgDustCase#setDebug(boolean)} was called with
466         * <code>true</code>.
467         * 
468         * @param service
469         *            an instance of GenericService
470         * 
471         * @return the GenericService instance that was passed in with all log
472         *         levels enabled, if it's a {@link GenericService}
473         */
474        private Object enableLoggingOnGenericService(final Object service) {
475                if (service instanceof GenericService) {
476                        ((GenericService) service).setLoggingDebug(isDebug);
477                        ((GenericService) service).setLoggingInfo(true);
478                        ((GenericService) service).setLoggingWarning(true);
479                        ((GenericService) service).setLoggingError(true);
480                        ((GenericService) service)
481                                        .removeLogListener(new ConsoleLogListener());
482                        ((GenericService) service).addLogListener(new ConsoleLogListener());
483                }
484                return service;
485        }
486 
487        private void preCopyingOfConfigurationFiles(final String[] srcDirs,
488                        final String excludes[]) throws IOException {
489                boolean isDirty = false;
490                for (final String src : srcDirs) {
491                        for (final File file : (List<File>) FileUtils.listFiles(new File(
492                                        src), null, true)) {
493                                if (!Arrays.asList(
494                                                excludes == null ? new String[] {} : excludes)
495                                                .contains(file.getName())
496                                                && !file.getPath().contains(".svn") && file.isFile()) {
497                                        if (CONFIG_FILES_TIMESTAMPS.get(file.getPath()) != null
498                                                        && file.lastModified() == CONFIG_FILES_TIMESTAMPS
499                                                                        .get(file.getPath())) {
500                                        } else {
501                                                CONFIG_FILES_TIMESTAMPS.put(file.getPath(), file
502                                                                .lastModified());
503                                                isDirty = true;
504                                        }
505                                }
506                        }
507                }
508                if (isDirty) {
509                        if (log.isDebugEnabled()) {
510                                log
511                                                .debug("Config files timestamps map is dirty an will be re serialized");
512                        }
513 
514                        FileUtil.serialize(TIMESTAMP_SER, CONFIG_FILES_TIMESTAMPS);
515                }
516 
517                FileUtil.setConfigFilesTimestamps(CONFIG_FILES_TIMESTAMPS);
518                FileUtil.setConfigFilesGlobalForce(CONFIG_FILES_GLOBAL_FORCE);
519        }
520 
521        private void forceGlobalScopeOnAllConfigs(final String dstDir)
522                        throws IOException {
523                if (perflib == null) {
524                        for (final File file : (List<File>) FileUtils.listFiles(new File(
525                                        dstDir), new String[] { "properties" }, true)) {
526                                new FileUtil().searchAndReplace("$scope=", "$scope=global\n",
527                                                file);
528                        }
529                } else {
530                        try {
531                                List<File> payload = (List<File>) FileUtils.listFiles(new File(
532                                                dstDir), new String[] { "properties" }, true);
533 
534                                Method schedule = perflib.getMethod("schedule", new Class[] {
535                                                int.class, List.class, Class.class, String.class,
536                                                Class[].class, List.class });
537 
538                                List<Object> list = new ArrayList<Object>();
539                                list.add("$scope=");
540                                list.add("$scope=global\n");
541                                schedule.invoke(perflib.newInstance(), 4, payload,
542                                                FileUtil.class, "searchAndReplace", new Class[] {
543                                                                String.class, String.class, File.class }, list);
544                        } catch (Exception e) {
545                                log.error("Error: ", e);
546                        }
547                }
548 
549        }
550 
551        static {
552                final String s = System.getProperty("SERIAL_TTL");
553                if (log.isDebugEnabled()) {
554                        log.debug(s == null ? "SERIAL_TTL has not been set "
555                                        + "using default value of: " + SERIAL_TTL
556                                        + " m/s or start VM with -DSERIAL_TTL=some_number_value"
557                                        : "SERIAL_TTL is set to:" + s);
558                }
559                try {
560                        SERIAL_TTL = s != null ? Long.parseLong(s) * 1000 : SERIAL_TTL;
561                } catch (NumberFormatException e) {
562                        log.error("Error using the -DSERIAL_TTL value: ", e);
563                }
564                CONFIG_FILES_TIMESTAMPS = FileUtil.deserialize(TIMESTAMP_SER,
565                                SERIAL_TTL);
566                CONFIG_FILES_GLOBAL_FORCE = FileUtil.deserialize(GLOBAL_FORCE_SER,
567                                SERIAL_TTL);
568 
569                try {
570                        perflib = Class
571                                        .forName("com.bsdroot.util.concurrent.SchedulerService");
572                } catch (ClassNotFoundException e) {
573                        log
574                                        .debug("com.bsdroot.util.concurrent experimantal performance library not found, continuing normally");
575                }
576        }
577 
578}

[all classes][atg.test]
EMMA 2.0.5312 (C) Vladimir Roubtsov