Create Additional JBoss Application Server Configurations

JBoss I thought to create an additional JBoss application server configuration one would have to use some kind of administration tool.

It turned out to be much simpler.

cp -R server/default server/betweengo

If you want to create an ATG application server configuration you can do this.

cp -R server/atg server/betweengo

The only difference between the default server configuration and the atg server configuration is that the latter has two additional datasource XML files for communicating with the SOLID database.

atg/deploy/atg-apps-solid-ds.xml
atg/deploy/atg-solid-ds.xml

For further reading please see JBoss configurations to run an application (need active ATG support contract to see this document) or Building Your Own JBoss Configuration or Using JBoss Application Server.

How to Import and Create Users in Oracle

Oracle When you do an import sometimes you will find you will need to also create a user for this new set of data. Today I find myself in that situation as I imported data from Bell Canada and set up a new user for that data.

  1. Create a new user for the data.

    This example creates a user named "foo" with the password "foo".  Foo is a typical user that can create sessions, synonyms, procedures and tables.

    CREATE USER FOO IDENTIFIED BY FOO
      DEFAULT TABLESPACE users
      TEMPORARY TABLESPACE temp
      QUOTA UNLIMITED ON users;
    
    GRANT CREATE SESSION to FOO;
    GRANT CREATE SYNONYM TO FOO;
    GRANT CREATE PROCEDURE TO FOO;
    GRANT CREATE TABLE TO FOO;
    GRANT CREATE VIEW TO FOO;

    For more information please read Oracle’s CREATE USER documentation.

    If you want this user to be a DBA you can grant that to her too

    GRANT DBA TO FOO;
  2. Import the dump using the new user.

    This example imports the dump from the file "dump.dmp". This dump was created using the user "foo" and is being imported to the user "foo". The dump will be logged in the file "dump.log".

    imp system/system@example file=dump.dmp log=dump.log fromuser=foo touser=foo

    For more information please read Oracle’s Import Export FAQ.

  3. To redo recreate user and then import again.

    If you need to redo an import the easiest thing to do is to drop the user, recreate her and then do the import again. When you drop the user specify CASCADE to drop all the objects in the user’s schema.

    DROP USER FOO CASCADE;

    For more information please read Oracle’s DROP USER documentation.

Could Not Install ATG 2007.1

Today I began a contract with Bell Canada on an eCommerce project.  This project is using ATG 2007.1 with Oracle 10g (supported environments for ATG 2007.1).

I downloaded the installer from ATG’s product downloads page which is only available if you have purchased a Standard or Premium Support contract.  Previously this page was publicly available but now it is hidden.  Fortunately I found the link at ATG’s Google group ATG_Tech where they announced that ATG 2007.1 is available.

ATG Support quickly responded to my email to support@atg.com and within a few hours fixed the installer and made available a new installer on the product downloads page.

Lesson learned: Contact ATG Support.  They’re good.

Recurring Illegal Access Errors in JBoss when running ATG

illegal access

I recently installed ATG 2007.1 with no patches on JBoss 4.0.5.GA.  When I started it up I continually saw these illegal access errors.

16:01:24,296 INFO  [WebappClassLoader] Illegal access: this web application instance has been stopped already.  Could not load org.apache.log4j.Level.
  The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1241)
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1201)
        at org.apache.commons.logging.impl.Log4jProxy$1.run(Log4jProxy.java:66)
        at java.security.AccessController.doPrivileged(Native Method)
        at org.apache.commons.logging.impl.Log4jProxy.threadContextClassLoader(Log4jProxy.java:88)
        at org.apache.commons.logging.impl.Log4jProxy.(Log4jProxy.java:94)
        at org.apache.commons.logging.impl.Log4JLogger.(Log4JLogger.java:39)
        at sun.reflect.GeneratedConstructorAccessor22.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:494)
        at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:529)
        at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:235)
        at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:372)
        at atg.nucleus.logging.commons.CommonsLoggingLogListener.logEvent(CommonsLoggingLogListener.java:106)
        at atg.nucleus.GenericService.sendLogEvent(GenericService.java:291)
        at atg.nucleus.GenericService.logInfo(GenericService.java:737)
        at atg.nucleus.GenericService.logInfo(GenericService.java:715)
        at atg.ui.j2edit.model.CachingJ2eeArchiveDirectoryAgent.updateCacheData(CachingJ2eeArchiveDirectoryAgent.java:513)
        at atg.ui.j2edit.model.CachingJ2eeArchiveDirectoryAgent.performScheduledTask(CachingJ2eeArchiveDirectoryAgent.java:573)
        at atg.service.scheduler.ScheduledJob.runJobs(ScheduledJob.java:441)
        at atg.service.scheduler.Scheduler$2handler.run(Scheduler.java:760)

