Blade Outside of the IDE

Learn how to leverage Blade outside of the IDEs

Introduction

I recently advocated for the demise of the specialized Liferay IDEs/IDE Plugins.

Advocating for them to be decommissioned is one thing, but sharing how you can live without them can be another thing...

So I thought I'd blog about how you can use a vanilla, off the shelf version of Eclipse/Intellij along with Blade & Gradle to accomplish similar goals. And I'll also cover a surprise IDE in the last section.

I'll cover the following for each IDE:

  • Creating a Liferay Workspace
  • Creating an OSGi Module from a Blade Template
  • Initializing and Deploying to the Liferay Bundle
  • Debugging
To get through this, it's important that you have the Blade CLI installed on your system. If you can't open a command window and issue the command blade version and not get an error, then you need to follow the instructions here to install the Blade CLI: https://learn.liferay.com/w/dxp/liferay-development/tooling/blade-cli/installing-and-updating-blade-cli

Okay, so pick your IDE poison and then follow along!

Eclipse

So to prove that I'm doing Eclipse w/o a Liferay IDE/Liferay Plugin, I went to https://www.eclipse.org/downloads/packages and then downloaded the Eclipse IDE for Enterprise Java and Web Developers.

After installing and starting, the About Eclipse dialog showed that I'm using 2024-03:

Creating a Liferay Workspace

So to create a new workspace, I dropped out to the terminal and, in the directory I wanted to work in, ran the command blade init eclipse-workspace (my workspace is named eclipse-workspace):

I chose portal-7.4-ga112 for my target. Once it was ready, I changed directory into the eclipse-workspace folder, then ran the command ./gradlew eclipse. This command creates all of the Eclipse project files.

Once this was finished, I returned to Eclipse and went to File -> Open Projects from File System, navigated to the eclipse-workspace folder, then clicked Finish.

Once this was done, my new workspace was open in Eclipse:

Next I right-clicked on the eclipse-workspace project, then to Configure -> Add Gradle Nature so Eclipse would know this was a Gradle project. In the Gradle Tasks view, I could then see the workspace gradle tasks:

Creating an OSGi Module from a Blade Template

Okay, so now we have a slight fork... You can continue to use the separate terminal, or you can use the Terminal tab that's available in Eclipse. You'll get similar results whichever way you go, but I'm going to highlight using the terminal in Eclipse because I find it easier than switching out to another window just to execute a command...

From the Terminal tab, click the Open a Terminal icon which is the left-most icon in the toolbar on the right side of the palette. Navigate to your eclipse-workspace directory.

To create a new module, in this case we'll create a Service Builder module, navigate into the modules folder, then issue the command blade create -t service-builder -p com.example example to create a new Service Builder module.

To get a list of available templates, use blade create -l.

The command I used will create new modules using the service-builder template, the default java package will be com.example and the module itself will be named example, and for the Service Builder template, I'll get example-api and example-service sub-modules.

So now the modules are ready, we just need to make Eclipse aware of them. I just needed to rebuild the Eclipse project files, and I did that (within the modules directory) by issuing the ../gradlew eclipse command.

You're going to get tired of issuing the various forms of gradlew, especially if it becomes like ../../../../gradlew command kind of thing. There are numerous different aliases you can find for replacing all of this with gw online. I'd recommend finding and using one that works for you and your platform. Personally I use alias gw='$(git rev-parse --show-toplevel)/gradlew $@' on my Mac. It does require each workspace to be set up for a git repo, but we do that anyways, right?

Going forward, I'll switch to using the gw command to keep things simple.

Next, and a step you'll be performing often, is to refresh the Eclipse project:

This is a step that you'll be repeating often, such as adding a new module/client extension/theme, when you make build.gradle changes (especially adding dependencies), etc.

In addition, you'll also need to refresh Gradle. Right click on the workspace, then go to Gradle -> Refresh Gradle Project to update for the new modules.

Initializing and Deploying to the Liferay Bundle

So the first thing we need to do is initialize our bundle. This is actually handled by the Liferay Workspace mostly.

