4.5.5. Registering DispatcherServlet from Application Component
In this section you will learn how to propagate the servlets and filters configuration from an application component to the owning application. To avoid the duplication of code in the web.xml file, you need to register your servlets and filters in the component using the special ServletRegistrationManager
bean.
The most common case of servlets registration is described through the example of HTTP servlet registration. Let’s consider a more complex example: an application component with a custom implementation of DispatcherServlet
for processing web requests.
This servlet loads its config from the demo-dispatcher-spring.xml
file, so to see it working you should create an empty file with such name in the root source directory (e.g. web/src
).
public class WebDispatcherServlet extends DispatcherServlet {
private volatile boolean initialized = false;
@Override
public String getContextConfigLocation() {
String configFile = "demo-dispatcher-spring.xml";
File baseDir = new File(AppContext.getProperty("cuba.confDir"));
String[] tokenArray = new StrTokenizer(configFile).getTokenArray();
StringBuilder locations = new StringBuilder();
for (String token : tokenArray) {
String location;
if (ResourceUtils.isUrl(token)) {
location = token;
} else {
if (token.startsWith("/"))
token = token.substring(1);
File file = new File(baseDir, token);
if (file.exists()) {
location = file.toURI().toString();
} else {
location = "classpath:" + token;
}
}
locations.append(location).append(" ");
}
return locations.toString();
}
@Override
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
ApplicationContext parent = AppContext.getApplicationContext();
wac = createWebApplicationContext(parent);
}
onRefresh(wac);
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
return wac;
}
@Override
public void init(ServletConfig config) throws ServletException {
if (!initialized) {
super.init(config);
initialized = true;
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
_service(response);
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
_service(res);
}
private void _service(ServletResponse res) throws IOException {
String testMessage = AppContext.getApplicationContext().getBean(Messages.class).getMainMessage("testMessage");
res.getWriter()
.write("WebDispatcherServlet test message: " + testMessage);
}
}
To register DispatcherServlet
, you have to load the class manually, instantiate it and initialize, otherwise different ClassLoaders may cause an issue in case of SingleWAR/SingleUberJAR deployment. Moreover, the custom DispatcherServlet
should be ready to double initialization - first time we initialize it manually, second time it is initialized by a servlet container.
Here is an example of a component that initializes WebDispatcherServlet
:
@Component
public class WebInitializer {
private static final String WEB_DISPATCHER_CLASS = "com.demo.comp.web.WebDispatcherServlet";
private static final String WEB_DISPATCHER_NAME = "web_dispatcher_servlet";
private final Logger log = LoggerFactory.getLogger(WebInitializer.class);
@Inject
private ServletRegistrationManager servletRegistrationManager;
@EventListener
public void initialize(ServletContextInitializedEvent e) {
Servlet webDispatcherServlet = servletRegistrationManager.createServlet(e.getApplicationContext(), WEB_DISPATCHER_CLASS);
ServletContext servletContext = e.getSource();
try {
webDispatcherServlet.init(new AbstractWebAppContextLoader.CubaServletConfig(WEB_DISPATCHER_NAME, servletContext));
} catch (ServletException ex) {
throw new RuntimeException("Failed to init WebDispatcherServlet");
}
servletContext.addServlet(WEB_DISPATCHER_NAME, webDispatcherServlet)
.addMapping("/webd/*");
}
}
The createServlet()
method of the injected ServletRegistrationManager
bean takes the application context from ServletContextInitializedEvent
and the fully-qualified name of the WebDispatcherServlet
class. In order to initialize the servlet, we pass the instance of ServletContext
obtained from ServletContextInitializedEvent
and the servlet name.
The servlet is registered with mapping webd
and will be available at /app/webd/
or /app-core/webd/
depending on the application context.