Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

As of uPortal 2.5.1, StatsRecorder is a Static Cover for a Spring-configured IStatsRecorder instance.

Expected advantages to be gained from this implementation include:

  • a design and implementation that is easier to understand, configure, and use
  • a more powerful and flexible implementation that allows multiple stats recorders, etc. Much of what is currently configured via static singleton, global configuration becomes instance configuration. it becomes possible to have four stats recorders each differently configured some operating in new threads etc.
  • Backwards-compatibility: IStatsRecorder API does not change. portal.properties driven configuration continues to work.

The Static Cover

The StatsRecorder static class becomes a "static cover", the legacy static way that code gets a handle to the IStatsRecorder instance declared as a Spring bean named "statsRecorder". Of course, anything that's being wired together in Spring need not use the static cover and can instead have its IStatsRecorder needs supplied via dependency injection.

The static cover tries to use the Spring bean. If it doesn't find the bean, then it falls back on the 2.5.0 behavior of using portal.properties configuration. If that doesn't work either, it falls back on recording no stats.

Implementing the legacy behaviors

Thread firing

Before uPortal 2.5.1, StatsRecorder used portal.properties properties to configure which IStatsRecorder methods, if any, it would propogate. It also used portal.properties to configure a thread pool and would fire a thread to execute actual event propogation. This allows, for instance, the statistics recording for channel rendering to happen outside of the thread actually trying to render channels and to manage channel rendering. Quite possibly necessary for recording channel rendering. Quite possibly overkill for recording user login, which is already an expensive operation anyway to adding some simple stats recording won't make a noticable difference.

In uPortal 2.5.1, the thread firing behavior moved out of StatsRecorder and into a particular IStatsRecorder wrapper implementation, ThreadFiringStatsRecorder.

Conditional event propogation

Before uPortal 2.5.1, StatsRecorder conditions event propogation on portal.properties properties and subsequent static singleton configuration of StatsRecorderSettings. This patch introduces a new interface, IStatsRecorderFlags, for which there are two implementations - a simple JavaBean implementation, and an implementation backed by the static singleton StatsRecorderSettings. A new IStatsRecorder wrapper implementation, ConditionalStatsRecorder, applies an IStatsRecorderFlags instance to decide which method calls to propogate to the wrapped IStatsRecorder. So, to achieve the legacy behavior, we use a ConditionalStatsRecorder wrapper configured to use the SettingsBackedStatsRecorderFlagsImpl.

New features

One requested feature for stats recording is that of being able to have multiple stats recorders. uPortal 2.5.1 supports this – and further allows each recorder to be differently configured if desired. The ListStatsRecorder is an IStatsRecorder which delegates to child IStatsRecorders.

Wiring examples

Each of these examples are what you could add to applicationContext.xml to configure an IStatsRecorder instance.

Legacy case: don't wire it

Get the legacy behavior by not wiring
<!-- 
 | no change to applicationContext.xml or to portal.properties 
 | or to any other configuration file is required to get the behavior of uPortal 2.5.0 
 +-->

If you don't declare any bean named "statsRecorder", then the StatsRecorder static cover will fall back on discovering StatsRecorder configuration from portal.properties. This is pursuant to the goal of "backwards compatibility".

In uPortal 2.6.0, as implemented in the current uPortal 2 HEAD, the legacy behavior goes away and failing to declare an IStatsRecorder instance via Spring configuration will result in no stats recording.

Doing nothing

Declaring a do-nothing stats recorder
<bean name="statsRecorder"
      class="org.jasig.portal.services.stats.DoNothingStatsRecorder"/>

Note that no factory is required - think of Spring as the ultimate Factory.

A logging example

Logging some IStatsRecorder events

<!--
 | The parent bean is the Conditional wrapper because first we want to filter down to
 | the events we're actually going to log.
 +-->
