<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Martin&#039;s Weekend Coding &#187; Java</title>
	<atom:link href="http://blog.alutam.com/category/java/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.alutam.com</link>
	<description>Sharing useful tips from my &#34;weekend projects&#34;</description>
	<lastBuildDate>Sun, 02 Oct 2011 03:15:06 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>ooomailer 1.0 &#8211; More Flexible Out-of-office Replies</title>
		<link>http://blog.alutam.com/2011/10/02/ooomailer-1-0-more-flexible-out-of-office-replies/#utm_source=feed&#038;utm_medium=feed&#038;utm_campaign=feed</link>
		<comments>http://blog.alutam.com/2011/10/02/ooomailer-1-0-more-flexible-out-of-office-replies/#comments</comments>
		<pubDate>Sun, 02 Oct 2011 03:15:06 +0000</pubDate>
		<dc:creator>Martin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[auto-replies]]></category>
		<category><![CDATA[ooomailer]]></category>

		<guid isPermaLink="false">http://blog.alutam.com/?p=139</guid>
		<description><![CDATA[In my day job I have to deal with a huge amount of e-mails each day &#8211; most of them are not urgent, but some are. When I go for vacation or on a business trip, I need to let people know I am not available and won&#8217;t respond until I get back (and provide [...]]]></description>
			<content:encoded><![CDATA[<p>In my day job I have to deal with a huge amount of e-mails each day &#8211; most of them are not urgent, but some are. When I go for vacation or on a business trip, I need to let people know I am not available and won&#8217;t respond until I get back (and provide an alternate contact for urgent matters). These days pretty much every mail server has a &#8220;vacation message&#8221; feature, that allows you to do exactly that &#8211; supports sending automated responses to all e-mails coming to your inbox, letting others know you won&#8217;t be able to read the message before a given date. However the implementation of that feature varies from server to server.</p>
<p>I have several e-mail accounts on several mail servers and I found myself in a situation when I needed more flexibility in the way I set up my vacation message. Being involved in several open source projects and subscribed to many open mailing lists, I needed to make sure I don&#8217;t spam those lists or users of those lists (who may not know me) with my vacation auto-replies. Even people I do want to notify don&#8217;t need to get my auto-reply to every message that reaches me &#8211; notification once in several days is good enough. And I needed to customize the vacation message for internal recipients (e.g. providing links to internal resources) and have it slightly more detailed than the auto-replies that go to external people.</p>
<p>So, a couple of months back, the weekend before my summer vacation I sat down and implemented a simple utility that provides me with this flexibility. Since then I used it several times, fixed a few issues, and <a href="http://blogs.oracle.com/alexismp">Alexis</a> (a colleague of mine) contributed some more improvements. At this point I am pretty confident the tool works smoothly, so decided to release version 1.0. Give it a try if you are in a similar situation &#8211; the tool and the source code are available at <a href="http://hg.alutam.com/ooomailer" title="http://hg.alutam.com/ooomailer">http://hg.alutam.com/ooomailer</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.alutam.com/2011/10/02/ooomailer-1-0-more-flexible-out-of-office-replies/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Jersey 1.9.1 Released</title>
		<link>http://blog.alutam.com/2011/09/16/jersey-1-9-1-released/#utm_source=feed&#038;utm_medium=feed&#038;utm_campaign=feed</link>
		<comments>http://blog.alutam.com/2011/09/16/jersey-1-9-1-released/#comments</comments>
		<pubDate>Fri, 16 Sep 2011 12:07:14 +0000</pubDate>
		<dc:creator>Martin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[JavaOne]]></category>
		<category><![CDATA[JAX-RS]]></category>
		<category><![CDATA[Jersey]]></category>
		<category><![CDATA[release]]></category>

		<guid isPermaLink="false">http://blog.alutam.com/?p=131</guid>
		<description><![CDATA[Over the past two weeks, I&#8217;ve been working with Pavel on finalizing and staging the bits for the hands-on-lab on OAuth, we are going to do at this year&#8217;s JavaOne. As part of that, I had to make a few more clean-ups in the Jersey OAuth client library, so we decided to make a branch [...]]]></description>
			<content:encoded><![CDATA[<p>Over the past two weeks, I&#8217;ve been working with <a href="http://blogs.oracle.com/PavelBucek/">Pavel</a> on finalizing and staging the bits for the <a href="https://oracleus.wingateweb.com/scheduler/modifySession.do?SESSION_ID=24761">hands-on-lab on OAuth</a>, we are going to do at this year&#8217;s <a href="http://www.oracle.com/javaone/index.html">JavaOne</a>. As part of that, I had to make a few more clean-ups in the <a href="http://jersey.java.net/nonav/apidocs/latest/contribs/jersey-oauth/oauth-client/index.html">Jersey OAuth client library</a>, so we decided to make a branch for 1.9.1 and make those clean-ups along with some other small fixes there. Now, 2 weeks after 1.9, we released it. This is the release we&#8217;ll be using for JavaOne and although the release cycle was so short, it does have two nice additions worth highlighting.</p>
<ul>
<li><strong>Un-/marshalling collection types</strong><br />
Until 1.9.1, JAXB un-/marshalling in Jersey worked only for Collection and List interfaces. I.e. if your resource method returned (or took as a parameter) Collection&lt;Foo&gt; or List&lt;Foo&gt; (where Foo was a JAXB bean), de-/serialization from/to XML/JSON would work, but if it returned LinkedList&lt;Foo&gt; or Set&lt;Foo&gt; or any other Collection subtype, it would not work. This is fixed in 1.9.1 and you can now return and retrieve any well-known interfaces that extend Collection (such as Set, Queue, etc.) and their implementations which have default public constructor.</li>
<li><strong>PostReplaceFilter improvements</strong><br />
<a href="http://jersey.java.net/nonav/apidocs/latest/jersey/com/sun/jersey/api/container/filter/PostReplaceFilter.html">PostReplaceFilter</a> can be used to support clients which can&#8217;t send the full range of HTTP methods. It enables converting POST requests to other methods such as PUT or DELETE. If a POST request comes with a different method specified in X-HTTP-Method-Override header, the filter will replace POST in the request with that specified method. This has been in Jersey for a while, but only supported method overriding using the X-HTTP-Method-Override header. In 1.9.1 you can now use &#8220;_method&#8221; query parameter as well, and when overriding POST to GET the filter will convert all the form parameters to query parameters. Whether both header and query parameter are looked at by the filter (or only the header or only the query parameter) is configurable. Thanks to <a href="http://java.net/jira/secure/ViewProfile.jspa?name=gk5885%40java.net">gk5885</a>, <a href="http://java.net/jira/secure/IssueNavigator.jspa?reset=true&amp;customfield_10010=ferdy_nagy%40java.net">Fredy Nagy</a> and <a href="http://java.net/jira/secure/IssueNavigator.jspa?reset=true&amp;customfield_10010=fhars%40java.net">Florian Hars</a> for sharing their views and patches.</li>
</ul>
<div>You can see the full list of changes in our <a href="http://java.net/projects/jersey/sources/svn/content/branches/jersey-1.9.1/jersey/changes.txt?rev=5399">changelog</a>. For more info on Jersey see <a href="http://jersey.java.net">http://jersey.java.net</a>.</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.alutam.com/2011/09/16/jersey-1-9-1-released/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Reading Password-Protected ZIP Files in Java</title>
		<link>http://blog.alutam.com/2009/10/31/reading-password-protected-zip-files-in-java/#utm_source=feed&#038;utm_medium=feed&#038;utm_campaign=feed</link>
		<comments>http://blog.alutam.com/2009/10/31/reading-password-protected-zip-files-in-java/#comments</comments>
		<pubDate>Sat, 31 Oct 2009 12:13:34 +0000</pubDate>
		<dc:creator>Martin</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Password]]></category>
		<category><![CDATA[ZIP]]></category>

		<guid isPermaLink="false">http://blog.alutam.com/?p=102</guid>
		<description><![CDATA[On a recent &#8220;fun&#8221; project, I needed my application to be able to access password-protected zip files of a particular format. It was one of these features I thought will take me no time to implement. Anyway, to my surprise, neither JDK supports password-protected ZIP files, nor I was able to find a suitable Java [...]]]></description>
			<content:encoded><![CDATA[<p>On a recent &#8220;fun&#8221; project, I needed my application to be able to access password-protected zip files of a particular format. It was one of these features I thought will take me no time to implement. Anyway, to my surprise, neither JDK supports password-protected ZIP files, nor I was able to find a suitable Java open source library I could use for that purpose. So, I ended up writing the utility class on my own. I wrote an implementation of <code>java.io.InputStream</code> that filters the ZIP file data and turns a password-protected ZIP into an unprotected one on the fly &#8211; so the stream can be nicely chained with <code>java.util.zip.ZipInputStream</code>. Although the class is specifically targeted at the particular type of ZIP files I had to deal with (see the limitations below), maybe other people have to deal with the same type of files, or this class can provide a good start for others to turn it into a utility that would work with any type of ZIP (maybe I will do it myself some day &#8211; for now I don&#8217;t have time).<br />
To implement this class I used the <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">ZIP File Format Specification</a> as the source of information. I also used the <a href="http://www.7-zip.org/">7-zip project</a> (C++) as a reference during the debugging to verify my understanding of the ZIP spec. and the CRC algorithm.<br />
So, here is the class:</p>
<pre class="brush:java">import java.io.IOException;
import java.io.InputStream;

public class ZipDecryptInputStream extends InputStream {
    private static final int[] CRC_TABLE = new int[256];
    // compute the table
    // (could also have it pre-computed - see http://snippets.dzone.com/tag/crc32)
    static {
        for (int i = 0; i &lt; 256; i++) {
            int r = i;
            for (int j = 0; j &lt; 8; j++) {
                if ((r &amp; 1) == 1) {
                    r = (r &gt;&gt;&gt; 1) ^ 0xedb88320;
                } else {
                    r &gt;&gt;&gt;= 1;
                }
            }
            CRC_TABLE[i] = r;
        }
    }

    private static final int DECRYPT_HEADER_SIZE = 12;
    private static final int[] LFH_SIGNATURE = {0x50, 0x4b, 0x03, 0x04};

    private final InputStream delegate;
    private final String password;
    private final int keys[] = new int[3];

    private State state = State.SIGNATURE;
    private int skipBytes;
    private int compressedSize;
    private int value;
    private int valuePos;
    private int valueInc;

    public ZipDecryptInputStream(InputStream stream, String password) {
        this.delegate = stream;
        this.password = password;
    }

    @Override
    public int read() throws IOException {
        int result = delegate.read();
        if (skipBytes == 0) {
            switch (state) {
                case SIGNATURE:
                    if (result != LFH_SIGNATURE[valuePos]) {
                        state = State.TAIL;
                    } else {
                        valuePos++;
                        if (valuePos &gt;= LFH_SIGNATURE.length) {
                            skipBytes = 2;
                            state = State.FLAGS;
                        }
                    }
                    break;
                case FLAGS:
                    if ((result &amp; 1) == 0) {
                        throw new IllegalStateException("ZIP not password protected.");
                    }
                    if ((result &amp; 64) == 64) {
                        throw new IllegalStateException("Strong encryption used.");
                    }
                    if ((result &amp; 8) == 8) {
                        throw new IllegalStateException("Unsupported ZIP format.");
                    }
                    result -= 1;
                    compressedSize = 0;
                    valuePos = 0;
                    valueInc = DECRYPT_HEADER_SIZE;
                    state = State.COMPRESSED_SIZE;
                    skipBytes = 11;
                    break;
                case COMPRESSED_SIZE:
                    compressedSize += result &lt;&lt; (8 * valuePos);
                    result -= valueInc;
                    if (result &lt; 0) {
                        valueInc = 1;
                        result += 256;
                    } else {
                        valueInc = 0;
                    }
                    valuePos++;
                    if (valuePos &gt; 3) {
                        valuePos = 0;
                        value = 0;
                        state = State.FN_LENGTH;
                        skipBytes = 4;
                    }
                    break;
                case FN_LENGTH:
                case EF_LENGTH:
                    value += result &lt;&lt; 8 * valuePos;
                    if (valuePos == 1) {
                        valuePos = 0;
                        if (state == State.FN_LENGTH) {
                            state = State.EF_LENGTH;
                        } else {
                            state = State.HEADER;
                            skipBytes = value;
                        }
                    } else {
                        valuePos = 1;
                    }
                    break;
                case HEADER:
                    initKeys(password);
                    for (int i = 0; i &lt; DECRYPT_HEADER_SIZE; i++) {
                        updateKeys((byte) (result ^ decryptByte()));
                        result = delegate.read();
                    }
                    compressedSize -= DECRYPT_HEADER_SIZE;
                    state = State.DATA;
                    // intentionally no break
                case DATA:
                    result = (result ^ decryptByte()) &amp; 0xff;
                    updateKeys((byte) result);
                    compressedSize--;
                    if (compressedSize == 0) {
                        valuePos = 0;
                        state = State.SIGNATURE;
                    }
                    break;
                case TAIL:
                    // do nothing
            }
        } else {
            skipBytes--;
        }
        return result;
    }

    @Override
    public void close() throws IOException {
        delegate.close();
        super.close();
    }

    private void initKeys(String password) {
        keys[0] = 305419896;
        keys[1] = 591751049;
        keys[2] = 878082192;
        for (int i = 0; i &lt; password.length(); i++) {
            updateKeys((byte) (password.charAt(i) &amp; 0xff));
        }
    }

    private void updateKeys(byte charAt) {
        keys[0] = crc32(keys[0], charAt);
        keys[1] += keys[0] &amp; 0xff;
        keys[1] = keys[1] * 134775813 + 1;
        keys[2] = crc32(keys[2], (byte) (keys[1] &gt;&gt; 24));
    }

    private byte decryptByte() {
        int temp = keys[2] | 2;
        return (byte) ((temp * (temp ^ 1)) &gt;&gt;&gt; 8);
    }

    private int crc32(int oldCrc, byte charAt) {
        return ((oldCrc &gt;&gt;&gt; 8) ^ CRC_TABLE[(oldCrc ^ charAt) &amp; 0xff]);
    }

    private static enum State {
        SIGNATURE, FLAGS, COMPRESSED_SIZE, FN_LENGTH, EF_LENGTH, HEADER, DATA, TAIL
    }
}</pre>
<p>These are the limitations:</p>
<ul>
<li>Only the &#8220;Traditional PKWARE Encryption&#8221; is supported (spec. section VII)</li>
<li>Files that have the &#8220;compressed length&#8221; information at the end of the data section (rather than at the beginning) are not supported (see &#8220;general purpose bit flag&#8221;, bit 3 in section V, subsection J in the spec.)</li>
</ul>
<p>And this is how you can use it in your code:</p>
<pre class="brush:java">import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

// usage: java Main [filename] [password]
public class Main {
    public static void main(String[] args) throws IOException {
        // password-protected zip file I need to read
        FileInputStream fis = new FileInputStream(args[0]);
        // wrap it in the decrypt stream
        ZipDecryptInputStream zdis = new ZipDecryptInputStream(fis, args[1]);
        // wrap the decrypt stream by the ZIP input stream
        ZipInputStream zis = new ZipInputStream(zdis);

        // read all the zip entries and save them as files
        ZipEntry ze;
        while ((ze = zis.getNextEntry()) != null) {
            FileOutputStream fos = new FileOutputStream(ze.getName());
            int b;
            while ((b = zis.read()) != -1) {
                fos.write(b);
            }
            fos.close();
            zis.closeEntry();
        }
        zis.close();
    }
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.alutam.com/2009/10/31/reading-password-protected-zip-files-in-java/feed/</wfw:commentRss>
		<slash:comments>54</slash:comments>
		</item>
	</channel>
</rss>

