Exciting New Tool: Liferay-Gen

A new developer tool can help generate Liferay code and springboard your new Liferay project.

Introduction

One of the great advantages I have being known in the community is really the opportunity to meet, talk and work with talented developers and teams.

Recently the team at ACSoftware announced in the Liferay Community Slack they were working on a new Liferay development tool, and I was fortunate enough to work with them on evaluation and testing. As it is just released, I wanted to introduce the tool because I think it can be a boon towards springboarding your next Liferay project.

Liferay-Gen is a command-line developer tool that leverages Yeoman to help the developer generate a Liferay Workspace, Service Builder modules and corresponding Liferay MVC portlet modules leveraging those services.

Installation

The primary site for the tool is https://liferay-code-generator.acsoftware.it and the Documentation page has the "How to Install" section, but basically it is a two step process after you meet the prerequisites:

  1. Use the npm install command to install the generator.
  2. At first launch, enter your license key that you'll have to download.

Basic Usage

The tool works from the command line. Let's start with the simple command to get app info:

$ yo liferay:app

██╗     ██╗███████╗███████╗██████╗  █████╗ ██╗   ██╗      ██████╗ ███████╗███╗   ██╗
██║     ██║██╔════╝██╔════╝██╔══██╗██╔══██╗╚██╗ ██╔╝     ██╔════╝ ██╔════╝████╗  ██║
██║     ██║█████╗  █████╗  ██████╔╝███████║ ╚████╔╝█████╗██║  ███╗█████╗  ██╔██╗ ██║
██║     ██║██╔══╝  ██╔══╝  ██╔══██╗██╔══██║  ╚██╔╝ ╚════╝██║   ██║██╔══╝  ██║╚██╗██║
███████╗██║██║     ███████╗██║  ██║██║  ██║   ██║        ╚██████╔╝███████╗██║ ╚████║
╚══════╝╚═╝╚═╝     ╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝         ╚═════╝ ╚══════╝╚═╝  ╚═══╝

        Powered By ACSoftware 2021 - v. 1.1.18
     info Checking for new updates....
     info Checking requirements...
     info Checking gradle installation...
✔ Gradle 6.8 Found!
     info Checking java installation...
✔ Java 1.8 Found!
     info Checking JPM installation...
✔ JPM 4 Found!
     info Checking Blade installation...
✔ Blade 4 Found!
     info Checking Node installation...
✔ Node 12.22 Found!
Reading projects from:/Users/dnebinger/liferay/Testing/modules
     info Current version is: 1.1.18 , latest version is: 1.1.18
     info No updates found!
    force .yo-rc.json
     info Task duration: 2749 ms

Another simple command to try is the help command:

$ yo liferay:help

██╗     ██╗███████╗███████╗██████╗  █████╗ ██╗   ██╗      ██████╗ ███████╗███╗   ██╗
██║     ██║██╔════╝██╔════╝██╔══██╗██╔══██╗╚██╗ ██╔╝     ██╔════╝ ██╔════╝████╗  ██║
██║     ██║█████╗  █████╗  ██████╔╝███████║ ╚████╔╝█████╗██║  ███╗█████╗  ██╔██╗ ██║
██║     ██║██╔══╝  ██╔══╝  ██╔══██╗██╔══██║  ╚██╔╝ ╚════╝██║   ██║██╔══╝  ██║╚██╗██║
███████╗██║██║     ███████╗██║  ██║██║  ██║   ██║        ╚██████╔╝███████╗██║ ╚████║
╚══════╝╚═╝╚═╝     ╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝   ╚═╝         ╚═════╝ ╚══════╝╚═╝  ╚═══╝

        Powered By ACSoftware 2021 - v. 1.1.18
✔ Data integrity check passed!
-------------------------------------------------------------------------------------
yo liferay:app
     Print info about the liferay generator
yo liferay:generate-search-classes
     Generate search classes for entities
yo liferay:help
     Show help
yo liferay:new-mvc-portlet
     Creates new MVC Portlet
yo liferay:new-service-builder
     Creates new Service Builder Project
yo liferay:new-workspace
     Creates new workspace
yo liferay:service-builder
     Add or Edit entities inside service.xml
yo liferay:service-builder-generate-classes
     Auto generate code for entities for CRUD operations and relationship management!
yo liferay:attach
     Command to be used to hook the generator to an existing workspace

