Thursday, 3 May 2012

Binding spring form using bean with map attribute retrieved using hibernate

Another one exception bit me today. Spring forms nicely support maps as bean properties using 'mapProperty[key].mapElementProperty' paths, but each time I tried to submit form, exception was thrown (see below). As it turns out, problem was caused by hibernate which returned bean proxy. When setting map element property values after form submit, Spring tries to find out what type of elements map contains using generics information (... using some kind of magic), but in the end it didn't work. Not sure, but I suppose it could be because javassist can't handle generics because of type erasure. Solution was simple - modify DAO method which returned editable object (getById(id)) so that it would not return a hibernate-managed object, but a cloned object. As soon as those changes were applied, everything started to work.
java.lang.NullPointerException
 at org.springframework.beans.BeanWrapperImpl.createDefaultPropertyValue(BeanWrapperImpl.java:620)
 at org.springframework.beans.BeanWrapperImpl.setDefaultValue(BeanWrapperImpl.java:614)
 at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:579)
 at org.springframework.beans.BeanWrapperImpl.getBeanWrapperForPropertyPath(BeanWrapperImpl.java:553)
 at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:914)
 at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:76)
 at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:692)
 at org.springframework.validation.DataBinder.doBind(DataBinder.java:588)
 at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:191)
 at org.springframework.web.portlet.bind.PortletRequestDataBinder.bind(PortletRequestDataBinder.java:113)
 at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter$PortletHandlerMethodInvoker.doBind(AnnotationMethodHandlerAdapter.java:570)
 at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.doBind(HandlerMethodInvoker.java:813)
 at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:367)
 at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:171)
 at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:362)
 at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter.doHandle(AnnotationMethodHandlerAdapter.java:349)
 at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter.handleAction(AnnotationMethodHandlerAdapter.java:283)
 at org.springframework.web.portlet.DispatcherPortlet.doActionService(DispatcherPortlet.java:641)
 at org.springframework.web.portlet.FrameworkPortlet.processRequest(FrameworkPortlet.java:519)
 at org.springframework.web.portlet.FrameworkPortlet.processAction(FrameworkPortlet.java:460)
 at org.jasig.portal.portlet.container.FilterChainImpl.doFilter(FilterChainImpl.java:130)
 at org.jasig.portal.portlet.container.FilterChainImpl.processFilter(FilterChainImpl.java:92)
 at org.jasig.portal.portlet.container.FilterManagerImpl.processFilter(FilterManagerImpl.java:119)
 at org.apache.pluto.container.driver.PortletServlet.dispatch(PortletServlet.java:359)
 at org.apache.pluto.container.driver.PortletServlet.doPost(PortletServlet.java:267)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
 at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646)
 at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:551)
 at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:488)
 at org.apache.pluto.driver.container.DefaultPortletInvokerService.invoke(DefaultPortletInvokerService.java:233)
 at org.apache.pluto.driver.container.DefaultPortletInvokerService.action(DefaultPortletInvokerService.java:101)
 at sun.reflect.GeneratedMethodAccessor295.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
 at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
 at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
 at org.jasig.portal.portlet.dao.jpa.ThreadContextClassLoaderAspect.doThreadContextClassLoaderUpdate(ThreadContextClassLoaderAspect.java:56)
 at sun.reflect.GeneratedMethodAccessor105.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
 at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
 at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
 at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
 at $Proxy207.action(Unknown Source)
 at org.apache.pluto.container.impl.PortletContainerImpl.doAction(PortletContainerImpl.java:251)
 at org.jasig.portal.portlet.rendering.PortletRendererImpl.doAction(PortletRendererImpl.java:165)
 at org.jasig.portal.portlet.rendering.worker.PortletActionExecutionWorker.callInternal(PortletActionExecutionWorker.java:46)
 at org.jasig.portal.portlet.rendering.worker.PortletActionExecutionWorker.callInternal(PortletActionExecutionWorker.java:31)
 at org.jasig.portal.portlet.rendering.worker.PortletExecutionWorker$1.call(PortletExecutionWorker.java:145)
 at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
 at java.util.concurrent.FutureTask.run(FutureTask.java:138)
 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
 at java.lang.Thread.run(Thread.java:662)

