Showing posts with label JSTL. Show all posts
Showing posts with label JSTL. Show all posts

Tuesday, February 23, 2016

HSQLDB ignores column aliases in select statement by default

Keywords:
HSQL HSQLDB column alias ignored NullPointerException JSTL

Problem:
I can't pin point where this change happened from the HSQLDB changelogs but somewhere between HSQL 1.8.0.7 and 2.2.5, the handling of column aliases has changed. This is particularly problematic if attempting to lookup via alias name in JSTL where failed lookup of results (due to wrong column names) come back as null.

This is a standalone JSP example:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sql" uri="http://java.sun.com/jsp/jstl/sql" %>

<sql:setDataSource var="dataSource" 
    url="jdbc:hsqldb:mem:example/aliasprob"
    driver="org.hsqldb.jdbcDriver"/>
<sql:update dataSource="${dataSource}">
    create table if not exists USERS (
        fullname varchar(255), 
        telephone varchar(255)
    );
</sql:update>
<sql:update dataSource="${dataSource}">
    insert into USERS (fullname, telephone) 
    values ('Joe Bloggs', '123-5555');
</sql:update>
<sql:update dataSource="${dataSource}">
    insert into USERS (fullname, telephone) 
    values ('Fred Twitters', '456-5555');
</sql:update>
<sql:query var="users" dataSource="${dataSource}">
    select fullname as "name"
    , telephone as "phone"
    from USERS
</sql:query>

<!DOCTYPE html>
<html>
    <head>
        <title>Alias Problems</title>
    </head>
    <body>
        <p>These are the users:</p>
        <table border="1">
            <thead>
                <tr>
                    <th>Name</th>
                    <th>Phone</th>
                </tr>
            </thead>
            <tbody>
                <c:forEach var="row" items="${users.rows}">
                    <tr>
                        <td>${row['name']}</td>
                        <td>${row['phone']}</td>
                    </tr>            
                </c:forEach>
            </tbody>
        </table>        
 </body>
</html>

<sql:update dataSource="${dataSource}">
    drop table USERS;
</sql:update>

With the above test, you get two rows in the result table with empty cells for the values. If the code above was changed to ignore the aliases used in the 'AS' statements and use the underlying column names the data would be visible. If I have code that uses column aliases in this way does the select statement have to be changed to work with HSQLDB?

Solution:
HSQLDB introduced the get_column_name property which "returns the underlying column name" (despite an alias being used) and is true by default. The documentation also states this is for "compatibility with other JDBC driver implementations". I suspect there's some inconsistency with with way the JSTL SQL tags are using ResultSet.getColumnLabel(int) vs ResultSet.getColumnName(int).

When using JSTL, there's no distinction between column labels or names when using the row['column'] syntax so using get_column_name=false in the HSQL connection URL is a quick fix - making the JSTL behaviour at least, consistent with other JDBC drivers.

In the example code above, this would look like:
<sql:setDataSource var="dataSource" 
    url="jdbc:hsqldb:mem:example/aliasprob;get_column_name=false"
    driver="org.hsqldb.jdbcDriver"/>

Monday, June 20, 2011

Convert Oracle CLOBs to String in JSTL and tag file

Keywords:
java.lang.ClassCastException oracle.sql.CLOB cast java.lang.String CLOB jstl tag requestScope requestContext pageContext jspContext

Problem:
It's annoying when SQL that works for other vendors fails for a specific one ... in this case a "text" column in a schema is defined as "clob" in the corresponding oracle schema. Problem is that this is not necessarily equivalent - especially when querying the data. This is even more complex when the SQL is in JSTL. So with the JSTL code (where textValue is a CLOB):

<sql:query var="data" >
select id,
textValue
from example
where id=?
<sql:param value="${param['id']}"/>
</sql:query>
<c:forEach items="${data.rows}" var="row">
<c:out value="${row.textValue}"/><br/>
</c:forEach>


You get the result:

oracle.sql.CLOB@e645e0
oracle.sql.CLOB@1f58913
oracle.sql.CLOB@fa6b82
...


Or if you try to use the textValue in something expecting a string, you'll get:
java.lang.ClassCastException: oracle.sql.CLOB cannot be cast to java.lang.String


How do you turn a Clob to a String without filling the JSP with vendor-specific code (leaving out the argument for not having SQL in the JSP for now)?

Solution:
Great discussion of this very issue is on the OTN Forum: JSP and CLOB. It essentially involves putting the Clob to String code in a scriptlet. To keep this vendor-neutral and take some of the "ugliness" out of the JSP I'd opt for putting this code in a tag file and stick to referencing just the java.sql.* interfaces.