Since we've seen the banner before, I'm going to exclude it going forward. It will be displayed in each command you execute, I just won't be showing them anymore here.

Workspace Usage

The generator is designed to work inside of a Liferay workspace. There are two ways to get started, first is using the new-workspace command:

$ yo liferay:new-workspace

? What is the name of your Workspace? Testing
? What is the global package of this workspace? com.example.testing
? Which version of liferay? dxp-7.3-ga1
     info Creating workspace with blade tool...
✔ Workspace creation OK!
    force .yo-rc.json
     info Creating yo.rc file...
    force Testing/.yo-rc.json
   create Testing/docker-compose-testing
     info Task duration: 32323 ms

When prompted for the version, you get to pick from the latest versions of 7.0, 7.1, 7.2 and 7.3 CE or DXP. Here I've picked DXP 7.3. You'll get a standard Liferay workspace, empty, but ready to go.

The second option is to attach to an existing workspace, basically putting the pieces in place that the generator will be able to use for executing the commands and providing the context so the generator will work without having to start the workspace over:

$ yo liferay:attach

Searching Liferay Worskpace from:/Users/dnebinger/temp/Unattached
     info settings.gradle found, Liferay Workspace identified!
? What is the global package of this workspace? com.example.unattached
? Which version of liferay? dxp-7.3-ga1
     info Creating yo.rc file...

Service Builder Usage

There's a number of commands used to work with Service Builder, so let's look at each one.

First, there's the new-service-builder task which is responsible for starting a new set of Service Builder modules:

$ yo liferay:new-service-builder

? What is the project name? Todo
? What is the Service builder namespace? Todo
? What is the package for your portlet? com.example.testing.todo
     info Creating service builder with blade tool: blade create -t service-builder -v 7.3 -p com.example.testing.todo Todo
Successfully created project Todo in /Users/dnebinger/liferay/Testing/modules
✔ Service Builder project Todo creation OK!
    force .yo-rc.json
    force modules/Todo/.yo-rc.json
   create modules/Todo/Todo-service/src/main/resources/portlet.properties
   create modules/Todo/Todo-service/src/main/resources/META-INF/resource-actions/default.xml
     info Task duration: 38008 ms
Check your Package for Portlet question; I haven't seen a default value here that I liked. It is only my opinion, of course, but as you can see it becomes the package used by Service Builder when generating classes, so your answer here is important.

Next, we can use the service-builder task to manipulate the service.xml and other files:

$ yo liferay:service-builder

Reading projects from:/Users/dnebinger/liferay/Testing/modules
     info Current version is: 1.1.18 , latest version is: 1.1.18
     info No updates found!
     info Creating entity for project:Todo
     info Reloading configuration of: Todo
     info Service XML path: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/service.xml
     info Default XML File: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/src/main/resources/META-INF/resource-actions/default.xml
     info Model Hints: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/src/main/resources/META-INF/portlet-model-hints.xml
     info Namespace:Todo
     info Found an existing service.xml, the changes will continue on that one
? Do you want to edit an existing entity or add new one? AddNew
     info Creating new entity...
? What is your entity name? Todo
? Do you want to generate standard entity ? (all audit fields with primary key <entity>Id)? Yes
? Has a local service impl? Yes
? Has a remote service impl? Yes
? Has a uuid? Yes
? Do you want to add your entity into Asset? Yes
? Do you want to add workflow fields? Yes
? Choose the workflow fields status, statusByUserId, statusByUserName, statusDate
? What kind of element you want to add? Column
? What is the column name? taskStatus
? What type is it? long
? Do you want to add validation rules or define model hints for your field? No
? Do you want to add a new column? Yes
? What is the column name? task
? What type is it? String
? Do you want to add localization to your field? No
? Do you want to add validation rules or define model hints for your field? Yes
? Which validation rules do you want to add? max-length( hint )
? What is the value of the validation rule? 4096
? Do you want to add validation rules or define model hints for your field? No
? Do you want to add a new column? Yes
? What is the column name? dueDate
? What type is it? Date
? Do you want to add validation rules or define model hints for your field? No
? Do you want to add a new column? No
? What kind of element you want to add? Finder
? Whats the finder name? TaskStatus
? What type it returns? Collection
? Whats the finder column name? taskStatus
? Do you want to add a new finder? Yes
? Whats the finder name? DueDate
? What type it returns? Collection
? Whats the finder column name? dueDate
? Do you want to add a new finder? Yes
? Whats the finder name? Owner
? What type it returns? Collection
? Whats the finder column name? userId
? Do you want to add a new finder? No
? What kind of element you want to add? Order
? Order by asc or desc? asc
? Whats your order column name? dueDate:Date
? What kind of element you want to add? Done
     info Auto-generating finder CompanyId