From the root directory of the workspace, issue the command gw initBundle. The bundle will be downloaded to your local system, expanded, and the configuration from the configs/local in the workspace will be copied in (this is how you can change portal-ext.properties, Tomcat's setenv.sh script, etc.).

The tomcat instance is created, so we need to add it to Eclipse. On the servers tab, click the link to create a new Server.

Set the server type as Tomcat 9 and click the Next button:

Give the server a name (I called mine Liferay), then use the Browse button to navigate to your eclipse-workspace directory, then in there find bundles and within there is your Tomcat directory. Mine was listed as tomcat-9.0.83, but yours could be different. Also pick the JRE for your server, I tend to run Zulu 11.0.19, so that's the one I assigned. With these settings in place, click the Finish button.

After clicking Finish, we'll see the new server listed:

Since the server is listed, we could right-click on it and choose Debug, Start, or Profile, but this would actually fail because the default setup for the server is not going to work.

To complete the server configuration, double-click on the server to bring up the Overview panel and change the Server Locations so that it is set to Use Tomcat Installation (takes control of Tomcat installation) option.

Next, click on the Open launch configuration link so we can add some parameters. You'll be prompted to save the changes to the server, approve those changes.

Click on the Arguments tab on the configuration panel so we can add to the VM arguments:

The arguments that we want to add is -Dfile.encoding=UTF-8 -Djava.locale.providers=JRE,COMPAT,CLDR -Djava.net.preferIPv4Stack=true -Duser.timezone=GMT -Xms8g -Xmx8g -XX:MaxNewSize=1536m -XX:MaxMetaspaceSize=1g -XX:MetaspaceSize=1g -XX:NewSize=1536m -XX:SurvivorRatio=7.

Feel free to change the memory settings as appropriate for your environment.

Click on the Ok button to close the dialog, and if the Overview panel is still open, it is okay to close that as well.

Now that the configuration is updated, when we right-click on it and choose Debug, Start, or Profile, Liferay will start up correctly.

When it comes to deployment, we're going to stick exclusively with the gw deploy command. If we run this at the root of the workspace, all client extensions, modules and themes will be built and deployed into the Liferay server. If we run this in an individual project like a client extension, a module or a theme, only the artifact from that project will be deployed into the Liferay server.

The goal here is to let the gw deploy command handle the deployment of the artifacts, not Eclipse.

Debugging

The last part to cover is how to handle debugging... Before we can dive into it, let's prepare some custom code to debug.

Earlier we used the Blade CLI to create a new Service Builder module. This would have given us the default service.xml that declares a Foo entity.

From the example-service directory, use gw buildService to generate the initial Service Builder code, then edit the com.example.service.impl.FooLocalServiceImpl class (or whatever package yours is using) and give it a simple public method:

public void testDebug() {
  int val = 10 / 0;
}

Save the file, then use gw buildService to rebuild the services and gw deploy in the example-api and example-service directories to deploy each module.

Set a breakpoint on the line, and make sure your server is started in Debug mode.

To test debugging, log into your instance as an administrator and then navigate to the Groovy control panel. Change the script to the following, then click on the Execute button.

import com.example.service.FooLocalServiceUtil;

FooLocalServiceUtil.testDebug();

You'll be prompted to switch to debug perspective in Eclipse, so accept the change. You'll likely face an ugly window such as:

Yes the source is part of your workspace, you just need to connect it up. Click the Edit Source Lookup Path button.

Click on the Add button to begin adding a new source path.

For workspace modules, you're going to use the Java Project option. To add external sources such as the portal source (so you can debug into Liferay code), use External Archive or File System Directory to point at the Liferay source for version you're targeting.

For the External Archive, it's helpful to download the source jar for the module(s) in question. You can find those in the various artifact folders here and here, but be sure to grab the right version for your target bundle.

Make sure that all of your modules are selected since you never know when you'll need them.

When your source lookup path is complete, you'll find that you're on the line where the breakpoint is set and you can begin stepping through the code...

Conclusion

So we've done all of the major things we need to do using the latest Eclipse and no LDS, no Liferay IDE, and no Liferay Eclipse plugins, thus proving that we don't need the IDE to be productive in Eclipse.

The other things we need to do, such as editing code, running gradle, etc. can also be done in the IDE without any extra effort, tools or setup.

Granted, many of these manual steps will not be necessary when using LDS or the Liferay IDE. I'm certainly not arguing that LDS or Liferay IDE aren't helpful. I'm just arguing that they're not necessary in order to use Eclipse and develop for Liferay.

One aspect to keep in mind, you're going to want to get used to completing the workspace refresh as well as the Gradle refresh. When you find that something you created via blade is not appearing at all or not appearing as expected, this is usually solved by refreshing one or both aspects.

IntelliJ

So to prove that I'm doing IntelliJ w/o a Liferay Plugin, I went to https://www.jetbrains.com/idea/download/ and then downloaded the IntelliJ IDEA Community Edition.

After installing and starting, the About IntelliJ IDEA dialog showed that I'm using 2023.3.6 (Community Edition):

Creating a Liferay Workspace

So to create a new workspace, I dropped out to the terminal and, in the directory I wanted to work in, ran the command blade init idea-workspace (my workspace is named idea-workspace):

I chose portal-7.4-ga112 for my target. For Eclipse we had to use the ./gradlew eclipse command to build the Eclipse project files, but we don't really need to do this for IntelliJ. The command ./gradlew idea is available though if we wanted to use it, and it creates all of the IntelliJ project files.

Once this was finished, I returned to IntelliJ and went to File -> New -> Project from Existing Sources, navigated to the idea-workspace folder, then clicked Open.

I changed the setting to import the project from an external model and I picked Gradle as the model, then clicked Create. I also had to trust the project I was importing:

The workspace project is now available in IntelliJ and we can move on to creating a new module.

Creating an OSGi Module from a Blade Template

Okay, so now we have a slight fork... You can continue to use the separate terminal, or you can use the Terminal tab that's available in IntelliJ. You'll get similar results whichever way you go, but I'm going to highlight using the terminal in IntelliJ because I find it easier than switching out to another window just to execute a command...

Click the Terminal icon from the toolbar on the left flyout. It will automatically open in the idea-workspace directory.

To create a new module, in this case we'll create a Service Builder module, navigate into the modules folder, then issue the command blade create -t service-builder -p com.example example to create a new Service Builder module.

To get a list of available templates, use blade create -l.

The command I used will create new modules using the service-builder template, the default java package will be com.example and the module itself will be named example, and for the Service Builder template, I'll get example-api and example-service sub-modules.

With Eclipse, we needed to manually create our Eclipse project folders using the command ./gradlew eclipse to generate the necessary files. There's also a corresponding ./gradlew idea that can do the same, but we'll be able to handle the project updates within IntelliJ itself, so this step is unnecessary.

You're going to get tired of issuing the various forms of gradlew, especially if it becomes like ../../../../gradlew command kind of thing. There are numerous different aliases you can find for replacing all of this with gw online. I'd recommend finding and using one that works for you and your platform. Personally I use alias gw='$(git rev-parse --show-toplevel)/gradlew $@' on my Mac. It does require each workspace to be set up for a git repo, but we do that anyways, right?

Going forward, I'll switch to using the gw command to keep things simple.

In the project tree, your new modules will be listed, however it will not be treated as Gradle projects yet.

On the right-side toolbar, click the Gradle icon to get the right-side flyout. Then click on the first icon to Reload All Gradle Projects to sync the new modules with IntelliJ. After the sync completes, the modules folder and the example folders will now carry the Gradle icon overlays.

Once this is done, the modules will be listed in the Gradle flyout, and you can drill into each level and see the tasks. While we're here, lets go ahead by going to idea-workspace -> modules -> example -> example-service -> Tasks -> build and double-click on buildService to generate the initial Service Builder code.

Initializing and Deploying to the Liferay Bundle

So the first thing we need to do is initialize our bundle. This is actually handled by the Liferay Workspace mostly.

From the root directory of the workspace, issue the command gw initBundle (or even use the Gradle task from idea-workspace -> Tasks -> bundle -> initBundle). The bundle will be downloaded to your local system, expanded, and the configuration from the configs/local in the workspace will be copied in (this is how you can change portal-ext.properties, Tomcat's setenv.sh script, etc.).

This will create the Tomcat bundle for us, but how do we use it in IntelliJ?

First, the easy case, if you are using IntelliJ Ultimate, you get Tomcat integration out of the box. Go to the Run menu and select Edit Configurations. Click the Add button, then find the Tomcat Server -> Local option and select it.

In the dialog that opens, click the Configure button next to the Application Server dropdown to define a new application server:

Set the Tomcat Home to your bundles/tomcat directory in your workspace, for me that was in the idea-workspace/bundles/tomcat-9.0.83 directory. The Tomcat Base should match the same value as Tomcat Home. Set the name of the app server as Liferay (or pick your own) and hit the OK button.

Back in the configuration panel, set the JVM that you want to use to launch Tomcat, in my case I used my local Zulu 11 installation. Also set the VM options that we want, I used -Dfile.encoding=UTF-8 -Djava.locale.providers=JRE,COMPAT,CLDR -Djava.net.preferIPv4Stack=true -Duser.timezone=GMT -Xms8g -Xmx8g -XX:MaxNewSize=1536m -XX:MaxMetaspaceSize=1g -XX:MetaspaceSize=1g -XX:NewSize=1536m -XX:SurvivorRatio=7. Use whatever memory settings are appropriate for your environment.

Clicking the OK button will save this configuration and make the local Tomcat available to run at any time. When we click Run or Debug, we'll be prompted for the configuration to pick, just use the Liferay one and you'll be off and running.

In this blog post, we're using Community Edition, and here our options are limited. There are some free plugins we could add, but they are really unnecessary.

I will typically just go to the terminal, go into the bundles folder, then execute tomcat/bin/startup.sh and let Tomcat just run. To make things simple, in the bundles folder I'll typically do ln -s tomcat-9.0.83/bin/startup.sh . && ln -s tomcat-9.0.83/bin/shutdown.sh . && ln -s tomcat-9.0.83/logs/catalina.out . so I don't have to do paths all the time. Then to start, I use ./startup.sh && tail -f catalina.out and Tomcat starts and the logs are displayed in the console.

You can do this either from an external terminal outside of IntelliJ or in the Terminal in IntelliJ (I'll add a new terminal tab and rename it to Tomcat and use it to start Tomcat).

Don't forget to edit the tomcat/bin/setenv.sh to configure for your local memory settings.

In this configuration, for deployment we're going to stick exclusively with the gw deploy command (or use the Gradle flyout and the appropriate deploy task). If we run this at the root of the workspace, all client extensions, modules and themes will be built and deployed into the Liferay server. If we run this in an individual project like a client extension, a module or a theme, only the artifact from that project will be deployed into the Liferay server.

The goal here is to let the gw deploy command handle the deployment of the artifacts, not IntelliJ.

Debugging

The last part to cover is how to handle debugging... Before we can dive into it, let's prepare some custom code to debug.

Earlier we used the Blade CLI to create a new Service Builder module. This would have given us the default service.xml that declares a Foo entity.

From the example-service directory, use gw buildService to generate the initial Service Builder code, then edit the com.example.service.impl.FooLocalServiceImpl class (or whatever package yours is using) and give it a simple public method:

public void testDebug() {
  int val = 10 / 0;
}

Save the file, then use gw buildService to rebuild the services and gw deploy in the example-api and example-service directories to deploy each module.

By this point, you're probably like me - you have the Gradle flyout open all the time, you have the tree expanded in the modules you're working in, and you're just flittering around double-clicking on buildService and the various deploy tasks. And Olaf wonders why I refer to IntelliJ as the Eclipse upgrade...

So code is all ready, we just now need to have Tomcat running in a Debug mode.

If we're in IntelliJ Ultimate and we have tomcat added as a run/debug configuration, this is mostly done for us, we just need to go to Run -> Debug... and then pick our Liferay server.

Since we're doing Community Edition, our only option is to use the Java Remote debugger.

First, we need to know how to launch Tomcat with remote debugging enabled. To do this, I make a copy of tomcat/bin/startup.sh and I call it tomcat/bin/debug.sh, then I edit the debug.sh file and change the last line so it includes jpda as below:

exec "$PRGDIR"/"$EXECUTABLE" jpda start "$@"

In the bundles directory, I will also ln -s tomcat-9.0.83/bin/debug.sh . so I have easy access to the script.

Then just start the debug.sh script and Tomcat will launch in debug mode.

Next, we need to connect to the process via the remote debugger in IntelliJ. Go to Run -> Edit Configurations, click on the Add button and choose the Remote JVM Debug item.

Set the name to be Liferay and change the port to 8000, then click OK to save the configuration.

You'll only have to do this step once per workspace. Going forward, just go to Run -> Debug and select the Liferay item from the list.

In the FooLocalServiceImpl's testDebug method, set a breakpoint on the line.

Next, to test, log in as an administrator and navigate to the Groovy Script control panel and enter the following script, then click on the Execute button:

import com.example.service.FooLocalServiceUtil;

FooLocalServiceUtil.testDebug();

IntelliJ will come to the front, switch to the Debug perspective, and you'll find yourself right on the line of code your breakpoint is set to:

Another great thing about IntelliJ, it already knows where all of the source is. As I clicked through the stack frame in the bottom left, the source files opened even for Liferay classes, so I didn't have to find them manually like we did in the Eclipse steps.

Conclusion

So we've done all of the major things we need to do using the latest IntelliJ and no Liferay plugins, thus proving that we don't need them to be productive in IntelliJ.

The other things we need to do, such as editing code, running gradle, etc. can also be done in IntelliJ without any extra effort, tools or setup.

Granted, many of these manual steps will not be necessary when using the Liferay plugin. I'm certainly not arguing that the Liferay plugin isn't helpful. I'm just arguing that the plugin is not necessary in order to use IntelliJ and develop for Liferay.

One aspect to keep in mind, you're going to want to get used to completing the Gradle refresh. After creating new client extensions, modules and themes, you'll need to refresh the Gradle aspect to get IntelliJ to include them in its list of Gradle projects in the workspace.

Bonus IDE: VSCode

Historically Liferay has recommended either Eclipse or IntelliJ since there were plugins available for them to enhance developer productivity under the older ways of developing for Liferay. Other IDEs didn't have plugins available or, in the case of VSCode, weren't really available (VSCode was released in 2015 and 6.2 was still the primary Liferay version).

However if you've gone through the above sections on Eclipse and/or IntelliJ, it should be clear now that IDE integration isn't really necessary. As long as the IDE supports Gradle and Java development, it should generally be suitable for Liferay development.

VSCode falls into that category, so let's do all of the same steps using VSCode to see how we could use it as our IDE instead of Eclipse or IntelliJ.

To start with, I went to https://code.visualstudio.com/download to download the latest version.

After downloading and installing, the About Visual Studio Code dialog shows I have version 1.87.2:

At this point I also added two extensions. Don't worry, they're not Liferay-specific. The first is the Gradle plugin and the second is the Community Server Connector (for Tomcat):

The Gradle one I'd consider recommended, but not necessary, and the server connectors is optional. Since we have Gradle in the workspace, we don't need the IDE support but, like we saw with IntelliJ, being able to just click to launch the Gradle tasks is really useful. The server connectors will allow us to launch within VSCode, but as we also saw with IntelliJ we can do fine starting/stopping/debugging with Tomcat running outside of the IDE.

Creating a Liferay Workspace

So to create a new workspace, I dropped out to the terminal and, in the directory I wanted to work in, ran the command blade init vscode-workspace (my workspace is named vscode-workspace):

I chose portal-7.4-ga112 for my target.

Once this was finished, I returned to VSCode and went to File -> Open Folder, navigated to the vscode-workspace folder, then clicked Open.

VSCode did ask me to trust the new project, so I clicked on the Yes button.

The workspace project is now available in VSCode and we can move on to creating a new module.

Creating an OSGi Module from a Blade Template

Okay, so now we have a slight fork... You can continue to use the separate terminal, or you can use the Terminal that's available in VSCode. You'll get similar results whichever way you go, but I'm going to highlight using the terminal in VSCode because I find it easier than switching out to another window just to execute a command...

Click the Terminal menu and select New Terminal. It will open a Terminal view, automatically starting in the vscode-workspace directory.

To create a new module, in this case we'll create a Service Builder module, navigate into the modules folder, then issue the command blade create -t service-builder -p com.example example to create a new Service Builder module.

To get a list of available templates, use blade create -l.

The command I used will create new modules using the service-builder template, the default java package will be com.example and the module itself will be named example, and for the Service Builder template, I'll get example-api and example-service sub-modules.

As soon as the blade command finished, VSCode asked if I wanted to synchronize the Java classpath. I took this opportunity to click on the Always button so I wouldn't have to do this as I created more modules in the future.

With Eclipse, we needed to manually create our Eclipse project folders using the command ./gradlew eclipse to generate the necessary files. We didn't need to execute ./gradlew idea that can do the same, but the command is available if we wanted it. There is no equivalent command available to create VSCode files.

You're going to get tired of issuing the various forms of gradlew, especially if it becomes like ../../../../gradlew command kind of thing. There are numerous different aliases you can find for replacing all of this with gw online. I'd recommend finding and using one that works for you and your platform. Personally I use alias gw='$(git rev-parse --show-toplevel)/gradlew $@' on my Mac. It does require each workspace to be set up for a git repo, but we do that anyways, right?

Going forward, I'll switch to using the gw command to keep things simple.

In the project tree, your new modules will be listed, however it will not be treated as Gradle projects yet.

Although the Java aspect was updated automatically, I did have to click on the Gradle icon on the left-side toolbar, then click the menu to get to the Refresh Gradle Projects View option (different than the Reload All Gradle Projects icon).

After doing this, the new modules appeared with their tasks available.

Like we saw with IntelliJ, we can go into the Gradle view, find the right module and the right task to execute, then double-click to launch them. While we're here, lets go ahead by going to example-service -> Tasks -> build and double-click on buildService to generate the initial Service Builder code.

At this point our new modules are ready, we can open them via the Explorer and begin making necessary changes.

Initializing and Deploying to the Liferay Bundle

So the first thing we need to do is initialize our bundle. This is actually handled by the Liferay Workspace mostly.

From the root directory of the workspace, issue the command gw initBundle (or even use the Gradle task from vscode-workspace -> Tasks -> bundle -> initBundle). The bundle will be downloaded to your local system, expanded, and the configuration from the configs/local in the workspace will be copied in (this is how you can change portal-ext.properties, Tomcat's setenv.sh script, etc.).

This will create the Tomcat bundle for us, but how do we use it in VSCode?

Well, we absolutely could follow the same steps as outlined above for IntelliJ Community Edition (basically running completely outside of the IDE). The same steps outlined there will work here too.

However, we installed the Community Server Connector into VSCode, so we have that as an option that we'll go into now.

Under the Explorer section, there's a Servers item. When I folded it down, I found a single entry:

I also saw (but didn't get a screen cap of) an error that the Community Server Connector (CSC) required Java 11 (Java 8 is my system default). It did give me an option to configure the JVM, when I clicked the button I was then editing a JSON file. I added the highlighted line below, saved the file and was then able to start the CSC.

The path at the top may help you find the similar file if you need to make a similar change.

After the CSC was started, I then used the Create New Server button to define a new server. It starts by asking if you want to download or not. Since I have the local bundle, I chose No.

It then opens a file browser. I navigated to the vscode-workspace/bundles/tomcat-9.0.83 folder (you should select the root Tomcat folder where the bundle lives, not the LIFERAY_HOME equivalent).

It identified the Tomcat 9 I had and gave me a simple form to complete. I changed the name of the server to Liferay, then I clicked the Finish button.

Under the Servers -> Community Server Connector, I now found my Liferay server. I right-clicked on the Liferay server, then chose Edit Server, which brought me into a JSON file to edit.

I changed the value for the args.vm.override.string to add -Dfile.encoding=UTF-8 -Djava.locale.providers=JRE,COMPAT,CLDR -Djava.net.preferIPv4Stack=true -Duser.timezone=GMT -Xms8g -Xmx8g -XX:MaxNewSize=1536m -XX:MaxMetaspaceSize=1g -XX:MetaspaceSize=1g -XX:NewSize=1536m -XX:SurvivorRatio=7. Use whatever memory settings are appropriate for your environment. After making this change, I saved and closed the file.

Next, I right-clicked on the Liferay server and selected Start, at which point it started up.

In this configuration, for deployment we're going to stick exclusively with the gw deploy command (or use the Gradle flyout and the appropriate deploy task). If we run this at the root of the workspace, all client extensions, modules and themes will be built and deployed into the Liferay server. If we run this in an individual project like a client extension, a module or a theme, only the artifact from that project will be deployed into the Liferay server.

The goal here is to let the gw deploy command handle the deployment of the artifacts, not VSCode.

Debugging

The last part to cover is how to handle debugging... Before we can dive into it, let's prepare some custom code to debug.

Earlier we used the Blade CLI to create a new Service Builder module. This would have given us the default service.xml that declares a Foo entity.

From the example-service directory, use gw buildService to generate the initial Service Builder code, then edit the com.example.service.impl.FooLocalServiceImpl class (or whatever package yours is using) and give it a simple public method:

public void testDebug() {
  int val = 10 / 0;
}

Save the file, then use gw buildService to rebuild the services and gw deploy in the example-api and example-service directories to deploy each module.

By this point, you're probably like me - you switch to the Gradle view, expand the modules you're working on, and you're just flittering around the Tasks double-clicking on buildService and the various deploy tasks. This too seems like an upgrade over Eclipse, and is also on-par with IntelliJ.

So code is all ready, we just now need to have Tomcat running in a Debug mode.

Since we have the Community Server Connector installed, this can be handled by right-clicking on the Liferay server and choose Debug Server to launch in Debug mode.

In the FooLocalServiceImpl, set a breakpoint in the code to force a stop, then log into Liferay as an admin and go to the Script control panel and enter the following script, then click on the Execute button:

import com.example.service.FooLocalServiceUtil;

FooLocalServiceUtil.testDebug();

VSCode will come to the front, switch to the Debug perspective, and you'll find yourself right on the line of code your breakpoint is set to:

Being new to this aspect of VSCode, I struggled with this for a moment. The Call Stack area shows all threads closed, but somewhere in the mix is the one that is currently stopped at a breakpoint. Find the one that is not in the Running status, but is Paused On Breakpoint, that's the call stack to expand and look at.

Otherwise, the debugger worked as expected. And, like IntelliJ, VSCode was able to access all of the Liferay source without having to manually configure like we did for Eclipse.

Conclusion

So we've done all of the major things we need to do using the VSCode and no Liferay plugins, thus proving that we don't need them to be productive in VSCode. The Gradle extension we added and is a nice to have, but is not required since we can use the terminal. The Community Server Connector we added, but it also wouldn't have been necessary (because VSCode can do the remote Java debugging also).

The other things we need to do, such as editing code, running gradle, etc. can also be done in VSCode without any extra effort, tools or setup.

One aspect to keep in mind, you're going to want to get used to completing the Gradle project view refresh. After creating new client extensions, modules and themes, you'll need to refresh the Gradle aspect to get VSCode to include them in its list of Gradle projects in the workspace.

Overall Conclusion

So, here we have it, three different IDEs and not a single Liferay plugin or customized IDE in sight.

The steps covered here prove that any IDE will work for Liferay development. Heck, you could even argue it is easier now to code with no IDE at all, just a command line and VI and you'd be able to do all that you need.

I hope you enjoyed this long, long blog post, and I hope it gives you the tools to use a newer, better, and Liferay-customization-free experience.

Blogs

Dear David, once again your contribution to life around Liferay is of exceptional quality and extremely useful. Thank you!

Awesome writeup about everything required for the setup! Thanks David.

I would like to mention the

target.platform.index.sources=true in gradle-local.properties

In intellj it works you can browse Liferays source modules very nicely, altought slows down startup/project indexing.