Step 1: create a /WEB-INF/tags/to-string.tag tag file


(Or in a subfolder - the path must start with /WEB-INF/tags/.. if using the tagdir approach).

This takes the CLOB (or other) column value and sets it back in the request context as a String.

<%--
Can turn a CLOB to String for Oracle schema
--%>
<%@ tag body-content="empty" %>
<%@ attribute name="var" required="true" type="java.lang.String" %>
<%@ attribute name="value" required="true" type="java.lang.Object" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ tag import="java.sql.*" %>
<%@ tag import="javax.servlet.jsp.*" %>
<%
String strValue = null;
if (value == null) {
strValue = ""; // NB: oracle empty string is null
} else if (value instanceof Clob) {
Clob clob = (Clob)value;
long size = clob.length();
strValue = clob.getSubString(1, (int)size);
} else {
strValue = value.toString();
}
jspContext.setAttribute(var, strValue, PageContext.REQUEST_SCOPE);
%>


In this tag file, var is the name of the variable to define in the requestScope. Note how this is done by referencing the jspContext variable.

Step 2: Use the to-string tag for your text, clob or Other columns


This involves first defining the new taglib (putting all .tag files in tagdir in the JSP scope using the "eg" prefix in this example) and then simply using the eg:to-string tag to put the string-value of the column in a "local" requestScope variable.

<%@ taglib prefix="eg" tagdir="/WEB-INF/tags" %>
<sql:query var="data" >
select id,
textValue
from example
where id=?
<sql:param value="${param['id']}"/>
</sql:query>
<c:forEach items="${data.rows}" var="row">
<eg:to-string var="textValue" value="${row.textValue}"/>
<c:out value="${textValue}"/><br/>
</c:forEach>


Wednesday, August 18, 2010

Type coercion in JSTL - for sql:param

Keywords:
jstl sql integer string type coercion postgres serial operator does not exist: bigint = character varying

Problem:
After upgrading from PostgreSQL 8.0 to 8.4 the following JSTL that queries a table by a passed in "ID":
<sql:query var="examples" dataSource="${exampleDataSource}">
    select ExampleName as "name"
    from ExampleTable 
    where ExampleId = ?
    order by ExampleName ASC
    <sql:param value="${param['ID']}"/>
</sql:query>

Fails with the exception:
javax.servlet.jsp.JspException:
    select ExampleName as "name"
    from ExampleTable
    where ExampleId = ?
    order by ExampleName ASC

: ERROR: operator does not exist: bigint = character varying
        at org.apache.taglibs.standard.tag.common.sql.QueryTagSupport.doEndTag(QueryTagSupport.java:220)
        ....
Caused by: java.sql.SQLException: ERROR: operator does not exist: bigint = character varying
        at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1471)
        at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1256)
        at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:175)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:389)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:330)
        at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:240)
        at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.executeQuery(DelegatingPreparedStatement.java:93)
        at org.apache.taglibs.standard.tag.common.sql.QueryTagSupport.doEndTag(QueryTagSupport.java:215)


Why are there data type errors all of a sudden? ... and how do you fix it?

Solution:
There is normally Type Coercion for EL expressions but it's a bit vague for sql:param. Ideally it should coerce the param into the type required but this would require knowing the schema & the SQL being executed. So looking at the source for QueryTagSupport it will just call setObject using the default type that was supplied in the param.

Contrary to the sql:param documentation the value does not have to be a String.

To get the right type into sql:param use type coercion in EL before the param gets the value. To coerce a String to Integer, you could multiply by 1. For example:
<sql:query var="examples" dataSource="${exampleDataSource}">
    select ExampleName as "name"
    from ExampleTable 
    where ExampleId = ?
    order by ExampleName ASC
    <sql:param value="${param['ID']*1}"/>
</sql:query>

Or in two steps:
<c:set var="exampleId" value="${param['ID']*1}"/>
<sql:query var="examples" dataSource="${exampleDataSource}">
    select ExampleName as "name"
    from ExampleTable 
    where ExampleId = ?
    order by ExampleName ASC
    <sql:param value="${exampleId}"/>
</sql:query>


Why did this come up after a PostgreSQL upgrade? It seems something to do with different handling of the SERIAL data type which is now compiled to it's actual representation of integer with sequence rather than leaving it as it's "notational convenience" name. Perhaps the PostgreSQL JDBC will coerce a String to a serial but not an integer?

It's hard to say the above work around is best practice but it will be harmless for databases that handle coercion at the JDBC level and necessary for those that don't.

Friday, June 19, 2009

JSTL formatNumber as percentage gets invalid attribute error