? Generate Services Code Now? No
    force .yo-rc.json
✔ Generated sucessfully!
     info Task duration: 724118 ms

There's a lot going on here, so let's review...

First, it's really a guided question/response cycle, so when you're adding a new column all of the questions are about the column, then you can add finders, order bys, etc.

We start by adding a new entity, Todo. We're then asked if we want the standard audit fields (these would be your company id, group id, create/modified dates, user id and user name) and whether we're adding both local and remote services as well as a UUID. We're also asked if we're going to make the entity an Asset (will require the MVC portlet generation capability for your Asset Renderer support) and whether to enable Workflow. If we say yes to Workflow, we then get to pick the workflow columns to include, I typically just add them all.

Next we can start adding Columns. For each one you're asked for a name and a type and whether you want to add validation rules or hint changes. For my first column I did taskStatus as a long value, so nothing special. For my next column I chose the String type, so it asked me if I wanted it to support localization, in this case I didn't. I also did add a model hint to set the max-length to 4096, this would have been the same if I had manually edited the model-hints.xml file to add the right <hint /> to the column and then regenerated the sources. The available list is quite extensive, so check them out when you are creating your columns.

Next I added a couple of finders, so I had to give them a name, the return type and the finder column tied to the finder.

Using a similar "interview" process, you can add references and relationships and other pieces.

I then selected "Done" as the element to end the cycle, and it asked me if I wanted to generate the services. I said "No" here so I can demonstrate the next command...

Before I do that, though, note that I can re-run this command to add a new entity or edit an existing entity, so this is not a "once and done" generator.

The next step is to build services, and there is a command to help with that:

$ yo liferay:service-builder-generate-classes

