The Liferay Slim Runtime provides the bare necessities for running Service Builder modules. It's useful for testing applications quickly in a Liferay runtime environment free of Liferay add-ons.
The Liferay Slim Runtime provides
- Caching infrastructure
- Database infrastructure
- HTTP support
- JAX-RS support
- Limited set of Liferay utility classes
- OSGi framework for running modules
- Service Builder runtime for Service Builder modules
- Spring infrastructure
- Transaction infrastructure
It does not provide
- Authentication/Authorization layers
- Layout templates
- Permissions
- Portlet support (no portlet container)
- Sites
- Themes
- etc.
To build the Slim Runtime, execute the following top-level Ant command:
ant all -Dbuild.profile=slim
It's built in the server directory specified by the app.server.properties
file's app.server.parent.dir
property. Note that the Slim Runtime only
supports Apache Tomcat 8+. This limitation simplifies packaging and
configuration.
To launch the Slim Runtime, run the Tomcat start scripts found in the
<tomcat>/bin
directory:
./startup.[sh|bat]
You can deploy modules from any of the default directories the
portal.properties
file defines (see properties below) or from a custom
auto-deploy directory you add to the module.framework.auto.deploy.dirs
property.
module.framework.base.dir=${liferay.home}/osgi
module.framework.configs.dir=${module.framework.base.dir}/configs
module.framework.marketplace.dir=${module.framework.base.dir}/marketplace
module.framework.modules.dir=${module.framework.base.dir}/modules
module.framework.war.dir=${module.framework.base.dir}/war
module.framework.auto.deploy.dirs=\
${module.framework.configs.dir},\
${module.framework.marketplace.dir},\
${module.framework.modules.dir},\
${module.framework.war.dir}
By default, a pristine Slim Runtime has no UI or apps. Requests to it result in 404 errors.
The modules you add provide all the functionality.
A web endpoint is the simplest type of function.
The following snippet demonstrates a simple servlet that responds to all
requests to http://localhost:8080[/*]
:
package web.sample;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
@Component(
immediate = true,
property = {
HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN + "=/*"
},
service = Servlet.class
)
public class SampleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.println("<h2>Hello You!</h2>");
}
}
The Slim Runtime creates the database schema automatically the first time it runs.
MariaDB [lportal]> show tables;
+------------------+
| Tables_in_lportal|
+------------------+
| ClassName_ |
| Configuration_ |
| Counter |
| Release_ |
| ServiceComponent |
+------------------+
5 rows in set (0.00 sec)
Only the following core services are available:
ClassNameLocalService
CounterLocalService
ReleaseLocalService
ServiceComponentLocalService
No other services are provided! Therefore, any service deployed to this Slim Runtime that depends on services other than these won't work.
The Service Builder runtime bootstraps all deployed Service Builder services (API and service modules).
For example, deploying the com.liferay.contacts.api
and
com.liferay.contacts.service
modules adds the Contacts_Entry
table to the
database:
MariaDB [lportal]> show tables;
+------------------+
| Tables_in_lportal|
+------------------+
| ClassName_ |
| Configuration_ |
| Contacts_Entry |
| Counter |
| Release_ |
| ServiceComponent |
+------------------+
6 rows in set (0.00 sec)
The servlet in the following snippet implements a simple web app that uses the contacts service.
package web.sample;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
import com.liferay.contacts.model.Entry;
import com.liferay.contacts.service.EntryLocalService;
import com.liferay.counter.kernel.service.CounterLocalService;
import com.liferay.portal.kernel.dao.orm.DynamicQuery;
import com.liferay.portal.kernel.dao.orm.QueryUtil;
import com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.kernel.util.Validator;
@Component(
immediate = true,
property = {
HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN + "=/*"
},
service = Servlet.class
)
public class SampleServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(
HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
String fullNameParameter = ParamUtil.getString(request, "fullName");
if (Validator.isNull(fullNameParameter)) {
writer.println("<h2>Hello You!</h2>");
writer.println("Do you want to sign up for this thing?<br/>");
writer.println("<form action='/join' method='post'>");
writer.println("<input type='text' name='fullName' placeholder='Full Name'><br>");
writer.println("<input type='text' name='emailAddress' placeholder='Email Address'><br>");
writer.println("<input type='submit' value='Sign Up'><br>");
writer.println("</form>");
List<Entry> entries = _entryLocalService.getEntries(QueryUtil.ALL_POS, QueryUtil.ALL_POS);
if (entries.isEmpty()) {
writer.println("I'm so lonely! :(<br/>");
}
else {
writer.println("Here's a list of others who've already signed up:<br/>");
for (Entry entry : _entryLocalService.getEntries(QueryUtil.ALL_POS, QueryUtil.ALL_POS)) {
writer.println(String.format("%s <%s><br/>", entry.getFullName(), entry.getEmailAddress()));
}
}
return;
}
String emailAddressParameter = ParamUtil.getString(request, "emailAddress");
if (Validator.isNull(emailAddressParameter)) {
writer.println(String.format("Ooops! %s, you forgot your emailAddress :(<br/>", fullNameParameter));
writer.println("<a href='/'>Retry?</a>");
return;
}
DynamicQuery dynamicQuery = _entryLocalService.dynamicQuery();
dynamicQuery.add(RestrictionsFactoryUtil.eq("emailAddress", emailAddressParameter));
long count = _entryLocalService.dynamicQueryCount(dynamicQuery);
if (count > 0) {
writer.println(String.format("Ooops! Someone already registered with the email address <%s> :(<br/>", emailAddressParameter));
writer.println("<a href='/'>Retry?</a>");
return;
}
long entryId = _counterLocalService.increment();
Entry entry = _entryLocalService.createEntry(entryId);
entry.setFullName(fullNameParameter);
entry.setEmailAddress(emailAddressParameter);
_entryLocalService.updateEntry(entry);
writer.println(String.format("Great! Thanks for signing up %s :D<br/>", fullNameParameter));
writer.println("<a href='/'>Go Back!</a>");
}
@Reference
private CounterLocalService _counterLocalService;
@Reference
private EntryLocalService _entryLocalService;
}
Note how it uses OSGi Declarative Services to reference an instance of Liferay
Core's CounterLocalService
and Contacts API's EntryLocalService
.