Keywords:
fmt:formatNumber <formatNumber> invalid type attribute: "percentage" percent

Problem:
The JSTL Tag Library documentation seems to indicate that to format a number as a percentage you'd use:

<fmt:formatNumber type="percentage" value="0.5"/>


This gives the error message
<formatNumber>, invalid type attribute: "percentage"

Solution:
Although the documentation for type says "Specifies whether the value is to be formatted as number, currency, or percentage" these aren't the possible values - it's "percent" for percentage:

<fmt:formatNumber type="percent" value="0.5"/>


Notes:
If you know a good reference for JSTL I'd like to know ...

The Appendix A from JSTL in Action is not bad - it documents possible values for formatNumber type at least.

Friday, October 17, 2008

Redirect from a JSP page (to another server)

Keywords:
best practice tips JSP redirect XML

Problem:
<jsp:forward> is not for redirecting to an external server (and the address you forward to will have access the request attributes and parameters, which may be desirable, see discussion here: when to use response redirect and jsp forward.

Is using response.sendRedirect(...) ok/best-practice?
Solution:
The short answer, is it's fine. In summary, there are four approaches I can think of:

Approach #1: Scriptlet to sendRedirect

<%
    response.sendRedirect("http://www.example.com");
%>

This has worked since the introduction of JSPs (see: Implementing a Redirect in a JSP Page) ... sometimes you need the servlet API in your JSP page either because there's other content (eg HTML) that makes it reasonable not to be a pure servlet. Other times it's just convenient to have a text file compiled on the fly.

Though there are many that would disagree (Sciptlet snobs? see How to redirect a page in JSP - I don't see why you'd get wound up about scriptlets. Why would you write a servlet when one line of code in a text file gives you the same result?).

Approach #2: Use Apache JSTL and the c:redirect tag

<c:redirect url="http://www.example.com"/>

or scriptlets again:
<c:redirect url="<%=scriptlet logic%>"/>

Sure, it's 'pure' XML but you'll need the taglib definition in the JSP file and include the JSTL jars in your web application - as discussed in the notes for a previous post.

Approach #3: Refresh meta tag


You could get the JSP to produce a HTML page that contains the Refresh meta tag.
<html>
<head>
  <meta http-equiv="Refresh" content="0; url=http://www.example.com/">
</head>
<body></body>
</html>

Where 0 is the delay in seconds. If more than zero, you'd probably want to put some text on the page to explain what's happening.

Approach #4: Javascript


As above, you could get the JSP to produce a HTML page that contains javascript to perform the redirect.
<html>
<head>
<script type="text/javascript">
window.location = "http://www.example.com/";
</script>
</head>
<body></body>
</html>

You could use setTimeout to get the redirect to happen after a period of milliseconds.


Notes:
Out of curiosity I checked TCP monitor for what the browser is actually receiving when you use Approach #1 or #2 (sendRedirect or c:redirect)
HTTP/1.1 302 Moved Temporarily
Location: http://www.example.com

So it saves you from 2 lines of servlet (or scriptlet) code:
response.setStatus(302);
response.setHeader("Location", "http://www.example.com");

So the difference to Approaches #3 & #4 isn't great or much more inefficient really, in all cases it's up to the browser to go to the specified address.

Wednesday, April 09, 2008

Call a stored procedure from a JSP with JSTL

Keywords:
Call execute SQL stored procedure JSP JSTL tag library

Problem:
The JSTL SQL tag library is a useful way of getting a rapid prototype going - it's all in a plain text file and will get compile on the fly. Examples I've seen show SELECT, UPDATE, INSERT and DELETE(s) ... can a stored procedure be run?

Solution:
The short answer is yes, the key thing is to know if the stored procedure is returning a result set or not as you have two tags available:

  • <sql:query> expects a ResultSet

  • <sql:update> does not expect a ResultSet - will throw an error if gets one. You do have access to an Integer result - eg rows updated.

For example, calling a stored procedure that returns a result set:
<sql:setDataSource var="myDataSource" dataSource="jdbc/myjndiref"/>
<sql:query var="examples" dataSource="${myDataSource}">
    exec ExampleProcLoadExample ?
    <sql:param value="${exampleId}"/>
</sql:query>
<c:choose>
    <c:when test="${fn:length(examples.rows) == 0}">
        <%-- no rows returned ! --%>
    </c:when>
    <c:otherwise>
        <c:set var="example" value="${examples.rows[0]}"/>
        <%-- got your object, can access columns with '.' notation --%>
    </c:otherwise>
</c:choose>


For example, calling a stored procedure that performs an 'update' (or insert/delete) returning number of rows updated:
<sql:setDataSource var="myDataSource" dataSource="jdbc/myjndiref"/>
<sql:update var="updateCount" dataSource="${myDataSource}">
    exec ExampleProcRemoveExample ?
    <sql:param value="${exampleId}"/>
</sql:update>
<c:choose>
    <c:when test="${updateCount le 0}">
        <%-- no rows updated ! --%>
    </c:when>
    <c:otherwise>
        <%-- some row(s) has been updated --%>
        <c:out value="${updateCount} row(s) have been updated"/>
    </c:otherwise>
</c:choose>


Notes:
When calling stored procedures you're getting into RDBMS vendor specific territory. Using the recommended JDBC drivers from the DB vendor for the DB version in use may make some of this work more smoothly.

Wednesday, March 21, 2007

JSP EL expressions are being ignored by tomcat

Keywords:
tomcat JSP JSTL EL

Problem:
Hopefully this is my last post about the possible problems with JSP/web-app specs and EL. In this case, there's a JSP using EL
Eg:

<%
String myUrl = (String)session.getAttribute("SESSION_KEY_MY_URL");
pageContext.setAttribute("myUrl", returnUrl);
%>
<form name="load" action="${myUrl}" method="GET">


It's coming out in HTML with the EL untouched (but the scriptlet is definitely being run).

<form name="load" action="${myUrl}" method="GET">


Why is the EL being ignored?

Solution:
As with the previous 2 posts on this issue, you have to fix the schema reference in the web.xml (there's too many differing copies of the web.xml(s) I'm dealing with here) and make sure the JSP has the right tablib uri reference - see this post.

For the first time I found this didn't immediately solve the problem! The HTML is coming out the same with EL unevaluated. The issue is that the JSP has been compiled to TOMCAT_HOME\work\Catalina\localhost\MyApp\org\apache\jsp\jsp\myfolder\MyJSP.java and tomcat sees no need to recompile even though I've updated the spec of the webapp.

The solution is to stop tomcat, remove the contents of TOMCAT_HOME\work and restart (OR update the timestamp on the JSP source file(s) to force recompilation). The JSP will be recompiled (to the right spec 2.0 now) and the EL will be evaluated as you expect.

Wednesday, February 28, 2007

org.apache.taglibs.standard.lang.jstl.parser.ParseException: EL functions are not supported.

Keywords:
Compile JSP tomcat JSTL error functions "org.apache.taglibs.standard.lang.jstl.parser.ParseException: EL functions are not supported"

Problem:
Some fairly standard use of JSTL:
<c:out value="${fn:escapeXml(myvalue)}" />

i.e. using a function in the value field of the c:out tag gives the error:
org.apache.jasper.JasperException:
    <h3>Validation error messages from TagLibraryValidator for c</h3>
    <p>41: tag = 'out' / attribute = 'value': 
        An error occurred while parsing custom action attribute "value" with value "${fn:escapeXml(myvalue)}": 
            org.apache.taglibs.standard.lang.jstl.parser.ParseException: EL functions are not supported.</p>


Solution:
It's the old mismatched spec issue again. Same solution as described in According to TLD or attribute directive in tag file, attribute X does not accept any expressions - namely, fix the schema reference in the web.xml and make sure the JSP has the right tablib uri reference.

Thursday, January 04, 2007

JSTL TransformerFactoryImpl ClassCastException on WAS 6.0.2.11

Keywords:
JSTL TransformerFactoryImpl ClassCastException WAS 6.0.2.11 xalan JAXP core xerces

Problem:
A web application that has the following properties:
  1. includes the JAXP api jar files (in WEB-INF\lib)
  2. deploys with the class loader properties of "Parent Last"
  3. uses JSTL core
Will encounter the following stack trace from WAS when ever a JSP is loaded that contains JSTL (core reference):

JSP Processing Error
HTTP Error Code: 500
java.lang.ClassCastException: org.apache.xalan.processor.TransformerFactoryImpl
at javax.xml.transform.TransformerFactory.newInstance(Unknown Source)
at com.ibm.ws.jsp.translator.visitor.validator.PageDataImpl._getInputStream(PageDataImpl.java:125)
at com.ibm.ws.jsp.translator.visitor.validator.PageDataImpl.getInputStream(PageDataImpl.java:117)
at org.apache.taglibs.standard.tlv.JstlBaseTLV.validate(JstlBaseTLV.java:156)
at org.apache.taglibs.standard.tlv.JstlCoreTLV.validate(JstlCoreTLV.java:96)
at com.ibm.ws.jsp.translator.visitor.validator.ValidateVisitor.validateTagLib(ValidateVisitor.java:939)
at com.ibm.ws.jsp.translator.visitor.validator.ValidateVisitor.visitJspRootStart(ValidateVisitor.java:453)
at com.ibm.ws.jsp.translator.visitor.JspVisitor.processJspElement(JspVisitor.java:124)
at com.ibm.ws.jsp.translator.visitor.JspVisitor.visit(JspVisitor.java:110)
at com.ibm.ws.jsp.translator.JspTranslator.processVisitors(JspTranslator.java:121)
at com.ibm.ws.jsp.translator.utils.JspTranslatorUtil.translateJsp(JspTranslatorUtil.java:168)
at com.ibm.ws.jsp.translator.utils.JspTranslatorUtil.translateJspAndCompile(JspTranslatorUtil.java:81)
at com.ibm.ws.jsp.webcontainerext.JSPExtensionServletWrapper.translateJsp(JSPExtensionServletWrapper.java:360)
at com.ibm.ws.jsp.webcontainerext.JSPExtensionServletWrapper._checkForTranslation(JSPExtensionServletWrapper.java:329)
at com.ibm.ws.jsp.webcontainerext.JSPExtensionServletWrapper.checkForTranslation(JSPExtensionServletWrapper.java:237)
at com.ibm.ws.jsp.webcontainerext.JSPExtensionServletWrapper.handleRequest(JSPExtensionServletWrapper.java:144)
at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:3003)
at com.ibm.ws.webcontainer.webapp.WebGroup.handleRequest(WebGroup.java:221)
at com.ibm.ws.webcontainer.VirtualHost.handleRequest(VirtualHost.java:210)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:1958)
at com.ibm.ws.webcontainer.channel.WCChannelLink.ready(WCChannelLink.java:88)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:472)
at com.ibm.ws.http.channel.inbound.impl.HttpInboundLink.handleNewInformation(HttpInboundLink.java:411)
at com.ibm.ws.http.channel.inbound.impl.HttpICLReadCallback.complete(HttpICLReadCallback.java:101)
at com.ibm.ws.tcp.channel.impl.WorkQueueManager.requestComplete(WorkQueueManager.java:566)
at com.ibm.ws.tcp.channel.impl.WorkQueueManager.attemptIO(WorkQueueManager.java:619)
at com.ibm.ws.tcp.channel.impl.WorkQueueManager.workerRun(WorkQueueManager.java:952)
at com.ibm.ws.tcp.channel.impl.WorkQueueManager$Worker.run(WorkQueueManager.java:1039)
at com.ibm.ws.util.ThreadPool$Worker.run(ThreadPool.java:1470)