Reading projects from:/Users/dnebinger/liferay/Testing/modules
     info Current version is: 1.1.18 , latest version is: 1.1.18
     info No updates found!
     info Creating entity for project:Todo
     info Reloading configuration of: Todo
     info Service XML path: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/service.xml
     info Default XML File: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/src/main/resources/META-INF/resource-actions/default.xml
     info Model Hints: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/src/main/resources/META-INF/portlet-model-hints.xml
     info Namespace:Todo
     info Found an existing service.xml, the changes will continue on that one
     info Loading portlet-model-hints-xml
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :modules:Todo:Todo-service:buildService
Building Todo
Writing src/main/java/com/example/testing/todo/service/persistence/impl/TodoPersistenceImpl.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/persistence/TodoPersistence.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/persistence/TodoUtil.java
Writing src/main/java/com/example/testing/todo/model/impl/TodoModelImpl.java
Writing src/main/java/com/example/testing/todo/model/impl/TodoBaseImpl.java
Writing src/main/java/com/example/testing/todo/model/impl/TodoImpl.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/model/TodoModel.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/model/Todo.java
Writing src/main/java/com/example/testing/todo/model/impl/TodoCacheModel.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/model/TodoWrapper.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/model/TodoSoap.java
Writing src/main/java/com/example/testing/todo/service/impl/TodoLocalServiceImpl.java
Writing src/main/java/com/example/testing/todo/service/base/TodoLocalServiceBaseImpl.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoLocalService.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoLocalServiceUtil.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoLocalServiceWrapper.java
Writing src/main/java/com/example/testing/todo/service/impl/TodoServiceImpl.java
Writing src/main/java/com/example/testing/todo/service/base/TodoServiceBaseImpl.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoService.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoServiceUtil.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoServiceWrapper.java
Writing src/main/java/com/example/testing/todo/service/http/TodoServiceHttp.java
Writing src/main/java/com/example/testing/todo/service/http/TodoServiceSoap.java
Writing src/main/resources/META-INF/module-hbm.xml
Writing src/main/resources/META-INF/portlet-model-hints.xml
Writing ../Todo-api/src/main/java/com/example/testing/todo/exception/TodoException.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/exception/NoSuchTodoException.java
Writing src/main/java/com/example/testing/todo/service/persistence/impl/constants/TodoPersistenceConstants.java
Writing src/main/resources/META-INF/sql/indexes.sql
Writing src/main/resources/META-INF/sql/tables.sql
Writing src/main/resources/META-INF/sql/tables.sql
Writing src/main/resources/service.properties

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.6.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 9s
1 actionable task: 1 executed
✔ Build service executed succesfully, starting generation...
     info Entity has column companyId and the relative finder!
     info Entity Primary Key Getter is: getTodoId()
     info --------- GENERATING IN LOCAL SERVICE -------------
     info Local Service Not found imports:
 [
  'import com.example.testing.todo.exception.TodoException;',
  'import com.example.testing.todo.exception.NoSuchTodoException;',
  'import com.example.testing.todo.model.Todo;',
  'import com.example.testing.todo.service.persistence.TodoUtil;',
  'import com.liferay.portal.kernel.util.Validator;',
  'import com.liferay.portal.kernel.language.LanguageUtil;',
  'import com.liferay.portal.kernel.search.Indexable;',
  'import com.liferay.portal.kernel.search.IndexableType;',
  'import com.liferay.portal.kernel.service.ServiceContext;',
  'import com.liferay.portal.kernel.exception.PortalException;',
  'import com.liferay.asset.kernel.service.AssetEntryLocalServiceUtil;',
  'import com.liferay.counter.kernel.service.CounterLocalServiceUtil;',
  'import com.liferay.portal.kernel.model.Group;',
  'import com.liferay.portal.kernel.model.ResourceConstants;',
  'import com.liferay.portal.kernel.model.User;',
  'import com.liferay.portal.kernel.service.UserLocalServiceUtil;',
  'import com.liferay.portal.kernel.service.GroupLocalServiceUtil;',
  'import com.liferay.portal.kernel.service.ResourceLocalServiceUtil;',
  'import com.liferay.portal.kernel.log.Log;',
  'import com.liferay.portal.kernel.log.LogFactoryUtil;',
  'import java.util.Locale;',
  'import java.util.ResourceBundle;',
  'import java.util.List;'
]
     info Adding logger declaration!
     info Search for method check
     info  method not found, generating at line 68
     info Search for method createNewTodo
     info  method not found, generating at line 125
     info Search for method deleteTodo
     info  method not found, generating at line 135
     info Search for method saveOrUpdateTodo
     info  method not found, generating at line 161
     info Search for method removeTodoRelationships
     info  method not found, generating at line 224
     info Search for method beforeCheckTodo
     info  method not found, generating at line 234
     info Search for method beforeSaveOrUpdateTodo
     info  method not found, generating at line 247
     info Search for method afterSaveOrUpdateTodo
     info  method not found, generating at line 261
     info Search for method customValidation
     info  method not found, generating at line 275
     info Search for method BeforeDeleteTodo
     info  method not found, generating at line 291
     info Search for method AfterDeleteTodo
     info  method not found, generating at line 300
     info Search for method findAll
     info  method not found, generating at line 309
     info Search for method getTodoCount
     info  method not found, generating at line 321
     info Search for method getTodoList
     info  method not found, generating at line 325
     info Search for method findByTaskStatus
     info  method not found, generating at line 335
     info Search for method findByDueDate
     info  method not found, generating at line 339
     info Search for method findByOwner
     info  method not found, generating at line 343
     info Search for method findByCompanyId
     info  method not found, generating at line 347
     info --------- GENERATING IN SERVICE -------------
     info ServiceImpl Not found imports:
 [
  'import com.liferay.portal.kernel.log.Log;',
  'import com.liferay.portal.kernel.log.LogFactoryUtil;',
  'import com.liferay.portal.kernel.service.ServiceContext;',
  'import com.liferay.portal.kernel.exception.PortalException;',
  'import com.liferay.portal.kernel.security.auth.PrincipalException;',
  'import com.liferay.portal.kernel.security.permission.ActionKeys;',
  'import com.liferay.portal.kernel.security.permission.resource.PortletResourcePermission;',
  'import com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;',
  'import com.liferay.portal.kernel.security.permission.resource.PortletResourcePermissionFactory;',
  'import com.liferay.portal.kernel.security.permission.resource.ModelResourcePermissionFactory;',
  'import com.liferay.portal.kernel.security.permission.ActionKeys;',
  'import com.example.testing.todo.model.Todo;',
  'import com.example.testing.todo.constants.TodoConstants;',
  'import com.example.testing.todo.constants.action.TestingActionKeys;',
  'import com.example.testing.todo.exception.TodoException;',
  'import java.util.HashMap;',
  'import java.util.List;'
]
     info Adding logger declaration!
     info Adding Portlet resource  declaration!
     info Adding Model resource declaration!
     info Search for method deleteTodo
     info  method not found, generating at line 66
     info Search for method deleteTodo
     info  method not found, generating at line 88
     info Search for method findAll
     info  method not found, generating at line 102
     info Search for method saveOrUpdateTodo
     info  method not found, generating at line 123
     info Model Todo not found in permission definition, creating it!
     info Creating content/Language.properties...
    force .yo-rc.json
   create modules/Todo/Todo-api/src/main/java/com/example/testing/todo/constants/TodoConstants.java
   create modules/Todo/Todo-api/src/main/java/com/example/testing/todo/constants/TestingTodoPortletKeys.java
   create modules/Todo/Todo-api/src/main/java/com/example/testing/todo/constants/action/TestingActionKeys.java
   create modules/Todo/Todo-service/src/main/java/com/example/testing/todo/internal/security/permission/resource/TodoModelResourcePermissionRegistrar.java
   create modules/Todo/Todo-service/src/main/java/com/example/testing/todo/internal/security/permission/resource/TodoPortletResourcePermissionRegistrar.java

