Blogs
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
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.
../../../../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.
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.
../../../../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).
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.
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.
../../../../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.
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.