Liferay portlets development in IntelliJ IDEA

You can develop Liferay portlets with IntelliJ IDEA exactly as in Eclipse Liferay IDE. Follow simple steps:

  1. Create debug configuration in IntelliJ IDEA using Local Tomcat
  2. Configure deployment artifact: add your WAR as “yourwar:war exploded”
  3. Create XML file with name “yourwar.xml” with the following content:
    <?xml version="1.0" encoding="UTF-8"?>
    <Context 
      docBase="/absolute/path/to/project/yourwar" 
      reloadable="false" 
      path="/yourwar"/>

    Please note, docBase have to be the same as “Output directory” setting in ItelliJ IDEA of the artifact you deploy. Also, path have to be the same as “Application context” in Run/Debug configuration

  4. Create post deployment action that will copy just created XML to Liferay’s autodeploy folder.

Enjoy automatic deployment, hot-swap and debugging your LIferay application.

Please note, I tested it with IntelliJ IDEA 12.1 and Liferay 6.0.5 on Tomcat 6

Integrating HornetQ with Camel and Spring

Since Apache Camel have only one built-in implementation for specific JMS provider – ActiveMQ, you have to use generic org.apache.camel.component.jms.JmsConfiguration.

In order to work with HornetQ with Camel and Spring first you have to add HornetQ dependencies to Maven pom.xml:

<dependency>
	<groupId>org.hornetq</groupId>
	<artifactId>hornetq-jms</artifactId>
	<version>${hornetq.version}</version>
</dependency>
<dependency>
	<groupId>org.hornetq</groupId>
	<artifactId>hornetq-jms-client</artifactId>
	<version>${hornetq.version}</version>
</dependency>
<dependency>
	<groupId>org.hornetq</groupId>
	<artifactId>hornetq-core-client</artifactId>
	<version>${hornetq.version}</version>
</dependency>
<dependency>
	<groupId>org.jboss.netty</groupId>
	<artifactId>netty</artifactId>
	<version>3.2.7.Final</version>
</dependency>

After that modify your spring.xml. Here is an example:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <bean id="hornetqServerLocatorFactory" class="com.example.HornetqServerLocatorFactory" />
    <bean id="hornetqServerLocator" factory-bean="hornetqServerLocatorFactory" factory-method="createServerLocator"/>
    <bean id="hornetqJmsConnectionFactory" class="org.hornetq.jms.client.HornetQJMSConnectionFactory">
        <constructor-arg name="serverLocator" ref="hornetqServerLocator"/>
    </bean>
    <bean id="hornetqJmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
        <property name="connectionFactory" ref="hornetqJmsConnectionFactory"/>
        <property name="transacted" value="false"/>
        <property name="requestTimeout" value="60000"/>
    </bean>
    <bean id="hornetq" class="org.apache.camel.component.jms.JmsComponent">
        <property name="configuration" ref="hornetqJmsConfig"/>
    </bean>
</beans>

And the last thing is to write your implementation of HornetqServerLocatorFactory. If you wish to configure HornetQ host and port from spring.xml, you can easily do it – just put them as members of the class and inject them via Spring.xml.

package com.example;

import java.util.HashMap;
import java.util.Map;

import org.hornetq.api.core.TransportConfiguration;
import org.hornetq.api.core.client.HornetQClient;
import org.hornetq.api.core.client.ServerLocator;
import org.hornetq.core.remoting.impl.netty.NettyConnectorFactory;
import org.springframework.beans.factory.annotation.Configurable;

@Configurable
public class HornetqServerLocatorFactory {

	public ServerLocator createServerLocator() {
		final Map<String, Object> connectionParams = new HashMap<String, Object>();
		connectionParams.put( "port", "hornetq-host.com" );
		connectionParams.put( "host", 5900 );

		final TransportConfiguration transportConfiguration = new TransportConfiguration( NettyConnectorFactory.class.getName(), connectionParams );
		final ServerLocator serverLocator = HornetQClient.createServerLocatorWithoutHA( new TransportConfiguration[] { transportConfiguration } );
		return serverLocator;
	}
}

That’s it!

Spell checking with suggestions with Hibernate Search 4 and Lucene 3.6

In latest releases of Hibernate Search 4 and Lucene 3.6 there was some changes in SpellChecker API’s.
Here is example of the new API that allows to use spell checking with suggested words:

public String[] getSuggestions(String txt){
	String[] suggestions =  new String[]{};

	FullTextSession fullTextSession = Search.getFullTextSession(sf.getCurrentSession());
	SearchFactory searchFactory = fullTextSession.getSearchFactory();
	IndexReader reader = searchFactory.getIndexReaderAccessor().open(MyEntity.class);
	try {
		FSDirectory spellCheckerDir = FSDirectory.open(new File("D:\lucene\spellchecker\com.site.model.MyEntity"));
		SpellChecker spellChecker = new SpellChecker(spellCheckerDir);
		Dictionary dictionary = new LuceneDictionary(reader, "description");
		IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_35, searchFactory.getAnalyzer("myAnalyzer"));
		spellChecker.indexDictionary(dictionary, config, true);
		suggestions = spellChecker.suggestSimilar(txt, 10);
	} catch (Exception e) {
		e.printStackTrace();
	}
	finally{
		searchFactory.getIndexReaderAccessor().close(reader);
	}
	return suggestions;
}

