According to TLD or attribute directive in tag file, attribute does not accept any expressions

Get the below exception when upgrade from servlet 2.3 to 3.x.

According to TLD or attribute directive in tag file, attribute does not accept any expressions

Turns out in the new jsp2.x, the Run-time Expression Value is default to false, so we need to explicitly add ‘<rtexprvalue>true</rtexprvalue>’ to make it accept jsp expression which is to be evaluated in the runtime. Also formated our rc.tld to make it jsp2.x standard.

Pay attention to <rtexprvalue>

The <rtexprvalue> is especially important because it tells you whether the value of the attribute is evaluated at translation or runtime. If the <rtexprvalue> is false, or the <rtexprvalue> isn’t defined, you can use only a String literal as that attribute’s value!

If you see this:

<attribute>
    <name>rate</name>
    <required>true</required>
    <rtexprvalue>false</rtexprvalue>
</attribute>

OR this:

image with no caption

Then you know THIS WON’T WORK!

image with no caption
This link gave me the original answer.
Advertisements

export html to excel/pdf with angularjs and servlet(spring mvc)

As we konw, the XHR request that angularjs uses cannot init a download.

iFrame version

One way to achieve is thru an invisible iframe and assign the url to the iframe for download.

The basic idea is to construct a iframe on the fly and append a html FORM in it and then submit the form to server in javascript. On the server side, return the stream with ‘Content-disposition’ header.

Angularjs code

just pass in fileType(here is pdf/xls) in your ng-click or from some other delegations

	$scope.exportReport = function (fileType) {
		var printIframe = angular.element("<iframe class='hidden'>");
		var formElement = angular.element("<form>");
		formElement.attr("action", "/scrc/rest/trace/export/" + fileType);
		formElement.attr("method", "post");
		var contentElement = angular.element("<input>").attr("type", "hidden").attr("name",
				"domContent").val(angular.element('.report-outer-wrapper').html());
		//build file name
		var fileName = $scope.reportData.mpid + '_' + $scope.reportData.periodDate.replace(' ', '_') + '_' + $scope.reportData.viewType;
		var fileNameElement = angular.element("<input>").attr("type", "hidden").attr("name",
				"fileName").val(fileName);
		formElement.append(contentElement);
		formElement.append(fileNameElement);
		printIframe.append(formElement);
		angular.element('body').append(printIframe);
		formElement.submit();
	};

Java code in Spring controller

The buildExportContent() is just to put the style and content into a html’s head and body and return a String.

    @RequestMapping(value = "/export/{fileType}", method = RequestMethod.POST)
    @ResponseBody
    public void handleExport(@PathVariable("fileType") String fileType, @RequestParam("domContent") String domContent,
        @RequestParam("fileName") String fileName, HttpServletResponse response) throws IOException, DocumentException
    {
        OutputStream out = response.getOutputStream();
        String finalFileName = fileName + "." + fileType;
        response.setHeader("Content-disposition", "attachment; filename=" + finalFileName);
        String styles = "";
        if ("pdf".equalsIgnoreCase(fileType))
        {
            String bootstrapStylePath = context.getRealPath("/css/bootstrap/bootstrap.min.css");
            styles = Files.toString(new File(bootstrapStylePath), StandardCharsets.UTF_8);
            //we need xhtml here.
            String exportContent = reportService.buildExportContent(domContent, styles).replaceAll("<br>", "<br/>");
            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocumentFromString(exportContent);
            renderer.layout();
            renderer.createPDF(out);
        }
        else if ("xls".equalsIgnoreCase(fileType))
        {
            styles = " table{border:1px solid gray !important}; " + " td, th {border:1px solid gray !important}; ";
            //we need xhtml here.
            String exportContent = reportService.buildExportContent(domContent, styles).replaceAll("<br>", "<br/>");
            out.write(exportContent.getBytes(StandardCharsets.UTF_8));
        }
        out.close();

    }

Blob version

another way is to use Blob and generate the url with browser. however only IE>10 support this. WHat a sad story.

<a download="content.txt" ng-href="{{ url }}">download

in your controller:

var content = 'file content';
var blob = new Blob([ content ], { type : 'text/plain' });
$scope.url = (window.URL || window.webkitURL).createObjectURL( blob );

in order to enable the URL:

app = angular.module(...);
app.config(['$compileProvider',
    function ($compileProvider) {
        $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|blob):/);
});

Please note that

Each time you call createObjectURL(), a new object URL is created, even if you’ve already created one for the same object. Each of these must be released by calling URL.revokeObjectURL() when you no longer need them. Browsers will release these automatically when the document is unloaded; however, for optimal performance and memory usage, if there are safe times when you can explicitly unload them, you should do so.