Wednesday, 28 March 2012

Centring DIV elements

Here's a HTML structure and required CSS:

<div id="container">
  <div class="centered">This is centered div</div>
</div>
#container { text-align:center;}
.centered { margin:0 auto; width:80%; } /* Set the width according to your needs */

Applying "margin: 0 auto;" would be sufficient in a world without IE. In order to get centred div element in IE, you have to set "text-align: center;" for parent element. That parent element could be another div (just like in example), body element (then style definition would be "body {text-align: center}") or anything else. There's also a lot more detailed explanation of this approach here, but I'm too lazy to read the whole article :(.

Already knew this? Good for you! I did have to spend some time reading discussions and long descriptions about this, but didn't find a single concise "here's a solution - copy, paste, and enjoy" answer.

Are there any drawbacks of this method? Haven't found such yet. If you know something that I'm not aware of, please, let me know this in comments.

Are there any alternatives? Yes:

  • I've seen (and used) alternatives where DIV element is being included into a table which is centred. But it is not recommended approach since page structure should be built using DIVs. I know - it's a challenging task, but that's an idea I've got from different discussions.
  • Also I've found a discussion where a fixed-width DIV gets positioned at the centre of parent element and then a negative margin is applied (haven't tried this one, but looks promising). Here's a CSS:
    .centered{
      width: 500px;
      position: relative;
      left: 50%;
      margin-left: -250px; /* half the width of the div */
    }

Wednesday, 21 March 2012

Bytecode instrumentation for Hibernate using Maven

Lazy property loading for hibernate is nice when you're using complex formulas for calculating values. But it requires bytecode instrumentation or otherwise hibernate will ignore 'lazy="true"' on property element and load the value eagerly. Here's the snippet of my pom.xml that does the instrumentation:
    <build>
        <plugins>
            ...
            <!-- Bytecode insturmentation in order to enable lazy property loading by Hibernate. -->
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.3</version>
                <executions>
                    <execution>
                        <id>Instrument domain classes</id>
                        <configuration>
                            <tasks>
                                <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
                                    <classpath>
                                        <path refid="maven.dependency.classpath" />
                                        <path refid="maven.plugin.classpath" />
                                    </classpath>
                                </taskdef>
                                <instrument verbose="true">
                                    <fileset dir="${project.build.outputDirectory}">
                                        <include name="com/example/domain/*.class" />
                                    </fileset>
                                </instrument>
                            </tasks>
                        </configuration>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.hibernate</groupId>
                        <artifactId>hibernate-core</artifactId>
                        <version>${hibernate.version}</version>
                    </dependency>
                    <dependency><!-- bytecode instrumentation library to use... note that cglib is deprecated -->
                        <groupId>org.javassist</groupId>
                        <artifactId>javassist</artifactId>
                        <version>${javassist.version}</version>
                    </dependency>
                    <dependency><!-- so that slf4j won't barf about missing binding -->
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-simple</artifactId>
                        <version>${slf4j.version}   </version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
Note that you'll have to define the versions of libraries that you're using (here are my values):
  <properties>
    <hibernate.version>3.6.7.Final</hibernate.version>
    <javassist.version>3.14.0-GA</javassist.version>
    <slf4j.version>1.6.1</slf4j.version>
  </properties>

Wednesday, 14 March 2012

Deploying uPortal 4.0.3

I won't go here into details about configuring, compiling and other basic stuff. Main reason of this post is to give you some of my experience that you could use when deploying your own portal. While working on improvements, a lot of them have been given back to community as patches and will be included in upcoming releases of uPortal. Also best regards to Eric Dalquist (lead developer of uPortal) who helped me a lot with some performance (... and not only performance) issues. That allowed ORTUS (portal of Riga Technical University) to be one of the first (if not the first) production deployments of uPortal 4.0.x.