> Task :modules:Todo:Todo-service:buildService
Building Todo
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoLocalService.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoLocalServiceUtil.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoLocalServiceWrapper.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoService.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoServiceUtil.java
Writing ../Todo-api/src/main/java/com/example/testing/todo/service/TodoServiceWrapper.java
Writing src/main/java/com/example/testing/todo/service/http/TodoServiceHttp.java
Writing src/main/java/com/example/testing/todo/service/http/TodoServiceSoap.java
Writing src/main/resources/service.properties

Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0.
Use '--warning-mode all' to show the individual deprecation warnings.
See https://docs.gradle.org/6.6.1/userguide/command_line_interface.html#sec:command_line_warnings

BUILD SUCCESSFUL in 4s
1 actionable task: 1 executed
✔ Build service executed successfully, starting generation...
     info Task duration: 21160 ms

This command does more than just invoke the Gradle task to build the services, it also injects methods into the EntityLocalServiceImpl and EntityServiceImpl classes. It also builds in support for permissions, workflow, exposing the finders, etc.

Another command is the generate-search-classes, although you need the Professional license to use this feature. If you have it, the generator will ask for the fields that should be indexed, and it will generate the classes to support all of the indexing and search functionality:

$ yo liferay:generate-search-classes

Reading projects from:/Users/dnebinger/liferay/Testing/modules
     info Current version is: 1.1.18 , latest version is: 1.1.18
     info No updates found!
     info Creating entity for project:Todo
     info Reloading configuration of: Todo
     info Service XML path: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/
       service.xml
     info Default XML File: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/
       src/main/resources/META-INF/resource-actions/default.xml
     info Model Hints: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/src/
       main/resources/META-INF/portlet-model-hints.xml
     info Namespace:Todo
     info Found an existing service.xml, the changes will continue on that one
? Please select Summary title field: task:String
? Please select Summary content field: task:String
? Please select search fields: userId:long, taskStatus:long, task:String, dueDate:Date
    force .yo-rc.json
     info Generating [object Object] TodoIndexField.java
     info Generating [object Object] TodoKeywordQueryContributor.java
     info Generating [object Object] TodoModelDocumentContributor.java
     info Generating [object Object] TodoModelIndexerWriterContributor.java
     info Generating [object Object] TodoModelSummaryContributor.java
     info Generating [object Object] TodoSearchRegistrar.java
   create modules/Todo/Todo-service/src/main/java/com/example/testing/todo/search/todo/
     field/TodoIndexerField.java
   create modules/Todo/Todo-service/src/main/java/com/example/testing/todo/search/todo/
     TodoKeywordQueryContributor.java
   create modules/Todo/Todo-service/src/main/java/com/example/testing/todo/search/todo/
     TodoModelDocumentContributor.java
   create modules/Todo/Todo-service/src/main/java/com/example/testing/todo/search/todo/
     TodoModelIndexerWriterContributor.java
   create modules/Todo/Todo-service/src/main/java/com/example/testing/todo/search/todo/
     TodoModelSummaryContributor.java
   create modules/Todo/Todo-service/src/main/java/com/example/testing/todo/search/todo/
     TodoSearchRegistrar.java
   info Task duration: 46688 ms