<bean name="statsRecorder"
    class="org.jasig.portal.services.stats.ConditionalStatsRecorder">
    <property name="flags">
        <!--
         | This JavaBean lets us configure which events we'd like the Conditional 
         | wrapper to propogate.  The flags default to false so we need only declared those
         | methods we would like to propogate.
         +-->
        <bean class="org.jasig.portal.services.stats.StatsRecorderFlagsImpl">
            <property name="recordChannelRendered" value="true"/>
        </bean>
    </property>

    <!-- 
     | The Conditional stats recorder will call this target when its condition is fulfilled, that is
     | when the method call is recordChannelRendered().  IStatsRecorder calls other than 
     | recordChannelRendered will have no effect, that is, are filtered away 
     | by the Conditional stats recorder. 
     +-->
    <property name="targetStatsRecorder">
        <!-- 
         | The target of the Conditional is the thread firing wrapper so that we'll use separate
         | threads to perform the actual logging. 
         +-->
        <bean class="org.jasig.portal.services.stats.ThreadFiringStatsRecorder">
            <!-- initial thread pool size -->
            <constructor-arg value="5"/>
            <!-- maximum thread pool size -->
            <constructor-arg value="15"/>
            <!-- thread priority -->
            <constructor-arg value="5"/>

            <!--
             | The target of the threads we fire is this LoggingStatsRecorder instance.
             +-->
            <property name="targetStatsRecorder">
                <bean class="org.jasig.portal.services.stats.LoggingStatsRecorder"/>
            </property>

        </bean>
    </property>
</bean>

This example responds only to the recordChannelRendered() IStatsRecorder method, executing the LoggingStatsRecorder using threads from a pool separate from the threads used for other aspects of uPortal.

Wiring in your custom stats recorder

Declaring your own stats recorder
<bean name="statsRecorder"
      class="edu.yale.its.portal.services.stats.YaleStatsRecorder"/>

Multiple stats recorders

An ambitious example involving multiple stats recorders

<!--
 | The parent bean is the Conditional wrapper because first we want to filter down to
 | the events we're actually going to use.
 +-->
<bean name="statsRecorder"
    class="org.jasig.portal.services.stats.ConditionalStatsRecorder">
    <property name="flags">
        <!--
         | This JavaBean lets us configure which events we'd like the Conditional 
         | wrapper to propogate.  The flags default to false so we need only declared those
         | methods we would like to propogate.
         +-->
        <bean class="org.jasig.portal.services.stats.StatsRecorderFlagsImpl">
            <property name="recordChannelRendered" value="true"/>
            <property name="recordFolderAddedToLayout" value="true"/>
        </bean>
    </property>

    <!-- 
     | The Conditional stats recorder will call this target when its condition is fulfilled, that is
     | when the method call is recordChannelRendered().  IStatsRecorder calls other than 
     | recordChannelRendered will have no effect, that is, are filtered away 
     | by the Conditional stats recorder. 
     +-->
    <property name="targetStatsRecorder">
        <!-- 
         | Here we're firing new threads.  If we're going to use a large list of stats recorders,
         | recording stats could take awhile so we want this to be undertaken in threads from the
         | pool for this purpose rather than engaging core channel rendering, session management, or
         | Servlet Container threads in this project.  We need to let go of the current thread to let it
         | get back to the work of rendering the response to the user.
         +-->
        <bean class="org.jasig.portal.services.stats.ThreadFiringStatsRecorder">
            <!-- initial thread pool size -->
            <constructor-arg value="5"/>
            <!-- maximum thread pool size -->
            <constructor-arg value="15"/>
            <!-- thread priority -->
            <constructor-arg value="5"/>

            <!--
             | The target of the threads we fire is the List recorder implementation, which
             | will delegate to our configured list of stats recorders.
             +-->
            <property name="targetStatsRecorder">
                <bean class="org.jasig.portal.services.stats.ListStatsRecorder">
                   <property name="children">
                       <list>
                           <!-- here we declare the IStatsRecorders we'd like to run. -->
                           <bean class="org.jasig.portal.services.stats.LoggingStatsRecorder"/>
                           <bean class="org.jasig.portal.services.stats.PrintingStatsRecorder"/>
                           <!-- 
                            | for example, you might use some of the included recorders 
                            | as well as a custom recorder implementation
                            +-->
                           <bean class="edu.someschool.DatabaseStatsRecorder">
                               <!--
                                | If your stats recorder needs a DataSource, you can inject it.
                                +--> 
                               <property name="dataSource">
                                   <bean 
                                       class="org.springframework.jndi.JndiObjectFactoryBean">
		                       <property 
                                           name="jndiName" 
                                           value="java:comp/env/jdbc/myDatasource" />
	                           </bean>
                              </property>
                           </bean>
                       </list>
                   </property>
                </bean>
            </property>

        </bean>
    </property>
</bean>

  • No labels