Saturday, 23 July 2016

How to override liferay class using ext

Ext also known as extension environment in Liferay is used to extend the functionality of Liferay. With the help of liferay extension environment we can modify liferay's core classes. Classes which are available under portal-impl.jar file are liferay's core classes and extending those classes in liferay is possible only with the help of "ext" environment.

There are very few simple steps to write code / extend liferay's core functionality.

Here are the 5 main steps:

1) Create a project in liferay's ext environment
2) Create {CustomName}LocalServiceImpl class extending {Entity}LocalServiceImpl class
3) Register newly created class as a spring bean 
4) Build and deploy
5) Test

Here are the detailed steps:

1) Create a project in liferay's ext environment

Just like any other plug in ext project can be created with "create" command from command prompt. To create a project called "myext" execute below command from "..\liferay-plugins-sdk-{version}\ext" package structure:

F:\..\liferay-plugins-sdk-{version}\ext>create myext "myext"

Once ext project called "myext-ext" is successfuly created you will get to see below message in a command prompt:

BUILD SUCCESSFUL

Total time: 35.173 secs

You can import "myext-ext" project into your eclipse / LR developer studio.

        By default package structure of ext project would look like this:



2) Create {CustomName}LocalServiceImpl class extending {Entity}LocalServiceImpl class

Depending on the requirement we may extend class available in portal-impl.jar file. In our example we will override "UserLocalServiceImpl" class to modify "authenticateByEmailAddress" method.

Lets create a class called "MyUserLocalServiceImpl" extending "UserLocalServiceImpl" class and add it under location "F:\..\liferay-plugins-sdk-{version}\ext\myext-ext\docroot\WEB-INF\ext-impl\src\com\liferay\portal\service\ext\impl".

Here is how "MyUserLocalServiceImpl" class look a like:

public class MyUserLocalServiceImpl extends UserLocalServiceImpl {

public int authenticateByEmailAddress(
long companyId, String emailAddress, String password,
Map<String, String[]> headerMap, Map<String, String[]> parameterMap,
Map<String, Object> resultsMap)
throws PortalException, SystemException {

System.out.println("Inside authenticateByEmailAddress");

return authenticate(
companyId, emailAddress, password, CompanyConstants.AUTH_TYPE_EA,
headerMap, parameterMap, resultsMap);
}

}



Note:
1) Since we need to override "authenticateByEmailAddress" method only hence only that method we will copy and paste into "MyUserLocalServiceImpl" class. Rest all the methods will work/inherit of "UserLocalServiceImpl" class only.


Hence after adding new class our project structure look like this:




3) Register newly created class as a spring bean 

So far we have created a new custom class and overridden a functionality of liferay's default class but liferay will not consider that class until we register MyUserLocalServiceImpl class as a spring bean.

To register newly created class as a spring bean we have to create a file called "ext-spring.xml" under the META-INF folder.

We need to create a "META-INF" folder under the location "F:\..\liferay-plugins-sdk-{version}\ext\myext-ext\docroot\WEB-INF\ext-impl\src" and need to add a file called "ext-spring.xml" inside it.

We need to add below entry inside "ext-spring.xml" file:

<?xml version="1.0"?>

<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
default-destroy-method="destroy"
default-init-method="afterPropertiesSet"
xsi:schemaLocation="http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean
id="com.liferay.portal.service.UserLocalService"
class="com.liferay.portal.service.ext.impl.MyUserLocalServiceImpl" />
</beans>

Above entry specifies the location of the newly created class and registers it as a spring bean.


Note:
1) ext-spring.xml is the default name used by liferay to register new classes as a spring bean. For more details please refer portal.properties -> spring.config property.
2) Default bean entry of the "UserLocalService" class is available under "portal-spring.xml" file. Kindly refer source file for more details/references of other spring beans/classes.

Here is how folder structure look like after adding "ext-spring.xml" file:



4) Build and deploy

Changes related to newly created class is all done now we have to build a war file for ext plugins. Here is the command we need to fire to build a war file of "myext-ext" project.

E:\liferay-plugins-sdk-{version}\ext\myext-ext>ant direct-deploy

Once above command is successfully executed a .war file will be created and placed under "dist" folder. you can copy "myext-ext.war" file and put it in "deploy" folder of liferay bundle.

If your server is already started you may get to see a message that "myext-ext" is successfully deployed you need to restart your server to see the changes of ext environment.

5) Test

After server is restarted just do a login to liferay portal with the help of emailAddress and password and in console you will see a message that "Inside authenticateByEmailAddress" method which is our custom message we placed in authenticateByEmailAddress" method.

We are done!

Cheers!
Henal Saraiya

