<?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>Penguin Dreams</title>
	<atom:link href="http://penguindreams.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://penguindreams.org</link>
	<description></description>
	<lastBuildDate>Fri, 23 Mar 2012 13:35:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Making the Thunderbird mail icon more useful on MacOS</title>
		<link>http://penguindreams.org/blog/making-the-thunderbird-mail-icon-more-useful-on-macos/</link>
		<comments>http://penguindreams.org/blog/making-the-thunderbird-mail-icon-more-useful-on-macos/#comments</comments>
		<pubDate>Tue, 31 Jan 2012 01:27:40 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[e-mail]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[preferences]]></category>
		<category><![CDATA[thunderbird]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=436</guid>
		<description><![CDATA[Up until Mozilla Thunderbird 3.0, the MacOS version would show you the number of new messages in Thunderbird since you last clicked on the window, in the dock icon. Now by default it shows you the total number of unread messages, which is pretty useless if there are just a ton of messages in your [...]]]></description>
			<content:encoded><![CDATA[<p>Up until Mozilla Thunderbird 3.0, the MacOS version would show you the number of new messages in Thunderbird since you last clicked on the window, in the dock icon. Now by default it shows you the total number of unread messages, which is pretty useless if there are just a ton of messages in your bulk mail accounts you have no intention of reading. A <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=518828">bug</a> report was filed for this issue, in order to restore the old functionality (or at least make it optional). Although the bug was closed and the option was added to Thunderbird, it&#8217;s not in the main user interface. It must be set using the advanced configuration editor.</p>

<p><span id="more-436"></span></p>

<p>The basic steps to setting this option is as follows:</p>

<p>1. Open Thunderbird&gt;Preference&gt;Advanced&gt;General&gt;Config Editor</p>

<p><img src="http://penguindreams.org/files/2012/01/Thunderbird1.png" alt="Open Preferences" title="Thunderbird1" width="384" height="190" class="alignnone size-full wp-image-438" /></p>

<p><img src="http://penguindreams.org/files/2012/01/Thunderbird2.png" alt="Thunderbird Advanced Configuration Editor" title="Thunderbird2" width="612" height="463" class="alignnone size-full wp-image-440" /></p>

<p>2. type: mail.biff.use_new_count_in_mac_dock and change setting to <strong>true</strong> </p>

<p><img src="http://penguindreams.org/files/2012/01/Thunderbird3.png" alt="User New Mail Count in Dock Configuration Setting" title="Thunderbird3" width="750" height="500" class="alignnone size-full wp-image-441" /></p>

<p>That&#8217;s all there is to it. Now your Thunderbird dock icon will only display the number of new messages since you last opened an e-mail, similar to the mail notification icon in other clients including Microsoft Outlook.</p>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/making-the-thunderbird-mail-icon-more-useful-on-macos/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RearViewMirror 0.8.8.6 Released</title>
		<link>http://penguindreams.org/blog/rearviewmirror-0-8-8-6-released/</link>
		<comments>http://penguindreams.org/blog/rearviewmirror-0-8-8-6-released/#comments</comments>
		<pubDate>Thu, 07 Jul 2011 20:31:54 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[rear view mirror]]></category>
		<category><![CDATA[release]]></category>
		<category><![CDATA[software]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=425</guid>
		<description><![CDATA[It&#8217;s been nearly two and a half years since the last release of RearViewMirror. There aren&#8217;t any new features on this release, just several updates to help improve speed and to check for future releases. For those who have never used it before, RearViewMirror is an over-glorified version of those mirrors office workers attach to [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s been nearly two and a half years since the last release of <a href="/projects/rearviewmirror">RearViewMirror</a>. There aren&#8217;t any new features on this release, just several updates to help improve speed and to check for future releases. </p>

<p>For those who have never used it before, RearViewMirror is an over-glorified version of those mirrors office workers attach to their monitors so people don&#8217;t sneak up on them. Instead of using a mirror though, it uses a webcam and allows users to share their webcams with others around the office.</p>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/rearviewmirror-0-8-8-6-released/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Storing Shibboleth IDP Logs in a Database with IP Addresses</title>
		<link>http://penguindreams.org/blog/storing-shibboleth-idp-logs-in-a-database-with-ip-addresse/</link>
		<comments>http://penguindreams.org/blog/storing-shibboleth-idp-logs-in-a-database-with-ip-addresse/#comments</comments>
		<pubDate>Tue, 31 May 2011 19:53:41 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[logging]]></category>
		<category><![CDATA[mssql]]></category>
		<category><![CDATA[shibboleth]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=411</guid>
		<description><![CDATA[Shibboleth&#8217;s IDP can store audit logs that indicate when people authenticate against the IDP web application. These files are written to disk by default using the settings in the logging.xml configuration file. This tutorial will show how audit logs can be placed in a MS SQL database and also include the IP addresses of the [...]]]></description>
			<content:encoded><![CDATA[<p>Shibboleth&#8217;s <acronym title="Identity Provider">IDP</acronym> can store audit logs that indicate when people authenticate against the <span class="caps">IDP </span>web application. These files are written to disk by default using the settings in the <code>logging.xml</code> configuration file. This tutorial will show how audit logs can be placed in a MS <span class="caps">SQL </span>database and also include the IP addresses of the connecting clients. <br />
<span id="more-411"></span><br />
Shibboleth uses the <a href="http://www.slf4j.org/" title="Simple Logging Facade for Java"><span class="caps">SLF4J</span></a>, a logging library that is a front end for a variety of backend loggers. By default, it uses <a href="http://logback.qos.ch/">logback</a> to handle process audit logs. <span class="caps">SLF4J </span>supports a <a href="http://logback.qos.ch/manual/appenders.html#DBAppender"><span class="caps">DBA</span>ppender</a>. Unlike the <code>RollingFileAppender</code>, the <code>DBAppender</code> doesn&#8217;t support <code>encoder/Pattern</code> configuration. It places all logging messages in a standard database schema which can be found in the <code>logback-0.9.28\logback-classic\src\main\java\ch\qos\logback\classic\db\dialect</code> directory of the logback source code. </p>

<p>Note that in version 0.9.28 of logback, there is a <a href="http://jira.qos.ch/browse/LBCLASSIC-266">bug</a> in the MS <span class="caps">SQL </span>schema where all the <code>event_id</code> fields have an invalid type of <code>DECIMAL(40)</code>. These must be changed to <code>DECIMAL(38)</code>. This tutorial assume the use of an MS <span class="caps">SQL </span>database. Other databases will work, but the triggers and schema will need to be adjusted for those dialects accordingly. </p>

<p>In the Shibboleth <code>logging.xml</code> configuration file, start by adding the following appender:</p>



<pre class="sh_sourceCode sh_xml">
    &lt;appender name=&quot;IDP_DB_APPENDER&quot; class=&quot;ch.qos.logback.classic.db.DBAppender&quot;&gt;
      &lt;connectionSource class=&quot;ch.qos.logback.core.db.DataSourceConnectionSource&quot;&gt;
        &lt;dataSource class=&quot;com.jolbox.bonecp.BoneCPDataSource&quot;&gt;
          &lt;driverClass&gt;com.microsoft.sqlserver.jdbc.SQLServerDriver&lt;/driverClass&gt;
          &lt;jdbcUrl&gt;jdbc:sqlserver://dbserver.example.edu:5555;databaseName=ShibAudit&lt;/jdbcUrl&gt;
          &lt;username&gt;someUsername&lt;/username&gt;
          &lt;password&gt;somePassword&lt;/password&gt;
        &lt;/dataSource&gt;
      &lt;/connectionSource&gt;
    &lt;/appender&gt;
</pre>

<p> </p>

<p>Replace the database name, server, usernames and passwords respectively. </p>

<p>For the above example, the <a href="http://jolbox.com/">BoneCP</a> <code>connectionSource</code> is used for connection pooling. The BoneCP libraries will be available if <a href="/blog/modifying-uapprove-for-microsoft-sql/">uApprove</a> has been installed. Additional <a href="http://jolbox.com/configuration.html">configuration options</a> can be specified for tweaking connection counts and partitions within the pool.</p>

<p>An alternative to BoneCP is the <a href="http://sourceforge.net/projects/c3p0/">c3p0</a> connection pool library. This library comes with Shibboleth and requires no extra jar files:</p>



<pre class="sh_sourceCode sh_xml">
  &lt;appender name=&quot;IDP_DB_APPENDER&quot; class=&quot;ch.qos.logback.classic.db.DBAppender&quot;&gt;
    &lt;connectionSource class=&quot;ch.qos.logback.core.db.DataSourceConnectionSource&quot;&gt;
      &lt;dataSource class=&quot;com.mchange.v2.c3p0.ComboPooledDataSource&quot;&gt;
        &lt;driverClass&gt;com.microsoft.sqlserver.jdbc.SQLServerDriver&lt;/driverClass&gt;
        &lt;jdbcUrl&gt;jdbc:sqlserver://dbserver.example.edu:5555;databaseName=ShibAudit&lt;/jdbcUrl&gt;
        &lt;user&gt;someUser&lt;/user&gt;
        &lt;password&gt;somePassword&lt;/password&gt;
      &lt;/dataSource&gt;
    &lt;/connectionSource&gt;
  &lt;/appender&gt;
</pre>



<p>Again, replace the database name, server, usernames and passwords respectively. Next, the new appender needs to be attached to the audit logger:</p>



<pre class="sh_sourceCode sh_xml">
    &lt;logger name=&quot;Shibboleth-Audit&quot; level=&quot;ALL&quot;&gt;
        &lt;appender-ref ref=&quot;IDP_AUDIT&quot; /&gt;
        &lt;appender-ref ref=&quot;IDP_DB_APPENDER&quot; /&gt;
    &lt;/logger&gt;
</pre>

<p> </p>

<p>At this point, Shibboleth <span class="caps">IDP </span>can be restarted and attempts to authenticate with the <span class="caps">IDP </span>should result in log entries in the database. Make sure this is working. If not, check the <code>idp-process.log</code> as well as Tomcat&#8217;s <code>cataline.out</code> log file to determine if there were errors creating the <code>DBAppender</code> object. </p>

<p>Shibboleth 2.2.1 places its audit logs in a <a href="https://wiki.shibboleth.net/confluence/display/SHIB2/IdPLogging">pipe delimited format</a> that can easily be parsed. Rather than let the logger store this long pipe delimited string in our database, it would be beneficial to convert these logs into structure that&#8217;s easier to query. Start by creating the following table in the same database the <code>DBAppender</code> writes to:</p>



<pre class="sh_sourceCode sh_sql">
CREATE TABLE audit_log (
   logtime datetime,
   remoteAddr VARCHAR(255),  
   requestbinding VARCHAR(100),
   requestId VARCHAR(50),
   relayingPartyId VARCHAR(255),
   messageProfileId VARCHAR(100),
   assertingPartyId VARCHAR(255),
   responseBinding VARCHAR(100),
   responseId VARCHAR(50),
   principalName VARCHAR(8),
   authNMethod VARCHAR(100),
   releasedAttributeIds VARCHAR(255),
   nameIdentifier VARCHAR(100),
   assertionIDs VARCHAR(100)
);

</pre>

<p> </p>

<p>Now, a trigger will be added to the <code>logging_event</code> table so that whenever a row is inserted, it will be parsed and placed into the new tables.</p>



<pre class="sh_sourceCode sh_sql">
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE TRIGGER [dbo].[parseLog]
   ON  [dbo].[logging_event]
   AFTER INSERT
AS 
BEGIN

	SET NOCOUNT ON;

  DECLARE @data VARCHAR(4000)
  DECLARE @xml XML
  DECLARE @unixTimestamp datetime
  
  --Convert UNIX timestamp, with ms, to mssql datetime
  SET @unixTimestamp = (SELECT dateadd(ms,CAST(RIGHT(timestmp,3) AS INT),dateadd(ss,CAST(timestmp AS BIGINT)/1000,'01/01/1970')) FROM INSERTED)
       
  --Data as XML for easy parsing
  SET @data = (SELECT formatted_message FROM INSERTED)
  SET @xml = '&lt;Cols&gt;&lt;Col&gt;' + REPLACE(@data,'|','&lt;/Col&gt;&lt;Col&gt;') + '&lt;/Col&gt;&lt;/Cols&gt;'    
  
  --Different Tables for different logger types
  DECLARE @type AS VARCHAR(254)
  SET @type = (SELECT logger_name FROM INSERTED)
  
  IF @type = 'Shibboleth-Audit'
  BEGIN 
	  --Fields to parse 
	   DECLARE @requestbinding AS VARCHAR(100)
	   DECLARE @requestId AS VARCHAR(50)
	   DECLARE @relayingPartyId AS VARCHAR(255)
	   DECLARE @messageProfileId AS VARCHAR(100)
	   DECLARE @assertingPartyId AS VARCHAR(255)
	   DECLARE @responseBinding AS VARCHAR(100)
	   DECLARE @responseId AS VARCHAR(50)
	   DECLARE @principalName AS VARCHAR(8)
	   DECLARE @authNMethod AS VARCHAR(100)
	   DECLARE @releasedAttributeIds AS VARCHAR(255)
	   DECLARE @nameIdentifier AS VARCHAR(100)
	   DECLARE @assertionIDs AS VARCHAR(100)
	   
	   --Store the event_id in the IP address. The event_property trigger
	   --  will replace this with the real IP
	   DECLARE @eventId AS DECIMAL(38,0)
	   SET @eventId = (SELECT event_id FROM INSERTED)

	  SELECT 
		@requestBinding = x.d.value('Col[2]', 'VARCHAR(100)'),
		@requestId = x.d.value('Col[3]', 'VARCHAR(50)'),
		@relayingPartyId = x.d.value('Col[4]', 'VARCHAR(255)'),
		@messageProfileId = x.d.value('Col[5]', 'VARCHAR(100)'),
		@assertingPartyId = x.d.value('Col[6]', 'VARCHAR(255)'),
		@responseBinding = x.d.value('Col[7]', 'VARCHAR(100)'),
		@responseId = x.d.value('Col[8]', 'VARCHAR(50)'),
		@principalName = x.d.value('Col[9]', 'VARCHAR(8)'),
		@authNMethod = x.d.value('Col[10]', 'VARCHAR(100)'),
		@releasedAttributeIds = x.d.value('Col[11]', 'VARCHAR(255)'),
		@nameIdentifier = x.d.value('Col[12]', 'VARCHAR(100)'),
		@assertionIDs = x.d.value('Col[13]', 'VARCHAR(100)')
	  FROM  @xml.nodes('/Cols') x(d)

	  INSERT INTO audit_log (logtime, remoteAddr,requestbinding,requestId,relayingPartyId,messageProfileId,assertingPArtyId,
		responseBinding,responseId,principalName,authNMethod,releasedAttributeIds, nameIdentifier, assertionIds)
		VALUES(@unixTimeStamp,@eventId, @requestBinding,@requestId,@relayingPartyId,
		@messageProfileId,@assertingPartyId,@responseBinding,@responseId,@principalName,
		@authNMethod,@releasedAttributeIds,@nameIdentifier,@assertionIDs)
  END
END
GO 
</pre>



<p>Since MS <span class="caps">SQL </span>has no built-in function for splitting fields on a delimiter, the above function replaces the pipe symbols with opening and closing <span class="caps">XML </span>tags so that MS <span class="caps">SQL&#8217;</span>s built-in <code>XPath</code> engine can be used to parse the field. A conversion must also be done to translate the <span class="caps">UNIX </span>timestamp to an MS <span class="caps">SQL </span><code>datetime</code> type. Finally, the <code>event_id</code> is used as a placeholder in the field containing the client&#8217;s IP address. This is because by default, Shibboleth&#8217;s audit logs do not contain IP information. This can be added however using a custom servlet filter with <span class="caps">SLF4J. </span></p>

<p><span class="caps">SLF4J </span>supports a <acronym title="Mapped Diagnostic Context">MDC</acronym> which allows for values to be mapped to a logger for a given thread instance. Since only one thread in a Java servlet container is used per connection at any given time, this can be used to hold items such as the connecting client&#8217;s IP address for logging. The <code>DBAppender</code> inserts these properties in the <code>logging_event_property</code> table. A custom filter can be written to create these properties, but <span class="caps">SLF4J </span>comes with the <a href="http://logback.qos.ch/manual/mdc.html#mis"><span class="caps">MDCI</span>nsertingServletFilter</a> which injects certain common attributes into the <span class="caps">MDC </span>such as user-agent, remote host, query string, etc. </p>

<p>In the <code>idp.war</code>, add the following filter to the <code>web.xml</code>.</p>



<pre class="sh_sourceCode sh_xml">
  &lt;filter&gt;
    &lt;filter-name&gt;MDCInsertingServletFilter&lt;/filter-name&gt;
    &lt;filter-class&gt;
      ch.qos.logback.classic.helpers.MDCInsertingServletFilter
    &lt;/filter-class&gt;
  &lt;/filter&gt;
  &lt;filter-mapping&gt;
    &lt;filter-name&gt;MDCInsertingServletFilter&lt;/filter-name&gt;
    &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
  &lt;/filter-mapping&gt;
</pre>



<p>The <span class="caps">SLF4J </span>documentation recommended adding the <span class="caps">MDC </span>as the first filter, but unless attributes are needed for logging within other filters from what the <span class="caps">MDC </span>injects, this isn&#8217;t always necessary. For the database audit logging, the filter can be placed at the end of the <code>idp.war</code>&#8216;s <code>web.xml</code> file. </p>

<p>Finally, a trigger must be added to handle the properties that are inserted, namely the remote IP address, and update the record of the original log entry.</p>



<pre class="sh_sourceCode sh_sql">
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE TRIGGER [dbo].[setLogIP]
   ON  [dbo].[logging_event_property]
   AFTER INSERT
AS 
BEGIN   
   IF (SELECT mapped_key FROM INSERTED) = 'req.remoteHost'
   BEGIN   
      UPDATE audit_log  SET remoteAddr = (SELECT mapped_value FROM INSERTED)
        WHERE remoteAddr = CONVERT(varchar(255),(SELECT event_id FROM INSERTED))
   END   
END
GO
</pre>



<p>The <code>DBAppender</code> is transaction based, so if any triggers or query statements fail (e.g. if permissions are not setup correctly for the database user), the entire logging transaction will be rolled back and no results will appear in the database table. It is best to implement this is stages, starting with getting the <code>DBAppender</code> working and then adding the new table and triggers.</p>

<p>Since the standard text based log files do not include an IP address, it is best to include this new attribute in the original files as well. This can be done by modifying the <code>RollingFileAppender</code> for the audit log. The following example adds the IP address and also sets the rollover for the log file to 1 year (365 days). This should be adjusted to the retention requirements of the institution&#8217;s legal department. </p>



<pre class="sh_sourceCode sh_xml">
    &lt;appender name=&quot;IDP_AUDIT&quot; class=&quot;ch.qos.logback.core.rolling.RollingFileAppender&quot;&gt;
        &lt;File&gt;/opt/shibboleth-idp/logs/idp-audit.log&lt;/File&gt;

        &lt;rollingPolicy class=&quot;ch.qos.logback.core.rolling.TimeBasedRollingPolicy&quot;&gt;
            &lt;FileNamePattern&gt;/opt/shibboleth-idp/logs/idp-audit-%d{yyyy-MM-dd}.log&lt;/FileNamePattern&gt;
            &lt;maxHistory&gt;365&lt;/maxHistory&gt;
        &lt;/rollingPolicy&gt;

        &lt;encoder class=&quot;ch.qos.logback.classic.encoder.PatternLayoutEncoder&quot;&gt;
            &lt;charset&gt;UTF-8&lt;/charset&gt;
            &lt;Pattern&gt;%X{req.remoteHost}|%msg%n&lt;/Pattern&gt;
        &lt;/encoder&gt;
    &lt;/appender&gt;
</pre>



<p>Having audit logs in a database makes it convenient to aggregate Shibboleth authentication requests for reports, as well as retrieve information quickly for legal and security requests. It is recommended to also keep the <code>RollingFileAppender</code> to use as a backup in case of problems with the database connection.</p>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/storing-shibboleth-idp-logs-in-a-database-with-ip-addresse/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Installing subversion on SuSE Enterprise Server 10</title>
		<link>http://penguindreams.org/blog/installing-subversion-on-suse-enterprise-server-10/</link>
		<comments>http://penguindreams.org/blog/installing-subversion-on-suse-enterprise-server-10/#comments</comments>
		<pubDate>Fri, 13 May 2011 14:59:08 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[opensuse]]></category>
		<category><![CDATA[subversion]]></category>
		<category><![CDATA[suse]]></category>
		<category><![CDATA[yast]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=402</guid>
		<description><![CDATA[At home I use the latest version openSUSE with network updates and patches, but in a corporate environment a server administrator or developer often finds him or herself having to deal with older enterprise versions of products. I was surprised to discover that SUSE Enterprise Server 10 SP2 did not contain a subversion package (or [...]]]></description>
			<content:encoded><![CDATA[<p>At home I use the latest version openSUSE with network updates and patches, but in a corporate environment a server administrator or developer often finds him or herself having to deal with older enterprise versions of products. I was surprised to discover that <span class="caps">SUSE</span> Enterprise Server 10 <span class="caps">SP2 </span>did not contain a subversion package (or anything above python 2.4.2, but that&#8217;s for another post). I found several <a href="http://forums.novell.com/novell-product-support-forums/suse-linux-enterprise-server-sles/sles-configure-administer/307336-question-there-subversion-package-suse-10-a.html">conflicting</a> and <a href="http://osmanoglu.org/index.php?option=com_content&amp;view=article&amp;id=61%3Ainstalling-subversion-on-suse-sles-10-yet-again&amp;catid=4%3Acomputing&amp;Itemid=13">outdated</a> instructions, so here is a current version of how to install subversion 1.6 on <span class="caps">SUSE</span> Enterprise Server 10.<br />
<span id="more-402"></span><br />
openSUSE does maintain some <span class="caps">SLE </span>rpms. Simply add the following repository. I did this by using Yast&gt;Software&gt;Installation Source&gt;Add. Yast will ask you to import the repository key.</p>

<p><code>http://download.opensuse.org/repositories/openSUSE:/Tools/SLE_10/</code></p>

<p>You will most likely need the installation/service pack <acronym title="s">DVD</acronym> mounted as well in order to install dependencies such as <code>neon</code> and <code>apr</code>. The dependencies will be pulled in automatically by running the following:</p>

<p><code>zypper install subversion</code></p>

<p>The output should look like the following:</p>



<pre class="sh_sourceCode">
zypper install subversion
Restoring system sources...
Parsing metadata for SUSE Linux Enterprise Server 10 SP3-20110408-123840...
Parsing metadata for -20110513-093220...
Parsing RPM database...
Summary:
&lt;install&gt;   [S3:0][package]neon-0.26.1-10.1.x86_64
&lt;install&gt;   [S2:1][package]libapr-util1-1.2.2-13.7.x86_64
&lt;install&gt;   [S2:1][package]libapr1-1.2.2-13.2.x86_64
&lt;install&gt;   [S3:0][package]subversion-1.6.16-29.2.x86_64
Continue? [y/n]: y
Downloading: [S2:1][package]libapr1-1.2.2-13.2.x86_64, 103.4 K(258.9 K unpacked)
Installing: [S2:1][package]libapr1-1.2.2-13.2.x86_64
Downloading: [S3:0][package]neon-0.26.1-10.1.x86_64, 102.6 K(311.4 K unpacked)
Installing: [S3:0][package]neon-0.26.1-10.1.x86_64
Downloading: [S2:1][package]libapr-util1-1.2.2-13.7.x86_64, 63.3 K(149.3 K unpacked)
Installing: [S2:1][package]libapr-util1-1.2.2-13.7.x86_64
Downloading: [S3:0][package]subversion-1.6.16-29.2.x86_64, 1.8 M(6.0 M unpacked)
Installing: [S3:0][package]subversion-1.6.16-29.2.x86_64
</pre>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/installing-subversion-on-suse-enterprise-server-10/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Handling Grace Logins from Novell e-Directory in Shibboleth</title>
		<link>http://penguindreams.org/blog/handling-grace-logins-from-novell-e-directory-in-shibboleth/</link>
		<comments>http://penguindreams.org/blog/handling-grace-logins-from-novell-e-directory-in-shibboleth/#comments</comments>
		<pubDate>Thu, 12 May 2011 19:47:57 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[ldap]]></category>
		<category><![CDATA[novell]]></category>
		<category><![CDATA[shibboleth]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=392</guid>
		<description><![CDATA[Many institutions are using Shibboleth for unified single sign-on between both internal and external web application. Shibboleth is an authentication engine and, as its backend, it can use a variety of sources for authentication including LDAP, a SQL database or other resources. It simply deals with authentication, so more advanced configurations, such as systems which [...]]]></description>
			<content:encoded><![CDATA[<p>Many institutions are using Shibboleth for unified single sign-on between both internal and external web application. Shibboleth is an authentication engine and, as its backend, it can use a variety of sources for authentication including <span class="caps">LDAP, </span>a <span class="caps">SQL </span>database or other resources. It simply deals with authentication, so more advanced configurations, such as systems which allow grace logins after a password expires, may require more customization. The following tutorial shows how to use Shibboleth with a Novell e-Directory server that allows grace logins after a user&#8217;s password has expired.<br />
<span id="more-392"></span><br />
Novell e-Directory server allows for logins to occur after the password expiration date has passed. When this happens the <span class="caps">LDAP </span>bind operation occurs successfully, but a custom header with the error <code>-233</code> is sent in the bind response. Standard <span class="caps">LDAP </span>requests from Java using <span class="caps">JNDI </span>cannot see this response header. To see this response in the bind, Novell specific Java <span class="caps">API </span>must be used. </p>

<p>Rather than change the <span class="caps">JAAS </span>layer to use Novell&#8217;s <span class="caps">LDAP API, </span>a usable approach is to read a user&#8217;s <span class="caps">LDAP </span>attributes after a login to verify the password hasn&#8217;t expired and, if so, to inform the user that he or she must change that password. To do this, extend the <code>edu.vt.middleware.ldap.auth.handler.BindAuthenticationHandler</code> handler as seen in the following example.  </p>



<pre class="sh_sourceCode sh_java">
public class GraceAuthenticationHandler extends BindAuthenticationHandler {
  
  protected final Log logger = LogFactory.getLog(this.getClass());

  public static final String MALFORMED_EXPIRATION_DATE = &quot;-90555&quot;;
  
  public static final String PASSWORD_EXPIRED = &quot;-223&quot;;
  
  public GraceAuthenticationHandler() {
  }

  public GraceAuthenticationHandler(final AuthenticatorConfig ac) {
    this.setAuthenticatorConfig(ac);
  }

  public void authenticate(final ConnectionHandler ch, final AuthenticationCriteria ac) throws NamingException {

    Ldap ldap = null;
    
    try {
        super.authenticate(ch, ac);

        ldap = new Ldap(this.config);
        
        Attributes attrs = ldap.getAttributes(ac.getDn(),new String[] { &quot;passwordExpirationTime&quot;, &quot;loginGraceRemaining&quot; } );        
        
        String exp = attrs.get(&quot;passwordExpirationTime&quot;) != null ? (String) attrs.get(&quot;passwordExpirationTime&quot;).get(0) : &quot;&quot;;
        String graceLogins = attrs.get(&quot;loginGraceRemaining&quot;) != null ? (String) attrs.get(&quot;loginGraceRemaining&quot;).get(0) : &quot;&quot;;

        logger.debug(&quot;UserDN: &quot; + ac.getDn() );
        logger.debug(&quot;Expiration Date: &quot; + exp);
        logger.debug(&quot;GraceLogins: &quot; + graceLogins);
        
        Date date;
        try {
          date = new SimpleDateFormat(&quot;yyyyMMddHHmmss&quot;).parse(exp);
        }
        catch (ParseException p) {
          throw new AuthenticationException(String.format(&quot;Error (%s): User's password expiration time is in incorrect format&quot;,MALFORMED_EXPIRATION_DATE));
        }
        
        if(date.before(new Date())) {
          throw new AuthenticationException(String.format(&quot;Error (%s): Password expired on %s. %s grace logins remaining&quot;,PASSWORD_EXPIRED,exp,graceLogins));
        }

    } catch(RuntimeException e) {
      throw e;
    } finally {
      if (ldap != null) {
        ldap.close();
      }
    }
  }

  public GraceAuthenticationHandler newInstance()
  {
    return new GraceAuthenticationHandler(this.config);
  }
}
</pre>



<p>It&#8217;s important to note that the password expired error code we are setting is the same as the Novell error code: <code>-223</code>. For an invalid expiration date, a number has been chosen randomly outside the range of standard Novell error codes. Typically, this error will be seen if the user specified in the <code>login.config</code> does not have permission to view the <code>passwordExpirationTime</code> and <code>loginGraceRemaining</code> attributes. </p>

<p>This custom authentication handler must be added to the <code>login.config</code> as seen below. </p>



<pre class="sh_sourceCode">
ShibUserPassAuth {

  edu.vt.middleware.ldap.jaas.LdapLoginModule required
      host=&quot;ldaps://ldap.example.edu&quot;
      base=&quot;o=example&quot;
      bindDn=&quot;cn=IDMUser,ou=admins,o=example&quot;
      bindCredential=&quot;somePassword&quot;
      ssl=&quot;true&quot;
      userField=&quot;uid&quot;
      subtreeSearch=&quot;true&quot;
      authenticationHandler=&quot;edu.example.shibboleth.idm.auth.GraceAuthenticationHandler&quot;;
};
</pre>



<p>When the <span class="caps">JAAS </span>layer encounters this exception, it copies the message from the exception into a new <code>LoginException</code>. Because of this, exceptions with new custom attributes cannot be used. Instead, the error string must be parsed to determine the cause of the error. This can be done in the <code>login.jsp</code> found in the <code>idp.war</code> by adding the following:</p>



<pre class="sh_sourceCode sh_java">
&lt;%@ page import=&quot;edu.example.shibboleth.idm.auth.GraceAuthenticationHandler&quot; %&gt;

...

&lt;% if (request.getAttribute(LoginHandler.AUTHENTICATION_EXCEPTION_KEY) != null) {

  Exception exception = (Exception) request.getAttribute(LoginHandler.AUTHENTICATION_EXCEPTION_KEY);
  String loginMsg = exception.getMessage().trim();
  String niceMsg = &quot;&quot;;

  String changePassUrl = &quot;https://example.edu/changePasswordApp&quot;;

  //display error message
  if(loginMsg.contains(&quot;Cannot authenticate dn, invalid dn&quot;) || loginMsg.contains(&quot;669&quot;)) {
  niceMsg = &quot;Invalid username or password&quot;;
  }
  else if(loginMsg.contains(GraceAuthenticationHandler.MALFORMED_EXPIRATION_DATE)) {
  niceMsg = &quot;Cannot authenticate. Account has invalid expiration date&quot;;
  }
  else if(loginMsg.contains(GraceAuthenticationHandler.PASSWORD_EXPIRED)) {
  niceMsg = String.format(&quot;Your password has expired. Click &lt;a href=\&quot;%s\&quot;&gt;here&lt;/a&gt; to change your password&quot;,changePassUrl);
  }
  else if(loginMsg.contains(&quot;222&quot;)) {
  niceMsg = &quot;Password has expired and you are out of grace logins. Please call the helpdesk.&quot;;
  }
  else if(loginMsg.contains(&quot;220&quot;)) {
  niceMsg = &quot;Account is disabled&quot;;
  }
  else if(loginMsg.contains(&quot;217&quot;)) {
  niceMsg = &quot;Number of concurrent connections exceeded&quot;;
  }
  else if(loginMsg.contains(&quot;197&quot;)) {
  niceMsg = &quot;Account is locked&quot;;
  }
  else if(loginMsg.contains(&quot;218&quot;)) {
  niceMsg = &quot;Login time limited&quot;;
  }
  else {
  niceMsg = &quot;An unknown authentication error occured&quot;;
  }

%&gt;
&lt;span style=&quot;color:red; background-color:white; padding: 5px;&quot;&gt;&lt;%= niceMsg %&gt;&lt;/span&gt;
&lt;% } %&gt;
</pre>



<p>The above example handles the most common Novell e-Directory error codes. It is important to note that with this example, the user is forced to change his or her password even if grace logins remain. Allowing the user to continue to authenticate after grace logins have expired would require an additional custom login servlet and possible a custom login handler. </p>

<p>Special thanks goes to Daniel Fisher who provided most of the code for the authentication handler, as well as the other users on the <a href="https://lists.internet2.edu/sympa/arc/shibboleth-dev">Shibboleth-dev mailing list</a> who helped me with writing and debugging. </p>

<p class="footnote" id="fn1"><sup>1</sup> <a href="http://support.novell.com/docs/Tids/Solutions/10067240.html"><span class="caps">LDAP </span>errors returned when <span class="caps">NDS </span>login, password, time and address restrictions are set</a> Novell. February 12, 2003</p>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/handling-grace-logins-from-novell-e-directory-in-shibboleth/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Modifying uApprove for Microsoft SQL</title>
		<link>http://penguindreams.org/blog/modifying-uapprove-for-microsoft-sql/</link>
		<comments>http://penguindreams.org/blog/modifying-uapprove-for-microsoft-sql/#comments</comments>
		<pubDate>Fri, 22 Apr 2011 13:15:50 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[mssql]]></category>
		<category><![CDATA[shibboleth]]></category>
		<category><![CDATA[uApprove]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=384</guid>
		<description><![CDATA[Shibboleth is an Internet2 project used to implement identity authentication and authorization across multiple domains (sometimes known as a single sign-on). Shibboleth also allows federated authentication, which allows an organization or institution to let a user on one domain to authenticate to another domain. This is common in academic settings where one university may want [...]]]></description>
			<content:encoded><![CDATA[<p>Shibboleth is an Internet2 project used to implement identity authentication and authorization across multiple domains (sometimes known as a single sign-on). Shibboleth also allows federated authentication, which allows an organization or institution to let a user on one domain to authenticate to another domain. This is common in academic settings where one university may want to allow users from another university to use their services using that first university&#8217;s authentication system. A plug-in for Shibboleth known as <a href="http://www.switch.ch/aai/support/tools/uApprove.html">uApprove</a> provides an approval screen so users can see what information is being shared before being logged into a remote system. uApprove is designed to work with MySQL, however this tutorial shows how it can be easily modified to use Microsoft <span class="caps">SQL</span> Server. </p>

<p><img src="http://penguindreams.org/files/2011/04/uApprove_attributes.png" alt="uApprove Attributes Screenshot" title="uApprove Screenshot" width="500"  class="aligncenter size-full wp-image-386" /></p>

<p><span id="more-384"></span></p>

<p><span style="color:red"><strong>Update</strong> (2011.12.05)</span>: <strong>uApprove 2.3 has changed to use <span class="caps">ANSI SQL.</span></strong> The default <span class="caps">SQL </span>configuration in uApprove 2.3 will work with Microsoft <span class="caps">SQL, </span>so long as the following changes are made to the schema:</p>



<pre class="sh_sourceCode sh_sql">
CREATE TABLE AttributeReleaseConsent (
userId         VARCHAR(104)                           NOT NULL,
relyingPartyId VARCHAR(104)                           NOT NULL,
attributeId    VARCHAR(104)                           NOT NULL,
valuesHash     VARCHAR(256)                           NOT NULL,
consentDate    DATETIME NOT NULL,

PRIMARY KEY (userId, relyingPartyId, attributeId)
);

CREATE TABLE ToUAcceptance (
userId         VARCHAR(104)                           NOT NULL,
version        VARCHAR(104)                           NOT NULL,
fingerprint    VARCHAR(256)                           NOT NULL,
acceptanceDate TIMESTAMP      NOT NULL,  
 PRIMARY KEY (userId, version)
);
</pre>



<p>For Microsoft <span class="caps">SQL, </span>the ToUAcceptance needs to have the <span class="caps">DEFAULT </span>and <span class="caps">CURRENT</span>_TIMESTAMP attributes removed and the AttributeReleaseConsent.consentDate needs to be changed to a <span class="caps">DATETIME.</span> The only other thing that needs to be changed is that in the <code>uApprove.properties</code> file, the <code>database.drive needs</code> to be changed to either <code>com.microsoft.sqlserver.jdbc.SQLServerDriver</code> if using the Microsoft native <span class="caps">JDBC </span>driver or <code>net.sourceforge.jtds.jdbc.Driver</code> if using the open source jTDS driver. Be sure to copy either the <code>sqljdbc4.jar</code> or the <code>jtds-1.2.5.jar</code> file into all the appropriate lib directories.</p>

<p>That&#8217;s it! The 2.3 version of uApprove requires significantly less modification over version 2.2. For everything else, follow the standard uApprove install instructions. </p>

<p><strong>The following is the original post for <span style="color:red">older version</span> of uApprove (2.2.1)</strong>:</p>

<p>Follow the <a href="https://www.switch.ch/aai/downloads/uApprove-2.2.1-manual.html">installation instructions</a> found on the <a href="http://www.switch.ch/aai/support/tools/uApprove.html">uApprove website</a>. There are only three major steps which need to be altered. When creating the database schema, use the following for Microsoft <span class="caps">SQL</span>:</p>



<pre class="sh_sourceCode sh_sql">
create table ArpUser (
  idxArpUser bigint identity(1,1) primary key,
  auUserName varchar(255) not null,
  auLastTermsVersion varchar(255),
  auFirstAccess datetime,
  auLastAccess datetime
);
create index idxUserName on ArpUser (auUserName );

create table ShibProvider (
  idxShibProvider bigint identity(1,1) primary key,
  spProviderName varchar(255)
);

SET IDENTITY_INSERT ShibProvider On;
insert into ShibProvider (idxShibProvider) values (1);
SET IDENTITY_INSERT ShibProvider Off;
create index idxProvidername on ShibProvider (spProviderName);

create table AttrReleaseApproval (
  idxAttrReleaseApproval bigint identity(1,1) primary key,
  araIdxArpUser bigint references ArpUser ( idxArpUser ),
  araIdxShibProvider bigint references ShibProvider( idxShibProvider ),
  araTimeStamp datetime not null,
  araTermsVersion varchar(255),
  araAttributes text
);

create table ProviderAccess (
  idxProviderAccess bigint identity(1,1) primary key,
  paIdxArpUser bigint references ArpUser( idxArpUser ),
  paIdxShibProvider bigint references ShibProvider( idxShibProvider ),
  paAttributesSent text,
  paTermsVersion varchar(255),
  paIdxAttrReleaseApproval bigint references AttrReleaseApproval ( idxAttrReleaseApproval ),
  paShibHandle varchar(255),
  paTimeStamp datetime not null
);
</pre>



<p>The major differences include using <span class="caps">MSSQL&#8217;</span>s <code>bigint</code> instead of MySQL&#8217;s <code>unsigned int</code>, using <code>identity(1,1)</code> instead <code>auto_increment</code>, replacing the <code>timestamp</code> fields with <code>datetime</code> fields and turning off the identity for inserting the first service provider record. You will need to create a standard <span class="caps">SQL </span>user and give it rights to this table in <span class="caps">SQL</span> Management Studio. If you use an Active Directory user with windows authentication, Shibboleth must be running on a Windows server and you&#8217;ll have to use the native authentication <code>dll</code>. Since I preformed this installation on Linux, that setup is outside the scope of this tutorial. </p>

<p>Next, you&#8217;ll notice in the uApprove documentation that all the <span class="caps">SQL </span>commands are stored in a <code>mysql.commands</code> file. Create a <code>microsoftSQL.commands</code> file and place the following in it:</p>



<pre class="sh_sourceCode sh_sh">
selGlobalShibProvider = select idxShibProvider as idx from ShibProvider where spProviderName is null

selIdxUser = select idxArpUser as idxUser from ArpUser where auUserName = '?'

selShibProvider = select idxShibProvider as idxProvider from ShibProvider where spProviderName = '?'

insShibProvider = insert into ShibProvider (spProviderName) values ( '?' )

selArpInfoByUsername1 = select idxArpUser as idxUser, convert(varchar,araTimeStamp,20) as ArpDate, araTermsVersion as TermsOfUseManager, araAttributes as Attributes, spProviderName as ShibProvider from ArpUser, AttrReleaseApproval, ShibProvider where auUserName='?' and idxArpUser=araIdxArpUser and araIdxShibProvider = idxShibProvider order by araTimeStamp desc

selArpInfoByUsername2 = select idxArpUser as idxUser, auLastTermsVersion as TermsOfUseManager, auLastAccess as ArpDate from ArpUser where auUserName='?'

insUser = insert into ArpUser (auUserName, auLastTermsVersion, auFirstAccess, auLastAccess ) values ( '?', '?', getdate(), getdate() )

updUser = update ArpUser set auLastTermsVersion = '?', auFirstAccess=auFirstAccess, auLastAccess=getdate() where auUsername = '?'
updUser1 = update ArpUser set auFirstAccess = auFirstAccess, auLastAccess = getdate() where auUserName = '?'

selGlobalArp = select count(*) as cnt from AttrReleaseApproval, ArpUser, ShibProvider where idxArpUser=araIdxArpUser and idxShibProvider = araIdxShibProvider and spProviderName is null and auUserName = '?'

insAttrApproval = insert into AttrReleaseApproval ( araIdxArpUser, araIdxShibProvider, araTimeStamp , araTermsVersion,araAttributes ) values ( ?, ?, getdate() , '?', null )

insAttrApproval1 = insert into AttrReleaseApproval (araIdxArpUser, araIdxShibProvider, araTimeStamp, araTermsVersion, araAttributes ) values ( ?, ?, getdate() , '?', '?' )

delAttrApproval = delete from AttrReleaseApproval where araIdxArpUser = ? and araIdxShibProvider = ?

updAttrApproval = update AttrReleaseApproval set araTermsVersion = '?', araAttributes = '?' where araIdxArpUser = ? and araIdxShibProvider = ?

selIdxAttrApproval = select idxAttrReleaseApproval as idxApproval, araIdxArpUser as IdxUser, araIdxShibProvider as idxProvider, araTermsVersion as TermsVersion, araAttributes as Attributes from ArpUser, AttrReleaseApproval, ShibProvider where auUserName='?' and idxArpUser=araIdxArpUser and spProviderName = '?' and araIdxShibProvider = idxShibProvider order by araTimeStamp desc

selIdxAttrApprovalGlobal = select idxAttrReleaseApproval as idxApproval, araIdxArpUser as IdxUser, araIdxShibProvider as idxProvider, araTermsVersion as TermsVersion, araAttributes as Attributes from ArpUser, AttrReleaseApproval, ShibProvider where auUserName='?' and idxArpUser=araIdxArpUser and spProviderName is null and araIdxShibProvider = idxShibProvider order by araTimeStamp desc

insProviderAccess = insert into ProviderAccess ( paIdxArpUser, paIdxShibProvider, paTermsVersion, paAttributesSent, paIdxAttrReleaseApproval, paTimeStamp ) values ( ?, ?, '?', '?', ?, getdate() )

selIdxProviderAccess = select idxProviderAccess as idxPA from ProviderAccess, AttrReleaseApproval where paIdxAttrReleaseApproval=idxAttrReleaseApproval and araIdxArpUser=?

clearReleaseForAccess = update ProviderAccess set paIdxAttrReleaseApproval = NULL, paTimeStamp = paTimeStamp where paIdxArpUser = ?

delAttrReleaseApprovals = delete from AttrReleaseApproval where araIdxArpUser = ?
</pre>



<p>The primary differences include replacing MySQL&#8217;s <code>now</code> function with <code>get_date</code> and replacing the <code>date_format</code> with <code>convert</code>. In MySQL, fields that assumed the current time when inserted with nulls must explicitly define the field and use the <code>get_date</code> function in <span class="caps">MSSQL. </span></p>

<p>The <code>database.properties</code> file must be changed to use the new <span class="caps">MSSQL </span>command file like so:</p>



<pre class="sh_sourceCode sh_bash">
sqlCommands=/opt/uApprove/conf/microsoftSQL.commands

driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
url=jdbc:sqlserver://&lt;hostname&gt;:&lt;port&gt;;databaseName=uApprove;intergratedSecurity=true
user=&lt;insert user&gt;
password=&lt;insert password&gt;
</pre>



<p>The version 3 <span class="caps">JDBC </span>drivers for <span class="caps">MSSQL </span>can be found on the Microsoft website at the following address:</p>

<p><a href="http://www.microsoft.com/downloads/en/details.aspx?FamilyID=%20a737000d-68d0-4531-b65d-da0f2a735707&amp;displaylang=en">http://www.microsoft.com/downloads/en/details.aspx?FamilyID=%20a737000d-68d0-4531-b65d-da0f2a735707&amp;displaylang=en</a></p>

<p>There are two jars in this package and you will only need one of them. For this installation which was for Tomcat 5 running on Java 1.6, I used sqljdbc4.jar. This jar needs to be added everywhere there is a mysql-connector.jar. This includes <code>uApprove-2.2.1/idp-plugin-2.2.1/lib</code>, <code>uApprove-2.2.1/viewer-2.2.1/webapp/WEB-INF/lib</code> and even the <code>shibboleth-idp/war/idp.war</code>. The <code>idp.war</code> is redeployed in the uApprove install instructions. You can add it before this point or add it manually to the jar&#8217;s <code>WEB-INF/lib</code> folder afterwords.</p>

<p>That should be all that is necessary to run the uApprove plug-in against Microsoft <span class="caps">SQL</span> Server. I&#8217;ve tested this in <span class="caps">MSSQL</span> 2005 but it should run fine on 2008 as well. If you run into issues, be sure to check the log files for uApprove, Shibboleth and Tomcat to help diagnose issues. </p>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/modifying-uapprove-for-microsoft-sql/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>My Account&#8217;s Been Hacked (No It Hasn&#8217;t)</title>
		<link>http://penguindreams.org/blog/my-accounts-been-hacked-no-it-hasnt/</link>
		<comments>http://penguindreams.org/blog/my-accounts-been-hacked-no-it-hasnt/#comments</comments>
		<pubDate>Thu, 24 Jun 2010 19:50:50 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[authorization]]></category>
		<category><![CDATA[passwords]]></category>
		<category><![CDATA[phishing]]></category>
		<category><![CDATA[security]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=360</guid>
		<description><![CDATA[Recently I&#8217;ve seen unsolicited SPAM e-mails coming directly from other peoples&#8217; e-mail and social networking accounts. They&#8217;ll often post messages afterwords claiming that their accounts had been hacked. I&#8217;ll usually ask these friends, &#8220;Do you use the same password on multiple websites?&#8221; and the ensuing &#8220;Yes&#8221; response from them is followed by, &#8220;Change your e-mail [...]]]></description>
			<content:encoded><![CDATA[<p>Recently I&#8217;ve seen unsolicited <span class="caps">SPAM </span>e-mails coming directly from other peoples&#8217; e-mail and social networking accounts. They&#8217;ll often post messages afterwords claiming that their accounts had been <em>hacked</em>. I&#8217;ll usually ask these friends, &#8220;Do you use the same password on multiple websites?&#8221; and the ensuing &#8220;Yes&#8221; response from them is followed by, &#8220;Change your e-mail password, and also, you need to learn something about password security.&#8221;<br />
<span id="more-360"></span><br />
If your e-mail or social networking (Twitter, Facebook, et cetera) account is sending out unsolicited messages on your behalf, it is unlikely your account has been <em>hacked</em> and much more likely that either your password has been scraped or that you&#8217;ve granted 3rd party access to a malicious service or application. If neither of these has occurred, then there is a possibility that you account has indeed been compromised using some type of exploit, however this typically is not the case.</p>

<p><strong>Password Security</strong></p>

<p>How can someone gain access to your e-mail account without knowing your password? Have you ever signed up for a new website or service you wanted to try out? Did you give them your e-mail address during registration? Did you use the same password you used for your e-mail account?</p>

<p>Newer websites and services that haven&#8217;t been globally trusted may be scraping your passwords during registration and testing them against the e-mail account you provided. Once gaining access, it&#8217;s easy to scan e-mail for messages from other services and begin requesting access to other accounts associated with it. </p>

<p>You may have also registered with a new website or web service that is not necessarily malicious. They may just be storing your password insecurely and unencrypted. A potential hacker may have discovered a security exploit allowing them to access that password data.</p>

<p>The solution isn&#8217;t to simply use only trusted sites. Using new services and trying new concepts is what helps the Internet grow. Plus, even large trusted services can have their security compromised by badly written code allowing third parties to gain access to your data. The better solution is to use unique password for every website. How would you remember a unique password for every website? You&#8217;d use a password algorithm.   </p>

<p><strong>Password Algorithms</strong></p>

<p>Many Internet users typically use the same password for multiple systems. Some people use a slightly more secure technique of having different levels of passwords. They may use one for unsecured sites, such as social networking or instant messaging, another for secure sites such as e-mail and a very strong one for computer and bank passwords. Although this is better than using the same password everywhere, it&#8217;s not replacement for the security provided by a unique password algorithm. </p>

<p>A good password algorithms can be based off the website the password is intended for. For instance, you can take the first or last letter of the website, combine it with a pin number or short password, and then tack on a character representing something else about the site. For example, you could add a letter representing the top level domain (e.g. The letter &#8216;A&#8217; for .com, &#8216;S&#8217; for .net, &#8216;D&#8217; for .org and &#8216;F&#8217; for others). </p>

<p>You could also use something on the site itself, like the company&#8217;s primary color or the background color of the website. Although using something on the page itself may change over time, this may also force you to continually change and rotate out passwords. </p>

<p>You can be creative and find many different ways to create a good password system. However there are a few basic rules you should try to follow to ensure good password selections. </p>


<ul>
<li>Make sure your pattern will always give you a password with at least one capital letter, one lowercase letter and one number</li>
<li>Ensure your system always gives you a password between 8 and 9 characters long. Many websites have restrictions on length and 8 or 9 characters ensures your password will never be too short or two long</li>
<li>Avoid special characters. Many websites ban these (due to ignorance) for security and it&#8217;s much more difficult to remember exceptions to your password system. </li>
<li>Chose a system you can remember easily, but that&#8217;s not easy to understand should someone get a hold of one of your passwords. </li>
</ul>



<p>In addition to individual passwords for each website, you should also have secure passwords for non-web related systems such as company computers/networks and home computers. </p>

<p>You don&#8217;t need to change every password at once when you come up with an algorithm. Just start changing passwords as you access sites you use more frequently. Whenever you access a website with your old password, go into that website&#8217;s account options and change it. You can then gradually adjust to a new password algorithm over the course of a few weeks. </p>

<p><strong>Additional Tips</strong></p>


<ul>
<li>Never use your web browser&#8217;s password store to remember passwords for you. Turn it off, remember your password system and commit them to memory.</li>
<li>Always remember to log-off your accounts on computers that are not yours or that you cannot secure. </li>
</ul>



<p><strong>Security Questions</strong></p>

<p>One serious piece of contention in the network security industry are the increasing use of security questions. Although they are used to alleviate customer services calls, the questions themselves are easily guessable.  </p>

<p>In an 2009 <span class="caps">IEEE</span> Symposium on Security and Privacy, researchers from Microsoft and Carnegie Mellon University showed that in a study involving 130 people, 28% of the people who were known and trusted by the studies participants could guess the correct answers to participants&#8217; security questions. Even people not trusted still had a 17% chance of guessing a participant&#8217;s answers<sup class="footnote"><a href="#fn1">1</a></sup>. </p>

<p>Some security experts suggest typing in random letters for security questions, going through the additional burden of calling the company should one&#8217;s user account become locked. Others suggest using a similar password algorithm as described above to generate a separate password for the security question. Without doing one or the other, the system a user is accessing gains an automatic backdoor to his or her account, defeating the purpose of having password based security in the first place. </p>

<p>A better solution to using security questions is to use reset codes sent to user&#8217;s e-mail addresses, or combining an e-mailed reset code with both security questions and locking an account after several bad attempts. While this is a better solution, many websites cannot implement these e-mail based password reset systems because they allow multiple accounts to use the same e-mail address.</p>

<p><strong>Authentication without Password</strong></p>

<p>Another item to watch out for are services which ask for a password to another services. For example, a new social networking site that may ask for your e-mail password in order to search for friends that might be using the new service. While at one time this was the only way for third parties to access information on your account, it isn&#8217;t any longer.<br />
<img src="http://penguindreams.org/files/2010/06/twitter-oauth.png" alt="" title="Twitter&amp;#039;s OAuth Permissions" width="400" class="alignright size-full wp-image-367" />
Now almost all major services support some type of single sign-on authentication. Twitter uses an OAuth based system, Facebook has their own Facebook Connect system, Microsoft has LiveID and Google has third party account authentication. These systems work by directing you to a given service (Facebook, Twitter, Google, etc.) with a security token. On that site, you log-in and authorize the token granting the other website limited access to your account. </p>

<p>The advantage of this system is that if the 3rd party service does start doing something malicious, such as sending spam messages to your contacts, you can simply revoke the applications access to your account without having to change any passwords. Most services even allow their users to report malicious applications, which could cause their application tokens to be rejected permanently for all users. </p>

<p><strong>Conclusions</strong></p>

<p>Password security is critical in a world where so much of your personal information an accounts can be accessed electronically. Developing and protecting a good password system is more important in many ways than defending your social security number. </p>

<p>At a minimum, you should have different levels of passwords: An insecure password for social networking websites and other trivial services, a high security password for e-mail, payment accounts and other high security services, and finally an ultra secure password for critical websites such as on-line banking. </p>

<p>Ideally, you should create a password algorithm for all web passwords. Algorithms should always generate 8 to 9 character passwords that include at least one capital letter and one number, and the system should be easy to remember yet difficult to understand should one or more password become compromised. </p>

<p>If a user suspects a malicious website or application has gained access to one of his or her accounts, the user should change the password on that account immediately. Using an algorithm for generating passwords ensures that other accounts won&#8217;t be comprised. Without it, a user may have to change password on several websites afterwords. </p>

<p>Switching to a password system from a single universal password may seem a bit cumbersome, but the transition is a lot easier than one would anticipate. I switched from a set of three passwords to an algorithm three years ago and now have a much greater degree of confidence about the security of my information.  </p>

<p><br/><br/><br/></p>

<p class="footnote" id="fn1"><sup>1</sup> <a href="http://www.technologyreview.com/web/22662/?a=f">Are Your Secret Questions Too Easily Answered?</a>. Lemons. Technology Review. May 18, 2009. </p>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/my-accounts-been-hacked-no-it-hasnt/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Running Beans Locally that use Application Server Data Sources</title>
		<link>http://penguindreams.org/blog/running-beans-that-use-application-server-datasources-locally/</link>
		<comments>http://penguindreams.org/blog/running-beans-that-use-application-server-datasources-locally/#comments</comments>
		<pubDate>Tue, 11 May 2010 14:03:15 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[data sources]]></category>
		<category><![CDATA[EJB]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[JDBC]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=327</guid>
		<description><![CDATA[When writing J2EE web applications, web services, enterprise Java beans (EJBs) or other pieces of code that run on a Java application server such as RedHat&#8217;s JBoss, IBM WebSphere or Apache Tomcat, a developer typically doesn&#8217;t load database drivers or connect to the database directly. Instead, a context lookup must be made in order to [...]]]></description>
			<content:encoded><![CDATA[<p>When writing <span class="caps">J2EE </span>web applications, web services, enterprise Java beans (EJBs) or other pieces of code that run on a Java application server such as RedHat&#8217;s JBoss, <span class="caps">IBM</span> WebSphere or Apache Tomcat, a developer typically doesn&#8217;t load database drivers or connect to the database directly. Instead, a context lookup must be made in order to get a <code>DataSource</code>, and from there a <code>Connection</code>. However what if one needs to run existing code locally, outside of the web server? This guide shows developers how to setup a local context so application server code can be run in a stand-alone application without modification. <br />
<span id="more-327"></span><br />
In a typical web application that uses a straight database connection without the assistance of <acronym title="Data Access Object">DAO</acronym> layer such as Hibernate or Spring <span class="caps">DAO, </span>a connection to the database is made using something like the following:</p>



<pre class="sh_sourceCode sh_java">
public class  MyServiceBean {

    public void startProcess() {
    
        DataSource ds = (DataSource) new InitialContext().lookup(&quot;jdbc/ds1&quot;);
        con = ds.getConnection();
        
        //do something with connection        
    }    
}
</pre>



<p>The <code>DataSource</code> is simply an interface which the underlying application container implements in order to allow applications to get connections. In this way, a web application server can control the way connections are handed out as well as keep connections in a pool to be reused. The information about the data source, including the driver, host name, user name, password and database name are all setup on the web application server. The way they&#8217;re configured varies depending on the server (most have a web administration console or <span class="caps">XML </span>configuration files), but all application servers provide an initial context containing references to these data sources for all running web applications. </p>

<p>If we simply tired to test this bean using a <code>main</code> function in its own stand-alone application like so:  </p>



<pre class="sh_sourceCode sh_java">
public static void main(String[] args) {        
    MyServiceBean b = new MyServiceBean();
    b.startProcess();              
}
</pre>



<p>We would get the following exception:</p>



<pre class="sh_sourceCode sh_java">
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file:  java.naming.factory.initial
	at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
        .....
</pre>



<p>In order to run code which looks up a <code>DataSource</code> this way in a stand-alone application, we must create our own implementation of a <code>DataSource</code> object as well as an <code>InitialContextFactory</code>, plus several intermediary objects, and then tell our currently running <acronym title="Java Runtime Environment">JRE</acronym> to use our context object for all subsequent calls to <code>new InitialContext()</code>. Because we&#8217;re running locally, we don&#8217;t need to implement everything out of all these interfaces, just the bare minimum in order to provide <code>DataSource</code> and <code>Connection</code> objects. </p>

<p>First, we&#8217;ll look at a very quick and dirty solution. In this solution, we declare new classes directly within the main function using the <code>final</code> keyword and hard-coding the driver and connection strings. This is a quick drop to simply test a single bean. </p>



<pre class="sh_sourceCode sh_java">
public static void main(String[] args) throws SQLException, ClassNotFoundException, NamingException
{
    final class LocalDataSource implements DataSource , Serializable {

            private String connectionString;
            private String username;
            private String password;
            
            LocalDataSource(String connectionString, String username, String password) {
                this.connectionString = connectionString;
                this.username = username;
                this.password = password;
            }
            
            public Connection getConnection() throws SQLException
            {
                return DriverManager.getConnection(connectionString, username, password);
            }
    
            public Connection getConnection(String arg0, String arg1)
                    throws SQLException
            {
                return getConnection();              
            }
    
            public PrintWriter getLogWriter() throws SQLException
            {
                return null;
            }
    
            public int getLoginTimeout() throws SQLException
            {
                return 0;
            }
    
            public void setLogWriter(PrintWriter out) throws SQLException {}
    
            public void setLoginTimeout(int seconds) throws SQLException {}

    }
    
    final class DatabaseContext extends InitialContext {

        DatabaseContext() throws NamingException {}

        @Override
        public Object lookup(String name) throws NamingException
        {
            try {
                //our connection strings
                Class.forName(&quot;com.mysql.jdbc.Driver&quot;);
                DataSource ds1 = new LocalDataSource(&quot;jdbc:mysql://dbserver1/dboneA&quot;, &quot;username&quot;, &quot;xxxpass&quot;);
                DataSource ds2 = new LocalDataSource(&quot;jdbc:mysql://dbserver1/dboneB&quot;, &quot;username&quot;, &quot;xxxpass&quot;);

                Properties prop = new Properties();
                prop.put(&quot;jdbc/ds1&quot;, ds1);
                prop.put(&quot;jdbc/ds2&quot;, ds2);
                
                Object value = prop.get(name);
                return (value != null) ? value : super.lookup(name);
            }
             catch(Exception e) {
                 System.err.println(&quot;Lookup Problem &quot; + e.getMessage());
                 e.printStackTrace();
             }  
             return null;            
        }        
        
    }

    final class DatabaseContextFactory implements  InitialContextFactory, InitialContextFactoryBuilder {

        public Context getInitialContext(Hashtable&lt;?, ?&gt; environment)
                throws NamingException
        {
            return new DatabaseContext();
        }

        public InitialContextFactory createInitialContextFactory(
                Hashtable&lt;?, ?&gt; environment) throws NamingException
        {
            return new DatabaseContextFactory();
        }
        
    }
    
    NamingManager.setInitialContextFactoryBuilder(new DatabaseContextFactory());   

    MyServiceBean b = new MyServiceBean();
    b.startProcess();
}
</pre>

<p> </p>

<p>In this example we&#8217;ll start from the bottom. Before we run our bean we see a call to <code>NamingManager.setInitialContextFactoryBuilder()</code>. This function sets the factory that will be called in our environment by all subsequent calls to <code>new InitialContext()</code> throughout our application. Underneath the hood of a web application server, this is one of the many things that happens well before a web application is loaded.  </p>

<p>The <code>DatabaseContextFactory</code> is a class we create that not only serves as a <code>ContextFacotry</code> but also a <code>ContextFactoryBuilder</code> through appropriate interfaces. Thanks to interfaces, we can simplify these two functionalities into a single class. It&#8217;s sole purpose is to return a <code>DatabaseContext</code>. </p>

<p>The <code>DatabaseContext</code> extends a regular <code>InitialContext</code> and we simply override the one function typically used by web server code, the <code>lookup()</code> function. It is here we can inject our own <code>LocalDataSource</code> objects, which return our <code>Connection</code> objects. On an actual web application server, the <code>DataSource</code> object would typically have some type of pooling mechanism that could return existing connections into a queue once the web application calls the <code>close()</code> function on them. </p>

<p>Although is is a decent quick solution, it&#8217;s really unclean and isn&#8217;t reusable without copying and pasting. It also ignores any environment parameters passed into the <code>InitialContext</code> and discards them. For a more permanent solution, each of the classes should be separated out and compatibility should be maintained with the environment properties passed in to our custom <code>Context</code> object. </p>

<p>For our clean and reusable solution, we&#8217;re only going to have one class with public visibility. It&#8217;s our factory class. All other classes will have default/package visibility because they shouldn&#8217;t be instantiated independently outside of our factory. Let&#8217;s start with the factory:</p>



<pre class="sh_sourceCode sh_java">
package org.penguindreams.db.local;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import javax.naming.spi.InitialContextFactoryBuilder;

public class LocalContextFactory {
	/**
	 * do not instantiate this class directly. Use the factory method.
	 */
	private LocalContextFactory() {}
	
	public static LocalContext createLocalContext(String databaseDriver) throws SimpleException {

		try { 
			LocalContext ctx = new LocalContext();
			Class.forName(databaseDriver);	
			NamingManager.setInitialContextFactoryBuilder(ctx); 			
			return ctx;
		}
		catch(Exception e) {
			throw new SimpleException(&quot;Error Initializing Context: &quot; + e.getMessage(),e);
		}
	}	
}
</pre>



<p>In the above code, we&#8217;re creating a new <code>LocalContext</code>. We&#8217;re also initializing our <span class="caps">JDBC </span>driver. As with the previous example, the <code>NamingManager.setInitialContextFactoryBuilder</code> function ensures the new context we&#8217;re creating will be given to all subsequent calls made to <code>new InitialContext()</code>. Also, as with the previous example, the <code>LocalContext</code> takes both the role of an <code>InitialContextFactory</code> and an <code>InitialContextFactoryBuilder</code> through the use of interfaces. </p>



<pre class="sh_sourceCode sh_java">
package org.penguindreams.db.local;

import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.spi.InitialContextFactory;
import javax.naming.spi.InitialContextFactoryBuilder;

class LocalContext extends InitialContext implements InitialContextFactoryBuilder, InitialContextFactory {

	Map&lt;Object,Object&gt; dataSources;
	
	LocalContext() throws NamingException {
		super();
		dataSources = new HashMap&lt;Object,Object&gt;();
	}
	
	public void addDataSource(String name, String connectionString, String username, String password) {
		this.
		dataSources.put(name, new LocalDataSource(connectionString,username,password));
	}

	public InitialContextFactory createInitialContextFactory(
			Hashtable&lt;?, ?&gt; hsh) throws NamingException {
		dataSources.putAll(hsh);
		return this;
	}

	public Context getInitialContext(Hashtable&lt;?, ?&gt; arg0)
			throws NamingException {
		return this;
	}

	@Override
	public Object lookup(String name) throws NamingException {
		Object ret = dataSources.get(name);
		return (ret != null) ? ret : super.lookup(name);
	}	
}
</pre>



<p>In the above example, we also see that we&#8217;ve allowed for some default behavior. For instance, properties that are given to initialize our <code>LocalContext</code> are stored in our local <code>HashMap</code>. If a lookup fails, we call the parent&#8217;s lookup method as well. The important function we add above is the <code>addDataSource()</code> method which creates new <code>LocalDataSource</code> to be looked up by the passed in <code>name</code> argument. Finally we have the <code>LocalDataSource</code> itself. </p>



<pre class="sh_sourceCode sh_java">
package org.penguindreams.db.local;

import java.io.PrintWriter;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import javax.sql.DataSource;

class LocalDataSource implements DataSource , Serializable {
	
	private String connectionString;
    private String username;
    private String password;
    
    LocalDataSource(String connectionString, String username, String password) {
        this.connectionString = connectionString;
        this.username = username;
        this.password = password;
    }
    
    public Connection getConnection() throws SQLException
    {
        return DriverManager.getConnection(connectionString, username, password);
    }

	public Connection getConnection(String username, String password)
			throws SQLException {return null;}
	public PrintWriter getLogWriter() throws SQLException {return null;}
	public int getLoginTimeout() throws SQLException {return 0;}
	public void setLogWriter(PrintWriter out) throws SQLException {	}
	public void setLoginTimeout(int seconds) throws SQLException {}
}
</pre>



<p>As you can see, many of the functions for the <code>DataSource</code> have been left unimplemented. We&#8217;ve only implemented enough functionality to get lookups working and to create simple, non-pooled connections for the end client. So our main function should now look something like the following:</p>



<pre class="sh_sourceCode sh_java">
public static void main(String[] args) {        
    
    LocalContext ctx = LocalContextFactory.createLocalContext(&quot;com.mysql.jdbc.Driver&quot;);
    ctx.addDataSource(&quot;jdbc/js1&quot;,&quot;jdbc:mysql://dbserver1/dboneA&quot;, &quot;username&quot;, &quot;xxxpass&quot;);
    ctx.addDataSource(&quot;jdbc/js2&quot;,&quot;jdbc:mysql://dbserver1/dboneB&quot;, &quot;username&quot;, &quot;xxxpass&quot;);
    
    MyServiceBean b = new MyServiceBean();
    b.startProcess();              
}
</pre>



<p>The call to <code>LocalContextFactory.createLocalContext()</code> initializes our environment with the <code>LocalContext</code> as the <code>InitialContext</code>. Then we can add the <code>DataSource</code> objects we need later using the <code>addDataSource()</code> method. All of our data objects are kept within their own packages and have limited visibility to ensure they can only be instantiated and fully initialized using the factory method. </p>

<p>There are some limitations to this <em>clean</em> implementation. For one, you can only use one type of database depending on what driver you specify with the <code>LocalContextFactory</code>. Also, not all of the <code>DataSource</code> methods are fully implemented, so you may run into problems in environments that depend on other functions and more complex implementations. </p>

<p>Still, the above code will work in a testing environment and, in a pinch, you can use the final classes used at the beginning of this tutorial directly in a <code>main</code> function for some quick and dirty testing. If you need a more complete implementation, the clean version of the code is an excellent starting point to begin building a full local implementation of your own custom <code>DataSource</code> objects. </p>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/running-beans-that-use-application-server-datasources-locally/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Building Java EAR files using Ant</title>
		<link>http://penguindreams.org/blog/building-java-ear-files-using-ant/</link>
		<comments>http://penguindreams.org/blog/building-java-ear-files-using-ant/#comments</comments>
		<pubDate>Mon, 12 Apr 2010 03:19:12 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[ant]]></category>
		<category><![CDATA[ear]]></category>
		<category><![CDATA[jar]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[war]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=307</guid>
		<description><![CDATA[When creating new Java web applications within an IDE such as Eclipse or NetBeans, the IDE creates a directory structure and uses its own internal builder to create WAR and EAR files. While these build tools may be convenient when starting to develop J2EE applications, when working on production grade projects, it&#8217;s important to create [...]]]></description>
			<content:encoded><![CDATA[<p>When creating new Java web applications within an <acronym title="Integrated Development Environment">IDE</acronym> such as Eclipse or NetBeans, the <span class="caps">IDE </span>creates a directory structure and uses its own internal builder to create <span class="caps">WAR </span>and <span class="caps">EAR </span>files. While these build tools may be convenient when starting to develop <span class="caps">J2EE </span>applications, when working on production grade projects, it&#8217;s important to create your own directory structure and build scripts to automate the building and deployment process. This tutorial will take you through automating the build process of a web application using Apache Ant as well as giving you a better understanding of exactly how web applications are laid-out and built within the <span class="caps">EAR </span>file. <br />
<span id="more-307"></span><br />
First let&#8217;s take a look at the structure of a web application. The following is a directory structure I created for an upcoming <a href="/tutorials/#j2eeTutorials">series of tutorials</a> on developing <span class="caps">J2EE </span>applications of which this tutorial is a part. As you can see, it&#8217;s slightly different from the version created in Eclipse or <span class="caps">IBM </span><acronym title="Rational Application Developer">RAD</acronym>. I removed the <code>WebContent</code> folder and moved my configuration files into the <code>conf</code> folder. I moved all web content, such as jsp files and images, into the <code>web</code> folder. Web app libraries and source code will be held in <code>lib</code> and <code>src</code> respectively. You can chose different names or a different origination pattern. The folder layout itself is not important as we will see later when constructing the build file. </p>

<p><img src="http://penguindreams.org/files/2010/04/ant_tutoral_dir_layout.png" alt="Web Application Directory Layout" title="Web Application Directory Layout" class="aligncenter size-full wp-image-308" /></p>

<p>Our <code>web.xml</code> for our <span class="caps">WAR </span>file is fairly basic. It contains a single servlet and a mapping to that servlet.</p>



<pre class="sh_sourceCode sh_xml">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;web-app xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot; xmlns:web=&quot;http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot; xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd&quot; id=&quot;WebApp_ID&quot; version=&quot;2.5&quot;&gt;
  &lt;display-name&gt;SimpleRest&lt;/display-name&gt;
   
  &lt;servlet&gt;
  	&lt;servlet-name&gt;RestService&lt;/servlet-name&gt;
  	&lt;servlet-class&gt;org.penguindreams.service.ServiceHandler&lt;/servlet-class&gt;
  &lt;/servlet&gt;
  
  &lt;servlet-mapping&gt;
	&lt;servlet-name&gt;RestService&lt;/servlet-name&gt; 
	&lt;url-pattern&gt;/models/*&lt;/url-pattern&gt;
  &lt;/servlet-mapping&gt;	
	
&lt;/web-app&gt;
</pre>



<p>Likewise, our <code>application.xml</code>, which is used to build an <span class="caps">EAR </span>file which can contain multiple <span class="caps">WAR </span>files, is very basic. We see it only contains support for one web module.</p>



<pre class="sh_sourceCode sh_xml">
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;application xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot; xmlns:application=&quot;http://java.sun.com/xml/ns/javaee/application_5.xsd&quot; xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd&quot; id=&quot;Application_ID&quot; version=&quot;5&quot;&gt;
  &lt;display-name&gt;SimpleRestEAR&lt;/display-name&gt;
  &lt;module&gt;
    &lt;web&gt;
      &lt;web-uri&gt;SimpleRest.war&lt;/web-uri&gt;
      &lt;context-root&gt;rest&lt;/context-root&gt;
    &lt;/web&gt;
  &lt;/module&gt;
&lt;/application&gt;
</pre>



<p>Our servlet overrides the <code>doGet</code> method, sets the current time as an attribute and then passes on control to a <acronym title="Java Server Page">JSP</acronym> to finish processing the request. </p>



<pre class="sh_sourceCode sh_java">
public class ServiceHandler extends HttpServlet {
	
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response)  { 		
		try {			
			request.setAttribute(&quot;timeStamp&quot;, new Date().getTime() );			
			getServletConfig().getServletContext().getRequestDispatcher(&quot;/hello.jsp&quot;).forward(request,response);			
		} catch (Exception e) {
			System.err.println(&quot;Fatal Servlet Error&quot;);
			e.printStackTrace();
		}			
	}
	
}
</pre>



<p>Likewise, our <span class="caps">JSP </span>page is very simple. It just displays a header image and the resulting time.</p>



<pre class="sh_sourceCode sh_html">
&lt;%@ page language=&quot;java&quot; contentType=&quot;text/html; charset=ISO-8859-1&quot;
    pageEncoding=&quot;ISO-8859-1&quot;%&gt;
&lt;!DOCTYPE html PUBLIC &quot;-//W3C//DTD XHTML 1.0 Strict//EN&quot; &quot;http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
&lt;html  xmlns=&quot;http://www.w3.org/1999/xhtml&quot; dir=&quot;ltr&quot; &gt;
&lt;head&gt;
	&lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=ISO-8859-1&quot; /&gt;
	&lt;title&gt;Sample Page&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
	&lt;p&gt;
		&lt;img src=&quot;images/penguindreams.gif&quot; alt=&quot;PenguinDreams&quot; /&gt;
		&lt;br /&gt;
		TimeStamp: &lt;%= request.getAttribute(&quot;timeStamp&quot;) %&gt;
	&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>



<p>So now that we have our application out of the way, let&#8217;s focus on the building process. The first part of the <code>build.xml</code> contains our variables. The sample application is built for JBoss. Depending on your web application server, the paths and variable names should be adjusted. Notice we&#8217;re pulling in both the jar libraries within our web application as well as the libraries found on the application server itself. </p>

<p>Eclipse does this automatically when you specify a server runtime. When building from an ant file, these libraries need to be specified for the build to complete. Be careful not to include libraries in your local <code>lib</code> folder that are all ready present on the application server (i.e. <span class="caps">JSF </span>libraries, Servlet <span class="caps">API,</span> Struts, log4j, et cetera). This could cause complications. For instance, deploying a <span class="caps">WAR </span>that contains a <code>log4j.jar</code> within it will cause the logging handlers within JBoss to stop working for your web application. </p>



<pre class="sh_sourceCode sh_xml">
    &lt;property name=&quot;build&quot; value=&quot;./build&quot; /&gt;
    &lt;property name=&quot;dist&quot; value=&quot;./dist&quot; /&gt;
    &lt;property name=&quot;conf&quot;  value=&quot;./conf&quot; /&gt;
    &lt;property name=&quot;src&quot;   value=&quot;./src&quot; /&gt;
    &lt;property name=&quot;lib&quot;   value=&quot;./lib&quot; /&gt;
    &lt;property name=&quot;web&quot;   value=&quot;./web&quot; /&gt;
    &lt;property name=&quot;version&quot; value=&quot;0.1&quot; /&gt;
    &lt;property name=&quot;jbossLocation&quot; value=&quot;/Users/myhomedir/jboss-6.0.0.M1/&quot; /&gt;
    &lt;property name=&quot;servletLib&quot; value=&quot;${jbossLocation}/common/lib&quot; /&gt;
    &lt;property name=&quot;deployDir&quot; value=&quot;${jbossLocation}/server/default/deploy/&quot; /&gt;
    &lt;property name=&quot;warFile&quot; value=&quot;${dist}/${ant.project.name}.war&quot; /&gt;
    &lt;property name=&quot;earFile&quot; value=&quot;${dist}/${ant.project.name}-${version}.ear&quot; /&gt;

    &lt;path id=&quot;build.classpath&quot;&gt;
	&lt;fileset dir=&quot;${lib}&quot; includes=&quot;**/*.jar&quot; /&gt;
	&lt;fileset dir=&quot;${servletLib}&quot; includes=&quot;**/*.jar&quot; /&gt;
    &lt;/path&gt;
</pre>

<p> </p>

<p>We need to create some simple cleanup and initialization targets to clear out old bytecode as well as setup our build environment. The compile task will depend on the initialization being complete and make use of the libraries we specified above. </p>



<pre class="sh_sourceCode sh_xml">
    &lt;target name=&quot;clean&quot;&gt;
    	&lt;delete dir=&quot;${build}&quot; /&gt;
    	&lt;delete dir=&quot;${dist}&quot; /&gt;
    &lt;/target&gt;

    &lt;target name=&quot;init&quot;&gt;
    	&lt;tstamp /&gt;
    	&lt;mkdir dir=&quot;${build}&quot; /&gt;
    	&lt;mkdir dir=&quot;${dist}&quot; /&gt;
    &lt;/target&gt;
    
    &lt;target name=&quot;compile&quot; depends=&quot;init&quot;&gt;
	&lt;javac srcdir=&quot;${src}&quot; destdir=&quot;${build}&quot; optimize=&quot;on&quot;&gt;
	    &lt;classpath refid=&quot;build.classpath&quot; /&gt;
	&lt;/javac&gt;
    &lt;/target&gt;
</pre>

<p> </p>

<p>The <span class="caps">WAR </span>file task is simply an extension of the <span class="caps">ZIP </span>file task that places certain types of files within the correct location of the <span class="caps">WAR </span>file. Here we specify our <code>web.xml</code> configuration file, our Java classes, the web application libraries and the static content.</p>



<pre class="sh_sourceCode sh_xml">
    &lt;target name=&quot;war&quot; depends=&quot;compile&quot;&gt;
    	&lt;war destfile=&quot;${dist}/${ant.project.name}.war&quot; webxml=&quot;${conf}/web.xml&quot;&gt;
    	  &lt;lib dir=&quot;${lib}&quot; /&gt;
    	  &lt;classes dir=&quot;${build}&quot;/&gt;
    	  &lt;zipfileset dir=&quot;${web}&quot;  /&gt; 
    	&lt;/war&gt;
    &lt;/target&gt;
</pre>

<p> </p>

<p>The resulting <span class="caps">JAR </span>file has the following structure: </p>


<pre class="sh_sourceCode sh_sh">
META-INF/
META-INF/MANIFEST.MF
WEB-INF/
WEB-INF/web.xml
WEB-INF/lib/
WEB-INF/classes/
WEB-INF/classes/org/
WEB-INF/classes/org/penguindreams/
WEB-INF/classes/org/penguindreams/service/
WEB-INF/classes/org/penguindreams/service/ServiceHandler.class
images/
hello.jsp
images/penguindreams.gif
</pre>



<p>An <span class="caps">EAR </span>file is also very simple. It&#8217;s just a collection of <span class="caps">WAR </span>files with an <span class="caps">XML </span>describing each individual web module and it&#8217;s Context Root. For this example, there is only one web module.</p>



<pre class="sh_sourceCode sh_xml">
    &lt;target name=&quot;ear&quot; depends=&quot;war&quot;&gt;
    	&lt;ear destfile=&quot;${dist}/${ant.project.name}-${version}.ear&quot; appxml=&quot;${conf}/application.xml&quot;&gt;
    		&lt;fileset dir=&quot;${dist}&quot; includes=&quot;*.war&quot; /&gt;
    	&lt;/ear&gt;
    &lt;/target&gt;
</pre>

<p> </p>

<p>Finally, we have a deployment task. Most application servers can be configured to monitor a deployment directory and automatically install <span class="caps">EAR </span>files copied into that directory. If your application server is on a different machine than your build environment, you may need to use a remote deployment task, such as those included with <span class="caps">IBM</span> WebSphere to deploy <span class="caps">EAR </span>files using either their <acronym title="Simple Object Application Protocol">SOAP</acronym> or <acronym title="Remote Method Invocation">RMI</acronym> protocols. For this example, our server is on the same machine, so we can do a simple copy:</p>



<pre class="sh_sourceCode sh_xml">
    &lt;target name=&quot;deploy&quot; depends=&quot;ear&quot;&gt;
    	&lt;copy todir=&quot;${deployDir}&quot;&gt;
    		&lt;fileset dir=&quot;${dist}&quot; includes=&quot;*.ear&quot; /&gt;
    	&lt;/copy&gt;
    &lt;/target&gt;
</pre>

<p> </p>

<p>So our final <code>build.xml</code> is shown below. Notice the project root element where we specify the project name as well as the default task to run.</p>



<pre class="sh_sourceCode sh_xml">
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;project name=&quot;SimpleRest&quot; basedir=&quot;.&quot; default=&quot;ear&quot;&gt;

	&lt;property name=&quot;build&quot; value=&quot;./build&quot; /&gt;
	&lt;property name=&quot;dist&quot; value=&quot;./dist&quot; /&gt;
	&lt;property name=&quot;conf&quot;  value=&quot;./conf&quot; /&gt;
	&lt;property name=&quot;src&quot;   value=&quot;./src&quot; /&gt;
	&lt;property name=&quot;lib&quot;   value=&quot;./lib&quot; /&gt;
	&lt;property name=&quot;web&quot;   value=&quot;./web&quot; /&gt;
	&lt;property name=&quot;version&quot; value=&quot;0.1&quot; /&gt;
	&lt;property name=&quot;jbossLocation&quot; value=&quot;/Users/myhomedir/jboss-6.0.0.M1/&quot; /&gt;
	&lt;property name=&quot;servletLib&quot; value=&quot;${jbossLocation}/common/lib&quot; /&gt;
	&lt;property name=&quot;deployDir&quot; value=&quot;${jbossLocation}/server/default/deploy/&quot; /&gt;
	&lt;property name=&quot;warFile&quot; value=&quot;${dist}/${ant.project.name}.war&quot; /&gt;
	&lt;property name=&quot;earFile&quot; value=&quot;${dist}/${ant.project.name}-${version}.ear&quot; /&gt;
	
	&lt;path id=&quot;build.classpath&quot;&gt;
		&lt;fileset dir=&quot;${lib}&quot; includes=&quot;**/*.jar&quot; /&gt;
		&lt;fileset dir=&quot;${servletLib}&quot; includes=&quot;**/*.jar&quot; /&gt;
	&lt;/path&gt;
	
	&lt;target name=&quot;clean&quot;&gt;
		&lt;delete dir=&quot;${build}&quot; /&gt;
		&lt;delete dir=&quot;${dist}&quot; /&gt;
	&lt;/target&gt;
	
	&lt;target name=&quot;init&quot;&gt;
		&lt;tstamp /&gt;
		&lt;mkdir dir=&quot;${build}&quot; /&gt;
		&lt;mkdir dir=&quot;${dist}&quot; /&gt;
	&lt;/target&gt;
	
	&lt;target name=&quot;compile&quot; depends=&quot;init&quot;&gt;
		&lt;javac srcdir=&quot;${src}&quot; destdir=&quot;${build}&quot; optimize=&quot;on&quot;&gt;
			&lt;classpath refid=&quot;build.classpath&quot; /&gt;
		&lt;/javac&gt;
	&lt;/target&gt;
	
	&lt;target name=&quot;war&quot; depends=&quot;compile&quot;&gt;
		&lt;war destfile=&quot;${dist}/${ant.project.name}.war&quot; webxml=&quot;${conf}/web.xml&quot;&gt;
		  &lt;lib dir=&quot;${lib}&quot; /&gt;
		  &lt;classes dir=&quot;${build}&quot;/&gt;
		  &lt;zipfileset dir=&quot;${web}&quot;  /&gt; 
		&lt;/war&gt;
	&lt;/target&gt;
	
	&lt;target name=&quot;ear&quot; depends=&quot;war&quot;&gt;
		&lt;ear destfile=&quot;${dist}/${ant.project.name}-${version}.ear&quot; appxml=&quot;${conf}/application.xml&quot;&gt;
			&lt;fileset dir=&quot;${dist}&quot; includes=&quot;*.war&quot; /&gt;
		&lt;/ear&gt;
	&lt;/target&gt;
	
	&lt;target name=&quot;deploy&quot; depends=&quot;ear&quot;&gt;
		&lt;copy todir=&quot;${deployDir}&quot;&gt;
			&lt;fileset dir=&quot;${dist}&quot; includes=&quot;*.ear&quot; /&gt;
		&lt;/copy&gt;
	&lt;/target&gt;
&lt;/project&gt;
</pre>



<p>We now have a complete project that can be built and deployed using a simple ant command. If you prefer to deploy your project within Eclipse, you can go to Project&gt;Properties and then select Builders and New to create a new Ant Builder. Once it is configured, preforming a build within Eclipse will launch an external delegate to run the <code>build.xml</code> file. Here is the output from running the deploy task.</p>



<pre class="sh_sourceCode">
$ ant deploy
Buildfile: build.xml

init:
    [mkdir] Created dir: /Users/myhomedir/Documents/workspace/SimpleRest/build
    [mkdir] Created dir: /Users/myhomedir/Documents/workspace/SimpleRest/dist

compile:
    [javac] Compiling 1 source file to /Users/myhomedir/Documents/workspace/SimpleRest/build

war:
      [war] Building war: /Users/myhomedir/Documents/workspace/SimpleRest/dist/SimpleRest.war

ear:
      [ear] Building ear: /Users/myhomedir/Documents/workspace/SimpleRest/dist/SimpleRest-0.1.ear

deploy:
     [copy] Copying 1 file to /Users/myhomedir/jboss-6.0.0.M1/server/default/deploy

BUILD SUCCESSFUL
Total time: 1 second
</pre>



<p>Immediately afterward, we can see JBoss pickup the new <span class="caps">EAR </span>file by watching its log file.</p>



<pre class="sh_sourceCode">
01:58:53,696 INFO  [Http11Protocol] Starting Coyote HTTP/1.1 on http-127.0.0.1-8080
01:58:53,743 INFO  [AjpProtocol] Starting Coyote AJP/1.3 on ajp-127.0.0.1-8009
01:58:53,759 INFO  [AbstractServer] JBossAS [6.0.0.M1 (build: SVNTag=JBoss_6_0_0_M1 date=200912040958)] Started in 34s:58ms
01:59:05,970 INFO  [TomcatDeployment] undeploy, ctxPath=/rest
01:59:06,231 INFO  [TomcatDeployment] deploy, ctxPath=/rest
</pre>



<p>Finally, we can open a web browser, open the appropriate <span class="caps">URL </span>to our servlet as determined by both the <code>application.xml</code> and the <code>web.xml</code>.</p>

<p><img src="http://penguindreams.org/files/2010/04/ant_tutorial_sample_page.png" alt="Servlet Sample Page" title="Servlet Sample Page" class="aligncenter size-full wp-image-316" /></p>

<p>Creating a web application with an ant build file is just the beginning. The <code>build.xml</code> can be modified with additional variables and tasks to assist in deploying web applications to different environments or for running automated unit tests prior to deployment. Variables defined within the build file can be overwritten using the <code>-D</code> command line option for ant. Scripts can be created on <span class="caps">UNIX </span>servers to automatically retrieve your projects from a source control system such as <acronym title="Concurrent Version System">CVS</acronym>, Subversion or Mercurial and run the ant script within the project with a particular set of arguments based on the environment (e.g. development, test and production).</p>

<p>Using ant to build projects is essential to streamlining development of <span class="caps">J2EE </span>web application and ensures consistency in builds and deployments. This tutorial just scratches the surface of building <span class="caps">J2EE </span>applications and is part of a series of tutorials for building solid enterprise services and web applications from the ground up. The full source code for the example application shown above can be downloaded as <a href="/files/progs/j2ee-ant-example-penguindreams.tar.bz2">j2ee-ant-example-penguindreams.tar.bz2</a> or <a href="/files/progs/j2ee-ant-example-penguindreams.zip">j2ee-ant-example-penguindreams.zip</a> </p>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/building-java-ear-files-using-ant/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Java&#8217;s Checked Exceptions</title>
		<link>http://penguindreams.org/blog/javas-checked-exceptions/</link>
		<comments>http://penguindreams.org/blog/javas-checked-exceptions/#comments</comments>
		<pubDate>Wed, 07 Apr 2010 17:48:42 +0000</pubDate>
		<dc:creator>sumdog</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://penguindreams.org/?p=301</guid>
		<description><![CDATA[Anyone who has programmed with Java should be familiar with the concept of Checked Exceptions. Although C++ and OCaml have optional support for exception checking, Java seems to be the only major programing language where it is a built-in and required part of the language. Enforcing at compile time that certain exceptions need to be [...]]]></description>
			<content:encoded><![CDATA[<p>Anyone who has programmed with Java should be familiar with the concept of <em>Checked Exceptions</em>. Although C++ and OCaml have optional support for exception checking, Java seems to be the only major programing language where it is a built-in and required part of the language. Enforcing at compile time that certain exceptions need to be caught may have seemed like a good idea at the time Java was developed, however no major languages developed since have adapted the concept. Many view <em>Checked Exceptions</em> as a design flaw. In this article, I attempt to show how this flaw can be overcome using a base exception class to encapsulate exception handling.<br />
<span id="more-301"></span><br />
Many of the libraries in java tend to throw a lot of different exceptions, especially when dealing with any type of Input/Output either on the disk or via network. Take the following example in which the <em>sendMail</em> function uses the JavaMail <span class="caps">API </span>in order to send a message:</p>



<pre class="sh_sourceCode sh_java">
try {
    sendMail(from,to,subject,body);
}
catch(AddressException e) {
    //Handle This Exception 
}
catch(UnknownHostException e) {
    //Handle This Exception
}
catch(MessagingException e) {
    //Handle This Exception
}
</pre>



<p>In some projects I would often catch the base exception class and use the <strong>instanceof</strong> keyword to clean up the exception handling as seen below. Although the code for this technique may be cleaner, it&#8217;s more difficult to add new exception types that may be introduced later by code changes:</p>



<pre class="sh_sourceCode sh_java">
String error = &quot;&quot;;
try {
    sendMail(from,to,subject,body);
}
catch(Exception x) {

    error = &quot;Internal Error: &quot;;

    if(x instanceof AddressException) {
            error += &quot;Invalid Internal Address&quot;;
    }
    else if(x instanceof UnknownHostException) {
            error += &quot;Could Not Find Mail Server&quot;;
    }
    else if(x instanceof MessagingException) {
            error += &quot;Messaging Problem&quot;;
    }
    else {
           error += &quot;Unexpected: &quot; + x.getMesssage();
    }
}
</pre>



<p>In both instances, programmers typically just log the exception and display a general error to the end user. Catching the individual exception types doesn&#8217;t really help in any meaningful way. Also, the exception message itself is often human readable and a clear indication of what error occurred. In my current projects, I use a base exception class and simply place any code that produces checked exceptions within a large try block and rethrow any exceptions as a property of my base exception class.</p>



<pre class="sh_sourceCode sh_java">
public class SimpleException extends Exception {

	private Throwable nestedException;
	
	public SimpleException(String s) {
		super(s);
	}
	
	public SimpleException(String s, Exception nestedException) {
		super(s);
		this.nestedException = nestedException;		
	}
	
	public Throwable getNestedException() {
		return this.nestedException;
	}
	
} 
</pre>

<p> </p>

<p>Using this class, the previous example would change to the following:</p>



<pre class="sh_sourceCode sh_java">
String error = &quot;&quot;;
try {
    sendMail(from,to,subject,body);
}
catch(Exception e) {
    throw new SimpleException(&quot;Error Occurred Sending E-mail&quot;,e);
}
</pre>



<p>This cleans up our code significantly. Under the old model, say that the implementation changed and new potential exceptions could possibly be thrown. Each one of those exceptions would have to be added to the throws clause to the appropriate function. If the class implemented a interface or extended and abstract class, those base methods would have to change as well, along with any code implementing the class. If we throw only one type of exception that wraps all other exceptions, we never have to worry about the method signature changing. </p>

<p>Some programmers may oppose this approach and classify it as an anti-pattern, similar to wrapping all checked exceptions in blocks that rethrow runtime exceptions. However, in my experience in working with large projects, using this approach is much cleaner and more maintainable. Furthermore, it allows Aspect Orientated Programming (AOP) interceptors to have a lot more information when dealing with exceptions.</p>

<p>This article is the first in a series of tutorials on <span class="caps">J2EE </span>development. The use of the except pattern I&#8217;ve used will be further expanded with seeing how it can integrate into web applications using Spring and <span class="caps">AOP. </span></p>]]></content:encoded>
			<wfw:commentRss>http://penguindreams.org/blog/javas-checked-exceptions/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

