tag:blogger.com,1999:blog-73058678022468516842024-02-19T06:34:47.579+02:00Albert's TechAlbert's technical blog on Java and computing in generalaltumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.comBlogger19125tag:blogger.com,1999:blog-7305867802246851684.post-34864259065383334322014-01-08T15:52:00.000+02:002014-01-08T18:10:46.226+02:00How to allow Ctrl-V pasting into Windows command line<div dir="ltr" style="text-align: left;" trbidi="on">Coming with Unix background, it's shocking to learn you cannot paste something into Windows command line using keyboard (Ctrl-V shortcut).<br />
Luckily, there is a tool that fixes that.<br />
Just open cmd.exe (not powershell) and run those two lines:<br />
<br />
<pre class="brush: powershell">@powershell -NoProfile -ExecutionPolicy unrestricted -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%systemdrive%\chocolatey\bin
cinst wincommandpaste
</pre><div><br />
As a bonus, you get <a href="http://chocolatey.org">Chocolatey</a> installed, and this is a cool tool by itself.<br />
<br />
</div></div>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-89228130335680416152013-11-09T19:16:00.000+02:002013-11-09T19:16:12.967+02:00Resource versioning with Spring MVC<div dir="ltr" style="text-align: left;" trbidi="on">
Since 3.0.4.RELEASE, Spring MVC <a href="http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html#mvc-static-resources">provides help</a> in managing web page static resources (images, CSS, javascripts). The most important for me was to make browser cache resources for a long time while loading the new version when the content changes. This is a best practice <a href="http://developer.yahoo.com/performance/rules.html#expires">recommendation</a> by YSlow and others. You just need to set a far future Expires header and change the resource URL each time the content changes.<br />
<br />
The feature description in Spring documentation looks simple, but in reality it requires some additional work:<br />
<ul style="text-align: left;">
<li>in web.xml, you should map the DispatcherServlet to root path (<span style="font-family: Courier New, Courier, monospace;"><url-pattern>/</url-pattern></span>). It was mapped to *.do previously (as usual in Spring MVC projects)</li>
<li>the location attributes relates to the root category (same level as WEB-INF). If you used to have images directory there and want to create mapping using mvc:resource, it should be like this: <span style="font-family: Courier New, Courier, monospace;"><mvc:resources mapping="/images/**" location="/images/" /></span></li>
<li>don't forget the trailing slash in the location attribute (<span style="font-family: Courier New, Courier, monospace;"><mvc:resources mapping="/images/**" location="/images/" /></span>)</li>
</ul>
Now, after images are mapped everything is working just as did before. The only difference is that images are now served by Spring DispatcherServlet instead of (Tomcat) default servlet. This will incur some perfomance penaly (measure and decide if it's ok in your case!).<br />
<br />
To introduce versioning, we need to add some string into image URLs. It could be a (Maven) version number or just whatever unique identifier. I decided to use timestamp - it's better in development environment where I have the same Maven version (1.2.3-SNAPSHOT) but the content changes all the time.<br />
<br />
<pre class="brush: xml"><mvc:resources mapping="/assets/#{properties['buildTime']}/**" location="/" cache-period="31556926" />
<util:map id="properties">
<entry key="buildTime" value="${buildTimestamp}"/>
</util:map>
</pre>
<br />
in pom.xml:<br />
<br />
<pre class="brush: xml"><properties>
<buildTimestamp>${maven.build.timestamp}</buildTimestamp>
<maven.build.timestamp.format>yyyyMMddHHmmss</maven.build.timestamp.format>
</properties>
</pre>
<br />
make sure your Spring configuration file is filtered by Maven, so that ${buildTimestamp} gets replaced by the corresponding Maven property:<br />
<br />
<pre class="brush: xml"><resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<includes>
<include>*.xml</include>
</includes>
</resource>
</resources>
</pre>
<br />
The only thing left is to replace all (or some) image URLs. One possibility (as suggested by the Spring doc) is to use <spring:eval> and <spring:url> tags on each JSP page. But I very much dislike such duplicating boilerplate. So I introduced an interceptor for all pages I need:<br />
<br />
<pre class="brush: xml"><bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<!-- You servlet mappings go here ...-->
</props>
</property>
<property name="interceptors" >
<list>
<ref bean="resourceUrlInterceptor"/>
</list>
</property>
</bean>
<bean id="resourceUrlInterceptor" class="com.example.interceptor.ResourceUrlInterceptor">
<property name="properties" ref="properties" />
</bean>
</pre>
<br />
And the interceptor itself is very simple:<br />
<br />
<pre class="brush: java">public class ResourceUrlInterceptor extends HandlerInterceptorAdapter {
private Map<String, String> properties;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
request.setAttribute("resourcesPath", response.encodeURL(request.getContextPath() + "/resources/" + properties.get("buildTime")));
return true;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
}
</pre>
<br />
Finally, we can use image links like:<br />
<br />
<pre class="brush: xml"><img src="${resourcesPath}/images/icons/myicon.png" />
</pre>
<br />
which will translate to something like:<br />
<br />
<pre class="brush: xml"><img src="/myapp/resources/20110725101003/images/icons/myicon.png" />
</pre>
<br />
Being an old fan of <a href="http://tapestry.apache.org/">Tapestry</a>, I cannot restrain myself from demonstrating its superiority :)<br />
In Tapestry, all you need to do is:<br />
<br />
<pre class="brush: xml"><img src="${context:images/icons/myicon.png}" />
</pre>
<br /></div>
altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-82155234536290612652013-11-09T18:46:00.003+02:002013-11-09T18:46:30.211+02:00Gotcha: Error handling in JSP<div dir="ltr" style="text-align: left;" trbidi="on">
Struggled a lot with error handling in JSP (yeah, JSP is not my first choice).<br />
<br />
The problem I've encountered: if an exception occurs directly in some JSP tag then:<br />
<ul style="text-align: left;">
<li> the rendered HTML appear broken at some random point</li>
<li> the error message bypasses the application log and ends up in application server's log (localhost.log in case of Tomcat)</li>
</ul>
<div>
Example problem in JSP:</div>
<br />
<pre class="brush: xml"><%@ page pageEncoding="UTF-8" contentType="text/html; charset=utf-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!-- some amount of markup here -->
<fmt:formatNumber value="100" type="currency" currencyCode="${myMissingAttribute}"/>
<!-- some more markup --></pre>
<br />
<br />
<div>
What I've found while investigating:</div>
<div>
<ol style="text-align: left;">
<li>JSP default buffer is 8k and auto flush is on. That means after rendering of each 8k chunk the result is sent back to browser. And if your error occurs later in the JSP, browser will end up with incomplete HTML.</li>
<li>To avoid having broken HTML, you should set up big enough buffer for each page and switch auto flush off, for example: <span style="font-family: Courier New, Courier, monospace;"><%@ page buffer="512kb" autoFlush="false" %></span></li>
<li>And last but not least in the JSP misery list: <span style="font-family: Courier New, Courier, monospace;"><jsp:include></span> fails silently when the page path is incorrect, no errors whatsoever in ANY log !</li>
</ol>
</div>
</div>
altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-25734174697818397842013-11-09T18:39:00.001+02:002013-11-09T18:39:32.110+02:00How to easily create local SVN repository on Mac<div dir="ltr" style="text-align: left;" trbidi="on">
Version control is addictive - once you get used to it, it's hard to go without, even on small temporary "pet" projects. You want to have a history of working versions to revert to if you get something totally messed up.<br />
But what if you don't want to commit to your usual "official" repo and also don't bother to set up <a href="http://www.assembla.com/">Assembla</a> or something? Local repository comes to rescue!<br />
<br />
If you are lucky to be on Mac it's a piece of cake to set up:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">mkdir -p ~/repositories/svn</span><br />
<span style="font-family: Courier New, Courier, monospace;">svnadmin create ~/repositories/svn/local</span><br />
<br />
And then you can use <span style="font-family: Courier New, Courier, monospace;">file:///Users/yourname/repositories/svn/local</span> as the SVN root for your projects. e.g. <span style="font-family: Courier New, Courier, monospace;">file:///Users/yourname/repositories/svn/local/MyProject</span><br />
<br />
Win!<br />
<div>
<br /></div>
</div>
altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-86973973932542207292012-01-02T11:53:00.000+02:002012-01-02T11:53:24.144+02:00Attempt to prevent XSS in JSP... failed<div dir="ltr" style="text-align: left;" trbidi="on">
As every web developer should know, you have to escape untrusted output to prevent <a href="http://en.wikipedia.org/wiki/Cross-site_scripting" style="background-color: white; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Verdana, sans-serif; font-size: 13px; line-height: 18px;">Cross-site scripting</a> vulnerabilities. Most web frameworks do it by default, but not JSP. In JSP, you have to do it manually every time, either using <span style="font-family: 'Courier New', Courier, monospace;"><c:out>...</c:out></span> or <span style="font-family: 'Courier New', Courier, monospace;">${fn:escapeXml(...)}</span>. Needless to say, it is easy to forget (especially if you are used to modern framework) and it clutters the template.<br />
<br />
It feels logical that this problem should have been solved somehow already, given that thousands of developers have been using JSP every day, for years. But I've found only one solution that looks promising: <a href="http://pukkaone.github.com/2011/01/03/jsp-cross-site-scripting-elresolver.html">http://pukkaone.github.com/2011/01/03/jsp-cross-site-scripting-elresolver.html</a>.<br />
<br />
I've given it a try and found that it basically works as expected. And the most important, you can override the escaping where needed, by using the custom tag. But there are 2 problems that prevented me from using it:<br />
<br />
<ol style="text-align: left;">
<li>The effect of this resolver is global (as expected), so you must remove existing escaping from every JSP where you have it, and re-test the whole application</li>
<li>If you use <span style="font-family: 'Courier New', Courier, monospace;"><jsp:include></span> with parameters (and I use it a lot), you must override escaping of the parameter values, to avoid double escaping</li>
</ol>
<div>
So, while the first problem is solvable (given the time and resources), the second makes the whole solution look doubtful. Instead of being careful to use escaping everywhere, now I need to be careful to override escaping on each <span style="font-family: 'Courier New', Courier, monospace;"><jsp:include></span><span style="font-family: inherit;">. And again, it creates a lot of clutter.</span></div>
</div>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-90920883237059200292011-05-15T13:24:00.002+03:002011-05-15T13:37:32.345+03:00Map of Android Market supported countries, UPDATED<div dir="ltr" style="text-align: left;" trbidi="on">
Good news, the Android Market has expanded <a href="http://www.google.com/support/androidmarket/developer/bin/answer.py?&answer=138294">list of countries where you can buy paid apps</a>. <br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP1j6lpzf_-iF5_VHPwE1UwX2dVyu2Osyaa-e6GOecftwRT8hcDEEjW24I3ZC9Znq0i79OfYEDgDtPZ-vCiLaHsVGQfozJGtQOgnHjNt-z-4Q1eGsOb5PDgwZY9c1p8V-N3wTqm4u501k/s1600/android-market-20110513.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP1j6lpzf_-iF5_VHPwE1UwX2dVyu2Osyaa-e6GOecftwRT8hcDEEjW24I3ZC9Znq0i79OfYEDgDtPZ-vCiLaHsVGQfozJGtQOgnHjNt-z-4Q1eGsOb5PDgwZY9c1p8V-N3wTqm4u501k/s400/android-market-20110513.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Click to see large image</td></tr>
</tbody></table>
Green countries - users can buy and developers can publish paid apps.<br />
Blue countries - users can buy but developers cannot publish.<br />
Yellow countries - same as blue but for some reason Google calls them "Rest of the world" (maybe some restrictions will apply).<br />
<br />
Sadly, no new countries added to support <a href="http://www.google.com/support/androidmarket/developer/bin/answer.py?answer=150324">paid apps developers</a>. </div>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-48472468559103758762011-04-04T09:38:00.000+03:002011-04-04T09:38:10.108+03:00ForkingAspect added to Common Spring Aspects<p>A small but useful addition to the <a href="http://code.google.com/p/common-spring-aspects/">common-spring-aspects</a> project: forked execution of a bean method.</p>
<p>Using the ForkAspect, it is possible to define bean methods to be executed in a separate thread. This is useful to avoid waiting on slow methods whose results we do not actually need to proceed (common examples - email sending, statistics event registration, etc).</p>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-82901470893402451442011-04-04T09:33:00.000+03:002013-11-08T15:25:26.626+02:00NoSuchAlgorithmException: SunTlsRsaPremasterSecret after Java upgrade on Mac OS X<div dir="ltr" style="text-align: left;" trbidi="on">
I've this weird problem after upgrading to new Java version on Mac. My (maven) project ran OK from command line but got the "SunTlsRsaPremasterSecret" error in Eclipse.<br />
<pre class="brush: xml">Caused by: java.security.NoSuchAlgorithmException: SunTlsRsaPremasterSecret KeyGenerator not available
at javax.crypto.KeyGenerator.<init>(DashoA13*..)
at javax.crypto.KeyGenerator.getInstance(DashoA13*..)
at com.sun.net.ssl.internal.ssl.JsseJce.getKeyGenerator(JsseJce.java:223)
at com.sun.net.ssl.internal.ssl.RSAClientKeyExchange.<init>(RSAClientKeyExchange.java:89)
</pre>
The solution was to remove Installed JREs in Eclipse configuration and add them again (using the "Search..." button).</div>
altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-25386671316245090542011-03-24T10:03:00.003+02:002011-05-13T20:28:28.653+03:00Map of Android Market supported countries<p>
As you might know, Android Market only allows people in certain countries to buy paid apps. Also, developers can only publish those applications if located in supported countries. The list of those countries is published <a href="http://market.android.com/support/bin/answer.py?hl=en&answer=138294">here</a>.
</p>
<p>
As I'm a visual type of person, I like to see maps. Surprisingly, there is no map of of Android Market supported countries available anywhere yet. So I've created one myself:
</p>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirYI4xA4AZI_2KyY1OfniG52RbdOrjDotKUdE6hS3UlTHkIL6s7vMIOgqVan9WsNod05F3ZqyxudPHVOgKUUGTm0lbL6RxPa8ON9Et6Lj2x7tBuTWDTHH3P_EGGX863Fd7usOVQGE1rOY/"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirYI4xA4AZI_2KyY1OfniG52RbdOrjDotKUdE6hS3UlTHkIL6s7vMIOgqVan9WsNod05F3ZqyxudPHVOgKUUGTm0lbL6RxPa8ON9Et6Lj2x7tBuTWDTHH3P_EGGX863Fd7usOVQGE1rOY/s400/android_market_supported_countries_for_paid_apps_and_developers.png" width="400" /></a>
</div>
<div class="separator" style="clear: both; text-align: center;">
(click to see full size)
</div>
<p>
Developers based in "green" countries can publish paid apps. Users based in "green" or "blue" countries can buy them.
</p>
<p>
As you see, all the "Western world" is covered. What is notably not covered is Eastern Europe and Asia. And that's unfortunate, as there is a lot of good developers (and potential buyers) in those regions.
</p>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-37028580860415061282010-07-28T16:12:00.000+03:002010-07-28T16:12:40.452+03:00Tomcat crashing silently<p>Sometimes I'm surprised to see strange Tomcat crash - no error message in log, the process just stopped running.</p>
<p>As usual, the root cause is between the keyboard and the chair - I have some infinite loop in the code :)
</p>
<p>Too bad that there is no log to give a hint.
So the only way to tell is to do remote debugging and find out the code line where the problem occurs.</p>
<p>Of course this can happen also on Jetty, or whatever other servlet container.</p>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-33341540557879984872010-07-07T09:40:00.003+03:002010-07-07T09:48:27.913+03:00Common Spring aspects<p>
It is amazing how many people still write some log.debug() to track execution time of their methods or create a caching proxy class for each class they want to cache.
</p>
<p>
Come on, this is done million times before, why not reuse ?
</p>
<p>
Today I've written some documentation on the <a href="http://code.google.com/p/common-spring-aspects/">Common Spring aspects</a> project. This project collects some handy aspects that I find myself using on almost every project - currently, performance logging (using <a href="http://jamonapi.sourceforge.net/">JaMon</a> library) and caching bean method invocations results (using <a href="http://ehcache.org/">Ehcache</a>). The code itself is fairly small, mostly it just delegates execution to the respective library.
</p>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-77272952082215175602010-05-20T20:16:00.004+03:002010-05-20T20:26:38.817+03:00Java for Mac OS X 10.5 Update 7 causes problem with Eclipse SVN<p>Yesterday I updated my Mac Java version to <a href="http://support.apple.com/kb/HT4140">Update 7</a> - it's always better to use new things, isn't it ? :)
Alas, after the upgrade my Eclipse Subversion plugin stopped working - every attempt of interaction with SVN (browsing, commit etc) caused freezing.</p>
<p>After long and painful investigation I've narrowed the problem: it occurs when using SVNKit with some (not all) SVN servers that use HTTPS client certificate authentication. It turned out that Java Update 7 fixes a <a href="http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2009-3555">security vulnerability</a> by disabling SSL renegotiation. As the server tries to renegotiate SSL session and client (using JSSE) refuses, the whole process enters infinite loop with SVNKit log showing "javax.net.ssl.SSLHandshakeException: renegotiation is not allowed".</p>
<p>The workaround is to re-enable SSL renegotiation in JSEE (yes, this means exposing yourself to the vulnerability again). To do this, set the JVM option <a href="http://java.sun.com/javase/javaseforbusiness/docs/TLSReadme.html">allowUnsafeRenegotiation</a>:
</p>
<pre class="brush: shell; gutter: false;">
-Dsun.security.ssl.allowUnsafeRenegotiation=true
</pre>
In case of Eclipse, it can be set in eclipse.ini.altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-48293174996040821152010-05-12T09:20:00.001+03:002010-05-12T09:20:40.593+03:00First contribution to the Tapestry 5 HowTo Wiki :)<a href="http://wiki.apache.org/tapestry/Tapestry5HowToAddAttributesConditionally">http://wiki.apache.org/tapestry/Tapestry5HowToAddAttributesConditionally</a>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-1446231023052052282010-03-31T14:01:00.002+03:002010-04-01T23:01:39.214+03:00Loading Java properties in UTF-8<p>One of the latest stupid problems I've encountered: Java *.properties files are always loaded in ISO-8859-1 encoding and there is no way to change that.
But in our project we wanted to have them in UTF-8 so that non-Latin files could be human readable. We use JSTL "fmt" tag library which loads messages from *.properties files (and guess in which encoding).</p>
<p>The simple solution I've come up is build-time native2ascii conversion. This can be done using the native2ascii-maven-plugin.
This example shows what to add to pom.xml (assuming that your *.properties files are in src/main/resources/l10n):</p>
<pre class="brush: xml">
<build>
...
<resources>
<resource>
<directory>src/main/resources</directory>
<excludes>
<!-- exclude resources handled by native2ascii-maven-plugin (otherwise they will be overwritten again) -->
<exclude>l10n/*</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>native2ascii-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>native2ascii</goal>
</goals>
<configuration>
<src>src/main/resources</src>
<includes>l10n/*</includes>
<encoding>UTF8</encoding>
</configuration>
</execution>
</executions>
</plugin>
</pre>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-58746545818195856012010-03-17T16:40:00.017+02:002010-03-17T17:06:38.603+02:00Servlet + SSI + Apache HTTPD<p>I was confronted with a requirement to add some SSI (Server Side Includes) processing to my Java web app. The application would get some external text containing SSI tags, prepend it with text containing SSI #set tags which will set some needed variables and all this would be processed by SSI to get the final HTML.
Sounds easy enough.</p>
<p>What I've found is that SSI is not actually a standard - it is more like an Apache HTTPD proprietary feature. Some other webservers claim to support it (most notably Tomcat), but certainly not 100%. For example, Tomcat does not understand regular expressions in #if tags and this makes Tomcat SSI totally unusable for my purpose.</p>
<p>So, the only working solution right now is to have Apache in front of Tomcat (or other servlet container) with help of mod_jk or mod_proxy.</p>
<p>After couple of hours try-and-fails, I have found how to configure it in httpd.conf:</p>
<pre class="brush: xml">
<Proxy *>
Options IncludesNoExec
SetOutputFilter INCLUDES
Order deny,allow
Allow from all
</Proxy>
</pre>
<p>The critical part here is the first two lines inside <Proxy></p>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-29305415516081403262009-09-21T22:56:00.003+03:002009-09-22T09:51:25.959+03:00Tomcat 6.0.16 quoting cookies<p>Recently I've stumbled upon a <a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=44679">feature</a> in Tomcat 6.0.16:</p>
<ul>
<li> browser cookies containing symbols like : or = get broken in Tomcat when reading using request.getCookies() - only part of the cookie value is returned </li>
<li> when writing such cookies using request.addCookie(), Tomcat automatically adds double quotes around the value </li>
</ul>
<p>Arguable, such cookies are malformed and using them involves security risks.</p>
<p>In reality, such cookies are quite popular among web developers - they allow to put together some user preferences like that:</p>
<pre>
MyCookie=language=en:currency=EUR
</pre>
<p>Even Google uses cookies like that.</p>
<p>What options we have to solve the problem ?</p>
<ol>
<li> use the correct cookie format (with double quotes) </li>
<li> stick to Tomcat 6.0.14 or older </li>
<li> do some custom hack </li>
</ol>
<p>Option 1 is the best if you can afford it (but you must still account for cookies already present in client browsers).</p>
<p>Option 2 is only a temporary solution - sooner or later you will need to upgrade (e.g. because of important security patches).</p>
<p>So the unfortunate souls like me are left with the Option 3. My cookie is used jointly by many web application and changing the format at once is not possible.</p>
<p>So here is the solution: write a servlet filter that does custom cookie reading and writing.</p>
<pre class="brush: java">
public class TomcatQuotedCookieFilter implements Filter {
private static final String MY_COOKIE_NAME = "MyCookie";
public static class CookieWrappedRequest extends HttpServletRequestWrapper {
public CookieWrappedRequest(HttpServletRequest request) {
super(request);
}
@Override
public Cookie[] getCookies() {
// getCookies() returns correct numbers of cookies, but myCookie value might be broken
Cookie[] cookies = super.getCookies();
int myCookieIndex = CookieUtils.findCookiePosition(MY_COOKIE_NAME, cookies);
if (myCookieIndex == -1) {
return cookies;
}
String cookieValue = CookieUtils.extractCookieValueFromHeader(MY_COOKIE_NAME, getHeader("Cookie"));
if (cookieValue != null) {
cookies[myCookieIndex] = new Cookie(MY_COOKIE_NAME, cookieValue);
}
return cookies;
}
}
public static class CookieWrappedResponse extends HttpServletResponseWrapper {
public CookieWrappedResponse(HttpServletResponse response) {
super(response);
}
@Override
public void addCookie(Cookie cookie) {
// catch my cookie and apply custom formatting
if (cookie.getName().equals(MY_COOKIE_NAME)) {
addHeader("Set-Cookie", CookieUtils.formatSetCookieHeaderValue(cookie));
} else {
super.addCookie(cookie);
}
}
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,
ServletException {
filterChain.doFilter(new CookieWrappedRequest((HttpServletRequest) request), new CookieWrappedResponse(
(HttpServletResponse) response));
}
// init, destroy...
}
</pre>
<p>CookieUtils methods are left as an exercise for the reader :) Those might be dependent on the specific cookie format that you are using.altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com1tag:blogger.com,1999:blog-7305867802246851684.post-2631485567219654902009-02-23T19:59:00.019+02:002009-09-21T14:49:06.027+03:00BigDecimal gotchasThe BigDecimal class is full of gotchas. Today's gotcha is: <br />
<br />
<pre class="brush: java; gutter: false;">new BigDecimal("1").equals(new BigDecimal("1.00")) == false
</pre><br />
To avoid this problem, use compareTo instead of equals. <br />
<br />
<br />
Another nice gotcha is: <pre class="brush: java; gutter: false;">new BigDecimal(1.23) == 1.229999999999999982236431605997495353221893310546875</pre>The correct way: <pre class="brush: java; gutter: false;">new BigDecimal("1.23")
</pre>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-71479354318117203442008-09-29T15:17:00.018+03:002010-03-04T19:22:54.512+02:00Tomcat 5.5 and JSTLHow to use JSTL tag library with Tomcat 5.5 and Maven: <ol><li>Add Maven dependencies: <pre class="brush: xml; gutter: false;"><dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</pre></li>
<li>Make sure you use at least Servlet version 2.4 in web.xml: <br />
<pre class="brush: xml; gutter: false;"><?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
version="2.4">
</web-app>
</pre></li>
<li>Add declaration in your JSP file: <br />
<pre class="brush: xml; gutter: false;"><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
</pre></li>
<li>Use JSTL tags: <br />
<pre class="brush: xml; gutter: false;"><c:set var="conditionValue" value="${true}"/>
<c:if test="${conditionValue}">
YES
<c:if>
</pre></li>
</ol>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0tag:blogger.com,1999:blog-7305867802246851684.post-91511945431835706702008-03-16T15:19:00.006+02:002009-09-21T14:49:40.518+03:00Timed Screenshots on MacWhen working as a time-based contractor, recording spent time in "time manager" is sometimes problematic and always tedious. It happened to me that at the end of the day I have trouble recalling what tasks did I do and it what proportions. Now maybe this little shell script can help me: <br />
<br />
<pre class="brush: bash; gutter: false;">#!/bin/bash
# Makes screenshots every $PERIOD minutes
# and puts them into /var/tmp/screens/ directory.
PERIOD=5
for ((i=0; i<=10*60/PERIOD; i+=1)); do
FILENAME=/var/tmp/screens/screen`date +%y%m%d_%H%M`.png
echo Capturing $FILENAME...
screencapture $FILENAME
echo Done, next capture in $(($PERIOD*60)) seconds.
sleep $(($PERIOD*60))
done
</pre>altumanohttp://www.blogger.com/profile/07644069616370903161noreply@blogger.com0