Fortunately this is a common problem and there are many support incidents about it.  Problem Report #144586 says that the problem occurs if:

  1. say “Yes” to deploying Quincy Fund during installation
  2. use startDynamoOnJBoss to run any other application

The work around is to remove Quincy Funds.ear from the JBoss deployment directory.  Or just don’t install Quincy Funds.

If you need to run the Quincy Funds demo then the Error running Quincy Funds demo on JBoss document suggests:

  1. stop the server
  2. delete the ATGDAF.ear from the JBoss installation
  3. start JBoss with the run.bat|sh command; the Quincy Funds demo is automatically started

For further reading please see How to use the startDynamoOnJBOSS script and JBoss configurations to run an application.  You may need to have an active ATG support contract to view these and other ATG support documents referenced in this article.

Questions to Consider When Starting an eCommerce Site

I have done many eCommerce projects and am looking forward to doing more.

The most interesting eCommerce projects are the ones that are starting from scratch.  I was fortunate to be a part of several such projects including NFLShop.com, CasualMale.com and OriginalPenguin.com.  Of these the OriginalPenguin.com project was by far the most interesting because I was simultaneously the director, lead engineer, and QA. 🙂

Whenever I start a project or am consulting for one I ask the following questions to help determine the scope and range of the project.

  1. What technology will be used?  J2EE?  ATG?  .Net?  PHP?  Ruby on Rails?
  2. What platform?  Windows?  Linux?  Solaris?  FreeBSD?
  3. How will it be hosted?  Locally?  Shared host?  Virtual host?  Exclusive host?
  4. What database do you use or plan to use?
  5. How many products, product categories and SKU’s do you have?
  6. How will catalog administration be done?  Should it be part of the web application?  Or will you use a separate third-party application?
  7. How will you manage price lists?
  8. How will you keep track of inventory?
  9. When an order is submitted, how will it be fulfilled?  Who does fulfillment?
  10. How will you handle payments?  Payflow Pro?  Cyber Cash?  CyberSource?  PayPal?
  11. How will you handle taxes?  TAXWARE?
  12. What kind of security do you want?  Will everything be handled securely via SSL?  Do you already have an SSL server certificate for the site?
  13. Will you require that to buy something you have to have an account?  If not will you want to still try to encourage buyers to get an account?  Will you be saving credit card numbers with the account?
  14. Can buyers track their order history, order status, etc.?
  15. What kind of emails do you want sent during the order/fulfillment process?
  16. Will you want to implement promotions and/or coupons?
  17. What kind of catalog navigation do you want?  Do you want a menu like navigation like Amazon.com?
  18. Do you want the buyer to be able to search for items?
  19. Do you want product comparison?

Unit Test for Threaded Logging

Brian Ploetz sent me this great unit test for threaded logging.  In it we are trying to find if a deadlock occurs.

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

import junit.framework.TestCase;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;

/**
 * Unit test for the ThrottlingFilter in a multi-threaded environment
 */
public class ThrottlingFilterThreadUTest extends TestCase {

  private static final Log logger = LogFactory.getLog(ThrottlingFilterThreadUTest.class);

  private static ThreadMXBean threadMXBean;

  @Override
  protected void setUp() throws Exception {
    super.setUp();
    threadMXBean = ManagementFactory.getThreadMXBean();
    logger.info("Thread contention monitoring supported: "
        + threadMXBean.isThreadContentionMonitoringSupported());
    logger.info("Thread contention monitoring enabled: "
        + threadMXBean.isThreadContentionMonitoringEnabled());
    threadMXBean.setThreadContentionMonitoringEnabled(true);
    logger.info("Thread contention monitoring enabled: "
        + threadMXBean.isThreadContentionMonitoringEnabled());
  }

