Making a AngularJS based Liferay portlet instanceable

In one of my previous posts I showed how to create a Liferay portlet based on AngularJS. The problem with that concept was, that only a single portlet of that kind could be placed on a portal page.
I will show now how to improve the concept to not only allow multiple AngularJS portlets on a single Liferay page, but to allow the same portlet multiple times on a page.

The key is to manually bootstrap the AngularJS apps on the page, so we can no longer simple use ng-app. Instead of:

  <div ng-app>
     
  </div>

We assign an ID to our root DIV und attach the AngularJS module to it:

<div id="angularjsPortletDemo">
 
</div>

<script type="application/javascript" th:inline="javascript">
  (function() {
    if (typeof(AUI) !== 'undefined') {
      /* We are within Liferay */
      AUI().ready(function() {
        startAngular();
      });
    } else {
      document.addEventListener("DOMContentLoaded", function(event) {
        startAngular();
      });
    }

    function startAngular() {
      var appRootElem = document.getElementById('angularjsPortletDemo');
      angular.bootstrap(appRootElem, ['angularjsPortletDemo']);
    }
  })();
</script>

To make the portlet acutally instanceable we have to do more:

  1. Get rid of the global JavaScript variables in the HEAD (which we used to pass parameters from the backend such as the AJAX URL).
  2. Replace the ID on the root DIV by the actual Liferay portlet ID
  3. Change the Liferay portlet configuration

For the first step we just move the global variables into the IIFE block where the AngularJS bootstrapping happens. And we pass the variables to AngularJS as values to be able to use it in the AngularJS dependency injection system:

function startAngular() {
    var ajaxURL = /*[[${ajaxURL}]]*/ "/testdata/";
    var isStandalone = /*[[${standalone}]]*/ true;
    var authenticatedUser = /*[[${authenticatedUser}]]*/ "anonymous";

    var app = angular.module('angularjsPortletDemo');
    app.value('ajaxUrl', ajaxURL);
    app.value('isStandalone', isStandalone);
    app.value('authenticatedUser', authenticatedUser);

    var appRootElem = document.getElementById('angularjsPortletDemo');
    angular.bootstrap(appRootElem, ['angularjsPortletDemo']);
}

For the second step we use Thymeleaf to inject the portletId into the HTML template:

@RenderMapping
public String view(RenderRequest request, RenderResponse response, ModelMap model) {
  //...
  ThemeDisplay themeDisplay = (ThemeDisplay) request.getAttribute(WebKeys.THEME_DISPLAY);
  PortletDisplay portletDisplay = themeDisplay.getPortletDisplay();
  model.addAttribute("portletId", portletDisplay.getId());
}
<div id="angularjsPortletDemo" th:id="${portletId}" 

</div>

var portletId = /*[[${portletId}]]*/ 'angularjsPortletDemo';

var appRootElem = document.getElementById(portletId);
angular.bootstrap(appRootElem, ['angularjsPortletDemo']);

The last and most simple step to make the portlet instantiable is to set the homonymous attribute in liferay-portlet.xml:

<portlet>
    <portlet-name>liferay-angularjs-portlet</portlet-name>
    <instanceable>true</instanceable>
    <requires-namespaced-parameters>false</requires-namespaced-parameters>
    <ajaxable>true</ajaxable>
    <header-portlet-css>/css/app.css</header-portlet-css>
    <footer-portlet-javascript>/js/vendor.js</footer-portlet-javascript>
    <footer-portlet-javascript>/js/app.js</footer-portlet-javascript>
</portlet>

And the best of all: The AngularJS app can still be launched standalone: Just run grunt server on the console!

The full code can be found on GitHub.

Spring autowire performance improvement in version 3.1.2

Spring finally fixed a long known problem with the autowire (by type) performance, or generally spoken, with the performance of bean lookup by type. The background is, that determination of the actual type of a Spring bean is not done with a simple instanceof, because most beans might be proxies. A Spring bean could even have multiple proxies. So, to determine the type it is necessary to iterate through all PostProcessors and ask them for the original type:

protected Class predictBeanType(String beanName, RootBeanDefinition mbd, Class... typesToMatch) {
	Class beanClass;
	if (mbd.getFactoryMethodName() != null) {
		beanClass = getTypeForFactoryMethod(beanName, mbd, typesToMatch);
	}
	else {
		beanClass = resolveBeanClass(mbd, beanName, typesToMatch);
	}
	// Apply SmartInstantiationAwareBeanPostProcessors to predict the
	// eventual type after a before-instantiation shortcut.
	if (beanClass != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
		for (BeanPostProcessor bp : getBeanPostProcessors()) {
			if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
				SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
				Class processedType = ibp.predictBeanType(beanClass, beanName);
				if (processedType != null) {
					return processedType;
				}
			}
		}
	}
	return beanClass;
}

This process can take astonishing long. And if you use autowiring in huge web-applications, with a lot of scoped beans, this can easily take a big part of the request time. Which has already been reported two years ago: SPR-6870

It was fixed now by simply caching all calls to DefaultListableBeanFactory.getBeansOfType(). See: SPR-9448

I recommend to update to Spring 3.1.2 if you’re using autowiring in scoped beans (especially request beans)!

Load JPA Entites from within multiple projects

The recently released Spring 3.1 opens up the possibility to scan the whole classpath for JPA entities. The idea is to bootstrap JPA programmatically without a persistence.xml file. You can find details about the new feature here: http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/new-in-3.1.html#new-in-3.1-jpa-without-xml

Instead of referring to a persistence unit you can simply define the Java packages containing your entity files, when creating a LocalContainerEntityManagerFactoryBean. And the whole classpath is scanned.

 highlight="3,6"]
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="packagesToScan">
    <array>
      <value>at.nonblocking.test.model<value>
      <value>at.nonblocking.test2.**.model<value>
    <array>
  <!-- <property name="persistenceUnitName" value="myPersistenceUnit" /> -->
  <property name="jpaVendorAdapter">
    <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
      <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
      <property name="generateDdl" value="true" />
      <property name="showSql" value="true" />
    </bean>
  </property>
</bean>

And even wildcards are possible!