Portlet Usage

So you can definitely stop here if you want to. Creating the service layer is often more challenging than creating a portlet, so you might not need or want the generated portlet. I prefer to include the generated portlet if only to get the full Asset Rendering support for the custom entities so I don't have to build them myself. I may not drop the portlet on a page, but at least the Asset Renderer will work.

To create the portlet, we use the new-mvc-portlet command:

$ yo liferay:new-mvc-portlet

Reading projects from:/Users/dnebinger/liferay/Testing/modules
     info Removing old projects configuration:todo-web
     info Current version is: 1.1.18 , latest version is: 1.1.18
     info No updates found!
? What is the name of your new module? todo-web
? What is the package for your portlet? com.example.testing.todo.internal.portlet
? What is the category of your new portlet? sample
? Do you want your module will be instancable? Yes
? Do you want to create a panel app for your portlet? Yes
? Which type of panel app category you want to insert?: Liferay Panel App Categories
? Insert panel app category name: SITE_ADMINISTRATION_CONTENT
? Do you want to create jsp for CRUD operations? Yes
     info Creating entity for project:Todo
     info Reloading configuration of: Todo
     info Service XML path: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/service.xml
     info Default XML File: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/src/main/resources/META-INF/resource-actions/default.xml
     info Model Hints: /Users/dnebinger/liferay/Testing/modules/Todo/Todo-service/src/main/resources/META-INF/portlet-model-hints.xml
     info Namespace:Todo
? Choose the entity for jsp creation Todo
? Choose the entity fields you want to show in list view todoId, taskStatus, task, dueDate
? Choose the column to use for sorting dueDate
     info Press <enter> if you want to skip relationships...
? Choose the entity fields you want to show in the input html form taskStatus, task, dueDate
? Crud portlet needs the generation of search indexes classes, do you want to generate it? No
     info Creating Toolbar Portlet...
    force .yo-rc.json
   create modules/todo-web/build.gradle
   create modules/todo-web/.gitignore
    force modules/todo-web/.yo-rc.json
   create modules/todo-web/bnd.bnd
   create modules/todo-web/src/main/resources/content/Language.properties
   create modules/todo-web/src/main/resources/portlet.properties
   create modules/todo-web/src/main/resources/META-INF/resource-actions/default.xml
   create modules/todo-web/src/main/java/com/example/testing/todo/internal/portlet/constants/Todo-WebPortletKeys.java
   create modules/todo-web/src/main/java/com/example/testing/todo/internal/web/internal/security/permission/resource/Todo-WebModelPermission.java
   create modules/todo-web/src/main/java/com/example/testing/todo/internal/web/internal/security/permission/resource/Todo-WebPermission.java
   create modules/todo-web/src/main/java/com/example/testing/todo/internal/util/TodoComparator.java
   create modules/todo-web/src/main/java/com/example/testing/todo/internal/portlet/Todo-WebPortletPanelApp.java
    force modules/toolbar-portlet/.yo-rc.json
   create modules/toolbar-portlet/build.gradle
   create modules/toolbar-portlet/bnd.bnd
   create modules/toolbar-portlet/src/main/resources/content/Language.properties
   create modules/toolbar-portlet/src/main/resources/META-INF/resources/init.jsp
   create modules/toolbar-portlet/src/main/resources/META-INF/resources/view.jsp
   create modules/toolbar-portlet/src/main/java/com/example.testing.toolbar.portlet/application/list/ToolbarPanelCategory.java
   create modules/toolbar-portlet/src/main/java/com/example.testing.toolbar.portlet/constants/ToolbarPanelCategoryKeys.java
   create modules/toolbar-portlet/src/main/java/com/example.testing.toolbar.portlet/util/SearchManagementToolbarDisplayContext.java
   create modules/todo-web/src/main/java/com/example/testing/todo/internal/portlet/Todo-WebPortlet.java
   create modules/todo-web/src/main/resources/META-INF/resources/init.jsp
   create modules/todo-web/src/main/resources/META-INF/resources/view.jsp
   create modules/todo-web/src/main/resources/META-INF/resources/edit.jsp
   create modules/todo-web/src/main/resources/META-INF/resources/entry_actions.jsp
   create modules/todo-web/src/main/resources/META-INF/resources/management_toolbar.jsp
   create modules/todo-web/src/main/resources/META-INF/resources/search_columns.jspf
   create modules/todo-web/src/main/resources/META-INF/resources/view_todo_list.jsp
     info Task duration: 81797 ms