  /**
   * Tests multiple threads using the same filter instance at the same time
   */
  public void testThreads() {
    Logger rootLogger = Logger.getRootLogger();
    assertNotNull(rootLogger);
    Appender fileAppender = rootLogger.getAppender("FILE");
    assertNotNull(fileAppender);
    ThrottlingFilter throttlingFilter = (ThrottlingFilter) fileAppender.getFilter();
    assertNotNull(throttlingFilter);

    ThreadGroup infoThreadGroup = new ThreadGroup("info-group");
    ThreadGroup errorThreadGroup = new ThreadGroup("error-group");
    Thread errorThread1 = new ErrorThread(errorThreadGroup, "error-thread-1");
    Thread infoThread1 = new InfoThread(infoThreadGroup, "info-thread-1");
    Thread errorThread2 = new ErrorThread(errorThreadGroup, "error-thread-2");
    Thread infoThread2 = new InfoThread(infoThreadGroup, "info-thread-2");
    infoThread1.start();
    errorThread1.start();
    errorThread2.start();
    infoThread2.start();

    while (true) {
      ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadMXBean.getAllThreadIds());
      for (int i = 0; i < threadInfos.length; i++) {
        ThreadInfo threadInfo = threadInfos[i];
        if (threadInfo != null && threadInfo.getThreadState() == Thread.State.BLOCKED) {
          System.out.println("Thread '" + threadInfo.getThreadName()
              + "' is blocked on the monitor lock '" + threadInfo.getLockName()
              + "' held by thread '" + threadInfo.getLockOwnerName() + "'");
        }
      }

      if (!infoThread1.isAlive() && !errorThread1.isAlive() && !infoThread2.isAlive()
          && !errorThread2.isAlive())
        break;
    }
  }

  public static class ErrorThread extends Thread {

    private static final Log logger = LogFactory.getLog(ErrorThread.class);

    public ErrorThread(ThreadGroup tg, String name) {
      super(tg, name);
    }

    public void run() {
      for (int i = 0; i < 10; i++) {
        try {
          test(0);
        } catch (Exception e) {
          long start = System.currentTimeMillis();
          logger.error("Error!", e);
          long end = System.currentTimeMillis();
          System.out.println("Took " + (end-start) + "ms to log error");
        }
      }
    }

    // simulate large stack traces
    private void test(int i) {
      if (i >= 500)
        throw new RuntimeException("D'OH!");
      test(i+1);
    }
  }

  public static class InfoThread extends Thread {

    private static final Log logger = LogFactory.getLog(InfoThread.class);

    public InfoThread(ThreadGroup tg, String name) {
      super(tg, name);
    }

    public void run() {
      for (int i = 0; i < 100; i++) {
        logger.info("Hi!");
      }
    }
  }
}

The log4j.xml test file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"
    debug="false">

    <!-- ================================= -->
    <!--           Appenders               -->
    <!-- ================================= -->

    <!-- A time/date based rolling file appender -->
    <appender name="FILE"
        class="org.apache.log4j.DailyRollingFileAppender">
        <param name="File" value="server.log" />
        <param name="Append" value="true" />

        <!-- Rollover at midnight each day -->
        <param name="DatePattern" value="'.'yyyy-MM-dd" />

        <layout class="org.apache.log4j.PatternLayout">
            <!-- The default pattern: Date Priority [Category] Message\n -->
            <param name="ConversionPattern" value="%d %-5p [%c] %m%n" />
        </layout>

        <filter class="com.betweengo.log4j.ThrottlingFilter">
          <param name="maxCountSameMessage" value="100"/>
          <param name="maxCountSavedMessages" value="100"/>
          <param name="waitInterval" value="60"/>
        </filter>
    </appender>

    <!-- ======================= -->
    <!-- Setup the Root category -->
    <!-- ======================= -->

    <root>
        <level value="INFO" />
        <appender-ref ref="FILE" />
    </root>

</log4j:configuration>

Get JSTL Vars from PageContext

JSTL sets its vars in the pageContext.  For example:

pageContext.setAttribute("foo", bar);


Therefore to get a JSTL variable use the pageContext within a tag or a JSP page.  For example:

// get item from pageContext and put in request
atg.servlet.DynamoHttpServletRequest drequest = atg.servlet.ServletUtil.getDynamoRequest(request);
drequest.setParameter("foo", pageContext.getAttribute("foo"));


Note that getAttribute assumes the variable is in the page scope.  If you want to get a variable from for example the request scope you would do this.

pageContext.getAttribute("foo", javax.servlet.PageContext.REQUEST_SCOPE);


For further reading please see PageContext.

YouTube Embedded Player Parameters

Here is a page describing the YouTube Embedded Player Parameters.  These parameters are query parameters you can add to the end of the YouTube URL.

There are different parameters you can add to the object and embed statements.  Here is an example.

<object width="320" height="265">
 <param name="movie" value="http://www.youtube.com/v/6sHenGpeGgo&hl=en&fs=1&autoplay=1"></param>
 <param name="allowFullScreen" value="true"></param>
 <param name="allowscriptaccess" value="always"></param
 <param name="wmode" value="transparent"></param>
 <embed src="http://www.youtube.com/v/6sHenGpeGgo&hl=en&fs=1&autoplay=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" wmode="transparent" width="320" height="265"></embed>
</object>

HTTP Proxy

Recently I was having a problem where a Flash video was not being displayed.  It turned out the Flash video had not been created properly and had a dependency on other Flash files that were not available.  When I was viewing this Flash video directly using JBoss I did not see any errors.  But when I viewed it using Apache the logs showed the dependency problems.

Another developer, Allan Scott, suggested next time I use Charles or Fiddler as HTTP proxies.

Did you try watching the HTTP traffic with a tool like Fiddler or Charles?

If there is a dependency on something and it failed to download it then you should be able to see the 404 or some other error.