How to Use Jsp/Jstl to Generate Dynamic CSS/JavaScript Files

Is it possible to use JSP/JSTL to generate dynamic css/javascript files?

What you want to do is assign the *.css servlet mapping to the JSPServlet.

In most containers, you will see a mapping like this (this is from Glassfish, in it's default-web.xml):

  <servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>

Here, it is declaring the JSP servlet, and mapping "*.jsp" to it. So, in this case, the JSP servlet reference name is, simply 'jsp'.

So you would want to add:

<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.css</url-pattern>
</servlet-mapping>

When you do that, "suddenly" ALL of your CSS files are, effectively, JSPs, so you can do with them whatever you want.

The detail is I don't know if 'jsp' is the same for ALL containers, so your web.xml MAY NOT be portable.

But that's the gist of what you want to do. If you don't want ALL CSS to be JSPs, you could put the files in their own directory, and map that to the JSP servlet. Then ANYTHING you put in there would be a JSP (css, js, etc.)

i'm dynamically generating css and js using jsp/jstl/spring. How to place this result in link tag at head section. not in script tag in head section

I achieved this.

I created a new route /resource/css|js , I use internal resource view resolver - (folder).

My link url is moething like /resource/css/qtn/page1.css.

In controller /resources/{type}/{page}.css.

Here i get type and page values and used this in my dynamic jsp page to achieve my result.

For security i checked the url is it comes from my own page or not.

Hint :

While using internal resource view resolver it will throw error when you pass page values directly to resource url because sometimes page url will be /page1/block1 so our controller throws error.

To solve this i passed page values as /resource/css/qtn/page1.css?sub=${page}.

In controller we can easily cath this sub value and send to our dynamic jsp as model.

how to use dynamic css in jsp file base on param value

You can append the request attribute's value to the class attribute in the JSP :

<td class="td-type<%=type%>">

As a side note, the use of scriptlets (java code in JSP's) is strongly discouraged. Use JSTL and EL instead. In this question you'll find out Why and how to avoid Java Code in JSP files.

<td class="td-type${type}">

Or, if you wanted to implement an if-else like construct, for instance :

<c:choose>
<c:when test="${type eq "2"}">
<c:set var="varclass" value="td-type2"/>
</c:when>
<c:otherwise>
<c:set var="varclass" value="td-type1"/>
</c:otherwise>
</c:choose>
<td class="${varClass}">

Adding external resources (CSS/JavaScript/images etc) in JSP

The reason that you get the 404 File Not Found error, is that your path to CSS given as a value to the href attribute is missing context path.

An HTTP request URL contains the following parts:

http://[host]:[port][request-path]?[query-string]

The request path is further composed of the following elements:

  • Context path: A concatenation of a forward slash (/) with the context
    root
    of the servlet's web application. Example: http://host[:port]/context-root[/url-pattern]

  • Servlet path: The path section that corresponds to the component
    alias that activated this request. This path starts with a forward
    slash (/).

  • Path info: The part of the request path that is not part of the
    context path or the servlet path.

Read more here.


Solutions

There are several solutions to your problem, here are some of them:

1) Using <c:url> tag from JSTL

In my Java web applications I usually used <c:url> tag from JSTL when defining the path to CSS/JavaScript/image and other static resources. By doing so you can be sure that those resources are referenced always relative to the application context (context path).

If you say, that your CSS is located inside WebContent folder, then this should work:

<link type="text/css" rel="stylesheet" href="<c:url value="/globalCSS.css" />" />

The reason why it works is explained in the "JavaServer Pages™ Standard Tag Library" version 1.2 specification chapter 7.5 (emphasis mine):

7.5 <c:url>
Builds a URL with the proper rewriting rules applied.

...

The URL must be either an absolute URL
starting with a scheme (e.g. "http:// server/context/page.jsp") or a
relative URL as defined by JSP 1.2 in JSP.2.2.1 "Relative URL
Specification". As a consequence, an implementation must prepend the
context path to a URL that starts with a slash
(e.g. "/page2.jsp") so
that such URLs can be properly interpreted by a client browser.

NOTE
Don't forget to use Taglib directive in your JSP to be able to reference JSTL tags. Also see an example JSP page here.


2) Using JSP Expression Language and implicit objects

An alternative solution is using Expression Language (EL) to add application context:

<link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/globalCSS.css" />

Here we have retrieved the context path from the request object. And to access the request object we have used the pageContext implicit object.


3) Using <c:set> tag from JSTL

DISCLAIMER
The idea of this solution was taken from here.

To make accessing the context path more compact than in the solution №2, you can first use the JSTL <c:set> tag, that sets the value of an EL variable or the property of an EL variable in any of the JSP scopes (page, request, session, or application) for later access.

<c:set var="root" value="${pageContext.request.contextPath}"/>
...
<link type="text/css" rel="stylesheet" href="${root}/globalCSS.css" />

IMPORTANT NOTE
By default, in order to set the variable in such manner, the JSP that contains this set tag must be accessed at least once (including in case of setting the value in the application scope using scope attribute, like <c:set var="foo" value="bar" scope="application" />), before using this new variable. For instance, you can have several JSP files where you need this variable. So you must ether a) both set the new variable holding context path in the application scope AND access this JSP first, before using this variable in other JSP files, or b) set this context path holding variable in EVERY JSP file, where you need to access to it.


4) Using ServletContextListener

The more effective way to make accessing the context path more compact is to set a variable that will hold the context path and store it in the application scope using a Listener. This solution is similar to solution №3, but the benefit is that now the variable holding context path is set right at the start of the web application and is available application wide, no need for additional steps.

We need a class that implements ServletContextListener interface. Here is an example of such class:

package com.example.listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class AppContextListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent event) {
ServletContext sc = event.getServletContext();
sc.setAttribute("ctx", sc.getContextPath());
}

@Override
public void contextDestroyed(ServletContextEvent event) {}

}

Now in a JSP we can access this global variable using EL:

<link type="text/css" rel="stylesheet" href="${ctx}/globalCSS.css" />

NOTE
@WebListener annotation is available since Servlet version 3.0. If you use a servlet container or application server that supports older Servlet specifications, remove the @WebServlet annotation and instead configure the listener in the deployment descriptor (web.xml). Here is an example of web.xml file for the container that supports maximum Servlet version 2.5 (other configurations are omitted for the sake of brevity):

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
...
<listener>
<listener-class>com.example.listener.AppContextListener</listener-class>
</listener>
...
</webapp>


5) Using scriptlets

As suggested by user @gavenkoa you can also use scriptlets like this:

<%= request.getContextPath() %>

For such a small thing it is probably OK, just note that generally the use of scriptlets in JSP is discouraged.


Conclusion

I personally prefer either the first solution (used it in my previous projects most of the time) or the second, as they are most clear, intuitive and unambiguous (IMHO). But you choose whatever suits you most.


Other thoughts

You can deploy your web app as the default application (i.e. in the default root context), so it can be accessed without specifying context path. For more info read the "Update" section here.

Using scriptlets inside a .CSS file?

The .jsp files are compiled by the container, so you have to write the you css class in .jsp file make sure the response content type is text/css and add the JSP file to your html like below.

<link rel="stylesheet" href="filename.jsp">

How to dynamically import javascript and css files

Sort of, yes.

boolean condition = evaluateItSomehow();
request.setAttribute("condition", condition);
request.getRequestDispatcher("/WEB-INF/page.jsp").forward(request, response);

Then in page.jsp using JSTL c:if:

<head>
<c:if test="${condition}">
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="script.js"></script>
</c:if>
...
</head>

Update: since you seem to have more than one files for this, you can even make it more flexible by just setting the desired filename suffix (or prefix, or even the entire name, what you like):

String suffix = evaluateItSomehow();
request.setAttribute("suffix", suffix);
request.getRequestDispatcher("/WEB-INF/page.jsp").forward(request, response);

and

<head>
<link rel="stylesheet" type="text/css" href="style_${suffix}.css">
<script type="text/javascript" src="script_${suffix}.js"></script>
...
</head>

If you set suffix to for example "foo", this will load style_foo.css and script_foo.js. I think this gives enough new insights.

Better way to set LI width in JSP / JSTL

You can simply do it like this :

<li style="width:${number}%;">hello</li>

The usage of c:out is useful to escape XML, for example if number content is :

<ul></ul>

the output will be

<ul></ul>

More info :

  • JSTL c:out


Related Topics



Leave a reply



Submit