Toturial of Creating JSON Web Service using Service Builder

Technical Blogs 6. Februar 2017 Von Neil Jin Staff

This article talks about how to create json web service based on Service Builder Service.
Knowledge: Service Builder
JSON Web Service
When we intent to make a service to be a web provider to serve json web service, we can utilize Service Builder to build our json web service.
We have an article to talk about how to use Service Builder to build.
I need to build a trading track system to record monthly trading.
I need to build a web service to pass data as JSON of my bank's monthly trading.
(Eventhough in real world the monthly data normally from a query rather than being save as a record, but let's make it like this)
Step 1, Define Your Entity and Service.
So first I need to build an entity and services from Service Builder. Please check Creating Service Builder MVC Portlet in Liferay 7 with Liferay IDE 3 blog as a guidance.
I call my project "monthly-trading".
Define our entity as following service.xml. Note that I set my remote-service to be "true".
<?xml version="1.0"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 7.0.0//EN" "">

<service-builder package-path="">
    <entity local-service="true" name="MonthlyTrading" remote-service="true" uuid="true">
        <!-- PK fields -->
        <column name="monthlyTradingId" primary="true" type="long" />

        <!-- Group instance -->
        <column name="groupId" type="long" />

       <!-- Audit fields -->
        <column name="companyId" type="long" />
        <column name="userId" type="long" />
        <column name="userName" type="String" />
        <column name="createDate" type="Date" />
        <column name="modifiedDate" type="Date" />

        <!-- Other fields -->
        <column name="year" type="int" />
        <column name="month" type="int" />
        <column name="volume" type="int" />

        <!-- Order -->
        <order by="asc">
            <order-column name="month" />

        <!-- Finder methods -->

        <finder name="Year" return-type="Collection">
           <finder-column name="year" />

Step 2 Build Your Service
Once you have finished, you can run buildService to build your service.
After all interfaces and impl has been generated, you can modify your LocalServiceImpl to add your own local service implementation.
In my example I simply added an add method in MonthlyTradingLocalServiceImpl ignoring all validation.
public MonthlyTrading addMonthlyTrading(int year, int month, int volume) {

    long pk = counterLocalService.increment();
    MonthlyTrading monthlyTrading = monthlyTradingPersistence.create(pk);

    return monthlyTradingPersistence.update(monthlyTrading);


 public List<MonthlyTrading> getMonthlyTradingByYear(int year) {
    return monthlyTradingPersistence.findByYear(year);
Run buildService again to regenerate interfaces.
Now I can modify my ServiceImpl to call my LocalService.
 public MonthlyTrading addMonthlyTrading(int year, int month, int volume) {

    return monthlyTradingLocalService.addMonthlyTrading(year, month, volume);

 public List<MonthlyTrading> getMonthlyTradingByYear(int year) {
    return monthlyTradingLocalService.getMonthlyTradingByYear(year);
Run buildService again and deploy.
By utilizing @JSONWebService Annotation, you can make you class to be whitelist/blacklist based, you can enable/igore a method in JSON web service.
For more detail please check Liferay Dev KB.
Best Practice tips:
It's a good practice to check user permission in Service Impl to make sure all remote service call is secure.
Step 3, Use Your Remote Service
Now you can navigate to http://localhost:8080/api/jsonws in your browser.
Choose "banking" in Context name.
Now the custom json web service is in the list.
You can find JavaScript Example, curl Example, URL Example after you invoke the service.
This is how we add a JSON web service through service builder.
Hope you enjoy it.
Workable jars for test: API, Service

Liferay DXP(EE) Only, Visualize your workflow.

Technical Blogs 24. Januar 2017 Von Neil Jin Staff

As a supervisor of my department, some times I want to check how much progress of a work is done, who is working on a certain trading? who is reviewing a loan application? If a new business plan application is getting stuck for 2 weeks who is in charge of that? I want more information than a simple word pending...


With workflow out of the box, you can't review any certain workflow's execution log without being assigned. However, the workflow has all the data you need, just need a little effort to achieve it.


In Liferay DXP there's a plugin called Kaleo Designer. You can design your workflow on a graphic panel by dragging and dropping nodes like tasks, states, conditions and transitions conveniently.

I see this plugin more than a designer, it can actually display any workflow definition. With integrating with log it can highlight the nodes with different color for different information. So you can display your workflow for each workflow instance like this:

Right now this kind of customization can only be done on Liferay DXP(EE).

If you are a DXP subscription client, please feel free to contact me, I would donate my solution for free.

Tutorial of using Chart.js in Soy Portlet

Technical Blogs 12. Januar 2017 Von Neil Jin Staff

The purpose of this tutorial is to let you know how to involve a 3rd part js lib in metal soy portlet and how to use them in ES6 script. 
In Liferay DXP we provide abundant OOTB js lib to help the development. Jquery, AlloyUI, metal, lexicon component and etc.
They are all good, however, in real world project development nothing can satisfy the requirement once for all. In this changing world, the requirement is unpredictable. 
For this we can extend our ootb library by utilizing nodeJS to get 3rd part js lib works in Liferay.
In this journey, I will lead you to take a glimpse of the power of Liferay's integrability.
    Google Closure Soy template.
    Liferay Soy Portlet.
    ECMA script 2015(ES6)
In Tutorial of Creating Soy Portlet article, we have learned how to create a metal soy portlet by Liferay IDE. 
Based on that, we are going to do a further development -- to create a Chart. (In my professional, many business requirement needs some sense of chart, this kind of requirement is very common.)
To build a chart js lib from scratch is not realistic. By utilizing Chart.js or other product on the market would help us to reduce develop effort and save project cost.
So let's start working on a portlet that display a chart.
Step 1, Create a soy portlet.
Create a Soy portlet as it shows in Tutorial of Creating Soy Portlet article with this file structure:
Step 2, ChartJS dependencies
To use ChartJS, we need to add NodeJS dependencies in package.json. Optionally you could manually copy a Chart.js bundle to your project path.
"chart.js": "^2.4.0"
Step 3, include it to your runtime environment.
Because Chart.js is a 3rd part js lib, it doesn't exist in the system(suppose it's not been used before). We have to involve it in our runtime -- in the jar.
So let's config our bnd.bnd to include the js to the right place. Add/Modify the following code to your bnd.bnd
Include-Resource: package.json,\
    META-INF/resources/js/Chart.js = node_modules/chart.js/dist/Chart.js
This means, I want a Chart.js in META-INF/resources/js of the jar, and the source is from node_modules/chart.js/dist/Chart.js
And add web context path in bnd.bnd
Web-ContextPath: /chartjs-soy
Step 4, Work with soy template
We are going to add a canvas for chart to use. So let's add the following code to
{namespace ChartjsSoy}
 * Display Chart canvas
{template .render}
    <canvas id="chartjs-soy">
Step 5, import your Chart.js in your Component
I believe all audience of this blog know how to use ChartJS in a traditional way, in html and javascript like this:
<script src="Chart.js"></script>
    var myChart = new Chart({...})
But this is not going to be our approach. We are going to take advantage ES6 to use Chart, to extend our knowledge :)
So add the import in in the head
import Chart from 'chartjs-soy/js/Chart';
You can see chartjs-soy is the web context path we define in bnd.bnd, and js/Chart is our web resource folder.
Step 6, create a chart with Chart.js
Add the following method to your Component
     * Create Chart
     * @protected
    createChart_() {
        let chartcanvas = document.getElementById("chartjs-soy");

        let data = {
            labels: ["January", "February", "March", "April", "May", "June", "July"],
            datasets: [
                    label: "My First dataset",
                    backgroundColor: [
                        'rgba(255, 99, 132, 0.2)',
                        'rgba(54, 162, 235, 0.2)',
                        'rgba(255, 206, 86, 0.2)',
                        'rgba(75, 192, 192, 0.2)',
                        'rgba(153, 102, 255, 0.2)',
                        'rgba(255, 159, 64, 0.2)'
                    borderColor: [
                        'rgba(54, 162, 235, 1)',
                        'rgba(255, 206, 86, 1)',
                        'rgba(75, 192, 192, 1)',
                        'rgba(153, 102, 255, 1)',
                        'rgba(255, 159, 64, 1)'
                    borderWidth: 1,
                    data: [65, 59, 80, 81, 56, 55, 40],

        let options = {
            scales: {
                xAxes: [{
                    stacked: true
                yAxes: [{
                    stacked: true

        let myBarChart = new Chart(chartcanvas, {
            type: 'bar',
            data: data,
            options: options

Step 7, constructor method to call
Let's add a constructor method in our component to call this createChart method
    constructor(opt_config) {


Step 8, compile, deploy, test, debug and play around
If you have done it correctly, you would have a chart sheet like mine.
You can see how easy we can involve a 3rd part js lib in our project to improve our UX. Don't be limited with only 1 js lib, play around and test! The only limitation is your imagination. 
Hope you enjoy it.
Attachment is a compiled jar for you to deploy and test.
For full workable source code please visit
Special Thanks to our Principal UI Engineer Chema Balsas, with his help I can get this tutorial done. Thanks Chema for many precious recommendations and help.

Tutorial of creating soy portlet

Technical Blogs 10. Januar 2017 Von Neil Jin Staff

In Liferay DXP it supports soy template with portlet. Through soy template we can take advantage of Google Closure to build rich web application.
In next sections I am going to lead you in to a journey of Soy portlet.
Tools we use: 
    Liferay IDE 3.1. -- Super awsome official tool to help us to create project and run gradle task.
    Atom Editor. -- Professional UI development tool.(Or you can use whatever you are familiar tool)
    Git Bash -- Reversion control and windows - Linus EOL/EOF convert.
    Familiar with Liferay OSGi portlet module development. If you are a stranger of this, please check this blog to learn how to create a mvc portlet.
    Familiar with NodeJS development.
    Familiar with Git
Step 1 Portlet Creation with soy frame work
Let's create a mvc portlet module. In my case I call my project sample-soy.
The difference between soy portlet and mvc portlet class is soy portlet class extends SoyPortlet which is a child class of MVCPortlet.
Modify the super class to be SoyPortlet
So you need to add the dependency to build.gradle correspondingly.
compileOnly group: "com.liferay", name: "", version: "3.0.0"
Make your Soy portlet component annotation like this:
    immediate = true,
    property = {
            "javax.portlet.display-name=Soy Sample Portlet",
    service = Portlet.class
According to this annotation, we will have as our defaule view. We will have SoySample.scss for our css style
Step 2 node config file
In the build tool, we utilize NodeJS to build and compile UI elements soy, metal, other js, css, and etc.
Create a package.json in project root folder with content:
    "dependencies": {
        "metal-component": "^2.4.5",
        "metal-soy": "^2.4.5"
    "devDependencies": {
        "liferay-module-config-generator": "^1.1.10",
        "metal-cli": "^1.3.1"
    "name": "soy-sample",
    "version": "1.0.0"
It's a good practice that make the version be consistent with version in bnd.bnd
Step 3 OSGi config
Add the following code to the bnd.bnd so that bnd tool knows package.json need to be in the jar file, and the OSGi bundle need soy capability.
Include-Resource: package.json
Require-Capability: soy;filter:="(type=metal)"
Step 4 Use Render method to define variables in soy template.
Add/Modify your render method in portlet class.
    public void render(
            RenderRequest renderRequest, RenderResponse renderResponse)
        throws IOException, PortletException {

        template.put("msg", "Good Job!");

        super.render(renderRequest, renderResponse);

We will have a parameter in our soy template called msg with the a string value "Good Job!"
Step 5 SoySample template
Create a file called in this path src/main/resources/META-INF/resources/resources
With the following code:
{namespace SoySample}

 * Show portlet message in the view.
 * @param id
 * @param msg
{template .render}
     <div id="{$id}">
        <div class="form-group">
            <div class="input-group">
                <span class="input-group-addon" id="inputGroupAddon01">Message:</span>
                <input aria-describedby="inputGroupAddon01" class="form-control" value="{$msg}" type="text">

        <div class="form-group">
            <div class="input-group">
                <input aria-describedby="inputGroupAddon02" class="form-control" placeholder="Recipient's username, ex. neil.jin ;)" type="text">
                <span class="input-group-addon" id="inputGroupAddon02"></span>

        <div class="form-group">
            <div class="input-group">
                <span class="input-group-addon">$</span>
                <input aria-label="Amount" class="form-control" type="text">
                <span class="input-group-addon">.00</span>

        <div class="form-group">
            <button class="btn btn-default" type="button">Donate</button>
Closure require the template's declaration in java-doc like comment. So the comment about the {templlate .render} is required. All the param has to be used in the template.
And {template .render} has to be the beginning or the line without and space characters.
Everytime you change the soy file, it's better to run build soy gradle task to generate soy.js
In my template I utilized Lexicon to build my UX.
In the same folder, create a file called SoySample.scss, just leave it blank.
Run buildSoy gradle command.
Step 6 test and check.
Now you can build your module and deploy the jar to your Liferay server and test.
If everything is correct you are suppose to see this view from your portlet.
When you build, the task tries to download necessary node_modules, it might take a while(about 100m). 
Sometimes it get stuck on npmInstall, you can stop the process and build again, it will work.
Step 7 javascript actions
I am going to add javascript to my soy template.
Create a file called with the same folder of with the following code:
import Component from 'metal-component/src/Component';
import core from 'metal/src/core';
import dom from 'metal-dom/src/dom';
import Soy from 'metal-soy/src/Soy';
import templates from './';

class SoySample extends Component {


//Register component
Soy.register(SoySample, templates);

export default SoySample;
Our js framework is done.
Step 8 add javascript method
We are going to create a donate method to send out a message to our js concole.
Add the following method to the class SoySample
     * donate to neil
     * @param  {MouseEvent} event
    donate(event) {
        console.log("donate successful");
Step 9, use method
We will make this method be executed when user click donate button, so add data-onclick="donate" attribute to donate button.
It will look like this:
<button class="btn btn-default" data-onclick="donate" type="button">Donate</button>
After you have done everything, you can start build and deploy your project. When you click on donate button, your browser console should show  a message "donate successful"
Because of an issue of soy compiler, the compiler can't identify the end of line and end of file character.
We have to manually convert CRLF to LF. On windows you may meet js error "TypeError: fn is not a function" in the browser.
To solve this we are going to utilize git.
You can create a file in your project's git repo named  .gitattributes, with the following content:
*.pom text eol=lf
*.soy text eol=lf
ivy.xml text eol=lf
After you have added your work to the index and commit, the CRLF should be automatically converted to LF.
You can also take advantage of IDE 3.1 with AnyEdit eclipse plugin. (Thanks @Greg for the info)
You can also utilize Atom Editor with line-ending-selector
Hope you enjoy.
The attachment is a compiled soy portlet jar, you can downlad and deploy to your portal for a test.

繁体译文 OSGi Module依赖

Company Blogs 7. Januar 2017 Von Neil Jin Staff

原文作者:David H Nebinger




假設在Liferay DXP平臺上開發module的時候,遇見了需要運行環境(runtime)的依賴的時候,該怎麼辦?




方法 1 - 放在全域(global)目錄下


這種方法最簡單也最粗暴。所有在全域類載入器(比如tomcat的lib和lib/ext)中的類可以被所有類訪問,包括Liferay OSGi容器。



方法 2 - 讓OSGi處理



當你確認OSGi中依賴可以被使用的時候,這個方法就可以使用。因為這個方法是利用portal中已有的依賴,或者你之前已經部署到OSGi容器中的依賴(有些jar可能已經包含了OSGi bundle的資訊,可以直接部署到容器中)。



runtime group: 'com.iowagie', name: 'itext', version: '1.4.8'

如果iText(和其依賴)已經成功的部署到OSGi容器中,通過運行環境依賴聲明就可以在你自己的module中使用了。如果iText不可用,你的module就不會啟動,並且會報錯 -- 依賴無法滿足。


方法 3 - 製作成一個超級胖子Module


像那些巨型jar一樣,巨型module會擁有所有依賴的類直接暴露在module jar中。




Include-Resource: @itext-1.4.8.jar


要注意的是,也需要引入依賴所以依賴的jar。例如,iText2.08依賴於BouncyCasle mail和prov,所以這些依賴也需要添加:

Include-Resource: @itext-2.0.8.jar,@bcmail-138.jar,@bcprov-138.jar


如果使用zip工具打開module jar包的話,會看見所有依賴的jar會被解壓,class檔會被直接放在module jar包中。


方法 4 - 在Module中引入整個jar


最後的方法是在module中引入jar,和巨型module不一樣,這個方法將整個jar包含到module jar中



首先需要定義Bundle-ClassPath屬性來引入module jar和其餘的依賴jar。在下面的例子中,我指定iText jar會包含在module jar中:












全域方法適用於只需要一種版本的jar包並且需要用到大量的依賴的情況。例如,專案中有20個不同的module全部依賴於iText 1.4.8,這樣,全域方法就是最好的選擇。

第二種方法只用於依賴jar是OSGi bundle的情況。在這種情況下,你可以使用不同的版本,並且不用擔心去編輯bnd檔。











例如在我使用google-gwt servlet jar的時候。我需要導入這個包:






Import-Package: \


译文:OSGi Module依赖

Technical Blogs 7. Januar 2017 Von Neil Jin Staff

原文作者:David H Nebinger




假设在Liferay DXP平台上开发module的时候,遇见了需要运行环境(runtime)的依赖的时候,该怎么办?




方法 1 - 放在全局目录下

这种方法最简单也最粗暴。所有在全局类加载器(比如tomcat的lib和lib/ext)中的类可以被所有类访问,包括Liferay OSGi容器。



方法 2 - 让OSGi处理



当你确认OSGi中依赖可以被使用的时候,这个方法就可以使用。因为这个方法是利用portal中已有的依赖,或者你之前已经部署到OSGi容器中的依赖(有些jar可能已经包含了OSGi bundle的信息,可以直接部署到容器中)。




runtime group: 'com.iowagie', name: 'itext', version: '1.4.8'


如果iText(和其依赖)已经成功的部署到OSGi容器中,通过运行环境依赖声明就可以在你自己的module中使用了。如果iText不可用,你的module就不会启动,并且会报错 -- 依赖无法满足。


方法 3 - 制作成一个超级胖子Module


像那些巨型jar一样,巨型module会拥有所有依赖的类直接暴露在module jar中。




Include-Resource: @itext-1.4.8.jar


要注意的是,也需要引入依赖所以依赖的jar。例如,iText2.08依赖于BouncyCasle mail和prov,所以这些依赖也需要添加:

Include-Resource: @itext-2.0.8.jar,@bcmail-138.jar,@bcprov-138.jar


如果使用zip工具打开module jar包的话,会看见所有依赖的jar会被解压,class文件会被直接放在module jar包中。


方法 4 - 在Module中引入整个jar


最后的方法是在module中引入jar,和巨型module不一样,这个方法将整个jar包含到module jar中



首先需要定义Bundle-ClassPath属性来引入module jar和其余的依赖jar。在下面的例子中,我指定iText jar会包含在module jar中:












全局方法适用于只需要一种版本的jar包并且需要用到大量的依赖的情况。例如,项目中有20个不同的module全部依赖于iText 1.4.8,这样,全局方法就是最好的选择。

第二种方法只用于依赖jar是OSGi bundle的情况。在这种情况下,你可以使用不同的版本,并且不用担心去编辑bnd文件。









例如在我使用google-gwt servlet jar的时候。我需要导入这个包:


Import-Package: \



Company Blogs 16. Dezember 2016 Von Neil Jin Staff

本文在于讨论如何在Liferay DXP(Liferay 7)中使用公共参数(public render parameter)支持IPC。





     声明之后,就可以像使用普通render parameter一样使用这个参数了。


    <portlet:renderURL var="senderURL">

        <portlet:param name="message" value="Prove you can receive me" />

Making portlet support public render parameter in DXP

Company Blogs 13. Dezember 2016 Von Neil Jin Staff

This is the way how to make IPC through public render parameter in Liferay DXP(Lfieray 7).

Key point:

     You need to add a supported render parameter in all portlet's Component Annotation.


     The "message" is the public render parameter name.

    After you have done this you can use this parameter as a regular render parameter in the portlet. 

    In your link you can add the parameter like this:

    <portlet:renderURL var="senderURL">

        <portlet:param name="message" value="Prove you can receive me" />
    You can receive the value like this:
Please check the attachment for a functional project.
After the module is installed you can find the portlets under Sample category.


Company Blogs 3. Juni 2016 Von Neil Jin Staff

业内知名门户(Portal)软件商Liferay已经将新产品重命名为DXP - 数字体验平台,剑指数字体验市场。


这种转变其实并不是Liferay特有的,它更像是一种自然演化。“传统portal中的功能,已无法满足当今客户的功能需求,在Liferay平台上我们需要持续开发和整合这些需求功能。所以基于这些转变,我们需要重定义我们在市场中的位置。”Liferay首席执行官Bryan Cheung如是说。





什么是Liferay DXP


Liferay DXP是由一些不同的组件构成。核心部分是portal平台本身。其次是整合的内置后台系统包括CRM、ERP、技术支持和其他系统。整合平台使用的是微服务架构,使其定制化与整合客户需求功能更加方便。


Chueng说,对于企业来说,深度数字化意味着经营方式的转变。Liferay DXP可以深入到业务流程处理,以助力客户系统设计与模块整合,使其可以重新配置和部署必要的商业服务。




基于Liferay DXP平台,可以轻松的创建网页站点、登录页面、移动站点、移动应用和表单等。亦可创建定向受众体验和为市场客户支持和服务定制个性化体验。


Liferay DPX 平台还有两个组件年内问世,营销活动管理(Campaign Management)和单一客户视图(Single View of Customer, SVOC)。SVOC组件可以用来定制化体验,它能够将零散的信息整理重构,然后集中显示。营销活动管理组件使市场营销人员通过一些活动,如促销活动、客户支持或服务,以提高目标客户的参与度。










Liferay DXP采用订阅服务以及混合式结局方案(例如,云服务中的营销活动管理和SVOC)的销售方式。在未来Liferay也会寻机提供更多的管理服务。










Liferay DXP数字体验平台,荣耀绽放:端对端的客户体验

Company Blogs 24. Mai 2016 Von Neil Jin Staff

知名软件厂商Liferay,已经宣布Lifeay Digital Experience Platform(DXP)的面世。





Liferay的产品副总裁Edwin Chueng,阐释了这一观点:
“当今的商务市场,早就超越了仅将目光放在用户的体验这一层面。Liferay DXP通过革命性的创新体验,帮助企业博得客户信心,赢得竞标,开创更多销售机遇。在深度整合的商务流程系统中,企业可以全面掌握客户偏好和历史事件,及时为客户提供优质资源及建议。”

Liferay DXP:惊鸿一瞥

Liferay DXP平台上,销售、市场,客户支持与服务团队可以真正的为创造优质的客户体验而协作。Liferay可以汇集不同的系统中的信息,以更全面的方式在平台上展现出来借以优化客户互动体验,整合企业运维流程。

Liferay DXP也可以促进合作伙伴和员工与客户间的交流,借以缔造更佳的企业印象。
  • 弹性架构 - 以变应变:通过高复用性的微服务与高整合性的应用,Liferay可以快速实现数字策略以适应不断更新的尖端技术。

  • 体验管理:基于精准的大数据信息,从促销活动一直到客户服务处理,每一个直接面对客户的商务环节都可以量身打造。

  • 单一客户视图(SVC):基于企业中不同的数据采集点,例如:客户情绪、兴趣点或重要对话,Liferay DXP将所有信息糅合一体,为企业提供单一的客户资料视图。

  • 定向数据:通过使客户访问视频、推广链接、社区活动与社交资料来丰富客户体验。

  • 与新兴技术对接:DXP设计初衷就是为了支持新兴的移动前段框架。这些框架被众多移动前段开发者钟爱和广泛应用。



  1. 真正涵盖(促成)客户旅程:秉承Liferay portal的一贯作风,Liferay DXP更加的适应整个的客户生命周期。生命周期通常是指客户从观望(网站匿名访客)到转变成客户(注册)再到拥护(在客户门户与客户服务系统中)的过程。其他关注CMS产品通常只关注在客户的观望周期。基于Liferay以往的丰富经验,Lifeay在银行业的地位独一无二。

  2. 岂止于客户,广泛的目标受众:除了客户之外,Liferay DXP也是为企业合作伙伴和员工设计的。设计的初衷是为了确保Liferay DXP能够胜任于企业内部协作和效率优化。

  3. 完善的商务平台:Liferay DXP缔造了丰富的协作功能,基于整合后台系统,将更多独立业务部门的信息整理有效利用。

更多信息,请访问Liferay DXP官网
原文作者:Kaya Ismail

使用Liferay IDE 3.0创建支持Service Builder的portlet module

Technical Blogs 4. Mai 2016 Von Neil Jin Staff

Liferay IDE 下载页面:

Liferay IDE 3针对Liferay 7.0(Liferay DXP)做了很多的改变。其中一项就是在Liferay Blade,Eclipse Gradle和BND tool的基础上完成了创建OSGi module portlle的向导,通过向导就可以简单的创建出portlet module了。在我的客户和培训学院中,所有的技术人员在开始最关心的问题都是在Liferay 7中,怎么创建一个使用Service Builder的MVC portlet。在Liferay IDE 3中,实现起来其实非常简单。


首先需要创建一个Liferay Workspace项目.

Liferay Workspace更像是一个Liferay项目的容器,而不是一个类似于eclipse workspace的工作空间。在Liferay Workspace中可以开发和管理Liferay插件。

接下来点击File -> New -> Liferay Module Project,然后选择servicebuilder为项目模板。

在Liferay Workspace视图模式下

我们可以看到在新建service builder的项目下有两个gradle项目。API项目包括了各种接口和服务定义。在6.2的项目中这些文件通常是在service文件夹下。Service项目中包含了服务的具体实现源码和服务配置。在6.2中是在src文件夹下面。

在service项目中,包含了service.xml。可以根据需求改变。接下来就可以在Gradle Task窗口中,运行service项目中的buildservice任务了,这样就可以生成所需的服务了。



我们可以创建一个mvc portlet类型的Liferay Module Project。然后修改build.gradle文件。按照我们文章中的示例,我们可以添加下列代码来声明依赖。

compile project(":modules:service-builder-project:service-builder-project-api")

这样在mvc portlet项目中就可以引用服务了。如果IDE的编辑器依然报告有依赖的错误,可以右键点击项目,然后gradle -> refreash gradle project,这样编辑器就会重新计算依赖了。

在部署项目的时候,需要部署三个jar,分别是api,service和mvc portlet。

Creating service builder mvc portlet in Liferay 7 with Liferay IDE 3.

Company Blogs 26. April 2016 Von Neil Jin Staff

Liferay IDE Download Page:

Liferay IDE 3 has massive change regarding to Liferay 7.0(Liferay DXP). One of the change is it streamlines the creation of OSGi module portlet by utilizing Liferay Blade tool, gradle elipse plugin and bnd tool.

One of the most concerned question by the Liferay developers is how we develop Service Builder with MVC portlet in new Liferay 7 development pattern.

With Liferay IDE 3 it's quite easy!

First we need to create a Liferay Space project.

A Liferay Workspace is a project container for Liferay porjects, within the Liferay workspace, we can develop and manage Liferay plugins.

And next we need to create a new Module Project by clicking File -> New -> Liferay Module Project, and choose the Project Template Name servicebuilder.

In the Liferay Workspace perspective 

We can see there are 2 gradle project under the service builder project. API project containse the service definition and interfaces, as the service path folder in 6.2. Service project contains the actual implementation of the service, as the service code in src folder in 6.2.

It contains a default service.xml. You can change the xml according to your need. After that, you can run buildservice task of service-builder-project-service in Gradle Task Window. 

After running buildService then run build task, the service will be ready to use.


How can we reference this service module in a mvc portlet module?

We use gradle to manage dependency.

We can create a mvc portlet Liferay Module Project. And modify build.gradle file. In our case we can add the following code to the dependency declaration.

compile project(":modules:service-builder-project:service-builder-project-api")

The service will be available to the mvc portlet. If the editor still complain about the dependency you can right click to the project -> gradle -> refreash gradle project to refreash the project.

When you deploy the project, remember to deploy 3 jars for api, service and mvc portlet.


You can download an example project here.

Modify existing datepicker that has been existed in the page.

Technical Blogs 5. November 2015 Von Neil Jin Staff

It's very convient to use <liferay-ui:input-date> tag to create a date field. But this just provide very basic use of aui date picker. And attributes doesn't cover all options of date picker. This makes me have to create my own aui date picker through javascript. But using <liferay-ui:input-date> can save me a lot of time. So I am going to introduce a practical way to apply advanced options to the date picker that has been generated by input-date tag. Actually you can use the same method to modify any date picker.

The component name of the date picker that is generated by tag is NAMESPACE+FIELD_NAME+DatePicker

For example :

Suppose we are in a portlet under name space "_PersonalInfo_WAR_PersonalInfoportlet_"

<liferay-ui:input-date name="birthday" yearParam="birthYear" yearValue="1984" monthParam="birthMonth" monthValue="01 dayParam="birthDate" dayValue="17" />

This tag generate a date picker called _PersonalInfo_WAR_PersonalInfoportlet_birthdayDatePicker

You can easiliy get the date picker's javascript object by this method:

var datePickerName = '_PersonalInfo_WAR_PersonalInfoportlet_birthdayDatePicker';

var datePicker = Liferay.component(datePickerName);

for example we want to limit the user's input range to be in last 100 days until today, you can modify the date picker's configuration

var today = new A.Date();
var calendar = datePicker.getCalendar();

if (calendar) {
    calendar.setAttrs({minimumDate:A.Date.addDays(today, -100), maximumDate:today);


Same idea if you want to change Date picker's default date format, you can change the mask. Basically if you get the date picker you can do whatever you want to do with it.



A perspective 3D Liferay desktop picture.

Company Blogs 5. März 2013 Von Neil Jin Staff

This one was designed by using minecraft screenshot! When I see this, I am always feel this grand brandy! Simple, strong, stable and enough space for imagination. 

Hope you will like it. The full picture size is 1920x1080.

If anyone wants some other size, please leave a comment, I would like to make it.



Making a remote javascript library loaded dynamically by AUI and be an AUI plugin

Company Blogs 27. Februar 2013 Von Neil Jin Staff

    In the real world project, there are times when a team develops some feature like google map, twitter, facebook that need remote javascript library between several portlets but for the same portal. There are many similiar features that needs to resuse many codes. We want out code to be consistence and easily to be managed and more importantly we don't want to waste time on mutiple people working on the same feature that ultimate increase the cost of a project and once there's a bug or requirement modification(people love to do that), that the whole team need to change their own code, which is really frustrating for the whole team.

    TWO ways we think of to solve this (actually there are more solutions):

    1. Making a shared js and code standard that everybody can read it and copy it to each own code.  This method still can not guarantee a developer that use the right code without updating their js file to the up-to-date.

    2. Making a global portlet as a dependency of the portlets. This portlet will mainly be set layout.static.portlets.all=<portletID>  in and add a <liferay-util:html-top> tag to view.jsp page that loads the remote javascript libiary , and that all pages in the portal loaded the remote javascript library.

BUT, when using code like <script src=";libraries=places" type="text/javascript"></script>, in some network environment like some companies or countries, facebook, google does not work very well, so the whole portal pages became extreamly slow to wait the until loading timeout. In my test sample, The longest time to wait a loading of facebook script on a page that does not use any feature was 11.98s and 1m 24s for loading google map script under a total refreshing page (shift + refresh).

    Why do we have to do this, load a unused script in a page in such a poor network environment? This really ruins the work of our core engineer. Core Engineers took really huge effort on decreasing some ms of loading time, but we just can simply increase seconds of loading time by adding 2 scripts. Fanstastic devil

    We don't have to do this. We can easily get rid of the features at the requirement analysis period.

    Once the features were made sure to use, we would choose a better way to handel this. At least, not let every page to load the script that is not useful.

    So We can still make a global portlet as a dependency, and load the script that we need. BUT, this script is not from the remote site directilly. This will be an aui plugin that handels the connection when we need. So that solves the problem.

For example to load twitter's script, the summary script is like this

AUI().add('aui-twitter', function(A) {

 var Lang = A.Lang,

 TWITTER = 'twitter',


 /*this version only support share button yet*/


 '<a href="" class="twitter-share-button">Tweet</a>'


 var Twitter = A.Component.create({


   ARRTS: {


   prototype: {

     initializer: function() {

       var instance = this;



    _loadScript: function() {




          onSuccess: function() {

           //alert("load twitter script success");





    createTwitterButton: function(options) {

      var instance = this;

      var button = A.Node.create(TWITTER_BUTTON);

      var dataLang = (options.dataLang != null ? options.dataLang : instance.LANG.ENGLISH);

      var dataCount = (options.dataCount != null ? options.dataCount : instance.COUNT_ALIGN.HORIZONTAL);

     var dataURL = (options.dataURL != null ? options.dataURL : '');

     if(options.largeButton) {

       button.setAttribute('data-size', 'large');


      button.setAttribute('data-lang', dataLang);

      button.setAttribute('data-count', dataCount);

      button.setAttribute('data-url', dataURL);;

      return button;


    //can add more features


      HORIZONTAL: 'horizontal',

      VERTICAL: 'vertical'


    LANG: {

      ARABIC: 'ar',

      CHINESE_SIMPLIFIED: 'zh-cn',


      DUTCH: 'nl',

      ENGLISH: 'en', //can add other languages




A.Twitter = Twitter;

}, '@VERSION@', {skinnable:false, requires:['aui-base', 'get']});
Once the script is loaded, if an aui object is not generated, the script won't be loaded.
In the jsp page can simply add code like:
<div id="<portlet:namespace />twitter-share"></div>

<aui:script use="aui-base, aui-twitter">

  AUI().use(''aui-twitter', function(A) {

    var tObj = new A.Twitter();

    var btnOpt = {

      contentBox: "#<portlet:namespace />twitter-share",
      dataLang: tObj.LANG.ENGLISH,
      dataCount: tObj.COUNT_ALIGN.VERTICAL,
      largeButton: true




If AUI can have an official plugin to support those famous libraries like google and facebook and etc it would be nicer. :)

A vertial 3D Liferay desktop

Company Blogs 23. Dezember 2011 Von Neil Jin Staff

Recently I tried to draw a 3D Liferay Logo with AI at home. Haven't been drawing since graduate. Maybe it just an okey job to draw this, but it can keep me thinking new ideas, create new things.

'Cause through a picture we can imagine a lot of stories. Like in this picture I can think of a boy encounters a girl in Liferay. And draw them out. And use that as my Valentine's Day's desktop. I mean it could be many ways to keep imagination.

Zeige 16 Ergebnisse.
Elemente pro Seite 20
von 1