Source: MDN

Another blob version

Pure html Form version

Bassically use a HTML form with ng-submit, on return add ‘Content-disposition’ header so that the page would not re-direct but just a file download.

code in Angular:

I did some customization to the form element so that the content of the report could be posted to the server side for the flying saucer to render and process to pdf file.

	$scope.printExcel = function (e) {
		var form = angular.element(e.target);
		form.attr("action", "/scrc/rest/trace/pdf");
		var contentElement = angular.element("<input>").attr("type", "hidden").attr("name",
				"printContent").val(angular.element('.report-outer-wrapper').html());
		form.append(contentElement);
		form.submit();
	};

HTML:

                       <form name="printPdfForm" method="POST" ng-submit="printPdf($event)">
                           <button type="submit" class="print-button">
                              <img src="/scrc/images/icon_pdf.gif" border="0" alt="download pdf"/>
                           </button>
                        </form>

Code in Spring controller.

The excel version is basically just return to the client the same content with a Content-disposition header which is for the origin server to suggest a default filename if the user requests that the content is saved to a file. It would be nice if we could do this in the client side with javascipt so that we can save a trip(Please notify me if you know how to do it.).

    @RequestMapping(value = "/pdf", method = RequestMethod.POST)
    @ResponseBody
    public void handlePdf(@RequestParam("printContent") String printContent, HttpServletResponse response) throws Exception
    {
        String fileName = "result.pdf";
        response.setHeader("Content-disposition", "attachment; filename=" + fileName);
        OutputStream out = response.getOutputStream();

        String content = printContent.replaceAll("<br>", "<br/>");
        ITextRenderer renderer = new ITextRenderer();
        renderer.setDocumentFromString(getPdfHtml(content));
        renderer.layout();

        renderer.createPDF(out);
        out.close();
    }

    private String getPdfHtml(String content) throws Exception
    {
        StringBuilder sb = new StringBuilder();
        sb.append("<html>");
        sb.append("<head><style language='text/css'>");
        //if performance is bad we could ignore this big css
        String rcStylePath = context.getRealPath("/css/bootstrap/bootstrap.min.css");
        sb.append(Files.toString(new File(rcStylePath), StandardCharsets.UTF_8));
        sb.append("</style></head>");
        sb.append("<body>");
        sb.append(content);

        sb.append("</body>");
        sb.append("</html>");

        return sb.toString();
    }

    @RequestMapping(value = "/excel", method = RequestMethod.POST)
    @ResponseBody
    public void handleExcel(@RequestParam("printContent") String printContent, HttpServletResponse response) throws IOException
    {
        String fileName = "result.xls";
        response.setHeader("Content-disposition", "attachment; filename=" + fileName);
        OutputStream out = response.getOutputStream();

        out.write(getExcelHtml(printContent).getBytes(StandardCharsets.UTF_8));
        out.close();
    }

    private String getExcelHtml(String content) throws IOException
    {
        StringBuilder sb = new StringBuilder();
        sb.append("<html>");
        sb.append("<head><style language='text/css'>");

        sb.append(" table{border:1px solid gray !important}; ");
        sb.append(" td, th {border:1px solid gray !important}; ");
        sb.append("</style></head>");
        sb.append("<body>");
        sb.append(content);

        sb.append("</body>");
        sb.append("</html>");

        return sb.toString();
    }

For some IE8 issue, look at my other POST

Servlet Container

1. What is a Web Server?

To know what is a Servlet container, we need to know what is a Web Server first.

web server