Required DB Schema changes for MS SQL Server

Since we are using MS SQL Server we had to disable or delete the single uniqueness index on table UP_DLM_EVALUATOR. This index should ensure that if fragment name is provided (is not null) then it must be unique. Unfortunately, MS SQL server is unable to provide such indexes since uniqueness check of 'null =? null' returns true when building index keys. This is not the case for other DBMS vendors (i.e. postgres).

Another pitfall was that we had to remove all @Transactional(readonly = true) annotations from DAO class methods. This annotation is required because selecting from text fields for Postgres requires read-only transaction. But this is a major slow-down for MS SQL Server.

Update (16.03.2012): If you're using SQL 2008 or later, you can (and should) create new filtered index to replace deleted index in order to maintain data integrity. Here's the required SQL query:

CREATE UNIQUE NONCLUSTERED INDEX [UP_DLM_FRAGMENT_NAME_UIDX] ON [dbo].[UP_DLM_EVALUATOR] ([FRAGMENT_NAME] ASC)
WHERE ([fragment_name] IS NOT NULL)

Memory configuration

uPortal 4.x uses caching a lot. This requires a decent amount of memory allocated for old generation objects. I would recommend you to dedicate around 4Gb of memory for old generation. There are guidelines stating that you should dedicate around half of memory for young generation (which did work well for us with uPortal 3.1.x) but for 4.x this won't work. Your server should have at least 8Gb RAM which should be partitioned as follows:

  • 6Gb for JVM (4Gb old and 2Gb new generations)
  • 512Mb for permanent generation - this will guarantee that in case of hot portlet deployment you won't run out of permanent generation space (note that you should also enable class unloading: -XX:+CMSClassUnloadingEnabled)
  • Leave the rest of memory for OS, other services (like nginx... which we're using in front of Tomcat) and OS caching.

With configuration above we get about 1s pause time each 8h on our server, which is not too bad but leaves a place for improvements. Full GCs can stop JVM for quite a while (especially in case of very big old generation space), hence I'm also thinking about switching to G1 garbage collector since it should lower pause times... but haven't field-tested it yet.

Cache configuration (ehcache.xml)

Another thing you should be aware about is caching. Default caches are nice, but in production environment you have to adjust your caches depending on your situation. What I've found out is that:

  • org.jasig.portal.groups.IEntity - default value TTL value is 6h. This means that after logging in your user won't get group updates next 6 hours. I found this troublesome and reduced it down to one hour and also set the idle time to half an hour.
  • org.jasig.portal.rendering.STRUCTURE_TRANSFORM - this cache rarely gets any hits (hit rate is around 0.1%), but consumes a lot of memory, hence reduced max element count in cache to 500 and set idle time to 10 minutes (600 seconds). I was also considering removing this cache at all, but since it gets some hits, then it does not hurt to leave it there.
  • org.jasig.portal.rendering.THEME_TRANSFORM - This cache is used a lot, but also uses quite a bit of memory, hence I've introduced max idle time with value of 10 minutes. With such configuration cache is usually filled with ~2k elements and hit rate is ~30-40% (for ~500 concurrent users... for more intensive use ).

Note that JConsole is your friend here. Make sure that you can connect with JConsole to your production machine since then you'll be able to adjust cache configuration of each cache instance at runtime. This is very useful.

Indexes for performance (fighting for milliseconds)

Here's a list of indexes that we're using. A lot of them also include columns so that after index seek, DB does not have to look up the value from table - column values are already returned by index. Since portal mostly issues SELECT statements, then we're using a lot of them.

CREATE INDEX UP_DLM_EVALUATOR_EVALUATOR_TYPE_FRAGMENT_NAME_IDX
  ON dbo.UP_DLM_EVALUATOR (EVALUATOR_TYPE, FRAGMENT_NAME)
CREATE INDEX UQ__UP_DLM_E__C0C7F43617C286CF
  ON dbo.UP_DLM_EVALUATOR (FRAGMENT_NAME)
CREATE INDEX UP_LOGIN_EVENT_AGGREGATE_INTERVAL_DATE_TIME_DIM_IDX
  ON dbo.UP_LOGIN_EVENT_AGGREGATE ([INTERVAL], DATE_DIMENSION_ID, TIME_DIMENSION_ID)
  INCLUDE (DURATION, LOGIN_COUNT, UNIQUE_LOGIN_COUNT, AGGREGATED_GROUP_ID)
CREATE INDEX UP_LOGIN_EVENT_AGGREGATE__UIDS_LOGIN_AGGR_ID_IDX
  ON dbo.UP_LOGIN_EVENT_AGGREGATE__UIDS (LOGIN_AGGR_ID)
  INCLUDE (uniqueUserNames)
CREATE INDEX UP_PRESON_ATTR_USER_DIR_ID_IDX
  ON dbo.UP_PERSON_ATTR (USER_DIR_ID)
  INCLUDE (id, ENTITY_VERSION, ATTR_NAME)
CREATE INDEX UP_PERSON_ATTR_VALUES_ATTR_ID_IDX
  ON dbo.UP_PERSON_ATTR_VALUES (ATTR_ID)
  INCLUDE (ATTR_VALUE, VALUE_ORDER)
CREATE UNIQUE INDEX UP_PERSON_DIR_USER_NAME_IDX
  ON dbo.UP_PERSON_DIR (USER_NAME)
  INCLUDE (USER_DIR_ID, ENTITY_VERSION, LST_PSWD_CGH_DT, ENCRPTD_PSWD)
CREATE INDEX UP_PORTAL_COOKIE_EXPIRES_IDX
  ON dbo.UP_PORTAL_COOKIES (EXPIRES)
CREATE UNIQUE INDEX UP_PORTALL_COOKIES_VALUE_IDX
  ON dbo.UP_PORTAL_COOKIES (COOKIE_VALUE)
  INCLUDE (PORTAL_COOKIE_ID, CREATED, ENTITY_VERSION, EXPIRES)
CREATE INDEX UP_PORTLET_COOKIES_PORTAL_COOKIE_ID_IDX
  ON dbo.UP_PORTLET_COOKIES (PORTAL_COOKIE_ID)
  INCLUDE (PORTLET_COOKIE_ID, COOKIE_COMMENT, COOKIE_DOMAIN, ENTITY_VERSION, EXPIRES, COOKIE_NAME, COOKIE_PATH, SECURE, COOKIE_VALUE, VERSION, portalCookie_PORTAL_COOKIE_ID)
CREATE INDEX UP_PORTLET_DEF_PARAM_PORTLET_DEF_ID_IDX
  ON dbo.UP_PORTLET_DEF_PARAM (PORLTET_DEF_ID)
CREATE INDEX UP_PORTLET_ENT_USER_ID_IDX
  ON dbo.UP_PORTLET_ENT (USER_ID)
CREATE INDEX UP_PORTLET_PREF_PORTLET_PREF_ID_IDX
  ON dbo.UP_PORTLET_PREF (PORTLET_PREF_ID)
CREATE INDEX UP_SS_USER_PREF_USER_ID_PROFILE_ID_IDX
  ON dbo.UP_SS_USER_PREF (PROFILE_ID, USER_ID)
  INCLUDE (SS_USER_PREF_ID, ENTITY_VERSION, UP_SS_DESCRIPTOR_ID)
CREATE INDEX UP_SS_USER_PREF_LAY_ATTR_SS_USER_PREF_ID_IDX
  ON dbo.UP_SS_USER_PREF_LAY_ATTR (SS_USER_PREF_ID)
  INCLUDE (UP_SS_USER_PREF_LAY_ATTR_ID, ENTITY_VERSION, NODE_ID)

Replace unique index on table UP_SS_USER_PREF with this one in order to include columns into index:

CREATE UNIQUE INDEX UP_SS_USER_PREF_PROFILE_USER_SSDESCRIPTOR_IDX
  ON dbo.UP_SS_USER_PREF (PROFILE_ID, USER_ID, UP_SS_DESCRIPTOR_ID)
  INCLUDE (SS_USER_PREF_ID, ENTITY_VERSION)

Tuesday, 31 January 2012

Customizing OpenAM pages

First, take a look at official WIKI page and documentation. Information there is pretty vague, but at least gives some hints. In the rest of this article I'll try to fill in some gaps.
I had to modify default login, logout and error pages for root realm. Although official documentation suggests not to modify default pages, but that was the solution that worked for me. I modified following pages in config/auth/default/ folder under OpenAM web context:
  • login_failed_template.jsp - displayed after unsuccessful login - usually because of invalid username/password
  • Login.jsp - login form
  • Logout.jsp - showed after user hits "logout" and terminates his SSO session
  • session_timeout.jsp - showed if user's session timed out due to inacitivty
The trick is to leave ALL jato:* elements in place. You can delete all tables, rows, replace them with DIVs, add additional DIV elements, etc, but JATO's must be left intact. I couldn't find a good documentation about that framework, hence all you can do is follow your intuition and find out the correct page structure using good old trial and error method.
In order to add additional localization (e.g. 'lv'):
  • Create new directory:
    mkdir /config/auth/default_lv
  • Copy the file that JATO uses for form i18n:
    cp /config/auth/default_en/DataStore.xml /config/auth/default_en/
  • Copy the file that contains all messages used int authentication views:
    cp WEB-INF/classes/amAuthUI.properties WEB-INF/classes/amAuthUI_lv.properties
  • ... translate copied files
None of *.jsp file changes require server restart since servlet container can pick up changes dynamically (at least Tomcat can), but .properties file changes won't be reflected until web context or server is restarted.
One more thing - you can change selected locale. If you want to provide users with an ability to switch between locales (override locale detected by browser request), you must specify a 'locale' request parameter (this one I had to dig out from source code). And in order to pass it to latter pages (not to lose it after unsuccessful login attempts), in the form at the end of Login.jsp add:
<input type="hidden" name="locale" value="<%=request.getParameter("locale")%>" />

Monday, 21 November 2011

Certificate based authentication on Linux

You can create your own certificate and use it in order to authenticate on Linux machines while connecting through SSH. But be aware that SSHD (SSH daemon is the one that accepts SSH connections, and can be perceived as SSH server) is very sensitive to file permissions. If you set wrong permissions on .ssh or .ssh/authorized_keys, your certificate won't work. Here are the correct file permissions (considering that you're in home folder of remote machine target user):

$ chmod 0700 .ssh
$ chmod 0600 .ssh/authorized_keys

Wednesday, 16 November 2011

Retrieving large resultsets from postgres using psql

Consider that you have a large table in database and you want to export it to a CSV file. What's your first idea? I started using PgAdmin - wrote a simple SELECT and ran "Execute to file". But it gave me out "out of memory for query result". It turns out that PgAdmin tries to load the resultset into memory (client side), but fails since memory limitations. Then I tried psql with same results. Then I found really good variable named FETCH_COUNT in http://www.postgresql.org/docs/9.1/static/app-psql.html and here's how I used it:

statistics=#\a
statistics=#\f ,
statistics=#\set FETCH_COUNT 500000
statistics=#select * from MY_HUGE_TABLE
statistics-#\g /home/arvids/statistics.csv
  1. Disable aligned mode, because we don't want extra whitespaces in CSV file
  2. Then set field separator to comma (it's a CSV file after all)
  3. Then set fetch count to 500000 so that client (my computer) could load the partial resultset into memory
  4. Now execute query
  5. ... and route it to a file