sf.getCurrentSession() gets standard Hibernate session. If you use EntityManager, there exists a method to obtain FullTextSession.
The example creates new index directory dedicated to spell checking. It adds terms from MyEntity’s description field to the index. spellChecker.indexDictionary adds the terms to the index, and optionally merges it with existing index.

Portlets with Wicket 1.5.x in Liferay portal

After Wicket dropped support for portlets and moving it to wicketstuff the settings in XMLs are changed:

portlet.xml should look like

<portlet>
	<description>wickettest4</description>
	<portlet-name>wickettest4</portlet-name>
	<display-name>wickettest4</display-name>
	<portlet-class>org.apache.wicket.portlet.WicketPortlet</portlet-class>
	<init-param>
		<name>wicketFilterPath</name>
		<value>/bla</value>
	</init-param>
	<supports>
		<mime-type>text/html</mime-type>
		<portlet-mode>VIEW</portlet-mode>
	</supports>
	<portlet-info>
		<title>wickettest4</title>
		<keywords>wickettest4</keywords>
	</portlet-info>
</portlet>

Note new portlet-class. Also, mime-type have to be text/html.

web.xml also changed, notice filter class:

<filter>
	<filter-name>wicket.wicket</filter-name>
	<filter-class>org.apache.wicket.portlet.PortletFilter</filter-class>
	<init-param>
		<param-name>applicationClassName</param-name>
		<param-value>org.wicket.WicketApplication</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>wicket.wicket</filter-name>
	<url-pattern>/bla/*</url-pattern>
	<dispatcher>REQUEST</dispatcher>
	<dispatcher>INCLUDE</dispatcher>
	<dispatcher>FORWARD</dispatcher>
</filter-mapping>

And of course Maven dependencies also changed:

<dependency>
	<groupId>org.apache.wicket</groupId>
	<artifactId>wicket-core</artifactId>
	<version>${wicket.version}</version>
</dependency>
<dependency>
	<groupId>org.apache.portals.bridges</groupId>
	<artifactId>portals-bridges-common</artifactId>
	<version>2.0</version>
</dependency>
<dependency>
	<groupId>org.wicketstuff</groupId>
	<artifactId>wicketstuff-portlet</artifactId>
	<version>${wicket.version}</version>
</dependency>

 

Create datasource programmatically on JBoss 7

In order to create new datasource programmatically on JBoss 7 without restart (on the fly) from Java you can use CLI Java API. You have to include jboss-as-controller-client to your project dependencies:

<dependency>
	<groupId>org.jboss.as</groupId>
	<artifactId>jboss-as-controller-client</artifactId>
	<version>7.0.2.Final</version>
</dependency>

The following example creates new datasource:

public void createDatasource() throws Exception{
	ModelNode request = new ModelNode();
	request.get(ClientConstants.OP).set(ClientConstants.ADD);
	request.get(ClientConstants.OP_ADDR).add("subsystem",
			"datasources");
	request.get(ClientConstants.OP_ADDR).add("data-source",
			"java:jboss/datasources/NewDatasource");

	request.get("jndi-name").set("java:jboss/datasources/NewDatasource");
	request.get("connection-url").set("jdbc:as400://1.2.3.4/SCHEME");
	request.get("driver-class").set("com.ibm.as400.access.AS400JDBCDriver");
	request.get("driver-name").set("jt400.jar");
	request.get("user-name").set("username");
	request.get("password").set("password");
	request.get("pool-name").set("pool_NewDatasource");

	ModelControllerClient client = ModelControllerClient.Factory.create(
			InetAddress.getByName("127.0.0.1"), 9999);
	client.execute(new OperationBuilder(request).build());
}

If you want to check if the datasource already exists, consider following snippet:

public boolean checkIfDatasourceExists() throws Exception {
	ModelNode request = new ModelNode();
	request.get(ClientConstants.OP).set("read-resource");
	request.get("recursive").set(false);
	request.get(ClientConstants.OP_ADDR).add("subsystem", "datasources");

	ModelControllerClient client = ModelControllerClient.Factory.create(
			InetAddress.getByName("127.0.0.1"), 9999);
	ModelNode responce = client.execute(new OperationBuilder(request).build());

	ModelNode datasources = responce.get(ClientConstants.RESULT).get("data-source");

	if (datasources.isDefined()) {
		for (ModelNode dataSource : datasources.asList()) {
			String dataSourceName = dataSource.asProperty().getName();
			if (dataSourceName.equals("java:jboss/datasources/NewDatasource")) {
				return true;
			}
		}
	}
	return false;
}

The examples have the same effect as trying to add datasource from Jboss Administration console.

Cleaning eclipse with -clean argument

For those of you who work a lot with Eclipse,
you probably run into all sorts of strange errors in Eclipse that
not really related to your code, and then you get the feeling that Eclipse has gone mad.

One way to solve these problems and clean the Eclipse is using -clean argument.
all you need to do is to edit the eclipse.ini file located in your <Eclipse install directory> and add it as the first argument on the first line.
After you upload the eclipse you can delete it until the next time you will need it.

Forcing JBoss 7 to apply changes to JSP’s immediately

JBoss AS 7 by default do not reflect any changes to JSP files in WAR deployment. So, during application development you have to redeploy WAR or restart JBoss. In order to change this behaviour it is necessary to change section of urn:jboss:domain:web:1.0 in standalone.xml so it looks like:

<subsystem xmlns="urn:jboss:domain:web:1.0" default-virtual-server="default-host">
    <configuration>
        <jsp-configuration development="true"/>
    </configuration>
    ...
</subsystem>

And now JBoss behaves as you expect it in development mode: all changes to JSP files are loaded automatically as soon as you change the JSP file.

Android browser recognizes CSS media type after page reloading

While trying to optimize site for mobile devices you have to add viewport meta tag:

<meta name="viewport" content="target-densitydpi=device-dpi, width=device-width, height=device-height, initial-scale=1.0" />

You can add additional CSS file that will be used on mobile device only. To achieve this just add CSS media type. In Android developer’s guide at http://developer.android.com/guide/webapps/targeting.html it suggests to target device DPI value like:

<link rel="stylesheet" href="css/mobile.css" type="text/css" media="screen and (-webkit-device-pixel-ratio:1.0)" />

It works fine, but Google Chrome on desktop PC also loads this CSS file.

There is another solution to target mobile devices (both Android, iPhone, Blackberry): to specify maximal width of the screen like:

<link rel="stylesheet" href="css/mobile.css" type="text/css" media="screen and (max-device-width:801px) and (max-width:801px)" />

The problem is that Android (at least 2.3 I’ve tested) recognizes this media type only after page reloading (refreshing) in browser. Googling the web I found the open issue.
I solved it using JavaScript:

if(isMobile){
	var cssLink = document.createElement("link");
	cssLink.setAttribute("type", "text/css");
	cssLink.setAttribute("rel", "stylesheet");
	cssLink.setAttribute("href", "css/mobile.css");
	document.head.appendChild(cssLink);
}

I tested it on my device an it works fine, unless some little delay: the CSS file loads only after JavaScript executes. When google will fix the issue (I hope so…), there will be no delay, since original link is in the HEAD section.

Of course you have to detect mobile device using the JavaScript, I used solution suggested here: http://localstreamer.posterous.com/javascript-code-snippet-how-to-detect-all-mob

Deploying JDBC driver in JBoss Maven plugin

With release of jboss-as maven plugin version 7.0.1.Final the new goal deploy-artifact is added. It allows to deploy additional artifacts to JBoss server, like JDBC DB driver JAR.

After trying do deploy JDBC driver with jboss-as maven plugin using following code in pom.xml I received the NullPointerException in Maven build.

Here is part of maven pom.xml used to deploy the driver to jboss:

					<plugin>
						<groupId>org.jboss.as.plugins</groupId>
						<artifactId>jboss-as-maven-plugin</artifactId>
						<version>7.0.1.Final</version>
						<executions>
							<execution>
								<id>deploy-driver-AS400</id>
								<phase>package</phase>
								<goals>
									<goal>deploy-artifact</goal>
								</goals>

								<configuration>
									<hostname>${jboss-hostname}</hostname>
									<groupId>com.ibm</groupId>
									<artifactId>as400</artifactId>
									<fileName>jt400.jar</fileName>
									<name>jt400.jar</name>
								</configuration>
							</execution>

						</executions>
					</plugin>

 

In the plugin’s manual at http://docs.jboss.org/jbossas/7/plugins/maven/latest/examples/deployment-example.html it said

The artifact must be already listed as a dependency in the projects pom.xml

but it do not tells in which scope the driver’s JAR have to be: it have to be in compile or runtime scope in order to work. I had it in provided scope. After changing the scope of dependency it works fine.
Continue reading

Enums in JSF

Finally I find the way to use enum with JSF:
Suppose you have enum:

public enum ActionTypeEnum {
	UPDATE,
	CREATE,
	COPY
}

In JSF you can write EL expressions in form:

<h:outputText value="someText"
	rendered="#{listAction.actionType == 'COPY'}"/>

The property actionType in managed bean listAction have to be of type ActionTypeEnum.

It works because each enum in have method valueOf(String stringValue) and JSF attempts to convert String value to enum.