Friday, 22 July 2016

Use log4j to log content on remote server

Many a times there is a requirement that we need to check the log details of a java program running on a different machines. Most of the time we are dependent on the availability of the machine where java program is running to see and understand actual error (to get local log file, generated on a specific machine). Instead of doing that we can use log4j to capture the live logs generated by java application running on different machines. That way we can independently monitor logs instead of waiting for someone to send us a local log file generated locally.

To achieve remote logging we can make use of "org.apache.log4j.net.SimpleSocketServer" class of log4j.

Here are the steps we need to follow to perform remote logging.

1) Add log4j dependency in pom.xml file
2) Configure log4j-server.properties
3) Start "SimpleSocketServer" to listen remote logging events
4) Configure log4j.properties at client program to specify where log events should be captured
5) Use "org.apache.log4j.Logger" class to add logs
6) Run and verify output

Here are the steps in detail to capture logs on remote server:

1) Add log4j dependency in pom.xml file

In our example we are going to make use of log4j-1.2.17.jar file to start our "SimpleSocketServer" server.

Since our demo application we are going to create in maven we will use below dependency in pom.xml file to automatically download log4j-1.2.17.jar file.

<dependency> 
<groupId>log4j</groupId> 
<artifactId>log4j</artifactId> 
<version>1.2.17</version> 
</dependency>

If you like to use ant to build your project then manually you can download log4j-1.2.17.jar file and add jar file in project's build path.

2) Configure log4j-server.properties

When we start our "SimpleSocketServer" we need to inform to "SimpleSocketServer" that how socket server should store the log events.

In our application log4j-server.properties file looks like this:

#Two arguments we need to specify in below line 1st logger level (DEBUG) in our case, 2nd appender name via which we configure other properties in our case (file)
log4j.rootLogger=DEBUG, file

#Define how the socket server should store the log events
log4j.appender.file=org.apache.log4j.RollingFileAppender

#Here is the name of the file which will capture the log events
log4j.appender.file.File=centralized-error.log

#Below two properties specifies that what should be the maximum size of a log file and up to how many files we need to capture
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=10

#Below properties are used to specify the format of the log events.
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSSS} %p %C %x = %m%n
#log4j.appender.file.layout.ConversionPattern=[%d] [%t] [%m]%n


3) Start "SimpleSocketServer" to listen remote logging events

Now, we can start our "SimpleSocketServer" with few input to it. Below are the inputs we need to provide:

1) Port number : 4713 (in our example)
2) configuration file to "SimpleSocketServer" : log4j-server.properties (in our example)

To start "SimpleSocketServer" we need to put "log4j-1.2.17.jar" and "log4j-server.properties" to a project directory called "C:/remoteloggingexample".

Now we can start our "SimpleSocketServer" as below:

C:/remoteloggingexample>java -classpath log4j-1.2.17.jar org.apache.log4j.net.SimpleSocketServer 4713 log4j-server.properties

4) Configure log4j.properties at client program to specify where log events should be captured

So far we are all set with the server side setup. Now, we need to configure properties at the client side / in our java application.

#Define the log4j configuration for local application
log4j.rootLogger=ALL, server

#We will use socket appender
log4j.appender.server=org.apache.log4j.net.SocketAppender

#Port where socket server will be listening for the log events
log4j.appender.server.Port=4713

#Host name or IP address of socket server
log4j.appender.server.RemoteHost=192.168.10.132


5) Use "org.apache.log4j.Logger" class to add logs

Lets create a dummy program to verify that events are being captured in the log file or not.

import org.apache.log4j.Logger;
import org.apache.log4j.NDC;
import org.apache.log4j.PropertyConfigurator;

public class StudentDemo {

static Logger logger = Logger.getLogger(StudentDemo.class);

static {
PropertyConfigurator.configure("log4j.properties");
}

public static void main(String[] args) {
// These logs will be sent to socket server as configured in log4j.properties
NDC.push("student1 : " + args[0]);
for (int i = 1; i <= 10; i++) {
logger.debug("Student loop started \t" + i);
}
}

}

Note: NDC.push("student1 : " + args[0]); is used to uniquely identify the details of the java application. Since at remote location log file is going to be common across all the places where our java application runs. NDC.push statement helps us to filter the log records.

6) Run and verify output

Once we run the client program we can see that all the logs are being stored on the remote server in our case "192.168.10.132" and a file named "centralized-error.log" will be generated on the remote server which we specified in the "log4j-server.properties" server property file. All the logs which are logged by client program i.e. "StudentDemo" program in our case would be available in "centralized-error.log" file.

We are done!

Cheers!
Henal Saraiya