A web server uses HTTP protocol to transfer data. In a simple situation, a user type in a URL (e.g. http://www.programcreek.com/static.html) in browser (a client), and get a web page to read. So what the server does is sending a web page to the client. The transformation is in HTTP protocol which specifies the format of request and response message.

2. What is a Servlet Container?

As we see here, the user/client can only request static webpage from the server. This is not good enough, if the user wants to read the web page based on his input. The basic idea of Servlet container is using Java to dynamically generate the web page on the server side. So servlet container is essentially a part of a web server that interacts with the servlets.

web server & servlet container

Servlet container is the container for Servlets.

3. What is a Servlet?

Servlet is an interface defined in javax.servlet package. It declares three essential methods for the life cycle of a servlet – init(), service(), and destroy(). They are implemented by every servlet(defined in SDK or self-defined) and are invoked at specific times by the server.

  1. The init() method is invoked during initialization stage of the servlet life cycle. It is passed an object implementing the javax.servlet.ServletConfig interface, which allows the servlet to access initialization parameters from the web application.
  2. The service() method is invoked upon each request after its initialization. Each request is serviced in its own separate thread. The web container calls the service() method of the servlet for every request. The service() method determines the kind of request being made and dispatches it to an appropriate method to handle the request.
  3. The destroy() method is invoked when the servlet object should be destroyed. It releases the resources being held.

From the life cycle of a servlet object, we can see that servlet classes are loaded to container by class loader dynamically. Each request is in its own thread, and a servlet object can serve multiple threads at the same time(thread not safe). When it is no longer being used, it should be garbage collected by JVM.

Like any Java program, the servlet runs within a JVM. To handle the complexity of HTTP requests, the servlet container comes in. The servlet container is responsible for servlets’ creation, execution and destruction.

4. How Servlet container and web server process a request?

  1. Web server receives HTTP request
  2. Web server forwards the request to servlet container
  3. The servlet is dynamically retrieved and loaded into the address space of the container, if it is not in the container.
  4. The container invokes the init() method of the servlet for initialization(invoked once when the servlet is loaded first time)
  5. The container invokes the service() method of the servlet to process the HTTP request, i.e., read data in the request and formulate a response. The servlet remains in the container’s address space and can process other HTTP requests.
  6. Web server return the dynamically generated results to the correct location

The six steps are marked on the following diagram:

servlet container - life cycle

5. The role of JVM

Using servlets allows the JVM to handle each request within a separate Java thread, and this is one of the key advantage of Servlet container. Each servlet is a Java class with special elements responding to HTTP requests. The main function of Servlet contain is to forward requests to correct servlet for processing, and return the dynamically generated results to the correct location after the JVM has processed them. In most cases servlet container runs in a single JVM, but there are solutions when container need multiple JVMs.

OTHER CONCEPTS

ServletContext

When the servletcontainer (like Apache Tomcat) starts up, it will deploy and load all webapplications. When a webapplication get loaded, the servletcontainer will create the ServletContext once and keep in server’s memory. The webapp’s web.xml will be parsed and every Servlet, Filter and Listener found in web.xml will be created once and kept in server’s memory as well. When the servletcontainer shuts down, it will unload all webapplications and the ServletContext and all Servlet, Filter and Listener instances will be trashed.

HttpServletRequest and HttpServletResponse

The servletcontainer is attached to a webserver which listens on HTTP requests on a certain port number, which is usually 80. When a client (user with a webbrowser) sends a HTTP request, the servletcontainer will create new HttpServletRequest and HttpServletResponse objects and pass it through the methods of the already-created Filter and Servlet instances whose url-patternmatches the request URL, all in the same thread.

The request object provides access to all information of the HTTP request, such as the request headers and the request body. The response object provides facility to control and send the HTTP response the way you want, such as setting headers and the body (usually with HTML content from a JSP file). When the HTTP response is committed and finished, then both the request and response objects will be trashed.

HttpSession

When a client visits the webapp for the first time and/or the HttpSession is to be obtained for the first time by request.getSession(), then the servletcontainer will create it, generate a long and unique ID (which you can get by session.getId()) and store it in server’s memory. The servletcontainer will also set a Cookie in the HTTP response with JSESSIONID as cookie name and the unique session ID as cookie value.

As per the HTTP cookie specification (a contract a decent webbrowser and webserver has to adhere), the client (the webbrowser) is required to send this cookie back in the subsequent requests as long as the cookie is valid. Using a HTTP header checker tool like Firebug you can check them. The servletcontainer will determine every incoming HTTP request header for the presence of the cookie with the name JSESSIONID and use its value (the session ID) to get the associated HttpSession from server’s memory.

The HttpSession lives until it has not been used for more than the time, a setting you can specify in web.xml, which defaults to 30 minutes. So when the client doesn’t visit the webapp anymore for over 30 minutes, then the servletcontainer will trash the session. Every subsequent request, even though with the cookie specified, will not have access to the same session anymore. The servletcontainer will create a new one.

On the other hand, the session cookie on the client side has a default lifetime which is as long as the browser instance is running. So when the client closes the browser instance (all tabs/windows), then the session will be trashed at the client side. In a new browser instance the cookie associated with the session won’t be sent anymore. A new request.getSession() would return a brand new HttpSession and set a cookie with a brand new session ID.

In a nutshell

  • The ServletContext lives as long as the webapp lives. It’s been shared among all requests in allsessions.
  • The HttpSession lives as long as the client is interacting with the webapp with the same browser instance and the session hasn’t timed out at the server side yet. It’s been shared among allrequests in the same session.
  • The HttpServletRequest and HttpServletResponse lives as long as the client has sent it until the complete response (the webpage) is arrived. It is not being shared elsewhere.
  • Any Servlet, Filter and Listener lives as long as the webapp lives. They are being shared among all requests in all sessions.
  • Any attribute which you set in ServletContext, HttpServletRequest and HttpSession will live as long as the object in question lives.

Threadsafety

That said, your major concern is possibly threadsafety. You should now have learnt that Servlets and filters are shared among all requests. That’s the nice thing of Java, it’s multithreaded and different threads (read: HTTP requests) can make use of the same instance. It would otherwise have been too expensive to recreate it on every request.

But you should also realize that you should never assign any request or session scoped data as aninstance variable of a servlet or filter. It will be shared among all other requests in other sessions. That’sthreadunsafe! The below example illustrates that:

public class MyServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

See also:

FROM HERE

Here is a good article for building Servlet Container yourself

中文解释文章

threadsafe servlet

When the Servlet container starts, it:

  1. reads web.xml;
  2. finds the declared Servlets in the classpath; and
  3. loads and instantiates each Servlet only once.

Roughly, like this:

String urlPattern = parseWebXmlAndRetrieveServletUrlPattern();
String servletClass = parseWebXmlAndRetrieveServletClass();
HttpServlet servlet = (HttpServlet) Class.forName(servletClass).newInstance();
servlet.init();
servlets.put(urlPattern, servlet); // Similar to a map interface.

Those Servlets are stored in memory and reused every time the request URL matches the Servlet’s associated url-pattern. The servlet container then executes code similar to:

for (Entry<String, HttpServlet> entry : servlets.entrySet()) {
    String urlPattern = entry.getKey();
    HttpServlet servlet = entry.getValue();
    if (request.getRequestURL().matches(urlPattern)) {
        servlet.service(request, response);
        break;
    }
}

The GenericServlet#service() on its turn decides which of the doGet()doPost(), etc.. to invoke based on HttpServletRequest#getMethod().

You see, the servletcontainer reuses the same servlet instance for every request. In other words: the servlets are shared among every request. That’s why it’s extremely important to write servlet code the threadsafe manner –which is actually simple: just do not assign request or session scoped data as servlet instance variables, but just as method local variables. E.g.

public class MyServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

 there is only one instance of the servlet which is reused for multiple requests from multiple clients. This leads to two important rules:

  • don’t use instance variables in a servlet, except for application-wide values, most often obtained from context parameters.
  • don’t make methods synchronized in a servlet

thread-safe servlets

Servlets are not thread safe. If you want to make it Servlet as Thread safe, you can implement SingleThreadInterface which is a blank Interface there is no methods (this is not recomend method, because it could slow the performance of your page) or you can synchronize methods by using synchronized keyword.

The SingleThreadModel interface is deprecated @ servlet2.4 and due for removal. The problem is that it causes a performance hit while only providing the illusion of thread safety.

As we know, when building a Website, we are primarily interested in the servlet’s two methods: doPost() and doGet(). The underlining method for both methods is service(). Later, I look closer at these methods and how they relate to thread safety, but first let’s review the concept of a thread.

A thread is a single execution process; in other words, an individual, sequential flow of control within a program. When we say that a program is multithreaded, we are not implying that the program runs two separate instances simultaneously (as if you concurrently executed the program twice from the command line). Rather, we are saying that the same instance (executed only once) spawns multiple threads that process this single instance of code. This means that more than one sequential flow of control runs through the same memory block.

So what do we mean by thread-safe, you ask? When multiple threads execute a single instance of a program and therefore share memory, multiple threads could possibly be attempting to read and write to the same place in memory. Let’s look at an example. If we have a multithreaded program, we will have multiple threads processing the same instance (see Figure 1).

Figure 1. A multithreaded application

What happens when Thread-A examines variableinstanceVar? Notice how Thread-B has just incremented instanceVar. The problem here isThread-A has written to the instanceVar and is not expecting that value to change unless Thread-Aexplicitly does so. Unfortunately Thread-B is thinking the same thing regarding itself; the only problem is they share the same variable. This issue is not unique to servlets. It is a common programming problem only present when multithreading an application. You are probably thinking; “Well I didn’t ask for multithreading. I just want a servlet!” And a servlet is what you have. Let me introduce you to our friend the servlet container.

Your servlet container is no dummy

A lot of magic happens between the Web browser’s HTTP request and the code we write within the doGet() and doPost()methods. The servlet container makes this “magic” possible. Like any Java program, the servlet must run within a JVM, but for a Web application, we also have the complexity of handling HTTP requests—that’s where the servlet container comes in. The servlet container is responsible for your servlets’ creation, destruction, and execution; the sequence of these events is referred to as the servlet’s lifecycle.

The servlet’s lifecycle is an important topic, and thus, you will find it on Sun’s Java certification exam. The reason for its importance is primarily because so much of the servlet’s lifecycle is outside the programmer’s control. We do not worry a lot (for the most part) about how many of our servlet’s instances exist at runtime. Nor are we generally concerned about memory utilization regarding the creation and destruction of our servlets. The reason for our lack of concern is because the servlet container handles this for us (yes, more magic).

Read More

filter 和 aop

现在AOP的设计开发理念在软件开发中用的越来越广泛,在我们开发的软件中也广泛进行了使用。而最常用的就是filter和interceptor。
Filter

该过滤器的方法是创建一个类XXXFilter实现此接口,并在该类中的doFilter方法中声明过滤规则,然后在配置文件web.xml中声明他所过滤的路径
<filter>
<filter-name>XXXFilter</filter-name>
<filter-class>
com.web.util.XXXFilter
</filter-class>
</filter>

<filter-mapping>
<filter-name>XXXFilter</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>

Interceptor

该过滤器的方法也是创建一个类XXXInterceptor实现此接口,在该类中intercept方法写过滤规则,不过它过滤路径的方法和Filter不同,它与strut.xml结合使用(其他开发框架各自有自己的切入点),

创建一个strus.xml的子配置文件struts-l99-default.xml,它继承与struts2的struts-default,此配置文件是其他子配置文件的父类,只要是继承与该文件的配置文件所声明的路径都会被它过滤 如下
<package name=”XXX-default” namespace=”/” extends=”struts-default”>

<interceptors>
<interceptor name=”authentication” />

<interceptor-stack name=”user”>
<interceptor-ref name=”defaultStack” />
<interceptor-ref name=”authentication” />
</interceptor-stack>

<interceptor-stack name=”user-submit”>
<interceptor-ref name=”user” />
<interceptor-ref name=”token” />
</interceptor-stack>

 

<interceptor-stack name=”guest”>
<interceptor-ref name=”defaultStack” />
</interceptor-stack>

 

<interceptor-stack name=”guest-submit”>
<interceptor-ref name=”defaultStack” />
<interceptor-ref name=”token” />
</interceptor-stack>

 

</interceptors>
<default-interceptor-ref name=”user” />

</package>
比较一,filter基于回调函数,我们需要实现的filter接口中doFilter方法就是回调函数,而interceptor则基于java本身的反射机制,这是两者最本质的区别。
比较二,filter是依赖于servlet容器的,即只能在servlet容器中执行,很显然没有servlet容器就无法来回调doFilter方法。而interceptor与servlet容器无关。
比较三,Filter的过滤范围比Interceptor大,Filter除了过滤请求外通过通配符可以保护页面,图片,文件等等,而Interceptor只能过滤请求。
比较四,Filter的过滤例外一般是在加载的时候在init方法声明,而Interceptor可以通过在xml声明是guest请求还是user请求来辨别是否过滤。

JSP Servlet Filter

Basic

Servlet Filters are the latest components that are added in Servlet 2.3 specifications. These filters are used basically for intercepting and modifying requests and response from server.  Consider a scenario where you want to check session from the every users request and if it is valid then only you want to let the user access the page. You can acheive this by checking sessions on all the servlet pages (or JSP pages) which users queries or you can do this by using Filter.

import java.io.IOException;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class LogFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse res,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        //Get the IP address of client machine.
        String ipAddress = request.getRemoteAddr();
        //Log the IP address and current timestamp.
        System.out.println("IP "+ipAddress + ", Time "
                            + new Date().toString());
        chain.doFilter(req, res);
    }
    public void init(FilterConfig config) throws ServletException {
        //Get init parameter
        String testParam = config.getInitParameter("test-param");
        //Print the init parameter
        System.out.println("Test Param: " + testParam);
    }
    public void destroy() {
        //add code to release any resource
    }
}
in Web.xml file:
<filter>
    <filter-name>LogFilter</filter-name>
    <filter-class>
        net.viralpatel.servlet.filters.LogFilter
    </filter-class>
    <init-param>
        <param-name>test-param</param-name>
        <param-value>This parameter is for testing.</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>LogFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

process sequence

Any processing you want to occur before the target resource is invoked must be prior to the chain doFilter() call. Any processing you want to occur after the completion of the target resource must be after the chain doFilter() call. This can include directly setting headers on the response.

filter-cycle