Solution:
In short, make sure the JSPs, JSTL refs and the web app are 2.4 compliant (see past post: What Spec?) and install the latest fix pack for WAS from IBM.

In detail, the following Problem ID is a different issue but underlying problem is the same - incorrect handling of loading XML & XSLT API classes for Parent Last apps - IBM - PK26233. The comment says it is resolved in the fix pack 6.0.2.15 for WebSphere Application Server, but I used 6.0.2.17 seeing it was newer - V6.0.2 Fix Pack 17.

Tuesday, December 12, 2006

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

Keywords:
Compile JSP tomcat JSTL error "According to TLD or attribute directive in tag file, attribute value does not accept any expressions"

Problem:
Compile error from tomcat when it encounters a JSP: "According to TLD or attribute directive in tag file, attribute value does not accept any expressions"

Solution:
For some reason the JSP is using the 1.2 JSP (and 1.0 JSTL) and EL expressions aren't understood. There's a lot of hits on the web for this but in summary there are 2 important things to do to ensure you're getting the right version of the spec:
  1. Reference the correct servlet specification in your deployment descriptor:
    <?xml version="1.0"?>
    <web-app version="2.4"
     xmlns="http://java.sun.com/xml/ns/j2ee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  2. Reference the correct JSTL uri in your JSP:
    change
    <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c'%>

    to
    <%@ taglib uri='http://java.sun.com/jsp/jstl/core' prefix='c'%>

Notes:


What Specification goes with what?
Web-app(deployment schema)2.32.42.5
http://java.sun.com/dtd/web-app_2_3.dtdhttp://java.sun.com/xml/ns/j2ee/web-app_2_4.xsdhttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd
Servlet2.32.42.5
JSP1.22.02.1
JSTL(core uri reference)1.01.11.2
http://java.sun.com/jstl/corehttp://java.sun.com/jsp/jstl/corehttp://java.sun.com/jsp/jstl/core
Tomcat4.x5.x6.x
WebSphere5.x6.x7.x (?)