So now I can generate and deploy the modules. The portlet dropped on a page looks like:


This shows the list, and either the edit or the add button in the upper right gets you to the form:


Since I also asked for the Panel App with the Site Content category, I can also see it in the control panel:


Are these UIs ready for production deployment? No, I don't think so. I was originally thinking my due date would just be a date, not a timestamp. The task status would be a number, but likely I'd make them constants representing states such as "Pending", "Completed", "Cancelled", etc.

So yeah, there would be changes I'd like to make, but I'm starting out so far ahead than if I had to create this interface all on my own.

Conclusion

Well this concludes the tour, please check your seats to ensure you have all of your personal belongings before exiting the train...

So I like this tool, I like what it generates and it really can be a time saver for launching a new Service Builder-based system.

Like all tools (including Liferay's), it is opinionated, but it has to be to support pulling all of the pieces together and have them work. And although it is opinionated, it doesn't feel like it is opinionated in a bad way, just a different way.

The generated code is complete and will compile for you and you can deploy it, all pretty much unchanged from the generated version. It's also of a sound quality, too, and in the version I reviewed I didn't find any significant defects.

So I encourage you to give it a try for your next Liferay project and see just how it might save you a big chunk of time...

Let me know what you think in the comments below...

Blogs

These kind of liferay tools always lack documentation after quite some time regarding different Liferay versions. I also miss the quite important information about the fact, that this tool seems to be only free for 30 days?

Only some features (search and portlet) fall under this category. I'm sure the team has plans for how the licensing will work, I'd check in with them to get the details.

Hi David, thank you so much to review this tool.

Seeing the screenshot about the Panel App deployed in the control panel, I can't see the Configuration option in the upper right corner on the left of the User's avatar.

What about the Configuration stack generation?

 

Hi David,

You evaluated and tested Liferay-Gen  and before Damascus.

I know that Damascus is a fantastic tool and deserves to be better known by the developers but until now I never tried Liferay-Gen.

My question is this: can you tell us which is the greatest value and the greatest defect of the two projects?

Hi, Ivano, I actually need to do a blog on Damascus too!

There is a lot of overlap in functionality between the tools. Each will generate the more complete SB layer including support for assets, workflow and search, and each can generate the table-based portlet as standalone or control panel.Usage is slightly different in that Damascus uses a JSON file to drive the operation while Liferay-Gen is a live interview. I think I like the interview because I have to think at each answer rather than working on getting syntax correct in the JSON, but that is a personal opinion and should not really determine which tool to use.

There are some code differences based upon the opinionated perspective of the separate teams. For example, Damascus exposes the PortletRequest through to the ServiceBuilder service implementation layer while Liferay-Gen uses the entity as a DTO to populate in the portlet layer and pass as a parameter to the service layer for persistence. Both of these methods work, but they will differ from what you see in standard Liferay code (not that this is a bad thing).

I guess the biggest difference is that Damascus is a scaffolding tool; you use it once to generate everything, whereas Liferay-Gen supports an iterative approach. For example, Liferay-Gen allows you to add a new entity later on and it can regen the code for that entity without stepping on the previously-created code. Again, this doesn't make one solution better than another, it just makes them different.

Hope this helps!

Hello David,

Do you have any information if the two projects Liferay-Gen and Damascus  are still alive and will they have a sequel?

​​​​​Also do you think that Liferay will ever release a similar official tool?

Thank you in advance for any comments on the topic of code generators for Liferay.

As far as I know, both are still active. I don't know that either are planning a sequel, that kind of would imply changes were necessary and I'm not sure either team feels that is a necessity...

That said, if you're aware of features or changes that are necessary, I'm sure both projects would entertain your ideas...