How to Disable Caching of Static Assets Like .CSS and .Js in Jsf2

How to disable caching of static assets like .css and .js in JSF2?

With the setHeader() you're overridding any previously set header. Rather use addHeader() instead, or just put all values commaseparated as the header value. Here's the complete set:

response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.

Your another mistake is that a PhaseListener isn't the best place for this. It's only invoked on JSF page requests, not on static resource requests which are independently invoked by the webbrowser. In other words, only the JSF page itself has caching disabled, but all <script>, <link>, <img>, etc will generate new requests which doesn't invoke that PhaseListener because those are not JSF pages.

Rather use a Filter.

@WebFilter("/*")
public class NoCacheFilter implements Filter {

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
response.setHeader("Pragma", "no-cache"); // HTTP 1.0.
response.setDateHeader("Expires", 0); // Proxies.
chain.doFilter(req, res);
}

// ... (just keep init() and destroy() NO-OP)
}

If you target a Servlet 3.0 container (Tomcat 7, Glassfish 3, etc), then web.xml (or faces-config.xml) registration is not necessary. The @WebFilter("/*") will autoregister it and map it on an URL pattern of /* which thus covers all requests.

See also:

  • How to control web page caching, across all browsers?

Unrelated to the concrete problem, disabling the static asset caching altogether isn't the best idea. It unnecessarily costs network bandwidth. Rather look for a different solution, for example including the server startup timestamp in the query string.

E.g.

<script src="foo.js?#{startup.time}"></script>

with in faces-config.xml

<managed-bean>
<managed-bean-name>startup</managed-bean-name>
<managed-bean-class>java.util.Date</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>

This example would force the browser to reload the assets whenever the server has restarted.

make sure a JSF web page is not cached according to YSlow

response.addHeader("Expires", "Mon, 8 Aug 2006 10:00:00 GMT");

8 august 2006 is as of today definitely not a "far future" expiration date.

You need to set a real far future expiration date, e.g. 30 days after today.

response.setDateHeader("Expires", System.currentTimeMillis() + (30 * 24 * 60 * 60 * 1000L));

See also:

  • Web application performance tips and tricks

Unrelated to the concrete problem, your cache control and pragma header tells that those resources should be not cached at all. This makes no sense. Remove them. Also, using a phase listener approach instead of a filter approach is clumsy.

How to use JSF versioning for resources in jar

That's unfortunately not possible. Library versioning is not supported for resources in JAR.

You've basically 2 options:

  1. Do it the easy and ugly way, include server's startup time as query string. Given that you're using OmniFaces, you could use its builtin #{startup} managed bean referring a java.util.Date instance in application scope:

    <h:outputStylesheet ... name="some.css?#{startup.time}" />
    <h:outputScript ... name="some.js?#{startup.time}" />

    Or perhaps you've the version already as some application variable.

    <h:outputStylesheet ... name="some.css?v=#{app.version}" />
    <h:outputScript ... name="some.js?v=#{app.version}" />

    Update: Notwithstanding, this doesn't work for <h:outputStylesheet>. See also: https://github.com/javaserverfaces/mojarra/issues/3945 or https://github.com/javaee/javaserverfaces-spec/issues/1395

    It works for <h:outputScript> though, which had a very simliar bug report which was implemented pretty soon https://github.com/javaserverfaces/mojarra/issues/1216

  2. Do the same as PrimeFaces, create a custom ResourceHandler.

    public class MyVersionResourceHandler extends ResourceHandlerWrapper {

    private ResourceHandler wrapped;

    public MyVersionResourceHandler(ResourceHandler wrapped) {
    this.wrapped = wrapped;
    }

    @Override
    public Resource createResource(String resourceName) {
    return createResource(resourceName, null, null);
    }

    @Override
    public Resource createResource(String resourceName, String libraryName) {
    return createResource(resourceName, libraryName, null);
    }

    @Override
    public Resource createResource(String resourceName, String libraryName, String contentType) {
    final Resource resource = super.createResource(resourceName, libraryName, contentType);

    if (resource == null) {
    return null;
    }

    return new ResourceWrapper() {

    @Override
    public String getRequestPath() {
    return super.getRequestPath() + "&v=1.0";
    }

    @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
    public String getResourceName() {
    return resource.getResourceName();
    }

    @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
    public String getLibraryName() {
    return resource.getLibraryName();
    }

    @Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
    public String getContentType() {
    return resource.getContentType();
    }

    @Override
    public Resource getWrapped() {
    return resource;
    }
    };
    }

    @Override
    public ResourceHandler getWrapped() {
    return wrapped;
    }

    }

    Or if you happen to already use OmniFaces, it could be done simpler:

    public class YourVersionResourceHandler extends DefaultResourceHandler {

    public YourVersionResourceHandler(ResourceHandler wrapped) {
    super(wrapped);
    }

    @Override
    public Resource decorateResource(Resource resource) {
    if (resource == null || !"mylib".equals(resource.getLibraryName())) {
    return resource;
    }

    return new RemappedResource(resource, resource.getRequestPath() + "&v=1.0");
    }

    }

    Either way, to get it to run, register it as <resource-handler> in /META-INF/faces-config.xml of the JAR.

    <application>
    <resource-handler>com.example.MyVersionResourceHandler</resource-handler>
    </application>

How to disable openfaces validation?

OpenFaces have aplication context parameter.
You can learn more here:http://openfaces.org/documentation/developersGuide/installation-and-configuration.html

Prevent suffix from being added to resources when page loads

This is doable with a custom ResourceHandler which returns in createResource() a Resource which in turn returns an "unmapped" URL on Resource#getRequestPath(). You only need to add the default JSF resource prefix /javax.faces.resource/* to the <url-pattern> list of the FacesServlet mapping in order to get it to be triggered anyway.

Further, you need to override isResourceRequest() to check if the URL starts with the JSF resource prefix and also the handleResourceRequest() to locate and stream the proper resource.

All with all, this should do:

public class UnmappedResourceHandler extends ResourceHandlerWrapper {

private ResourceHandler wrapped;

public UnmappedResourceHandler(ResourceHandler wrapped) {
this.wrapped = wrapped;
}

@Override
public Resource createResource(final String resourceName, final String libraryName) {
final Resource resource = super.createResource(resourceName, libraryName);

if (resource == null) {
return null;
}

return new ResourceWrapper() {

@Override
public String getRequestPath() {
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
String mapping = externalContext.getRequestServletPath();

if (externalContext.getRequestPathInfo() == null) {
mapping = mapping.substring(mapping.lastIndexOf('.'));
}

String path = super.getRequestPath();

if (mapping.charAt(0) == '/') {
return path.replaceFirst(mapping, "");
}
else if (path.contains("?")) {
return path.replace(mapping + "?", "?");
}
else {
return path.substring(0, path.length() - mapping.length());
}
}

@Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
public String getResourceName() {
return resource.getResourceName();
}

@Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
public String getLibraryName() {
return resource.getLibraryName();
}

@Override // Necessary because this is missing in ResourceWrapper (will be fixed in JSF 2.2).
public String getContentType() {
return resource.getContentType();
}

@Override
public Resource getWrapped() {
return resource;
}
};
}

@Override
public boolean isResourceRequest(FacesContext context) {
return ResourceHandler.RESOURCE_IDENTIFIER.equals(context.getExternalContext().getRequestServletPath());
}

@Override
public void handleResourceRequest(FacesContext context) throws IOException {
ExternalContext externalContext = context.getExternalContext();
String resourceName = externalContext.getRequestPathInfo();
String libraryName = externalContext.getRequestParameterMap().get("ln");
Resource resource = context.getApplication().getResourceHandler().createResource(resourceName, libraryName);

if (resource == null) {
super.handleResourceRequest(context);
return;
}

if (!resource.userAgentNeedsUpdate(context)) {
externalContext.setResponseStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
}

externalContext.setResponseContentType(resource.getContentType());

for (Entry<String, String> header : resource.getResponseHeaders().entrySet()) {
externalContext.setResponseHeader(header.getKey(), header.getValue());
}

ReadableByteChannel input = null;
WritableByteChannel output = null;

try {
input = Channels.newChannel(resource.getInputStream());
output = Channels.newChannel(externalContext.getResponseOutputStream());

for (ByteBuffer buffer = ByteBuffer.allocateDirect(10240); input.read(buffer) != -1; buffer.clear()) {
output.write((ByteBuffer) buffer.flip());
}
}
finally {
if (output != null) try { output.close(); } catch (IOException ignore) {}
if (input != null) try { input.close(); } catch (IOException ignore) {}
}
}

@Override
public ResourceHandler getWrapped() {
return wrapped;
}

}

Register it as follows in faces-config.xml:

<application>
<resource-handler>com.example.UnmappedResourceHandler</resource-handler>
</application>

Extend the FacesServlet URL pattern with ResourceHandler.RESOURCE_IDENTIFIER:

<servlet-mapping>
<servlet-name>facesServlet</servlet-name>
<url-pattern>*.xhtml</url-pattern>
<url-pattern>/javax.faces.resource/*</url-pattern>
</servlet-mapping>

How do I override default PrimeFaces CSS with custom styles?

There are several things you need to take into account of which one or more might be relevant you your specific case

Load your CSS after PrimeFaces one

You need to ensure that your CSS is loaded after the PrimeFaces one. You can achieve this by placing the <h:outputStylesheet> referencing your CSS file inside <h:body> instead of <h:head>:

<h:head>
...
</h:head>
<h:body>
<h:outputStylesheet name="style.css" />
...
</h:body>

JSF will automatically relocate the stylesheet to the end of the generated HTML <head> and this will thus ensure that the stylesheet is loaded after the PrimeFaces' default styles. This way the selectors in your CSS file which are exactly the same as in PrimeFaces CSS file will get precedence over the PrimeFaces one.

You'll probably also see suggestions to put it in <f:facet name="last"> of <h:head> which is understood by PrimeFaces-specific HeadRenderer, but this is unnecessarily clumsy and would break when you have your own HeadRenderer.

Understand CSS specificity

You also need to ensure that your CSS selector is at least as specific as the PrimeFaces' default CSS selector on the particular element. You need to understand CSS Specificity and Cascading and Inheritance rules. For example, if PrimeFaces declares a style by default as follows

.ui-foo .ui-bar {
color: pink;
}

and you declare it as

.ui-bar {
color: purple;
}

and the particular element with class="ui-bar" happen to have a parent element with class="ui-foo", then the PrimeFaces' one will still get precedence because that's the most specific match!

You can use the webbrowser developer tools to find the exact CSS selector. Rightclick the element in question in the webbrowser (IE9/Chrome/Firefox+Firebug) and choose Inspect Element to see it.

Partial overriding

If you need to override a style for only a specific instance of the component and not all instances of the same component, then add a custom styleClass and hook on that instead. It is another case where specificity is used/applied. For example:

<p:dataTable styleClass="borderless">
.ui-datatable.borderless tbody,
.ui-datatable.borderless th
.ui-datatable.borderless td {
border-style: none;
}

If a component does not support a styleClass and you are on jsf 2.2 or up, you can also use passtrough attributes and add a pt:class and have it end-up on the output.

<p:clock pt:class="borderless" />

Never use !important

In case you fail to properly load the CSS file in order or to figure the right CSS selector, you'll probably grab the !important workaround. This is Plain Wrong. It's an ugly workaround and not a real solution. It only confuses your style rules and yourself more in long term. The !important should only be used in order to override the values hardcoded in HTML element's style attribute from a CSS stylesheet file on (which is in turn also a bad practice, but in some rare cases unfortunately unavoidable).

See also:

  • How to reference CSS / JS / image resource in Facelets template?
  • Mozilla Developer Network > CSS > Specificity (great article, a must read!)
  • Understanding Style Precedence in CSS: Specificity, Inheritance, and the Cascade

Why do you specify the size when using malloc in C?

Simple logic: If you do not park in a legal parking space, nothing might happen but occasionally your car might get towed and you might get stuck with a huge fine. And, sometimes, as you try to find your way to the pound where your car was towed, you might get run over by a truck.

malloc gives you as many legal parking spots as you asked. You can try to park elsewhere, it might seem to work, but sometimes it won't.

For questions such as this, the Memory Allocation section of the C FAQ is a useful reference to consult. See 7.3b.

On a related (humorous) note, see also a list of bloopers by ART.



Related Topics



Leave a reply



Submit