<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2823669911800907659</id><updated>2011-11-27T18:25:31.508-05:00</updated><category term='mobile'/><category term='rules'/><category term='x.509'/><category term='disney'/><category term='swing'/><category term='ServiceLoader'/><category term='Hibernate'/><category term='highavailability'/><category term='slony-i'/><category term='tomcat'/><category term='SELinux'/><category term='Security'/><category term='SPI'/><category term='syntax'/><category term='MAC'/><category term='roadmap'/><category term='pixar'/><category term='redhat'/><category term='css'/><category term='redbull'/><category term='spring'/><category term='internet'/><category term='eclipse'/><category term='syntaxhighlighter'/><category term='offices'/><category term='kerberos'/><category term='authorization'/><category term='database'/><category term='disneyworld'/><category term='linux'/><category term='pki'/><category term='choice'/><category term='centos'/><category term='certificates'/><category term='cubicle'/><category term='slony'/><category term='MySQL'/><category term='windowsmobile'/><category term='relations'/><category term='UserType'/><category term='seam'/><category term='authentication'/><category term='comcast'/><category term='smartcards'/><category term='Junit'/><category term='verizon'/><category term='pgcluster'/><category term='ocsp'/><category term='Java'/><category term='jboss tools'/><category term='fios'/><category term='monopolies'/><category term='gui'/><category term='oracle'/><category term='netbeans'/><category term='databses'/><category term='databases'/><category term='CAC'/><category term='blogger'/><category term='PostgreSQL'/><category term='sql'/><category term='servlets'/><category term='drools'/><category term='disneyland'/><category term='palm'/><category term='jboss'/><category term='odyssi'/><category term='workspaces'/><category term='testing'/><category term='release'/><category term='biometrics'/><category term='j2ee'/><category term='architecture'/><category term='widget'/><category term='designpatterns'/><category term='replication'/><category term='google'/><title type='text'>WhoAmI?  The Java and Identity Management Blog</title><subtitle type='html'>A discussion of Java, information security, PKI, and identity management through the development of the Odyssi PKI Suite</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>29</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-7170406958534151477</id><published>2008-12-29T13:46:00.003-05:00</published><updated>2008-12-29T13:58:17.372-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='x.509'/><category scheme='http://www.blogger.com/atom/ns#' term='pki'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='netbeans'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='certificates'/><title type='text'>Announcement:  KeyCenter4NB 0.1</title><content type='html'>I'm very torn when it comes to choosing my Java development environment.  I often swap between &lt;a href="http://www.eclipse.org/"&gt;Eclipse&lt;/a&gt; and &lt;a href="http://www.netbeans.org/"&gt;Netbeans&lt;/a&gt;.  In fact, when I create a new project, I ensure that the project structure is compatible between the two, just in case the mood strikes me to swap back and forth.  Eclipse is a fine IDE, however it is just a bit too rough around the edges in some respects for me.  Netbeans, on the other hand, has some great features (UML, Matisse, a very powerful debugger and profiler, etc.) but I find it's Java editor very lacking and non-intuitive.  The other problem I have with Netbeans is the lack of plugins.  The Eclipse community has a far greater number of plugins available for it.&lt;br /&gt;&lt;br /&gt;Recently, while working on some PKI-related code, I found myself wishing I had a plugin for viewing X.509 certificate details.  After looking around, I really couldn't find anything suitable for my needs.  I've never been really big on desktop GUI programming, mostly because I've never had the need to do it.  All of my professional development has been on the J2EE side of things.  However, I decided that if you want something done right you have to do it yourself, and decided I was going to write the plugin I was looking for.&lt;br /&gt;&lt;br /&gt;After investigating both the Eclipse and Netbeans platforms, I decided to target Netbeans.  I like the fact that Netbeans is pure Swing, and its GUI builder is second to none.  I began playing around with module development, and was very pleasantly surprised with the experience.  Yes, the Netbeans RCP has a very steep learning curve, and I've not even scratched the surface of what it is capable of.  However, I've managed to create a very rough initial version of my plugin, &lt;a href="http://www.sourceforge.net/projects/keycenter4nb"&gt;KeyCenter4NB&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Initially, I was just looking for a plugin that would let me view the details of an X.509 certificate.  However, the more I got into developing it, the more I wanted to eventually implement.  As a result, the initial 0.1 version is just a certificate view.  Future releases, though, will include tools for creating and manipulating Java keystores, creating PKCS #10 certificate requests, checking CRLs/OCSP, and other PKI-related functionality that a developer may need to take advantage of.  A grand vision I'd like to implement (if I actually get enough time) would be a standalone application based on the Netbeans RCP that focuses on certificate and key management.  However, that is a long way away.&lt;br /&gt;&lt;br /&gt;In the meantime, check out the &lt;a href="http://www.sourceforge.net/projects/keycenter4nb"&gt;KeyCenter4NB project page&lt;/a&gt; and download version 0.1.  Right now, you must download the .NBM packages and manually install them.  I hope to have an update center for the project configured and running shortly.  As I mentioned, it's very basic and I'm sure it's rough around the edges.  But, I'll continue to update is as I learn more.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-7170406958534151477?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/7170406958534151477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=7170406958534151477' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/7170406958534151477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/7170406958534151477'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2008/12/announcement-keycenter4nb-01.html' title='Announcement:  KeyCenter4NB 0.1'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-7191229664615896513</id><published>2008-09-22T15:33:00.003-05:00</published><updated>2008-09-22T15:40:48.147-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='blogger'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='widget'/><category scheme='http://www.blogger.com/atom/ns#' term='syntaxhighlighter'/><category scheme='http://www.blogger.com/atom/ns#' term='syntax'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Syntax Highlighting in Blogger</title><content type='html'>Recently, I've gone through and added syntax highlighting to some of my previous posts.  This was done for Java, XML, and SQL.  There is a great set of CSS files and JavaScript to accomplish this called &lt;a href="http://code.google.com/p/syntaxhighlighter/"&gt;SyntaxHighlighter&lt;/a&gt;.  The downside, however, is that Blogger does not make integrating these tools very easy.  I tried several times, but always managed to screw something up.  As a result, I was very happy to come across a &lt;a href="http://fazibear.googlepages.com/blogger.html"&gt;Blogger Widget for SyntaxHighlighter&lt;/a&gt;.  All you need to do is add this widget to your blogger layout.  Remember to leave the title blank so that you don't see an empty section in your sidebar.  Once you've added this widget, you can add syntax highlighting similar to what you see &lt;a href="http://odyssi.blogspot.com/2008/07/intro-to-jboss-seam-security-part-3.html"&gt;here&lt;/a&gt;.  To highlight Java, for example, edit the HTML of your post and surround your Java code with :&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;pre name="code" class="java"&amp;gt;&lt;br /&gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's all there is to it.  A finished example would look like this, once highlighted:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;public class MyClass {&lt;br /&gt;   &lt;br /&gt;   private int myInt = -1;&lt;br /&gt;&lt;br /&gt;   public int getMyInt() {&lt;br /&gt;     return this.myInt;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void setMyInt(int myInt) {&lt;br /&gt;     this.myInt = myInt;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-7191229664615896513?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/7191229664615896513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=7191229664615896513' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/7191229664615896513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/7191229664615896513'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2008/09/syntax-highlighting-in-blogger.html' title='Syntax Highlighting in Blogger'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-5300888075817077309</id><published>2008-07-30T08:05:00.025-05:00</published><updated>2008-09-23T12:45:01.028-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rules'/><category scheme='http://www.blogger.com/atom/ns#' term='drools'/><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='authorization'/><category scheme='http://www.blogger.com/atom/ns#' term='seam'/><category scheme='http://www.blogger.com/atom/ns#' term='authentication'/><category scheme='http://www.blogger.com/atom/ns#' term='jboss'/><title type='text'>Intro to JBoss Seam Security, Part 3 -- Authorization Rules with Drools</title><content type='html'>In previous articles, we have looked at how &lt;a href="http://www.seamframework.org/"&gt;JBoss Seam&lt;/a&gt; handles security, particularly user authentication. Through the use of its Identity and Authenticator APIs, Seam provides an easy way to authenticate a prospective user without forcing a developer to implement hundreds of lines of code. In addition, Seam also provides &lt;a href="http://en.wikipedia.org/wiki/Captcha"&gt;CAPTCHA&lt;/a&gt; support, enabling an application to test for the presence of a human user, rather than a bot attempting to access an application. No doubt, Seam 2.1's forthcoming identity management API will make the process of creating and managing user accounts even easier. (Although, that is an article for another day)&lt;br /&gt;&lt;br /&gt;While authentication is vital to application security, it is not the only aspect of security that must be accounted for. Authorization -- the process whereby it is determined what resources or actions a user may access -- takes authentication one step further, and cannot be overlooked. It is easy to come up with examples of where authorization should be determined within an application. A banking site would want to ensure that an authenticated user is, in fact, the owner of the bank account they are trying to access. A health insurance provider needs to check if an individual should be allowed to view recent claims that have been submitted. To accomplish this, some sort of authorization rules need to be established. Java EE already allows for programmtic authorization checks through the use of the &lt;a href="http://java.sun.com/j2ee/1.4/docs/api/javax/servlet/http/HttpServletRequest.html#isUserInRole%28java.lang.String%29"&gt;HttpServletRequest.isUserInRole()&lt;/a&gt; method. Seam, however, takes authorization further and allows for integration within enterprise applications through the use of an authorization rules engine.&lt;br /&gt;&lt;br /&gt;Seam uses the &lt;a href="http://www.jboss.org/drools/"&gt;Drools&lt;/a&gt; business rules engine to allow complex authorization rules to exist outside of the Java code itself. Business rules engines have long been used to drive business operations or make decisions related to a specific business process (i.e. Approve a loan for an individual if they make more than $150,000 per year and have less than $25,000 in existing debt.) Now, these same types of rules can be applied to application security logic. Imagine a site like Facebook and how it approaches security. Facebook has very well-defined rules for how its information can be accessed. A profile is only visible to a member if the profile owner is friends with the member. Picture X can only be viewed by family members and friends, not coworkers. Member A can send a friend request to Member B provided that: 1) Member A != Member B, and 2) Member A is not already friends with Member B. It would be very easy to implement rules like this in Java code, either in the view or business layer. However, in our example application, we will separate these rules from our Java code and implement them using Drools.&lt;br /&gt;&lt;br /&gt;Our simple example will be a social network site. Like any social network, ours will have some basic rules established to ensure the security of our site. The rules we will implement are:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Administrators may do whatever they please&lt;/li&gt;&lt;li&gt;A friend request may only be submitted if the sender != recipient&lt;/li&gt;&lt;li&gt;A profile is only visible by its owner and their friends&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Our example will consist of two main objects:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;public class Member implements Serializable {&lt;br /&gt;&lt;br /&gt;   ...snip...&lt;br /&gt;&lt;br /&gt;   public Set&amp;lt;Member&amp;gt;getFriends() {&lt;br /&gt;      ...&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void setFriends(Set&amp;lt;Member&amp;gt; friends) {&lt;br /&gt;      ...&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Name(value="member")&lt;br /&gt;public class MemberAction {&lt;br /&gt;&lt;br /&gt;   ...snip...&lt;br /&gt;&lt;br /&gt;   @In&lt;br /&gt;   private Member authenticatedMember = null;&lt;br /&gt;&lt;br /&gt;   public void viewProfile(Member member) {&lt;br /&gt;      ...&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void sendFriendRequest(Member recipient) {&lt;br /&gt;      ...&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public void deleteProfile(Member member) {&lt;br /&gt;      ...&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;For the sake of time, we will not discuss basic dependency injection concepts. It should be obvious that MemberAction.authenticatedMember is related to the profile of a user who has successfully authenticated against the application. There are two primary ways we can perform an authorization check in the business layer using Seam: 1) Through annotations, or 2) Inline, using Java code.  In addition, we can also perform security checks in the view layer to display or hide links or data based on the outcome of the check.  We will discuss all of these methods for performing the checks as we move forward.&lt;br /&gt;&lt;br /&gt;Before we are able to write and test our security rules, we must first enable them in our Seam configuration, and ensure our rules file is located in the right place. The components.xml file must have the following lines to tell Seam that we wish to use Drools for authorization:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;drools:rule-base name="securityRules"&amp;gt;&lt;br /&gt; &amp;lt;drools:rule-files&amp;gt;&lt;br /&gt;     &amp;lt;value&amp;gt;/META-INF/security.drl&amp;lt;/value&amp;gt;&lt;br /&gt; &amp;lt;/drools:rule-files&amp;gt;&lt;br /&gt;&amp;lt;/drools:rule-base&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, we have configured our security rules file to be located at &lt;span style=";font-family:courier new;font-size:85%;"  &gt;/META-INF/security.drl&lt;/span&gt;.  It is important that this file exists, otherwise our applications will not work as we expect.  To tackle our first rule, "&lt;span style="font-style: italic;"&gt;Administrators may do whatever they please&lt;/span&gt;", we need to establish a rule that allows administrators to be granted whatever permissions they request. Since this is a relatively simple rule, it will serve as a good starting point for our introduction into Drools syntax. Our rule will look like this:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;package MySecurityRules;&lt;br /&gt;&lt;br /&gt;import org.jboss.seam.security.PermissionCheck;&lt;br /&gt;import org.jboss.seam.security.Role;&lt;br /&gt;&lt;br /&gt;rule AdminsCanDoAnything&lt;br /&gt;   when&lt;br /&gt;      c: PermissionCheck(name == "member")&lt;br /&gt;      Role(name == "admin")&lt;br /&gt;   then&lt;br /&gt;      c.grant();&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let's take a look at the syntax for this rule.  On line 1, you will see a &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;package &lt;/span&gt;&lt;/span&gt;declaration. This is not the same as Java packages. Drools packages are used to group Drools rules together. They are not used for anything else. On lines 2 and 3, you see two &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;import&lt;/span&gt;&lt;/span&gt; statements. This lets Drools know that we will be referrencing the specified classes as part of our rule. It is imporant to include these lines, otherwise our rule will fail. Line 4 marks the beginning of the rule itself. Our rule is called "&lt;span style="font-style: italic;"&gt;AdminsCanDoAnything&lt;/span&gt;". There is no specific naming requirement for your rule, other than the fact that it must be unique within the given package. Typically, the rule name is a description of what the rule actually accomplishes. The rule is divided into 2 sections, the "&lt;span style=";font-family:courier new;font-size:85%;"  &gt;when&lt;/span&gt;" and the "&lt;span style=";font-family:courier new;font-size:85%;"  &gt;then&lt;/span&gt;". The "&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;when&lt;/span&gt;&lt;/span&gt;" section consists of one or more conditions that must be true for this rule to fire. If any of these conditions is not met, the rule will not fire. Once all conditions have been met, the rules engine moves on to the "&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;then&lt;/span&gt;&lt;/span&gt;" section, and performs whatever commands are specified.&lt;br /&gt;&lt;br /&gt;Lines 6 and 7 provide the authorization check itself. Line 6 is the first condition that must be met for this rule to fire. The line:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;c: PermissionCheck(name == "member")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;can be explained as, "&lt;span style="font-style: italic;"&gt;there must exist a PermissionCheck object with a name property of "member" within the current working memory. This object will be called 'c' in this rule.&lt;/span&gt;" If this condition is met, the engine moves on to line 7. Line 7 specifies that, "&lt;span style="font-style: italic;"&gt;there must exist a Role with the name 'admin' within the current memory.&lt;/span&gt;" By now you're probably wondering, "&lt;span style="font-style: italic;"&gt;What is 'the current memory'?&lt;/span&gt;" Drools has its own context or working memory that it uses when evaluating a rule. It essentially acts as a session for the rules engine. Before an object can be considered within a rule, it must be added to the working memory. Whenever a permission check is performed via the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;hasPermission()&lt;/span&gt;&lt;/span&gt; method, a &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;PermissionCheck&lt;/span&gt;&lt;/span&gt; object is created and inserted into the working memory. This object contains the name of the object the check corresponds to, and the action that is being attempted. In our example, the name of the object is "&lt;span style="font-style: italic;"&gt;member&lt;/span&gt;". You'll notice that we have not specified an action in our check. This results in the rule corresponding to all possible actions pertaining to member. In addition, any Roles that a user belongs to will be inserted into the working memory prior to a rule check. This is how we determine the contents of our example security rule. It is also possible to insert an arbitrary object into the working memory by calling&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;((RuleBasedIdentity) RuleBasedIdentity.instance()).getSecurityContext().insert();&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This will come in handy in our next article as we discuss Seam security rules futher.  Now that we have defined our authorization rule, we can apply it and perform a permission check.  For this example, we want to hide the link for the deleteProfile action from our view layer.  Normally, however, we would also apply this permission check in the action itself to prevent users from circumventing our view-layer check.  To perform a permission check on our link, we simply add the following to our JSF file:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;s:link value="Delete Profile" action="#{member.deleteProfile(...)}"&lt;br /&gt;rendered="#{s:hasPermission('member', 'deleteProfile', null)}" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, when our view is displayed, the permission check will be performed prior to rendering the link.  Since our admin users have been granted all permissions, the link will be rendered.  If we wrote an additional rule restricting access to the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;deleteProfile()&lt;/span&gt;&lt;/span&gt;, our admins would still be able to view the link, while other users that fail the permission check will not see the link.&lt;br /&gt;&lt;br /&gt;In my next article, I will discuss how to implement our two remaining authorization rules, as well as the other ways of performing a permission check within your code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-5300888075817077309?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/5300888075817077309/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=5300888075817077309' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/5300888075817077309'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/5300888075817077309'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2008/07/intro-to-jboss-seam-security-part-3.html' title='Intro to JBoss Seam Security, Part 3 -- Authorization Rules with Drools'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-280528255070537526</id><published>2008-07-26T12:09:00.059-05:00</published><updated>2008-09-22T15:10:46.211-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='jboss tools'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='j2ee'/><category scheme='http://www.blogger.com/atom/ns#' term='seam'/><category scheme='http://www.blogger.com/atom/ns#' term='tomcat'/><category scheme='http://www.blogger.com/atom/ns#' term='servlets'/><category scheme='http://www.blogger.com/atom/ns#' term='jboss'/><title type='text'>Building Seam Apps for Tomcat with JBoss Tools</title><content type='html'>One of the things I really like about the &lt;a href="http://www.seamframework.org/"&gt;Seam framework&lt;/a&gt; is how much easier J2EE development is, particularly with respect to pulling the various components together.  With Seam, it's very easy to build applications that utilize JSF, EJBs, and JPA.  However, it's often preferable to develop more lightweight applications.  While EJB3 has made enterprise development much more lightweight than in the past, there is still considerable overhead associated with it, particularly when you make use of a full-blown J2EE application server like &lt;a href="http://www.jboss.com/"&gt;JBoss&lt;/a&gt;.  Sometimes, it's better to utilize a simpler approach and deploy your application using a Servlet Container like &lt;a href="http://tomcat.apache.org/"&gt;Tomcat&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://www.jboss.org/tools/"&gt;JBoss Tools&lt;/a&gt; project goes a long way towards making the actual development process with Seam much more user friendly.  JBoss Tools provides a set of Eclipse plugins designed to work seamlessly (pun intended) with the framework.  One of the drawbacks of JBoss Tools, however, is its lack of proper integration when creating a project you wish to deploy to Tomcat.  In this post, I'll show you the changes you'll need to make to your Eclipse projects if you want to deploy your application to Tomcat.&lt;br /&gt;&lt;br /&gt;To start, I'll assume that you have already downloaded Tomcat and defined a Server for it in Eclipse.  It's very important to remember that you need Tomcat 6, in order to have support for the Servlet 2.5 specification.  Once you've downloaded and setup Tomcat in Eclipse, you're ready to create your JBoss Tools project.&lt;br /&gt;&lt;br /&gt;Create your JBoss Tools Seam project, just as you would a regular Seam project, by clicking File-&gt;New-&gt;Seam Web Project.  This will start the new project wizard.  However, when selecting your Target Runtime, select your Apache Tomcat 6.0 runtime, as shown in the image below.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_mBoF1HA1ncU/SIvIyir1r1I/AAAAAAAAAEc/yOyiZsp4Jbk/s1600-h/Picture+1.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_mBoF1HA1ncU/SIvIyir1r1I/AAAAAAAAAEc/yOyiZsp4Jbk/s320/Picture+1.jpg" alt="" id="BLOGGER_PHOTO_ID_5227492563123285842" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Click next, and select whatever project facets you may need.  Click next through the wizard and fill in the appropriate fields according to your project needs, until you get to the following screen.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_mBoF1HA1ncU/SIvJTzrUeKI/AAAAAAAAAEk/wjWiuFYQwTc/s1600-h/Picture+2.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_mBoF1HA1ncU/SIvJTzrUeKI/AAAAAAAAAEk/wjWiuFYQwTc/s320/Picture+2.jpg" alt="" id="BLOGGER_PHOTO_ID_5227493134620194978" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In this screen, be sure to select a WAR file for your deployment, as Tomcat only understands WARs, not EARs.  Click Finish, and your project will be created.  Now, we need to make a couple of changes to support Tomcat in our project.  We'll start by ensuring that the necessary Seam libraries are a part of your project.  The following libraries need to be placed in your &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;WEB-INF/lib&lt;/span&gt;&lt;/span&gt; directory:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;antlr.jar&lt;/li&gt;&lt;li&gt;asm.jar&lt;/li&gt;&lt;li&gt;cglib.jar&lt;/li&gt;&lt;li&gt;commons-beanutils.jar&lt;/li&gt;&lt;li&gt;common-collections.jar&lt;/li&gt;&lt;li&gt;commons-digester.jar&lt;/li&gt;&lt;li&gt;commons-lang.jar&lt;/li&gt;&lt;li&gt;commons-logging.jar&lt;/li&gt;&lt;li&gt;dom4j.jar&lt;/li&gt;&lt;li&gt;hibernate-annotations.jar&lt;/li&gt;&lt;li&gt; hibernate-commons-annotations.jar&lt;/li&gt;&lt;li&gt;hibernate-entitymanager.jar&lt;/li&gt;&lt;li&gt;hibernate.jar&lt;/li&gt;&lt;li&gt;hibernate-validator.jar&lt;/li&gt;&lt;li&gt;javassist.jar&lt;/li&gt;&lt;li&gt;jboss-archive-browsing.jar&lt;/li&gt;&lt;li&gt;jboss-el.jar&lt;/li&gt;&lt;li&gt;jboss-seam-debug.jar&lt;/li&gt;&lt;li&gt;jboss-seam.jar&lt;/li&gt;&lt;li&gt;jboss-seam-ui.jar&lt;/li&gt;&lt;li&gt;jsf-api.jar&lt;/li&gt;&lt;li&gt;jsf-facelets.jar&lt;/li&gt;&lt;li&gt;jsf-impl.jar&lt;/li&gt;&lt;li&gt;jstl.jar&lt;/li&gt;&lt;li&gt;jta.jar&lt;/li&gt;&lt;li&gt;persistence-api.jar&lt;/li&gt;&lt;li&gt; richfaces-api.jar&lt;/li&gt;&lt;li&gt;richfaces-impl.jar&lt;/li&gt;&lt;li&gt;richfaces-ui.jar&lt;/li&gt;&lt;/ul&gt;Once these libraries are in place, your project will build successfully and deploy correctly to Tomcat.  Now, we need to look at the Resource configuration to support JPA for object persistence.  First, we need to create a &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;context.xml&lt;/span&gt;&lt;/span&gt; file and place it your &lt;span style=";font-family:courier new;font-size:85%;"  &gt;META-INF&lt;/span&gt; directory.  This is the file that Tomcat uses when deploying your project to setup the context root, as well as any resources that need to be made available to your project.  With a JBoss deployment, your database settings are placed in a separate file, like &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;MyProject-ds.xml&lt;/span&gt;&lt;/span&gt;.  With Tomcat, we will place all of these settings in our context.xml file.  Once completed, it will look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;Context crossContext="true" debug="5" docBase="myproject" path="/myproject" reloadable="true"&amp;gt;&lt;br /&gt;  &amp;lt;Resource auth="Container" driverClassName="com.mysql.jdbc.Driver" maxActive="20"&lt;br /&gt;    maxIdle="10" maxWait="-1" name="jdbc/myproject type="javax.sql.DataSource"&lt;br /&gt;    url="jdbc:mysql://localhost:3306/myproject"&lt;br /&gt;    username="myuser" password="mypassword" /&amp;gt;&lt;br /&gt;&amp;lt;/Context&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We also need to modify the persistence.xml file used by JPA.  The JNDI name that Tomcat utilizes differs from what JBoss uses.  Instead of "&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;java:/&lt;/span&gt;&lt;/span&gt;", our datasource needs to start with "&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;java:comp/env&lt;/span&gt;&lt;/span&gt;".  Our persistence.xml file will now look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;persistence xmlns="http://java.sun.com/xml/ns/persistence" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"&amp;gt;&lt;br /&gt;  &amp;lt;persistence-unit name="MyProject" type="RESOURCE_LOCAL"&amp;gt;&lt;br /&gt;     &amp;lt;provider&amp;gt;org.hibernate.ejb.HibernatePersistence&amp;lt;/provider&amp;gt;&lt;br /&gt;     &amp;lt;jta-data-source&amp;gt;java:comp/env/jdbc/MyProjectDatasource&amp;lt;/jta-data-source&amp;gt;&lt;br /&gt;     &amp;lt;properties&amp;gt;&lt;br /&gt;         &amp;lt;property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5InnoDBDialect" /&amp;gt;&lt;br /&gt;         &amp;lt;property name="hibernate.hbm2ddl.auto" value="update" /&amp;gt;&lt;br /&gt;         &amp;lt;property name="hibernate.show_sql" value="true" /&amp;gt;&lt;br /&gt;         &amp;lt;property name="hibernate.format_sql" value="true" /&amp;gt;&lt;br /&gt;         &amp;lt;property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup" /&amp;gt;&lt;br /&gt;     &amp;lt;/properties&amp;gt;&lt;br /&gt; &amp;lt;/persistence-unit&amp;gt;&lt;br /&gt;&amp;lt;/persistence&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Lastly, we need to modify some settings in our components.xml file.  First, remove the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;jndi-pattern="@jndiPattern@"&lt;/span&gt;&lt;/span&gt; attribute from the following line:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;&amp;lt;core:init debug="true" pattern="@jndiPattern@"&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;At this point, we should be ready to deploy our applicaion and test it out.  Take a good look at the log files in case your application doesn't deploy correctly.  However, if you followed the above steps, everything should work as expected.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-280528255070537526?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/280528255070537526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=280528255070537526' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/280528255070537526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/280528255070537526'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2008/07/building-seam-apps-for-tomcat-with.html' title='Building Seam Apps for Tomcat with JBoss Tools'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_mBoF1HA1ncU/SIvIyir1r1I/AAAAAAAAAEc/yOyiZsp4Jbk/s72-c/Picture+1.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-1009883899097505047</id><published>2008-03-28T13:29:00.002-05:00</published><updated>2008-03-28T13:42:18.591-05:00</updated><title type='text'>Dogtag -- Open Source PKI from Red Hat</title><content type='html'>Earlier this month, Red Hat &lt;a href="http://www.press.redhat.com/2008/03/19/source-code-for-red-hat-certificate-system-released/"&gt;announced&lt;/a&gt; they were open-sourcing the PKI platform they &lt;a href="http://www.internetadsales.com/modules/news/article.php?storyid=4056"&gt;purchased from Netscape&lt;/a&gt; in 2004.  The resulting project, &lt;a href="http://pki.fedoraproject.org/wiki/PKI_Main_Page"&gt;Dogtag&lt;/a&gt;, contains an open-source version of the code used to power Red Hat Certificate System, previously known as Netscape Certificate Management Server.  While Red Hat is most known for their Linux distribution (and, more recently, their JBoss middleware products), RHCS is the basis for the world's largest PKI, developed and maintained by the U.S. Department of Defense.  This PKI is responsible for nearly 10 million certificates, including those issued on the DoD's &lt;a href="http://www.cac.mil/"&gt;Common Access Card (CAC)&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So what does this mean for the open-source community at-large?  Plenty.  Up until now, there has been no enterprise-class, open-source PKI solution (despite the efforts of your's truly and the &lt;a href="http://www.sourceforge.net/projects/odyssipki"&gt;Odyssi PKI&lt;/a&gt; project).  While the community often argues that PKI is too bloated and unnecessary when compared with PGP or other lightweight products, it serves as the backbone for security in many large-scale enterprises.  Being able to deploy an open-source PKI is yet one more component that Red Hat is able to provide in the application stack.  This, combined with their support of the &lt;a href="http://freeipa.org/page/Main_Page"&gt;FreeIPA&lt;/a&gt; project for providing identity, audit, and policy management, means that Red Hat is becoming an even more formidable player in the enterprise space.&lt;br /&gt;&lt;br /&gt;I have worked extensively with Netscape CMS, the basis for RHCS.  The features, scalability, and security it provides are top-notch.  In fact, many of the features in CMS served as the inspiration for things I had planned for Odyssi PKI.  Will I continue development of Odyssi PKI now that Dogtag is available?  Maybe, maybe not.  Life seems to be getting in the way of any development projects for right now.  However, in the future I may have time to continue with Odyssi PKI, or even contribute to Dogtag.  We'll just have to wait and see.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-1009883899097505047?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/1009883899097505047/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=1009883899097505047' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1009883899097505047'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1009883899097505047'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2008/03/dogtag-open-source-pki-from-red-hat.html' title='Dogtag -- Open Source PKI from Red Hat'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-4121869868118899623</id><published>2008-02-07T11:26:00.002-05:00</published><updated>2008-05-01T08:48:15.368-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='databases'/><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='seam'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='jboss'/><title type='text'>Intro to JBoss Seam Security, Part 2 - Advanced Authentication</title><content type='html'>In my &lt;a href="http://odyssi.blogspot.com/2008/01/intro-to-jboss-seam-security-part-1.html"&gt;last post&lt;/a&gt;, I gave a brief introduction into how &lt;a href="http://www.jboss.com/products/seam"&gt;JBoss Seam&lt;/a&gt; handles user authentication for web applications.  By simply creating an &lt;span style="font-style: italic;"&gt;authenticator&lt;/span&gt; class, it is possible to handle login/logout events for your application.  Seam's ability to inject an &lt;span style="font-style: italic;"&gt;identity&lt;/span&gt; object means that a user's identity can be accessed wherever necessary.&lt;br /&gt;&lt;br /&gt;This method works great for simple use cases.  But, what if you need more advanced functionality?  The ability to enable/disable an account; the ability to manage account creation; the ability to add or remove a role programmatically.  Suddenly, there is a lot more code necessary for our application to function appropriately.  Thankfully, Seam has an answer to this problem.&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://docs.jboss.com/seam/2.0.1.CR2/api/org/jboss/seam/security/management/IdentityStore.html"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;IdentityStore&lt;/span&gt;&lt;/span&gt;&lt;/a&gt; interface defines the methods for creating accounts, changing passwords, and many other account lifecycle functions.  By implementing this interface and its corresponding methods, it is possible to hook into existing authentication stores, such as LDAP, and still be able to manage the account via an administrative interface.&lt;br /&gt;&lt;br /&gt;Seam also provides a sample implementation that can be used to store accounts and roles in a database, &lt;span style="font-size:85%;"&gt;&lt;a href="http://docs.jboss.com/seam/2.0.1.CR2/api/org/jboss/seam/security/management/JpaIdentityStore.html"&gt;&lt;span style="font-family:courier new;"&gt;JpaIdentityStore&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;.  The &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;JpaIdentityStore&lt;/span&gt;&lt;/span&gt; class makes use of the Java Persistence API for storing account and role objects in a SQL database.  In order to make use of the IdentityStore implementation, we need to tell Seam that we will be using it.  In addition, we need to define the class that will be used as our account model object.  To do this, we need to add the following line to our components.xml file:&lt;br /&gt;&lt;br /&gt;&lt;pre class="programlisting"&gt;&amp;lt;identity-management:jpa-identity-store name="identityStore" account-class="com.myapp.UserAccountModel"/&amp;gt;&lt;/pre&gt;&lt;br /&gt;As you can see, we have defined our account model as &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;com.myapp.UserAccountModel&lt;/span&gt;&lt;/span&gt;.  All that is needed is to implement this object and add the appropriate annotations so that &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;JpaIdentityStore&lt;/span&gt;&lt;/span&gt; is able to make use of our data model.&lt;br /&gt;&lt;br /&gt;The IdentityStore interface is simple enough that it can be implemented in a matter of hours.  Whether you want to use JNDI, Kerberos, or a custom authentication method, it is simple to get up and running quickly.  For more information on the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;IdentityStore&lt;/span&gt;&lt;/span&gt; and identity management APIs provided by Seam, take a look at &lt;a href="http://docs.jboss.com/seam/2.0.1.CR2/reference/en/html/security.html#d0e8151"&gt;this section&lt;/a&gt; in the documentation.&lt;br /&gt;&lt;br /&gt;Next time, we'll take a further look at &lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;JpaIdentityStore&lt;/span&gt;&lt;/span&gt; and how to setup and configure it to work with your existing SQL table schema.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-4121869868118899623?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/4121869868118899623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=4121869868118899623' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/4121869868118899623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/4121869868118899623'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2008/02/intro-to-jboss-seam-security-part-2.html' title='Intro to JBoss Seam Security, Part 2 - Advanced Authentication'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-1475825363767554714</id><published>2008-01-23T11:59:00.002-05:00</published><updated>2008-09-22T15:17:10.820-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='seam'/><category scheme='http://www.blogger.com/atom/ns#' term='jboss'/><title type='text'>Intro to JBoss Seam Security, Part 1 - Authentication</title><content type='html'>Recently, I &lt;a href="http://odyssi.blogspot.com/2008/01/jboss-seam-j2ee-development-framework.html"&gt;mentioned&lt;/a&gt; how I had just started working with the &lt;a href="http://www.jboss.com/products/seam"&gt;JBoss Seam application framework&lt;/a&gt;.  The more I have worked with it, the more impressed I have become at how it makes things that are typically difficult using standard Java EE much simpler and more streamlined.  One of the areas in which Seam really shines is security.  While Java EE defines &lt;a href="http://java.sun.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html"&gt;JAAS&lt;/a&gt; for use in securing applications, it is left up to the developer to ingrain this security down to each facet of the application.  With Seam, it is easy to define security constraints at all levels of an application, simply through using annotations.  In addition, the complexity of authenticating users with JAAS is reduced through Seam's &lt;a href="http://docs.jboss.com/seam/2.0.1.CR1/reference/en/html/security.html#d0e6900"&gt;authenticator&lt;/a&gt; mechanism.  This article will give an introduction to Seam authentication, and show how to write your own custom authenticator.&lt;br /&gt;&lt;br /&gt;Seam's authenticator construct hides the complexity of managing a JAAS configuration, and allows you to implement authentication how you see fit.  Perhaps your organization relies on a simple username/password combination for authenticating user accounts in LDAP.  Maybe you use a SecureID token, and the accounts are stored in a SQL database.  By writing your own authenticator class, or making use of publicly available ones, you can control the way authentication is done in your organization.&lt;br /&gt;&lt;br /&gt;To get started with your own authenticator, you must first declare it in the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;components.xml&lt;/span&gt;&lt;/span&gt; file.  This file manages much of the configuration for Seam.  To add your authenticator, you simply define the class and method that will be used for authentication.  For example:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&amp;lt;components xmlns="http://jboss.com/products/seam/components"&lt;br /&gt;     xmlns:core="http://jboss.com/products/seam/core"&lt;br /&gt;     xmlns:security="http://jboss.com/products/seam/security"&lt;br /&gt;     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;     xsi:schemaLocation=&lt;br /&gt;         "http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd&lt;br /&gt;          http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.0.xsd"&amp;gt;         &lt;br /&gt;&lt;br /&gt;&amp;lt;security:identity method="#{authenticator.authenticate}"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/components&amp;gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You'll notice the #{} syntax used here.  This is JBoss' expression language pointing to a class with the instance name of &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;authenticator&lt;/span&gt;&lt;/span&gt;, where the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;authenticate&lt;/span&gt;&lt;/span&gt; method will be used to login a user.  Now that we have declared an authenticator to Seam, we're ready to implement it.  Our example will be quite simple.  If the user enters a username of &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;admin&lt;/span&gt;&lt;/span&gt;, with a password of &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;password&lt;/span&gt;&lt;/span&gt;, they will be authenticated successfully.  In addition, we will assign them to the role of &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;admin&lt;/span&gt;&lt;/span&gt;, so that they can perform some sort of administrative function within our application.  The implementation of our authenticator would look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;@Name("authenticator")&lt;br /&gt;public class Authenticator {&lt;br /&gt;&lt;br /&gt;private static final String valid_user = "admin";&lt;br /&gt;private static final String valid_password = "password";&lt;br /&gt;&lt;br /&gt;public boolean authenticate() {&lt;br /&gt;String username = Identity.instance().getUsername();&lt;br /&gt;String password = Identity.instance().getPassword();&lt;br /&gt;&lt;br /&gt;if((username.equals(valid_user)) &amp;amp;&amp;amp; (password.equals(valid_password))) {&lt;br /&gt;     return true;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;return false;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;Our example is rather trivial.  However, it gives a slight glimpse into how Seam authentication works.  The first thing that you should notice is the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;@Name&lt;/span&gt;&lt;/span&gt; annotation.  This annotation prompts Seam to create a bean with the name specified in the annotation.  In this case, the name is &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;authenticator&lt;/span&gt;&lt;/span&gt;, which is how we arrive at the value specified in our &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;components.xml&lt;/span&gt;&lt;/span&gt; file.  Our &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;authenticate &lt;/span&gt;&lt;/span&gt;method will return &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;true &lt;/span&gt;&lt;/span&gt;if authentication was successful, and &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;false &lt;/span&gt;&lt;/span&gt;otherwise.&lt;br /&gt;&lt;br /&gt;So how does the authenticate method get the username and password?  This is done via the Identity class.  The standard &lt;span style="font-size:85%;"&gt;&lt;a style="font-family: courier new;" href="http://docs.jboss.com/seam/2.0.1.CR1/api/org/jboss/seam/security/Identity.html"&gt;Identity&lt;/a&gt;&lt;/span&gt; class that comes with Seam is quite extensive, but basically provides support for a username/password combination.  It is possible to subclass &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;Identity&lt;/span&gt;&lt;/span&gt;, however, to support whatever authentication mechanisms you may need.  You could implement code to support getting a SecureID token value from a user, or a SPNEGO ticket.  All that is needed to make use of the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;Identity &lt;/span&gt;&lt;/span&gt;subclass is to add the following annotations to your implementation:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;@Name("org.jboss.seam.security.identity")&lt;br /&gt;@Scope(SESSION)&lt;br /&gt;@Install(precedence = APPLICATION)&lt;br /&gt;@BypassInterceptors&lt;br /&gt;@Startup&lt;br /&gt;public class MyCustomIdentity extends Identity&lt;br /&gt;{ ... }&lt;/pre&gt;&lt;br /&gt;Your custom &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;Identity &lt;/span&gt;&lt;/span&gt;subclass is now ready for use.&lt;br /&gt;&lt;br /&gt;Now that we have our authentication classes in place, we are ready to create our login form.  This is trivial to create using Seam, particularly because of Seam's use of the JBoss expression language in forms.  Our login form fragment would look like the following:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;div&amp;gt;&lt;br /&gt;   &amp;lt;h:outputlabel for="name" value="Username"&amp;gt;&lt;br /&gt;   &amp;lt;h:inputtext id="name" value="#{identity.username}"&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div&amp;gt;&lt;br /&gt;   &amp;lt;h:outputlabel for="password" value="Password"&amp;gt;&lt;br /&gt;   &amp;lt;h:inputsecret id="password" value="#{identity.password}"&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div&amp;gt;&lt;br /&gt;   &amp;lt;h:commandbutton value="Login" action="#{identity.login}"&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all there is to it.  You are now ready to authenticate users via your own custom login form and authenticator.  While this is an introduction to the simplified form of authentication in Seam, it should give you a good foundation to learn and explore on your own.&lt;br /&gt;&lt;br /&gt;Next time, we will look at how authentication is used throughout an application, not just at the entry point.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-1475825363767554714?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/1475825363767554714/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=1475825363767554714' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1475825363767554714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1475825363767554714'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2008/01/intro-to-jboss-seam-security-part-1.html' title='Intro to JBoss Seam Security, Part 1 - Authentication'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-4614164963956996334</id><published>2008-01-17T15:16:00.000-05:00</published><updated>2008-01-17T15:41:29.237-05:00</updated><title type='text'>JBoss Seam - J2EE Development Framework</title><content type='html'>I know I have repeatedly said that an update to &lt;a href="http://www.sourceforge.net/projects/odyssipki"&gt;OdyssiPKI&lt;/a&gt; is forthcoming.  But, I keep getting sidetracked by interesting technologies that I just have to check out.  With this update of OdyssiPKI, I decided to make it a bit more robust, and to spend more time focusing on the overall design and engineering of the application.  With that came the decision to make use of &lt;a href="http://java.sun.com/products/ejb/"&gt;EJB's&lt;/a&gt;, particularly EJB3.  In addition, I decided to try moving away from &lt;a href="http://struts.apache.org/"&gt;Struts&lt;/a&gt; and look into &lt;a href="http://java.sun.com/javaee/javaserverfaces/"&gt;Java Server Faces (JSF)&lt;/a&gt; for the main user interface.  Of course, all these changes mean that there is a lot more that I need to get familiar with before the next release is ready.&lt;br /&gt;&lt;br /&gt;The primary motivation for such a radical change in the project design was to make OdyssiPKI a more robust application.  Currently, the user interface is very lacking.  It is a simple Struts interface, and is generally not very user-friendly.  In addition, the backend components of the application are very simple.  Transactions, scalability, and enhanced security were not a primary focus of the first release.  The combination of EJB3 and JSF makes correcting these issues much easier.  The security provided by EJB3 annotations means more fine-grained control.  In addition, by utilizing EJBs for the business logic, the scalability of the application is enhanced.  Further, anyone who has looked at &lt;a href="https://woodstock.dev.java.net/"&gt;Woodstock&lt;/a&gt;, &lt;a href="http://labs.jboss.com/jbossrichfaces/"&gt;RichFaces&lt;/a&gt;, or &lt;a href="http://myfaces.apache.org/"&gt;MyFaces&lt;/a&gt;, and it's &lt;a href="http://myfaces.apache.org/trinidad/index.html"&gt;spinoff&lt;/a&gt; &lt;a href="http://myfaces.apache.org/tobago/index.html"&gt;projects&lt;/a&gt; has seen what a rich interface JSF is able to provide.&lt;br /&gt;&lt;br /&gt;The downside to all of this change?  Overhead.  EJB3 and JSF add a lot of overhead to the application structure.  Annotations, XML files, deployment descriptors, etc.  All of this creates a pretty steep learning curve, and means that development takes even longer.  It is frustrating, as a developer, to have to spend so much time writing XML files when I'd rather be writing code.  This is why I was very intrigued to discover &lt;a href="http://www.jboss.com/products/seam"&gt;JBoss Seam&lt;/a&gt;.  Since I was already planning to develop and deploy using &lt;a href="http://www.jboss.org/"&gt;JBoss&lt;/a&gt;, it seemed like it might be worth investigating this new application framework.&lt;br /&gt;&lt;br /&gt;JBoss Seam is an application framework designed specifically for JavaEE applications.  It integrates extremely well with JSF and EJBs, as well as other Java technologies, such as Spring and Hibernate.  Seam handles dependency injection as well as dependency "outjection" of various components, making it easy to assemble application services.  The result?  The overhead and time-to-market for JavaEE applications is drastically reduced.  Seam is as close to Rapid Application Development (RAD) as I've ever seen when it comes to Java applications.  And, the  code generation tools included with the Seam distribution make project development even faster.&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;One of the most interesting tools included with Seam is &lt;a href="http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossSEAMGen"&gt;seam-gen&lt;/a&gt;.  The seam-gen utility has the ability to generate a complete project structure for Eclipse or NetBeans, simply by running Ant and answering a few questions about the overall structure of your application.  Deployment descriptors, a basic interface and project files are automatically generating using the parameters you supply.  In addition, you can even generate data models from an existing SQL database.  All you need to do is supply the necessary connection parameters.&lt;br /&gt;&lt;br /&gt;If you are looking for an application framework that allows you to develop robust applications very quickly, make sure you take a look at Seam.  In addition, I highly recommend downloading &lt;a href="http://labs.jboss.com/tools/"&gt;JBoss Tools&lt;/a&gt;.  JBoss Tools is a group of Eclipse plugins for working with JBoss, Seam, Hibernate, and other Java technologies.  Getting started with Seam in Eclipse is as simple as creating a new "Seam Plugin" and following the wizard.  All of the necessary files, projects, and basic classes are created for you automatically.&lt;br /&gt;&lt;br /&gt;I'm still experimenting and getting familiar with Seam myself, but will sure to post a tutorial in the near future.  In the meantime, here are a few good articles explaining Seam a bit further.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.intelligentedu.com/blogs/post/best_new_training_sites/3676/15-best-jboss-seam-tutorials"&gt;15 Best JBoss Seam Tutorials&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.infoq.com/articles/jboss-seam"&gt;InfoQ Introduction to JBoss Seam&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.onjava.com/pub/a/onjava/2006/03/15/jboss-seam.html"&gt;JBoss Seam introduction&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-4614164963956996334?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/4614164963956996334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=4614164963956996334' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/4614164963956996334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/4614164963956996334'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2008/01/jboss-seam-j2ee-development-framework.html' title='JBoss Seam - J2EE Development Framework'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-3221347998005661431</id><published>2007-11-14T14:23:00.001-05:00</published><updated>2007-11-14T14:55:40.316-05:00</updated><title type='text'>The Year of PKI?  Finally?</title><content type='html'>Over on &lt;a href="http://www.news.com"&gt;news.com&lt;/a&gt;, there is an article saying to &lt;a href="http://www.news.com/8301-10784_3-9816996-7.html?part=rss&amp;amp;subj=news&amp;amp;tag=2547-1_3-0-5"&gt;Expect more PKI in 2008&lt;/a&gt;.  Since the late 1990's, several years have been deemed the "Year of PKI".  The major push by the U.S. Department of Defense to build the &lt;a href="http://www.eweek.com/article2/0,1895,2026654,00.asp"&gt;world's largest PKI&lt;/a&gt; has provided a lot of momentum and investment in the PKI space.  The cost of deploying smartcards across an enterprise for use with PKI, workstation login, and physical security has been greatly reduced due to the economies of scale created by DoD PKI.  Microsoft's latest PKI-related products, combined with PKI integration built into Windows Server 2003 has reduced many barriers to entry for even small organizations.  However, the question still remains:  Will there every be a "Year of PKI?"  There are many obstacles that must first be overcome before we can definitively answer that question.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Inertia&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Many organizations have grown comfortable with the username/password combination.  Passwords aren't secure?  Just change the password policy to lengthen the minimum requirements, add special characters, etc.  They see no need to move towards client-side certificates, smartcard login, or other strong authentication mechanisms until a major change in their environment requires such an effort.  The same is true for intra-server communication.  In most organizations, data being transmitted between web servers, application servers, database servers, etc. goes over the network in plaintext.  The additional effort involved in configuring SSL for dozens of servers is often enough to justify overlooking this important security fix.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Ease of Use&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;In the past, rolling out an enterprise-wide PKI was an enormous undertaking in terms of the amount of work required.  If an organization wanted to use client-side certificates for authentication, root certificates had to be manually installed on each workstation.  Users were enrolled in a time-consuming, manual process.  Additionally, the PKI products themselves were often too difficult to administer.  A skilled PKI expert also came with a hefty salary.  Today, however, many PKI products have become more administrator-friendly.  Microsoft's Certificate Server, included within Windows Server 2003, makes deploying client-side certificates much easier by integrating with an organization's existing Active Directory infrastructure.  However, this would require an organization to use Active Directory throughout, which many places are unwilling to do.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Interoperability&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;By this, I do not mean on interoperability between products.  There are numerous well-defined standards governing PKI that even Microsoft adheres to.  I am referring to interoperability between disparate PKI setups.  A PKI is a hierarchical trust system created by an organization.  The root certificate serves as the basis for this trust hierarchy.  The problem arises when two organizations making use of PKI want to interoperate with one another.  Imagine if two PKI-enabled companies, WidgetWorld and GadgetBarn, wish to interoperate with one another.  Each organization has their own root certificate.  Establishing a trust between these two companies would involve exchanging root and subordinate CA certificates so that SSL and client certificates can be verified all the way up the trust chain.  While there are companies such as &lt;a href="http://www.cybertrust.com"&gt;Cybertrust&lt;/a&gt; or &lt;a href="http://www.verisign.com"&gt;Verisign&lt;/a&gt; that offer CA certificate signing using a ubiquitous root, that leads us to our next, and most major obstacle.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Cost $$$&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Commercial, hosted PKI solutions are typically very expensive.  To have certificates that are globally trusted, however, they are necessary.  It is possible to create a PKI that is internal to an organization.  But, as we discussed earlier, a problem occurs if you want to extend that trust outside of your organization's boundaries.  The inclusion of Microsoft Certificate Server in its Windows Server 2003 product, along with &lt;a href="http://www.sourceforge.net/projects/odyssipki"&gt;open-source PKI solutions&lt;/a&gt;, has greatly reduced the cost of deploying an internal PKI.  However, this does not address the cost of implementing, maintaining, and monitoring the PKI.  In addition, there are equipment costs associated with the servers, smartcards, and smartcard readers necessary to deploy a full-scale, enterprise-wide PKI.&lt;br /&gt;&lt;br /&gt;So, given these issues, will there ever be a "Year of PKI?"  The answer is "probably not."  However, this does not mean that PKI adoption will not continue to grow within the enterprise arena.  As more and more organizations realize the potential severity of a data security breach, they are increasingly looking at strong authentication solutions.  The benefits to implementing a PKI begin to look very attractive when weighed against the nightmare of a major security breach.  In addition, PKI adoption can enable an organization to implement additional security measures, such as encrypted file systems, 802.1x network authentication, code and e-mail digital signatures, and VPN access.&lt;br /&gt;&lt;br /&gt;While it is clear there will be no global explosion of PKI use any time soon, the future of PKI adoption does look very bright.  The number of PKI implementations will most likely continue to grow in an increasingly rapid manner.  However the amount of effort and investigation required with rolling out a PKI -- as with any other security-related endeavor -- will ensure that the transition will not occur overnight.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-3221347998005661431?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/3221347998005661431/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=3221347998005661431' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/3221347998005661431'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/3221347998005661431'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/11/year-of-pki-finally.html' title='The Year of PKI?  Finally?'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-6069603879841525045</id><published>2007-10-05T12:19:00.002-05:00</published><updated>2008-09-23T08:37:12.295-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='highavailability'/><category scheme='http://www.blogger.com/atom/ns#' term='pgcluster'/><category scheme='http://www.blogger.com/atom/ns#' term='PostgreSQL'/><category scheme='http://www.blogger.com/atom/ns#' term='slony'/><category scheme='http://www.blogger.com/atom/ns#' term='replication'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='slony-i'/><title type='text'>PostgreSQL Replication with Slony-I</title><content type='html'>In &lt;a href="http://odyssi.blogspot.com/2007/08/postgresql-replication-with-pgcluster.html"&gt;an earlier blog post&lt;/a&gt;, we looked at synchronous, master-master replication in &lt;a href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; using &lt;a href="http://www.pgcluster.org/"&gt;PGCluster&lt;/a&gt;.  PGCluster's load balanacing and replication features provide PostgreSQL with high availability features that are not included in the core distribution.  While PGCluster does provide an adequate solution for many environments, it is not the only replication mechanism available for PostgreSQL.  In addition, its drawbacks may be too great to be deployed in many circumstances.&lt;br /&gt;&lt;br /&gt;In this post, we look at another replication mechanism available for PostgreSQL, &lt;a href="http://www.slony.info/"&gt;Slony-I&lt;/a&gt;.  Slony-I is an asynchronous, master-slave replication system.  With PGCluster, we had the ability to load balance connections to the PostgreSQL database, knowing that data that is modified in one server will be replicated across to the other server.  Additionally, its synchronous nature gave us confidence that, in the event of a failure, all completed transactions will be accounted for.  With Slony-I, we run into a very different type of replication.  Slony-I is a master-slave system, designed to utilize one master database, and one or more slaves.  Data is replicated in a one-way fashion, from the master to the slave(s).  It does not include a built-in load balancing feature like PGCluster, and there is no automatic failover from a failed master to one of the slaves.  Failover must be manually configured using a third-party utility, such as heartbeat.  Slony-I's asynchronous nature means that, in the event of a database failure, there may be uncommitted transactions that have not been replicated across.  Slony-I performs batching of transaction replication in order to improve performance.  However, if the master fails prior to a batch of transactions being replicated, those transactions are lost.&lt;br /&gt;&lt;br /&gt;On the surface, it may appear as though Slony-I is a less-than-ideal choice for database replication.  However, upon further investigation, it becomes clear that Slony-I may be an acceptable choice, depending on the needs of your organization.  In our example, we have one master and one slave.  Our master is on a host called &lt;span style="font-family:courier new;"&gt;pgmaster&lt;/span&gt;, while the slave is on a host called &lt;span style="font-family:courier new;"&gt;pgslave&lt;/span&gt;.  The database that we wish to replicate is called &lt;span style="font-family:courier new;"&gt;repl_test_db&lt;/span&gt;.  The database consists of a single, basic table, &lt;span style="font-family:courier new;"&gt;repl_test_tbl&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Before we can begin replicating our data, we need to examine a few of the core concepts involved in Slony-I replication.  The first is the notion of a '&lt;span style="font-style: italic;"&gt;cluster&lt;/span&gt;'.  A cluster is simply a collection of database nodes that are connected together.  These are the databases that will eventually be replicated.  A '&lt;span style="font-style: italic;"&gt;replication set&lt;/span&gt;' describes the actual data (tables, sequences, etc.) that will be replicated.  So, while a cluster describes the underlying database that will support replication, a replication set describes the underlying database objects that will be replicated.&lt;br /&gt;&lt;br /&gt;To make our replication simpler, we will first declare some environment variables that will be used throughout the commands that we will issue.  We will store these environment variables in a shell script called &lt;span style="font-family:courier new;"&gt;env.sh&lt;/span&gt;.  The contents are as follows:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;#!/bin/sh&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;REPLICATIONUSER=postgres&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;CLUSTERNAME=cluster_a&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;MASTERDB=repltestdb&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SLAVEDB=${MASTERDB}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;MASTERHOST=pgmaster&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SLAVEHOST=pgslave&lt;/span&gt;  &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;PSQL=/usr/bin/psql&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;CREATEDB=/usr/bin/createdb&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;CREATELANG=/usr/bin/createlang&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;CREATEUSER=/usr/bin/createuser&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;PGDUMP=/usr/bin/pg_dump&lt;/span&gt;  &lt;span style="font-family:courier new;"&gt;&lt;br /&gt;&lt;br /&gt;export REPLICATIONUSER CLUSTERNAME MASTERDBNAME SLAVEDBNAME MASTERHOST SLAVEHOST&lt;/span&gt; &lt;span style="font-family:courier new;"&gt;export PSQL CREATEDB CREATELANG CREATEUSER PGDUMP&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now that our environment variables have been established, we are ready to begin setting up the necessary databases.  Once again, I recommend creating a shell script that will encapsulate all the commands needed to perform our setup steps.  This script will first create the &lt;span style="font-family:courier new;"&gt;repltestdb &lt;/span&gt;database on our master host, and then ensure that the &lt;span style="font-family:courier new;"&gt;plpgsql&lt;/span&gt; language is installed.  This step is vital, as Slony-I requires that &lt;span style="font-family:georgia;"&gt;plpgsql&lt;/span&gt; be installed in order to run.  Once the master database has been setup, our table will be created and populated with some simple test data.  From here, we begin to setup the slave host.  The slave database is created, and then a &lt;span style="font-family:courier new;"&gt;pg_dump&lt;/span&gt; is performed to copy the initial data from our master database into the slave database.  &lt;span style="font-weight: bold;"&gt;NOTE&lt;/span&gt;:  It is important that our master and slave databases are able to communicate with each other.  This will involve modifying the &lt;span style="font-family:courier new;"&gt;pg_hba.conf&lt;/span&gt; file to ensure that the database permissions are set properly.  For more information on how to accomplish this, see the &lt;a href="http://www.postgresql.org/docs/8.2/static/index.html"&gt;PostgreSQL 8.2 referrence manual&lt;/a&gt;, or the post on &lt;a href="http://odyssi.blogspot.com/2007/08/postgresql-replication-with-pgcluster.html"&gt;PGCluster replication&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For our tests, we will use a very simple table consisting of 2 columns.  In our next article, we will look at the performance differences between PGCluster and Slony-I when using larger and more complex data sets.  For this example, however, our simple setup will be sufficient.  The SQL script looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;DROP TABLE IF EXISTS repl_test_tbl;&lt;br /&gt;&lt;br /&gt;CREATE SEQUENCE repl_test_tbl_id_seq;&lt;br /&gt;CREATE TABLE repl_test_tbl (&lt;br /&gt;    id INTEGER DEFAULT nextval('repl_test_tbl_id_seq') NOT NULL,&lt;br /&gt;    my_str VARCHAR(32),&lt;br /&gt;    PRIMARY KEY (id)&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;INSERT INTO repl_test_tbl (my_str) VALUES ('This is my #1 string');&lt;br /&gt;INSERT INTO repl_test_tbl (my_str) VALUES ('This is my #2 string');&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;After our databases have been created and setup, we are ready to start initializing the replication sets.  The &lt;span style="font-family:courier new;"&gt;slonik&lt;/span&gt; utility is used to issue commands to the Slony-I replication engine.  The easiest way to enter these commands is using a shell script like the one in this example.  Our replication script looks like this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;#!/bin/sh&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;echo Setting PostgreSQL environment variables...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;. ./env.sh&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;SQL_FILE=repl_test.sql&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;echo Initializing master database...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${CREATEDB} -O ${REPLICATIONUSER} -h ${MASTERHOST} ${MASTERDB}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${CREATELANG} -U ${REPLICATIONUSER} plpgsql -h ${MASTERHOST} ${MASTERDB}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;echo Initializing master database data...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${PSQL} -U ${REPLICATIONUSER} ${MASTERDB} &lt; ${SQL_FILE}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;echo Initializing slave database...&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${CREATEDB} -O ${REPLICATIONUSER} -h ${SLAVEHOST} ${SLAVEDB}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${CREATELANG} -U ${REPLICATIONUSER} plpgsql -h ${SLAVEHOST} ${SLAVEDB}&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;${PGDUMP} -s -U ${REPLICATIONUSER} -h ${MASTERHOST} ${MASTERDB} | ${PSQL} -U ${REPLICATIONUSER} -h ${SLAVEHOST} ${SLAVEDB}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;echo Initializing the Slony-I cluster...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;slonik &lt;&lt;_eof_&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;cluster name = ${CLUSTERNAME};&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;node 1 admin conninfo = 'dbname=${MASTERDB} host=${MASTERHOST} user=${REPLICATIONUSER}';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;node 2 admin conninfo = 'dbname=${SLAVEDB} host=${SLAVEHOST} user=${REPLICATIONUSER}';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;init cluster (id = 1, comment = 'Master Node');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;create set (id = 1, origin = 1, comment = 'Test repl. table');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;set add table (set id = 1, origin = 1, id = 1, full qualified name = 'public.repl_test_tbl', comment = 'Test repl. table');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;set add sequence (set id = 1, origin = 1, id = 2, full qualified name = 'public.repl_test_tbl_id_seq', comment = 'Test repl. table PK');&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;store node (id = 2, comment = 'Slave Node');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;store path (server = 1, client = 2, conninfo = 'dbname=${MASTERDB} host=${MASTERHOST} user=${REPLICATIONUSER}');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;store path (server = 2, client = 1, conninfo = 'dbname=${SLAVEDB} host=${SLAVEHOST} user=${REPLICATIONUSER}');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;store listen (origin = 1, provider = 1, receiver = 2);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;store listen (origin = 2, provider = 2, receiver = 1);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It is important to run this script as the postgres user, so make sure you &lt;span style="font-family:courier new;"&gt;su&lt;/span&gt; to postgres before running it.  In this script, you will notice how we add our &lt;span style="font-family:courier new;"&gt;repl_test_tbl&lt;/span&gt; table to the replication set.  It is also important to note that the &lt;span style="font-family:courier new;"&gt;repl_test_tbl_id_seq&lt;/span&gt; sequence must be added to the replication set as well.  Slony-I requires sequences to be explicity added to the replication set, and it also requires that every table has a primary key.&lt;br /&gt;&lt;br /&gt;Now that our cluster and replication sets have been defined, it is time to start the replication engine and subscribe to the replication set.  Subscribing to a replication set tells the database to start replicating to the defined slaves.  The &lt;span style="font-family:courier new;"&gt;slon&lt;/span&gt; command is used to start and stop the replication engine.  Once again, it is best to make use of our environment variable shell script to make things easier on us.  Run the first command as postgres at a command prompt on the master host, and the second set of commands on the slave host:&lt;br /&gt;&lt;br /&gt;Master Host:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;. ./env.sh&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;slon $CLUSTERNAME "dbname=$MASTERDB user=$REPLICATIONUSER" &amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Slave Host:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;. ./env.sh&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;slon $CLUSTERNAME "dbname=$SLAVEDB user=$REPLICATIONUSER" &amp;amp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If all was successful, we are ready to subscribe to the replication sets.  The shell script for performing the subscription follows:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;#!/bin/sh&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;. ./env.sh&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;echo Subscribing to replication set...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;slonik &lt;&lt;_eof_&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;cluster name = ${CLUSTERNAME};&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;node 1 admin conninfo = 'dbname=${MASTERDB} host=${MASTERHOST} user=${REPLICATIONUSER}';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;node 2 admin conninfo = 'dbname=${SLAVEDB} host=${SLAVEHOST} user=${REPLICATIONUSER}';&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;subscribe set (id = 1, provider = 1, receiver = 2, forward = yes);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Once again, run this script as the postgres user.  We are now ready to test our data replication.  First, connect to the PostgreSQL master and do a SELECT to see the data in our table prior to replication.  If you perform the same SELECT on the PostgreSQL slave, you should see the same data.  This is the result of the &lt;span style="font-family:courier new;"&gt;pg_dump&lt;/span&gt; that we performed earlier.  Now it is time to see if our replication is successful.  Once again, connect to the master database, and, this time, INSERT data into our replicated table.  If we have configured our replication properly, you should be able to connect to the slave database and see that the newly INSERTed data has been successfully replicated to the slave.&lt;br /&gt;&lt;br /&gt;If you followed the steps listed above, you can see our replication works as expected.  But, how does this compare to PGCluster for data replication?  As evidenced by the number of commands we were required to issue above to get the replication working, it is obvious that Slony-I requires more administration and has a steeper learning curve.  Slony-I also requires us to manually define the replication sets and the data to be replicated.  With PGCluster, our databases are replicated automatically.  This is due to the fact that PGCluster uses rsync as its underlying mechanism to replicate the data.  However, this reliance on rsync may prove to reduce performance, particularly with large datasets.  Slony-I uses a trigger-based mechanism for replicating the data.  In addition, its asynchronous nature may win out in environments that do not need the high availablity that a synchronous, multi-master solution can provide.&lt;br /&gt;&lt;br /&gt;So, it is obvious that PGCluster wins out in terms of ease of administration.  But what about performance?  You'll have to wait until my next article to see how each of these replication mechanisms handle larger datasets, including PGBench.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-6069603879841525045?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/6069603879841525045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=6069603879841525045' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6069603879841525045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6069603879841525045'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/10/postgresql-replication-with-slony-i.html' title='PostgreSQL Replication with Slony-I'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-6916660077196343519</id><published>2007-09-21T10:29:00.000-05:00</published><updated>2007-09-21T10:49:12.202-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='databases'/><category scheme='http://www.blogger.com/atom/ns#' term='PostgreSQL'/><category scheme='http://www.blogger.com/atom/ns#' term='replication'/><category scheme='http://www.blogger.com/atom/ns#' term='slony-i'/><title type='text'>More PostgreSQL Replication</title><content type='html'>It appears as though my article on &lt;a href="http://odyssi.blogspot.com/2007/08/postgresql-replication-with-pgcluster.html"&gt;Synchronous Multi-Master Replication in PostgreSQL using PGCluster&lt;/a&gt; filled an important need.  It has become the most read article on this blog in a very short time.  As such, I've started wondering about the other replication mechanisms available for &lt;a href="http://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt;, as well as for other databases, such as &lt;a href="http://www.mysql.com"&gt;MySQL&lt;/a&gt;.  While &lt;a href="http://www.pgcluster.org"&gt;PGCluster&lt;/a&gt; does provide a good set of capabilities, it does have its drawbacks and limitations.  As a result, the next PostgreSQL mechanism I will be evaluating is &lt;a href="http://www.slony.info"&gt;Slony-I&lt;/a&gt;.  Slony-I is an asynchronous, master-slave replication mechanism.  While this type of replication does not compare to the synchronous, multi-master capabilities of PGCluster, the Slony-I project is more mature, and the way replication is performed may be a better choice in some environments.&lt;br /&gt;&lt;br /&gt;While I've found the official &lt;a href="http://slony.info/documentation/"&gt;Slony-I documentation&lt;/a&gt; to be a bit thin when it comes to providing a good introduction, I did come across a &lt;a href="http://cbbrowne.com/info/slonyintro.html"&gt;lengthy introduction&lt;/a&gt; that has been quite helpful in getting things started.  I am still working on my article about Slony-I, but, in the meantime, take a look at the Slony-I introduction to get familiar with some of the concepts used.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-6916660077196343519?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/6916660077196343519/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=6916660077196343519' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6916660077196343519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6916660077196343519'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/09/more-postgresql-replication.html' title='More PostgreSQL Replication'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-8218014142511967177</id><published>2007-08-27T08:00:00.005-05:00</published><updated>2008-09-23T08:49:52.069-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='redhat'/><category scheme='http://www.blogger.com/atom/ns#' term='highavailability'/><category scheme='http://www.blogger.com/atom/ns#' term='pgcluster'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='centos'/><category scheme='http://www.blogger.com/atom/ns#' term='PostgreSQL'/><category scheme='http://www.blogger.com/atom/ns#' term='replication'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>PostgreSQL Replication with PGCluster</title><content type='html'>I don't know why I am fascinated with databases.  I don't work with them on a daily basis.  I don't know very much about them.  I don't have very much experience working with them.  My experience is limited to a few simple SQL queries executed on open-source databases.  However, for some reason, I have always been intrigued by the idea of large, complex databases.  Perhaps it is because I see the business potential in making extremely large amounts of data available for analysis within a business.  Or, perhaps it is due to the unique security challenges that exist when trying to make that data available to different groups of entities, each with their own security concerns.  Regardless of the reason, I have become particularly interested in high-availability databases and database replication.&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://odyssi.blogspot.com/2007/07/postgresql-replication-pgcluster.html"&gt;an earlier post&lt;/a&gt;, I briefly mentioned the &lt;a href="http://www.pgcluster.org/"&gt;PGCluster&lt;/a&gt; project.  PGCluster is an extension of the &lt;a href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; database, designed to give it synchronous, multi-master replication.  Replication is one of the areas in which PostgreSQL is lacking in comparison to other proprietary databases, such as Oracle or MS SQL Server.  However, after playing around with PGCluster, I have become very impressed with its capabilities.  If development continues, PGCluster could offer a sound solution to a very important problem that large enterprises will encounter if they wish to role out PostgreSQL in a high-availability situation.&lt;br /&gt;&lt;br /&gt;To that end, this article will show the basics of setting up a synchronous, multi-master replicated instance of PGCluster.  For simplicity and ease of use, I am running this tutorial using virtual instances of &lt;a href="http://www.centos.org/"&gt;CentOS 5&lt;/a&gt; deployed using the &lt;a href="http://www.vmware.com/products/player/"&gt;VMWare Player&lt;/a&gt;.  I highly recommend you obtain this free utility, as it gives you the freedom to run virtual machine instances without having to pay too much.  Once you have downloaded and installed VMWare Player, you can download a &lt;a href="http://www.vmware.com/vmtn/appliances/directory/820"&gt;pre-built CentOS 5 instance&lt;/a&gt; to run in the player.  I made a copy of the CentOS 5 VM in another directory so that I can run 2 separate, virtual instances of it for this tutorial.  You will need to configure each VM instance to have bridged network support, utilizing DHCP.  In a real-world scenario, you would use static IPs for addressing, but for the purposes of this tutorial it is not necessary.  After starting the VM instances for the first time, you will need to install some compiler tools in order to build PGCluster.  This includes gcc, bison, and flex, as well as their respective -devel packages.  To install these packages, simply run the following from the command prompt in each VM instance:&lt;br /&gt;&lt;br /&gt;&lt;commands&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;yum -y install gcc gcc-c++ flex flex-devel bison bison-devel&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Once your development environments are setup, you are ready to download and compile PGCluster.  There are 2 different methods for building PGCluster.  The first involves downloading a patch to the original PostgreSQL source distribution.  You simply apply the patch prior to compiling PostgreSQL in order to add the PGCluster support.  This method is very useful if there are other patches you wish to install prior to building PostgreSQL.  The second method involves downloading the complete PGCluster distribution.  This distribution includes the PostgreSQL source tree already patched with PGCluster.  We will use the second, full-distribution method for simplicity.  As of this writing, the latest version of PGCluster is 1.7.0rc5, and is available for &lt;a href="http://pgfoundry.org/frs/shownotes.php?release_id=817"&gt;download here&lt;/a&gt;.  Download the tar/gz file and unpack it on each VM instance.  Building PGCluster is as simple as running:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;./configure; make; make install&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;This will install PGCluster to /usr/local/pgsql, by default.  There is &lt;a href="http://pgcluster.projects.postgresql.org/1_3/install.html"&gt;more information&lt;/a&gt; about the installation process available at the PGCluster website.  Now that we have successfully built PGCluster, it's time to start the configuration process.&lt;br /&gt;&lt;br /&gt;First, some terminology.  PGCluster consists of 3 main components:&lt;br /&gt;&lt;/commands&gt;&lt;ul&gt;&lt;li&gt;Clusters&lt;/li&gt;&lt;li&gt;Load Balancers&lt;/li&gt;&lt;li&gt;Replicators&lt;/li&gt;&lt;/ul&gt;A &lt;span style="font-style: italic;"&gt;Cluster&lt;/span&gt; is simply a database instance.  The data in the clusters is what is replicated.  A &lt;span style="font-style: italic;"&gt;Load Balancer&lt;/span&gt; exists to share the query load between all the databases in the replication scheme.  Lastly, the &lt;span style="font-style: italic;"&gt;Replicator&lt;/span&gt; is used to replicate, or synchronize, data between all the clusters.  In our tutorial, we will build a replication scheme with the following components:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Two clusters, clusterdb1 and clusterdb2&lt;/li&gt;&lt;li&gt;Once load balancer, pglb&lt;/li&gt;&lt;li&gt;Two replicators, pgrepl1 and pgrepl2&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Our logical design will look like this when we are finished:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_mBoF1HA1ncU/RtGdt2_FdUI/AAAAAAAAABI/eZ8vT-DFHmQ/s1600-h/pgcluster_logical.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_mBoF1HA1ncU/RtGdt2_FdUI/AAAAAAAAABI/eZ8vT-DFHmQ/s320/pgcluster_logical.jpg" alt="" id="BLOGGER_PHOTO_ID_5103033263967270210" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;graphic&gt;&lt;br /&gt;It is possible to install more than one PGCluster component (cluster, load balancer, replicator) on a single system.  For our example, we will put 1 cluster on each VM, with the load balancer on VM 1, and the replicators on each VM.  In practice, however, you may find you receive better performance by having each node run as a dedicated PGCluster component.  When finished, the physical design will look like this:&lt;br /&gt;&lt;br /&gt;&lt;/graphic&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_mBoF1HA1ncU/RtDNC2_FdSI/AAAAAAAAAA4/A6NZw624x5k/s1600-h/pgcluster_physical.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_mBoF1HA1ncU/RtDNC2_FdSI/AAAAAAAAAA4/A6NZw624x5k/s320/pgcluster_physical.jpg" alt="" id="BLOGGER_PHOTO_ID_5102803826814317858" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;graphic&gt;&lt;br /&gt;Prior to configuring PGCluster, it is important to ensure that the hostnames for all the PGCluster components can be resolved to IP addresses.  You may do this using DNS, or simply add the required entries to the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;/etc/hosts&lt;/span&gt;&lt;/span&gt; file.  In our case, &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;clusterdb1&lt;/span&gt;&lt;/span&gt;, &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;pglb&lt;/span&gt;&lt;/span&gt;, and &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;pgrepl1&lt;/span&gt;&lt;/span&gt; all resolve to &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;192.168.72.128&lt;/span&gt;&lt;/span&gt;.  Both &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;clusterdb2&lt;/span&gt;&lt;/span&gt; and &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;pgrepl2&lt;/span&gt;&lt;/span&gt; resolve to &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;192.168.72.129&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;It is also important that PGCluster run as a non-privileged user.  In our case, this user is &lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;font-family:courier new;" &gt;postgres&lt;/span&gt;&lt;/span&gt;.  You can see the steps for creating the &lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;font-family:courier new;" &gt;postgres&lt;/span&gt;&lt;/span&gt; user and setting the appropriate file permissions at the &lt;a href="http://pgcluster.projects.postgresql.org/1_3/install.html"&gt;PGCluster install page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;PGCluster makes use of several configuration files, each specific to the component you are installing.  First, we will configure each of the clusterdb instances.  In the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;/usr/local/pgsql/data&lt;/span&gt; directory, you will find 3 files that need to be modified:  &lt;span style=";font-family:courier new;font-size:85%;"  &gt;cluster.conf&lt;/span&gt;, &lt;span style=";font-family:courier new;font-size:85%;"  &gt;postgresql.conf&lt;/span&gt;, and &lt;span style=";font-family:courier new;font-size:85%;"  &gt;pg_hba.conf&lt;/span&gt;.   The &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;cluster.conf &lt;/span&gt;&lt;/span&gt;file defines characteristics of the database cluster, as well as the replication server that will be used.  The &lt;span style="font-family:courier new;"&gt;postgresql.conf&lt;/span&gt; is the standard PostgreSQL configuration file.  The &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;pg_hba.conf&lt;/span&gt;&lt;/span&gt; file is used for defining PostgreSQL security and access controls.  This file must be modified to trust connections originating from all other databases in the cluster.  Below you will find the parameters that must be added or defined for each of these files.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;cluster.conf&lt;br /&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;&lt;pre name="code" class="sql"&gt;&amp;lt;replicate_server_info&amp;gt;&lt;br /&gt;&amp;lt;host_name&amp;gt;pgrepl1&amp;lt;/host_name&amp;gt;&lt;br /&gt;&amp;lt;port&amp;gt;8001&amp;lt;/port&amp;gt;&lt;br /&gt;&amp;lt;recovery_port&amp;gt;8101&amp;lt;/recovery_port&amp;gt;&lt;br /&gt;&amp;lt;/replicate_server_info&amp;gt;&lt;br /&gt;&amp;lt;recovery_port&amp;gt;7001&amp;lt;/recovery_port&amp;gt;&lt;br /&gt;&amp;lt;rsync_path&amp;gt;/usr/bin/rsync&amp;lt;/rsync_path&amp;gt;&lt;br /&gt;&amp;lt;rsync_option&amp;gt;ssh -1&amp;lt;/rsync_option&amp;gt;&lt;br /&gt;&amp;lt;rsync_compress&amp;gt;yes&amp;lt;/rsync_compress&amp;gt;&lt;br /&gt;&amp;lt;pg_dump_path&amp;gt;/usr/local/pgsql/bin/pg_dump&amp;lt;/pg_dump_path&amp;gt;&lt;br /&gt;&amp;lt;when_stand_alone&amp;gt;read_only&amp;lt;/when_stand_alone&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;pg_hba.conf&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;&lt;span&gt;host    all        all    192.168.72.128    255.255.255.0    trust&lt;br /&gt;host    all        all    192.168.72.129    255.255.255.0    trust&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;postgresql.conf&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;&lt;span&gt;tcpip_socket = true&lt;br /&gt;port = 5432 &lt;/span&gt;&lt;/span&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"  &gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/graphic&gt;&lt;br /&gt;Now that the cluster instances have been configured, we can configure the replicators.  We have 2 replicators defined, pgrepl1 and pgrepl2.  This allows for multi-master replication.  The configuration files for each instance follow.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;pgreplicate.conf for pgrepl1&lt;/span&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;Cluster_Server_Info&amp;gt;&lt;br /&gt;   &amp;lt;Host_Name&amp;gt;clusterdb1&amp;lt;/Host_Name&amp;gt;&lt;br /&gt;   &amp;lt;Port&amp;gt;5432&amp;lt;/Port&amp;gt;&lt;br /&gt;   &amp;lt;Recovery_Port&amp;gt;7001&amp;lt;/Recovery_Port&amp;gt;&lt;br /&gt;&amp;lt;/Cluster_Server_Info&amp;gt;&lt;br /&gt;&amp;lt;Cluster_Server_Info&amp;gt;&lt;br /&gt;   &amp;lt;Host_Name&amp;gt;clusterdb2&amp;lt;/Host_Name&amp;gt;&lt;br /&gt;   &amp;lt;Port&amp;gt;5432&amp;lt;/Port&amp;gt;&lt;br /&gt;   &amp;lt;Recovery_Port&amp;gt;7001&amp;lt;/Recovery_Port&amp;gt;&lt;br /&gt;&amp;lt;/Cluster_Server_Info&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;loadbalance_server_info&amp;gt;&lt;br /&gt;   &amp;lt;host_name&amp;gt;pglb&amp;lt;/host_name&amp;gt;&lt;br /&gt;   &amp;lt;recovery_port&amp;gt;6001&amp;lt;/recovery_port&amp;gt;&lt;br /&gt;&amp;lt;/loadbalance_server_info&amp;gt;&lt;br /&gt;&amp;lt;host_name&amp;gt;pgrepl1&amp;lt;/host_name&amp;gt;&lt;br /&gt;&amp;lt;replication_port&amp;gt;8001&amp;lt;/replication_port&amp;gt;&lt;br /&gt;&amp;lt;recovery_port&amp;gt;8101&amp;lt;/recovery_port&amp;gt;&lt;br /&gt;&amp;lt;rlog_port&amp;gt;8301&amp;lt;/rlog_port&amp;gt;&lt;br /&gt;&amp;lt;response_mode&amp;gt;normal&amp;lt;/response_mode&amp;gt;&lt;br /&gt;&amp;lt;use_replication_log&amp;gt;no&amp;lt;/use_replication_log&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;pgreplicate.conf for pgrepl2&lt;/span&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;Cluster_Server_Info&amp;gt;&lt;br /&gt;   &amp;lt;Host_Name&amp;gt;clusterdb1&amp;lt;/Host_Name&amp;gt;&lt;br /&gt;   &amp;lt;Port&amp;gt;5432&amp;lt;/Port&amp;gt;&lt;br /&gt;   &amp;lt;Recovery_Port&amp;gt;7001&amp;lt;/Recovery_Port&amp;gt;&lt;br /&gt;&amp;lt;/Cluster_Server_Info&amp;gt;&lt;br /&gt;&amp;lt;Cluster_Server_Info&amp;gt;&lt;br /&gt;   &amp;lt;Host_Name&amp;gt;clusterdb2&amp;lt;/Host_Name&amp;gt;&lt;br /&gt;   &amp;lt;Port&amp;gt;5432&amp;lt;/Port&amp;gt;&lt;br /&gt;   &amp;lt;Recovery_Port&amp;gt;7001&amp;lt;/Recovery_Port&amp;gt;&lt;br /&gt;&amp;lt;/Cluster_Server_Info&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;loadbalance_server_info&amp;gt;&lt;br /&gt;   &amp;lt;host_name&amp;gt;pglb&amp;lt;/host_name&amp;gt;&lt;br /&gt;   &amp;lt;recovery_port&amp;gt;6001&amp;lt;/recovery_port&amp;gt;&lt;br /&gt;&amp;lt;/loadbalance_server_info&amp;gt;&lt;br /&gt;&amp;lt;host_name&amp;gt;pgrepl2&amp;lt;/host_name&amp;gt;&lt;br /&gt;&amp;lt;replication_port&amp;gt;8001&amp;lt;/replication_port&amp;gt;&lt;br /&gt;&amp;lt;recovery_port&amp;gt;8101&amp;lt;/recovery_port&amp;gt;&lt;br /&gt;&amp;lt;rlog_port&amp;gt;8301&amp;lt;/rlog_port&amp;gt;&lt;br /&gt;&amp;lt;response_mode&amp;gt;normal&amp;lt;/response_mode&amp;gt;&lt;br /&gt;&amp;lt;use_replication_log&amp;gt;no&amp;lt;/use_replication_log&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Once the necessary configuration changes have been made for the clusterdb and replicator instances, we can configure the load balancer.  Our load balancer will be located on the same virtual machine as clusterdb1.  The configuration file for the load balancer is located in &lt;span style="font-family:courier new;"&gt;/usr/local/pgsql/etc/pglb.conf&lt;/span&gt;.  The configuration file follows below.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;pglb.conf&lt;/span&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;cluster_server_info&amp;gt;&lt;br /&gt;   &amp;lt;host_name&amp;gt;clusterdb1&amp;lt;/host_name&amp;gt;&lt;br /&gt;   &amp;lt;port&amp;gt;5432&amp;lt;/port&amp;gt;&lt;br /&gt;   &amp;lt;max_connect&amp;gt;32&amp;lt;/max_connect&amp;gt;&lt;br /&gt;&amp;lt;/cluster_server_info&amp;gt;&lt;br /&gt;&amp;lt;cluster_server_info&amp;gt;&lt;br /&gt;   &amp;lt;host_name&amp;gt;clusterdb2&amp;lt;/host_name&amp;gt;&lt;br /&gt;   &amp;lt;port&amp;gt;5432&amp;lt;/port&amp;gt;&lt;br /&gt;   &amp;lt;max_connect&amp;gt;32&amp;lt;/max_connect&amp;gt;&lt;br /&gt;&amp;lt;/cluster_server_info&amp;gt;&lt;br /&gt;&amp;lt;host_name&amp;gt;pglb&amp;lt;/host_name&amp;gt;&lt;br /&gt;&amp;lt;backend_socket_dir&amp;gt;/tmp&amp;lt;/backend_socket_dir&amp;gt;&lt;br /&gt;&amp;lt;receive_port&amp;gt;5433&amp;lt;/receive_port&amp;gt;&lt;br /&gt;&amp;lt;recovery_port&amp;gt;6001&amp;lt;/recovery_port&amp;gt;&lt;br /&gt;&amp;lt;max_cluster_num&amp;gt;128&amp;lt;/max_cluster_num&amp;gt;&lt;br /&gt;&amp;lt;use_connection_pooling&amp;gt;no&amp;lt;/use_connection_pooling&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;With our configuration out of the way, we are ready to start the services and begin working with PGCluster.  The order in which you start the services is important.  The order is as follows:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;All replicator instances&lt;/li&gt;&lt;li&gt;All cluster instances&lt;/li&gt;&lt;li&gt;All load balancer instances&lt;/li&gt;&lt;/ol&gt;To start the replicator services, run the following command as the &lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;font-family:courier new;" &gt;postgres &lt;/span&gt;&lt;/span&gt;user on each replicator VM:&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;/usr/local/pgsql/bin/pgreplicate -D /usr/local/pgsql/etc&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To start the clusterdb services, run the following command as the &lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;font-family:courier new;" &gt;postgres&lt;/span&gt;&lt;/span&gt; user on each clusterdb VM:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -o "-i" start&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Lastly, to start the load balancer service, run the following command as the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;&lt;span style="font-style: italic;"&gt;postgres&lt;/span&gt;&lt;/span&gt; user on the load balancer VM:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;/usr/local/pgsql/bin/pglb -D /usr/local/pgsql/etc&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can find more information on how to start and stop PGCluster services on &lt;a href="http://pgcluster.projects.postgresql.org/1_3/startup.html"&gt;this page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;With our services started, we can begin testing PGCluster.  All changes made to either database, either through a direct connection or one coming in through the load balancer, should be replicated across to all the other databases.  The screenshot below shows a database being created.  In this case, I ran the command against the clusterdb1 database.  You can see the log output for this command in the top left VM.  If you look at the lower right VM, you will see the log output for the replication that takes place between the two hosts.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_mBoF1HA1ncU/RtGbgG_FdTI/AAAAAAAAABA/G8hlmLY2cXE/s1600-h/Screenshot.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_mBoF1HA1ncU/RtGbgG_FdTI/AAAAAAAAABA/G8hlmLY2cXE/s320/Screenshot.png" alt="" id="BLOGGER_PHOTO_ID_5103030828720813362" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The log output shows our database creation and replication was successful.  From here, we can create tables, views, and other SQL objects and know that they will be replicated across to all the other systems we have configured.&lt;br /&gt;&lt;br /&gt;If you run into problems, here are some common issues that my be creating the errors you encounter:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Make sure that the hostnames used for each cluster, load balancer, and replicator can be resolved to an IP address&lt;/li&gt;&lt;li&gt;Your firewall may be blocking incoming connections.  Ensure that each host is able to communicate on all the necessary ports&lt;/li&gt;&lt;li&gt;Check your &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;pg_hba.conf&lt;/span&gt;&lt;/span&gt; file if you are getting errors about rejected connections.  You must set an entry for each IP address to &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;trust&lt;/span&gt;&lt;/span&gt; in order for the data to be replicated successfully&lt;/li&gt;&lt;/ul&gt;This was just a short introduction into data replication using PGCluster.  My next experiment will be to try and configure PGCluster for use with &lt;a href="http://odyssi.blogspot.com/2007/04/security-enhanced-postgresql.html"&gt;SE-PostgreSQL&lt;/a&gt; for added security.  But, that's a post for another day.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-8218014142511967177?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/8218014142511967177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=8218014142511967177' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/8218014142511967177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/8218014142511967177'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/08/postgresql-replication-with-pgcluster.html' title='PostgreSQL Replication with PGCluster'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_mBoF1HA1ncU/RtGdt2_FdUI/AAAAAAAAABI/eZ8vT-DFHmQ/s72-c/pgcluster_logical.jpg' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-8158344198336939837</id><published>2007-08-07T10:12:00.000-05:00</published><updated>2007-10-05T12:43:42.015-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='smartcards'/><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='palm'/><category scheme='http://www.blogger.com/atom/ns#' term='windowsmobile'/><category scheme='http://www.blogger.com/atom/ns#' term='CAC'/><category scheme='http://www.blogger.com/atom/ns#' term='certificates'/><title type='text'>Palm CAC Solution -- Mobile Smartcards for the DoD</title><content type='html'>Recently, Palm &lt;a href="http://msmobiles.com/news.php/6571.html"&gt;announced&lt;/a&gt; the availability of a &lt;a href="http://en.wikipedia.org/wiki/Common_Access_Card"&gt;Common Access Card&lt;/a&gt; solution for their Treo smartphone line.  For those of you not familiar with the CAC, it is a smartcard designed for use within the U.S. Department of Defense.  The CAC acts as an individuals ID card, with photos and information about the user's identity, as well as a smartcard with PKI credentials for logging into a network, encrypting e-mail, etc.&lt;br /&gt;&lt;br /&gt;Palm's CAC solution is quite interesting, particularly with respect to how the smartphone interacts with the smartcard.  Palm has developed a Bluetooth-enabled smartcard reader that also acts as a badge holder.  The photo, name, and expiration information is still visible, but the smartcard chip itself makes contact with a reader.  This allows your smartphone to make use of the credentials on the card when sending e-mail or accessing a network.&lt;br /&gt;&lt;br /&gt;This is a truly innovative way to handle smartcards, and with more and more enterprises evaluating the use of PKI and strong authentication, I can see this being a viable solution for the commercial world, as well.  The phone itself doesn't have any bulky externally-attached readers.  This makes it possible to maintain the phone's current usability and mobility, without sacrificing the security that smartcards provide.&lt;br /&gt;&lt;br /&gt;The possibilities are endless when discussing how this technology could be used within the enterprise.  Users can login to the corporate VPN from anywhere in the world, using mobile broadband connections or even WiFi (provided you have an adapter for your phone).  E-mails can be digitally signed and encrypted prior to being sent.  Given the &lt;a href="http://www.newsfactor.com/story.xhtml?story_id=54388"&gt;lack of security in public WiFi hotspots&lt;/a&gt;, this is a capability that is long overdue.  In addition, being able to encrypt the data on the phone itself, or lockdown access to it without the smartcard, provides the user with confidence that a lost phone will not result in data compromise.&lt;br /&gt;&lt;br /&gt;For more information on how this product works, see &lt;a href="http://www.palm.com/us/business/solutions/cac_demo_start.html"&gt;Palm's Flash demo&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-8158344198336939837?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/8158344198336939837/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=8158344198336939837' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/8158344198336939837'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/8158344198336939837'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/08/palm-cac-solution-mobile-smartcards-for.html' title='Palm CAC Solution -- Mobile Smartcards for the DoD'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-1461814535874723284</id><published>2007-07-30T13:21:00.002-05:00</published><updated>2008-12-11T16:12:00.775-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='designpatterns'/><category scheme='http://www.blogger.com/atom/ns#' term='SPI'/><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='pki'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='odyssi'/><category scheme='http://www.blogger.com/atom/ns#' term='ServiceLoader'/><title type='text'>Java - Implement your own Service Provider Interface</title><content type='html'>As I continue development on the next release of &lt;a href="http://www.sf.net/projects/odyssipki"&gt;Odyssi PKI&lt;/a&gt;, I've tried to apply some of the lessons I've learned regarding extensibility in object-oriented code.  &lt;a href="http://odyssi.blogspot.com/2006/07/odyssics-v01-released.html"&gt;Version 0.1 of Odyssi PKI&lt;/a&gt; was relatively static, in terms of what formats and technologies were supported.  The request types that the CA was able to handle were statically defined.  While you could implement the X509CertificateRequest interface to define your own request format, the rest of the CA was unable to handle the request beyond a certain point.  This made for a very limited, inflexible design.  Want to add XKMS support?  Get ready to rework a LOT of code.  As a result, this lack of preparation for the future was high on my list of things to redesign for the next release.&lt;br /&gt;&lt;br /&gt;As I sought out the best way to handle this problem, I realized that working with X.509 certificate request formats/objects is very similar to the way Java already handles certificates, encryption keys, and signature algorithms.  In each case, the algorithms and formats for these objects is implemented through the use of a &lt;a href="http://en.wikipedia.org/wiki/Service_Provider_Interface"&gt;Service Provider Interface (SPI)&lt;/a&gt;.  In this example, the SPI is the implementation of the &lt;a href="http://java.sun.com/products/jce/javase.html"&gt;Java Cryptography Extension&lt;/a&gt; that is configured for the JVM.  If your preferred algorithm is not supported, perhaps one of the &lt;a href="http://www.bouncycastle.org/"&gt;other JCE implementations&lt;/a&gt; has it.  All that is needed is to configure the JVM to recognize the JCE implementation, and ensure that it is located on your classpath.&lt;br /&gt;&lt;br /&gt;Upon realizing this, I decided that there must be a way to take advantage of this type of design to handle X.509 certificate request objects.  I needed to look no further than Java's &lt;a href="http://java.sun.com/javase/6/docs/api/java/util/ServiceLoader.html"&gt;ServiceLoader&lt;/a&gt; class.  The ServiceLoader class is used to locate classes that implement a given SPI interface or extend an abstract SPI base class.  In our example, we have several classes and interfaces that will be used to handle X.509 certificate request objects.  The first interface is &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;X509CertificateRequest&lt;/span&gt;&lt;/span&gt;.  This interface defines several methods that are common to all types of certificate requests:  &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;getPublicKey()&lt;/span&gt;&lt;/span&gt;, &lt;span style=";font-family:courier new;font-size:85%;"  &gt;getSignatureAlgorithm()&lt;/span&gt;, etc.  We also define a class called &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;X509CertificateRequestFactory&lt;/span&gt;&lt;/span&gt;.  This class is a factory class that creates &lt;span style=";font-family:courier new;font-size:85%;"  &gt;X509CertificateRequest&lt;/span&gt; objects.  It is also the class that will make use of the ServiceLoader object.  Lastly, we need to define an interface called &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;X509CertificateRequestFactorySpi&lt;/span&gt;&lt;/span&gt;.  This interface defines the methods that must be present in all of our SPI implementations.  We'll look at &lt;span style=";font-family:courier new;font-size:85%;"  &gt;X509CertificateRequestFactory&lt;/span&gt; first, as it is the most important.&lt;br /&gt;&lt;br /&gt;The &lt;span style=";font-family:courier new;font-size:85%;"  &gt;X509CertificateRequestFactory&lt;/span&gt; class has a static method called &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;getInstance()&lt;/span&gt;&lt;/span&gt; that takes as its parameter a certificate request format.  This format could be PKCS #10, XKMS, CRMF, or any other format that we like, so long as we have an SPI implementation for it.  The constructor for this class is private, and takes a &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;X509CertificateRequestFactorySpi&lt;/span&gt;&lt;/span&gt; object as its only parameter.  This is done because the SPI implementation object will be used by the factory class to provide all of its underlying functionality.&lt;br /&gt;&lt;br /&gt;Now that we've discussed the general structure of the factory class, let's look at the SPI interface and the methods it defines.  Since the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;X509CertificateRequestFactorySpi&lt;/span&gt;&lt;/span&gt; class is used to provide all of the underlying functionality for the request factory, it must provide methods such as &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;getCertificateRequest(...)&lt;/span&gt;&lt;/span&gt; that will be used to generate the request object itself from another object (&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;byte[]&lt;/span&gt;&lt;/span&gt;, &lt;span style=";font-family:courier new;font-size:85%;"  &gt;String&lt;/span&gt;, XML document, etc.)  In addition, another method &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;isSupported()&lt;/span&gt;&lt;/span&gt; is defined, which takes as its only parameter a String, denoting the request format.  This method returns true if the request format is supported by this SPI implementation.&lt;br /&gt;&lt;br /&gt;So how do we use the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;ServiceLoader&lt;/span&gt;&lt;/span&gt; class?  The ServiceLoader class looks for a provider configuration file located in the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;META-INF/services&lt;/span&gt;&lt;/span&gt; directory of the SPI implementation's JAR file.  This file is the fully-qualified binary name of the services type.  That is, if our request factory's full name is &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;net.odyssi.certserv.x509.request.X509CertificateRequestFactory&lt;/span&gt;&lt;/span&gt;, the provider configuration file would be called &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;net.odyssi.certserv.x509.request.X509CertificateRequestFactory&lt;/span&gt;&lt;/span&gt;, and would be located in the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;META-INF/services&lt;/span&gt; directory of the implementation JAR file.  This file contains the name(s) of the SPI implementation class(es) contained within the JAR.  So, for example, if we wanted to support PKCS #10 certificate requests, our provider configuration file may have one entry that reads:&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;"  &gt;net.odyssi.certserv.x509.request.impl.PKCS10CertificateRequestFactory&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The &lt;span style=";font-family:courier new;font-size:85%;"  &gt;PKCS10CertificateRequestFactory&lt;/span&gt; class provides all of the dirty work in generating an &lt;span style=";font-family:courier new;font-size:85%;"  &gt;X509CertificateRequest&lt;/span&gt; object from an arbitrary source, such as an &lt;span style="font-family:courier new;"&gt;InputStream&lt;/span&gt; or a &lt;span style=";font-family:courier new;font-size:85%;"  &gt;byte[]&lt;/span&gt;.  Now that we've defined our provider configuration file with our SPI implementation class, we're ready to put the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;ServiceLoader&lt;/span&gt;&lt;/span&gt; class to work.&lt;br /&gt;&lt;br /&gt;Within the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;getInstance()&lt;/span&gt;&lt;/span&gt; method of the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;X509CertificateRequestFactory&lt;/span&gt; class, we would have the following:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;public static X509CertificateRequestFactory getInstance(String format) {&lt;br /&gt;&lt;br /&gt;ServiceLoader sl = ServiceLoader.load(X509CertificateRequestFactorySpi.class);&lt;br /&gt;Iterator it = sl.iterator();&lt;br /&gt;while(it.hasNext()) {&lt;br /&gt;   X509CertificateRequestFactorySpi impl = (X509CertificateRequestFactorySpi)it.next();&lt;br /&gt;   if(impl.isSupported(format)) {&lt;br /&gt;       return new X509CertificateRequestFactory(impl);&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;return null;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this method, the &lt;span style=";font-family:courier new;font-size:85%;"  &gt;ServiceLoader sl = ServiceLoader.class(X509CertificateRequestFactorySpi.class)&lt;/span&gt; line instructs the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;ServiceLoader&lt;/span&gt;&lt;/span&gt; to find all implementations of the &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;X509CertificateRequestFactorySpi&lt;/span&gt;&lt;/span&gt; interface.  From here, we iterate through the returned results and see if an implementation can be found for the given request format.  The returned &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;X509CertificateFactory&lt;/span&gt;&lt;/span&gt; would then used the SPI implementation to provide its underlying functionality for &lt;span style=";font-family:courier new;font-size:85%;"  &gt;getCertificateRequest(...)&lt;/span&gt;, etc.&lt;br /&gt;&lt;br /&gt;The &lt;span style=";font-family:courier new;font-size:85%;"  &gt;ServiceLoader&lt;/span&gt; class provides a great mechanism for adding functionality and future-proofing your applications.  Unlike other mechanisms such as &lt;a href="http://www.springframework.org/"&gt;Spring's&lt;/a&gt; dependency injection, no modifications are necessary for your code to take advantage of the new SPI implementation.  The SPI implementation JAR file simply needs to be located on the classpath for the functionality to be available.  By making use of a well defined SPI, it is possible to interchange components with your application without losing functionality.  It also provides a great way of adding new capabilities to your already existing applications.  For more information about &lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;ServiceLoader&lt;/span&gt;&lt;/span&gt;, take a look at the &lt;a href="http://java.sun.com/javase/6/docs/api/java/util/ServiceLoader.html"&gt;javadocs&lt;/a&gt;, or &lt;a href="http://www.javaspecialists.co.za/archive/newsletter.do?issue=139"&gt;this article&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-1461814535874723284?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/1461814535874723284/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=1461814535874723284' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1461814535874723284'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1461814535874723284'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/07/java-implement-your-own-service.html' title='Java - Implement your own Service Provider Interface'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-7989043618578822470</id><published>2007-07-26T09:56:00.000-05:00</published><updated>2007-07-26T10:14:23.653-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='databases'/><category scheme='http://www.blogger.com/atom/ns#' term='PostgreSQL'/><category scheme='http://www.blogger.com/atom/ns#' term='replication'/><category scheme='http://www.blogger.com/atom/ns#' term='databses'/><title type='text'>PostgreSQL Replication - PGCluster</title><content type='html'>Earlier, I did an article on &lt;a href="http://odyssi.blogspot.com/2007/04/mysql-master-master-replication.html"&gt;MySQL multi-master replication&lt;/a&gt;.  While &lt;a href="http://www.mysql.com/"&gt;MySQL&lt;/a&gt; is a very fast database, able to provide the vast majority of features that most people need, there are definite shortcomings.  As a result, I've begun looking more and more into &lt;a href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt; as my database of choice for my development work.  The major shortcoming for me, however, has always been PostgreSQL's replication and failover support.  The &lt;a href="http://slony.info/"&gt;Slony-I&lt;/a&gt; project provides replication, however it is asynchronous and master-slave.  In many environments, this just isn't good enough.  I came across PGCluster, only to see on its project site that the latest code was for PostgreSQL 7.3.&lt;br /&gt;&lt;br /&gt;At first, I thought that this was reason enough to abandon PostgreSQL and focus primarily on MySQL.  However, I have since found that I am mistaken.  The PGCluster site that I initially came across was an old site for the project.  The &lt;a href="http://www.pgcluster.org/"&gt;new PGCluster site&lt;/a&gt; shows that PGCluster has been updated for PostgreSQL 8.2.  This is great news for anyone looking to use PostgreSQL in an enterprise-wide capacity.  PGCluster provides synchronous, multi-master replication.  It is designed for high-availability, as well as load-balancing.  While I haven't had a chance to install the latest version of PGCluster, I will be giving it a long, hard look in the near future.&lt;br /&gt;&lt;br /&gt;While working on the &lt;a href="http://www.sourceforge.net/projects/odyssipki"&gt;Odyssi PKI&lt;/a&gt; project's next release, I've spent a bit more time focusing on the database aspect.  Certificate Authority servers are typically considered to have high-security requirements, so they are often run using dedicated database servers.  You wouldn't typically want your certificate data in the same database server with your general business data.  Normally, the database instance is dedicated to the CA, and is hardened for security to prevent compromise.  However, in situations where high numbers of certificates are issued, performance and scalability may become a factor.  In a hosted PKI environment, for example, large numbers of certificates being issued may put a strain on the database server.  In addition, CA servers have high availability requirements, particularly for CRL issuance, etc.  The need for failover replication becomes apparent.&lt;br /&gt;&lt;br /&gt;With &lt;a href="http://www.pgcluster.org"&gt;PGCluster&lt;/a&gt;, it is possible to implement a database architecture that allows for the performance and reliability needed for this type of scenario.  &lt;a href="http://www.pgcluster.org/wiki/Example"&gt;The example provided&lt;/a&gt; on the PGCluster website shows a simple replication scenario between 3 servers; two are located in close proximity, with the third connecting remotely over a VPN.  When designing a database layout for an Odyssi PKI deployment, you would typically want to have a minimum of 2 databases, acting in a load-balanced, multi-master configuration.  This will provide you with failover capability in the event of a server failure, as well as the ability to split processing between servers.&lt;br /&gt;&lt;br /&gt;Starting with Odyssi PKI's next release, I plan to include some documentation outlining recommended architecture, best-practices, etc. for designing a PKI.  Now that I have discovered PGCluster is not a dead project, I will be sure to use it in my examples.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-7989043618578822470?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/7989043618578822470/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=7989043618578822470' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/7989043618578822470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/7989043618578822470'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/07/postgresql-replication-pgcluster.html' title='PostgreSQL Replication - PGCluster'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-7413161757097966083</id><published>2007-07-20T08:56:00.000-05:00</published><updated>2007-07-20T09:04:28.039-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='pki'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='odyssi'/><title type='text'>Odyssi PKI - 1,000 Downloads</title><content type='html'>As of this morning, version 0.1 of &lt;a href="http://sourceforge.net/projects/odyssipki"&gt;Odyssi PKI&lt;/a&gt; has been &lt;a href="http://sourceforge.net/project/showfiles.php?group_id=145808"&gt;downloaded&lt;/a&gt; 1,000 times.  My thanks to all those who downloaded the program.  I am currently working on version 0.2, which will represent a major rewrite and restructuring of the entire codebase.  Version 0.1 was more intended as a proof-of-concept.  In writing it, I was able to experiment with several technologies I hadn't played with before, like Spring or Hibernate.  The development turned out to be a great learning experience.&lt;br /&gt;&lt;br /&gt;Version 0.2 will be much better from a design and functionality standpoint.  It is being rewritten from the ground up, and will include many functions and features that were missing in version 0.1.  This includes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Easier CA administration, through the use of a web-based admin console&lt;/li&gt;&lt;li&gt;Easier creation and management of Registration Authorities&lt;/li&gt;&lt;li&gt;Enhanced security requirements, such as enforcement of strong authentication in the Registration Authority console&lt;/li&gt;&lt;li&gt;CRL support&lt;/li&gt;&lt;li&gt;XKMS (possibly)&lt;/li&gt;&lt;li&gt;Enhanced certificate template support, allowing admins to define the characteristics of different types of certificates that will be generated&lt;/li&gt;&lt;li&gt;Support for numerous X.509 certificate extensions&lt;/li&gt;&lt;li&gt;Stand-alone release (using an included distribution of Apache Tomcat) for easier deployment and administration&lt;/li&gt;&lt;/ul&gt;This is just a small list of the features I plan on including in the next version.  I have no firm dates on when it will be released, as I'm working on it in my free time (which is minimal, now that I have a toddler).  However, work is progressing quickly and I hope to have something posted really soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-7413161757097966083?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/7413161757097966083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=7413161757097966083' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/7413161757097966083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/7413161757097966083'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/07/odyssi-pki-1000-downloads.html' title='Odyssi PKI - 1,000 Downloads'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-6343493408533968233</id><published>2007-05-30T08:40:00.000-05:00</published><updated>2007-05-31T14:20:09.064-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='monopolies'/><category scheme='http://www.blogger.com/atom/ns#' term='comcast'/><category scheme='http://www.blogger.com/atom/ns#' term='choice'/><category scheme='http://www.blogger.com/atom/ns#' term='fios'/><category scheme='http://www.blogger.com/atom/ns#' term='verizon'/><category scheme='http://www.blogger.com/atom/ns#' term='internet'/><title type='text'>Free at Last, Free at Last!  FiOS is Here!</title><content type='html'>This morning, I was awakened by the sound of jackhammers outside of our house.  Normally, jackhammers at 7:00 would be quite an unpleasant situation.  However, this morning, that sound is a welcome one.  Why?  Because it means that, soon, there will be a fiber optic cable buried in the ground on our street.  And it is this cable that represents my freedom.  Freedom from our local cable company, &lt;a href="http://www.comcast.com/"&gt;Comcast&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Anyone who has ever had Comcast for their cable or Internet service can most likely relate to my feelings about the company.  Slow service.  Outrageous prices (and frequent price increases).  Terrible customer support.  The list goes on, and on.  Soon, however, I will be able to break free of the shackles of oppression  (Comcast), and experience the freedom that is &lt;a href="http://www.verizonfios.com/"&gt;Verizon FiOS&lt;/a&gt;.  Now, don't get me wrong.  I'm well aware of the fact that FiOS will likely cost me just as much as Comcast does right now.  However, the mere fact that I'm able to get away from Comcast in the first place makes it all worth it.&lt;br /&gt;&lt;br /&gt;When I purchased my house (about 6 years ago), my bill for cable and Internet service was about $80.  This included basic cable, and a 4Mbps (advertised) Internet connection.  At the time, I had no complaints whatsoever.  My cable rarely, if ever, went out.  And my Internet connection was always speedy and reliable.  Over time, however, the situation has changed dramatically.  My combined bill is now over $100.  And what has the addition $20 gotten me in 6 years?  2 additional TV channels (only one of which I'm remotely interested in), and an increased in the &lt;span style="font-style: italic;"&gt;advertised&lt;/span&gt; Internet connection speed.  Note the word &lt;span style="font-style: italic;"&gt;&lt;span style="font-weight: bold;"&gt;advertised&lt;/span&gt;&lt;/span&gt;.  The advertisement in question is quick to point out that this is a &lt;span style="font-style: italic;"&gt;maximum&lt;/span&gt; connection speed.  In other words, Comcast could provide me with all of 1bps, and would still be offering the service they advertise.&lt;br /&gt;&lt;br /&gt;In reality, my Internet service has gotten consistently worse over the last 6 years.  In the first 3 years that I lived there, I lost service only twice.  In both instances, I was able to resolve my problem by simply disconnecting my cable modem, waiting a couple of minutes, and reconnecting it.  Now, however, I am faced with connection problems on an almost weekly basis.  And, sadly, most of these are issues I am unable to resolve.  Periodically, I will end up with little to no connectivity.  The indicator light on the modem tells me that there is a signal coming in.  But, for whatever reason, I can't connect to anything.  Other times, I am faced with DNS resolution issues.  I can get out if I know the IP address.  But, good luck relying on Comcast's DNS servers.  What upsets me most, though, is that my downstream service has gotten progressively slower over the last 3 years.  My bill has gone up, my connection speed has gone down.&lt;br /&gt;&lt;br /&gt;I'm sure that many of these problems boil down to one thing:  Comcast doesn't feel compelled to spend money on improving service when they've already got a monopolistic hold on a market.  Why invest in infrastructure improvements if your customers don't have an alternative?  Such sentiment exists whenever a company has a monopoly.  However, when an attractive alternative opens up, customers who feel abused by a monopoly are often quick to migrate to that alternative.  Look at &lt;a href="http://www.microsoft.com"&gt;Microsoft&lt;/a&gt;, and the monopoly it has on the software (particularly Operating System) market.  Consumers, fed up with the ongoing instability and security issues found in Windows, have begun migrating to &lt;a href="http://www.apple.com"&gt;Apple&lt;/a&gt;'s Mac OS X.  While Apple's market share is still small compared to Microsoft, it is growing steadily, particularly in the notebook computer segment.  When I walk through our neighborhood, I inevitably hear someone talking to one of the workers laying Verizon's fiber optic cable.  The discussion always seems to start out the same way:  "When can I call and have FiOS installed at our house?"  Consumers are chomping at the bit to find a better alternative.&lt;br /&gt;&lt;br /&gt;While Comcast still has a stranglehold on numerous markets (including ours, for the time being), this situation demonstrates how quickly things can change in any market.  &lt;a href="http://www.dell.com"&gt;Dell&lt;/a&gt; at one point had a commanding lead over its competitors, based primarily on its reputation for quality and good customer service.  However, recent &lt;a href="http://www.crn.com/it-channel/174300958"&gt;declines in quality&lt;/a&gt; combined with &lt;a href="http://www.newsforge.com/article.pl?sid=03/10/20/194207"&gt;poor customer support due to outsourcing&lt;/a&gt; has caused Dell's market share to slip, and its reputation to be tarnished.  &lt;a href="http://en.wikipedia.org/wiki/Edwin_Catmull"&gt;Ed Catmull&lt;/a&gt;, one of the creative geniuses behind &lt;a href="http://www.pixar.com"&gt;Pixar&lt;/a&gt;, once said, "&lt;a href="http://www.hollywoodreporter.com/hr/content_display/news/e3i3b47d6942f2f90f1904fd01b36f6dc9c"&gt;Quality is the best business plan&lt;/a&gt;."  By offering its customers sub-par products at an ever-increasing price, Comcast is risking losing those customers to competitors such as Verizon or &lt;a href="http://www.directv.com"&gt;DirecTV&lt;/a&gt;.  I, for one, will be scheduling an installation appointment just as soon as the Verizon trucks pull out of our street.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-6343493408533968233?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/6343493408533968233/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=6343493408533968233' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6343493408533968233'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6343493408533968233'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/05/free-at-last-free-at-last-fios-is-here.html' title='Free at Last, Free at Last!  FiOS is Here!'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-1117189695143862839</id><published>2007-05-11T09:55:00.000-05:00</published><updated>2007-05-31T14:21:58.359-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pki'/><category scheme='http://www.blogger.com/atom/ns#' term='odyssi'/><title type='text'>New Odyssi PKI Project Location</title><content type='html'>I have modified the Sourceforge project location for OdyssiCS.  The project has moved to its new page, &lt;a href="http://www.sourceforge.net/projects/odyssipki"&gt;&lt;span style="text-decoration: underline;"&gt;http://www.sourceforge.net/projects/odyssipki&lt;/span&gt;&lt;/a&gt;&lt;a href="http://www.sourceforge.net/odyssipki"&gt;&lt;/a&gt;, and has been given a new project name, Odyssi PKI, to reflect the overall goals of the project.  With the inclusion of some additional features, such as an OCSP responder and GUI-based certificate/key management tools, it became clear that I was developing more than just a Certificate Authority server.  As a result, I've renamed the project Odyssi PKI to reflect the suite of tools that will eventually be released.  Although the project page has changed locations, the &lt;a href="http://www.odyssi.net/"&gt;website for the project&lt;/a&gt; remains the same.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-1117189695143862839?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/1117189695143862839/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=1117189695143862839' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1117189695143862839'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1117189695143862839'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/05/new-odyssi-pki-project-location.html' title='New Odyssi PKI Project Location'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-6318066884740795832</id><published>2007-05-04T13:44:00.000-05:00</published><updated>2007-05-04T14:03:08.102-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='roadmap'/><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='pki'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='ocsp'/><category scheme='http://www.blogger.com/atom/ns#' term='odyssi'/><title type='text'>OdyssiCS Update and Milestones</title><content type='html'>This morning, I logged into the &lt;a href="http://www.sourceforge.net/"&gt;SourceForge&lt;/a&gt; &lt;a href="http://www.sourceforge.net/projects/odyssica"&gt;project page for OdyssiCS&lt;/a&gt;.  I was curious to see how many downloads of the application have occurred.  To my surprise, the &lt;a href="http://sourceforge.net/project/stats/?group_id=145808&amp;ugn=odyssica"&gt;number of downloads&lt;/a&gt; since release 0.1 last year has topped 800.  I don't know how many of these downloads resulted in any substantial use, but it's still refreshing to know that people are at least looking at it.  To all of you that have downloaded and tried OdyssiCS, thank you.  And, please, give me your feedback!  It will go a long way to helping me in my development.&lt;br /&gt;&lt;br /&gt;I have been working on version 0.2 in what little spare time I have.  I've been fortunate to learn a great deal about &lt;a href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt;, &lt;a href="http://www.springframework.org/"&gt;Spring&lt;/a&gt;, and general Java security since the first release.  The main areas I have been focusing on for release 0.2 are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Security&lt;/li&gt;&lt;li&gt;Ease of Administration&lt;/li&gt;&lt;li&gt;Inclusion of more advanced features&lt;/li&gt;&lt;/ul&gt;Release 0.1 was rather weak in terms of security.  While I did perform basic group membership checks, I realized that I was not designing for security as much as I'd originally intended.  As a result, I have gutted a great deal of the original code to ensure that it is designed with security in mind from the ground up.  I've also been spending a bit of time working on a secure JAAS-based application framework that I hope to have included in version 0.3.&lt;br /&gt;&lt;br /&gt;Administration in release 0.1 was also quite lacking.  In an effort to get the first release out there, I didn't spend as much time on developing an administrative interface.  This includes both CA server administration, as well as certificate administration for Registration Authorities.  I've sketched out quite a few ideas that I would like to incorporate into the web interface for administration.  Hopefully, this will make OdyssiCS easier to deploy and maintain, making it viable for some real-world work.  Additionally, for version 0.3 I plan to include an embedded Tomcat server and Windows GUI installer to make installation and configuration even easier.  My plans for version 0.3 are still quite sketchy, but hopefully it will represent a really high-quality, feature-rich release.&lt;br /&gt;&lt;br /&gt;Lastly, there was quite a bit of functionality missing in version 0.1.  This is being addressed in version 0.2, starting with the use of certificate extensions and templates.  Part of the power of X.509 certificates is the ability to include certificate extensions.  These extensions outline additional characteristics about the certificate, such as the constraints that exist pertaining to what it can be used for, CRL information, and policies that apply to the certificate.  I had some code in version 0.1 for working with extensions, but just wasn't happy with how it turned out.  I shelved it, deciding it would be best for the next release.  Certificate templates provide a way to define the characteristics of the different types of certificates a CA can create.  For example, SSL server certificates might have different properties (extensions, validity periods, key sizes, etc.) from e-mail certificates.  This will add a great deal of functionality to OdyssiCS, and I look forward to having that code completed.&lt;br /&gt;&lt;br /&gt;Some of the features I plan on implementing for version 0.2 include:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Certificate revocation, with full CRL support&lt;/li&gt;&lt;li&gt;OCSP (Online Certificate Status Protocol) for reporting certificate revocation information&lt;/li&gt;&lt;li&gt;Completely redesigned GUI for both end-entities and administrators&lt;/li&gt;&lt;li&gt;Enhanced security features throughout all domains of the application&lt;/li&gt;&lt;li&gt;Revised SQL schema, and SQL scripts for MySQL, PostgreSQL, and other SQL databases&lt;/li&gt;&lt;li&gt;X.509 certificate extensions and certificate templates&lt;/li&gt;&lt;/ul&gt;This is just a sample of some of the things I'm working on for the next release.  I don't have a timeline for it, as my free time for development has been limited.  Please feel free to leave a comment if there are additional features you would like me to investigate for this release.  I've already started pondering what I want for version 0.3 (XKMS, JAAS-based security framework, JMX management, etc.) so please pass on your suggestions!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-6318066884740795832?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/6318066884740795832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=6318066884740795832' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6318066884740795832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6318066884740795832'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/05/odyssics-update-and-milestones.html' title='OdyssiCS Update and Milestones'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-8964723289432135204</id><published>2007-04-02T10:54:00.005-05:00</published><updated>2008-09-23T10:25:58.795-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='databases'/><category scheme='http://www.blogger.com/atom/ns#' term='UserType'/><category scheme='http://www.blogger.com/atom/ns#' term='pki'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='odyssi'/><category scheme='http://www.blogger.com/atom/ns#' term='certificates'/><title type='text'>Hibernate CompositeUserType and X509Certificate</title><content type='html'>Often, when working with &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; as your ORM tool, you find the need to persist an object to the database that doesn't match one of the supported types.  For example, what if you wanted to persist an XML object?  One such way would be to convert the XML to a String type and persist it that way.  Of course, that messes up our object model, doesn't it?  Wouldn't it be nice if a setXXX() method could handle the XML object directly?  Using Hibernate's &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/UserType.html"&gt;UserType&lt;/a&gt; object, we have the flexibility to handle these types of conversions outside of our object model.&lt;br /&gt;&lt;br /&gt;I was faced with a very similar problem while reworking the way X.509 certificates are persisted in &lt;a target="_blank" href="http://www.odyssi.net/"&gt;OdyssiCS&lt;/a&gt;.  Previously, I would conver the certificate to a byte[] and store its base64 encoded representation in the database.  This resulted in a very ugly object model.  I contemplated making use of a UserType object for my X.509 certificate, but was then faced with a secondary problem:  How would I be able to perform queries against properties of the certificate?  What if I wanted to locate a certificate with a specific serial number?  One approach would be to maintain those properties separately.  However, this results, again, in a polluted object model.  After further review, I decided upon Hibernate's &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html"&gt;CompositeUserType&lt;/a&gt; object.  A CompositeUserType is an extension of UserType that allows for properties to be derefferenced when performing a Hibernate query.  Since I couldn't find much information about how to work with CompositeUserType objects, I decided to post some of my findings here to serve as a reference to others.  First, let's look at what we want our end result to be.&lt;br /&gt;&lt;br /&gt;In my object model, I have a CertificateModelImpl class that contains one method related to what we're doing with CompositeUserType:  getCertificate().  This method returns the X.509 certificate object.  Our database has a table called certificate_tbl with the following columns:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;ID -- A simple incremented row identifier&lt;/li&gt;&lt;li&gt;SUBJECT_ID -- Corresponds to the table containing our subject distinguished names&lt;/li&gt;&lt;li&gt;START_DATE -- The start of the certificate validity&lt;/li&gt;&lt;li&gt;END_DATE -- The expiration date for the certificate&lt;/li&gt;&lt;li&gt;SERIAL_NUMBER -- The certificate serial number&lt;/li&gt;&lt;li&gt;CERTIFICATE_DATA -- Contains the certificate in byte[] format&lt;/li&gt;&lt;/ul&gt;In our relational model, SUBJECT_ID is a foreign key pointing to another table.  For our discussion, it is not relavant.  We would like to focus on the remaining columns.  Our end goal is this:  We want to be able to call setCertificate() on CertificateModel, passing an &lt;a target="_blank" href="http://java.sun.com/javase/6/docs/api/java/security/cert/X509Certificate.html"&gt;X509Certificate&lt;/a&gt; object as a parameter.  We also need to be able to use properties of the certificate, such as serial number and expiration date, in a Hibernate query.  We want to store the certificate properties in the corresponding columns of the table, but don't want them available to our object model.  If these properties were available, we could run into an issue with data inconsistancy.&lt;br /&gt;&lt;br /&gt;To start implementing our X509CertificateUserType object, we must implement a couple of preliminary methods.  The &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#returnedClass%28%29"&gt;returnedClass()&lt;/a&gt; method tells Hibernate what type of object we are dealing with.  In our case, we return an X509Certificate.class object.  The &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#equals%28java.lang.Object,%20java.lang.Object%29"&gt;equals()&lt;/a&gt; and &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#hashCode%28java.lang.Object%29"&gt;hashCode()&lt;/a&gt; methods are pretty standard fare, acting as they normally do in Java.  There are a couple of other methods that must be implemented, such as &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#deepCopy%28java.lang.Object%29"&gt;deepCopy()&lt;/a&gt; and &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#isMutable%28%29"&gt;isMutable()&lt;/a&gt;.  Take a look at the &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html"&gt;Hibernate Javadocs&lt;/a&gt; for more information on what the remaining methods accomplish.&lt;br /&gt;&lt;br /&gt;Our first goal is to be able to store an X509Certificate object in the database as a byte[] and retrieve it later as an X509Certificate object.  The two methods of CompositeUserType that accomplish this are &lt;a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#nullSafeSet%28java.sql.PreparedStatement,%20java.lang.Object,%20int,%20org.hibernate.engine.SessionImplementor%29" target="_blank"&gt;nullSafeSet()&lt;/a&gt; and &lt;a href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#nullSafeGet%28java.sql.ResultSet,%20java.lang.String%5B%5D,%20org.hibernate.engine.SessionImplementor,%20java.lang.Object%29" target="_blank"&gt;nullSafeGet()&lt;/a&gt;.  The nullSafeSet() method is responsible for converting the certificate to a byte[] and storing it in the appropriate column.  This is also where we store the other certificate properties, such as expiration information and serial number.  For our puirposes, this is what the implementation of this method looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;public void nullSafeSet(PreparedStatement preparedStatement, Object object,&lt;br /&gt;    int i, SessionImplementor sessionImplementor) throws HibernateException, SQLException {&lt;br /&gt;  X509Certificate cert = (X509Certificate) object;&lt;br /&gt;  Hibernate.DATE.nullSafeSet(preparedStatement, cert.getNotBefore(), i);&lt;br /&gt;  Hibernate.DATE.nullSafeSet(preparedStatement, cert.getNotAfter(), i + 1);&lt;br /&gt;  Hibernate.BIG_INTEGER.nullSafeSet(preparedStatement, cert.getSerialNumber(), i + 2);&lt;br /&gt;&lt;br /&gt;  try {&lt;br /&gt;    preparedStatement.setBytes(i + 3, cert.getEncoded());&lt;br /&gt;  } catch (CertificateEncodingException e) {&lt;br /&gt;    throw new HibernateException(e);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You see here that we, first, set the additional properties for certificate.  How do we know which columns the values are set in?  This is set as part of our Hibernate mapping file, which I will explain later.  From there, we convert the certificate to a byte[] and set that in the database as well.  We're now ready to implement the method for retrieving the certificate from the database.  Our nullSafeGet() implementation looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;&lt;br /&gt;public Object nullSafeGet(ResultSet resultSet, String[] strings, &lt;br /&gt;          SessionImplementor sessionImplementor, Object object)&lt;br /&gt;          throws HibernateException, SQLException {&lt;br /&gt;   X509Certificate cert = null;&lt;br /&gt;   byte[] certData = resultSet.getBytes(strings[3]);&lt;br /&gt;   if (!resultSet.wasNull()) {&lt;br /&gt;      try {&lt;br /&gt;         CertificateFactory cf = CertificateFactory.getInstance("X.509");&lt;br /&gt;         cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certData));&lt;br /&gt;      } catch (CertificateException e) {&lt;br /&gt;         throw new HibernateException(e);&lt;br /&gt;      }&lt;br /&gt;   } &lt;br /&gt;   return cert;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here, you'll se we create a &lt;a href="http://java.sun.com/javase/6/docs/api/java/security/cert/CertificateFactory.html" target="_blank"&gt;CertificateFactory&lt;/a&gt; object to parse our byte[] into an X509Certificate object.  At no point do we concern ourselves with the additional certificate properties we set earlier.  Our only concern is the certificate itself.  At this point, we can store and retrieve an X509Certificate object with no problems.&lt;br /&gt;&lt;br /&gt;We now need the ability to reference properties of an X509Certificate in a Hibernate query to simplify with searching.  The &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#getPropertyNames%28%29"&gt;getPropertyNames()&lt;/a&gt;, &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#getPropertyTypes%28%29"&gt;getPropertyTypes()&lt;/a&gt;, &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#getPropertyValue%28java.lang.Object,%20int%29"&gt;getPropertyValue()&lt;/a&gt;, and &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/v3/api/org/hibernate/usertype/CompositeUserType.html#setPropertyValue%28java.lang.Object,%20int,%20java.lang.Object%29"&gt;setPropertyValue()&lt;/a&gt; methods of CompositeUserType assist with this goal.  The getPropertyNames() method returns the names of the properties we wish to make available when querying for a certificate.  In our case, these values are:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;issueDate&lt;/li&gt;&lt;li&gt;expirationDate&lt;/li&gt;&lt;li&gt;serialNumber&lt;/li&gt;&lt;li&gt;certificateData&lt;/li&gt;&lt;/ul&gt;The getPropertyTypes() method specifies the Hibernate type that corresponds to each of these properties.  So, in our case, the following types would be returned:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Hibernate.DATE&lt;/li&gt;&lt;li&gt;Hibernate.DATE&lt;/li&gt;&lt;li&gt;Hibernate.BIG_INTEGER&lt;/li&gt;&lt;li&gt;Hibernate.BLOB&lt;/li&gt;&lt;/ul&gt;The getPropertyValue() method is responsible for determining which property is being queried and then returning the appropriate value.  In our case, the implementation looks like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;public Object getPropertyValue(Object object, int i) throws HibernateException {&lt;br /&gt;   if (object == null) {&lt;br /&gt;      return null;&lt;br /&gt;   }&lt;br /&gt;   X509Certificate cert = (X509Certificate) object; &lt;br /&gt;   if (i == 0) {&lt;br /&gt;      return cert.getNotBefore();&lt;br /&gt;   } else if (i == 1) {&lt;br /&gt;      return cert.getNotAfter();&lt;br /&gt;   } else if (i == 2) {&lt;br /&gt;      return cert.getSerialNumber();&lt;br /&gt;   } else if (i == 3) {&lt;br /&gt;      try {&lt;br /&gt;         return cert.getEncoded();&lt;br /&gt;      } catch (CertificateEncodingException e) {&lt;br /&gt;         throw new HibernateException(e);&lt;br /&gt;      }&lt;br /&gt;   } else {&lt;br /&gt;      return null;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The final method, setPropertyValue(), does nothing in our implementation.  We, obviously, don't want the certificate properties to be modifiable, so our method implementation simply does nothing and then returns.&lt;br /&gt;&lt;br /&gt;Now that we have implemented the necessary methods, we need to create the Hibernate mapping for our object.  In our CertificateModelImpl.hbm.xml file, we have the following mapping for our certificate property:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;property name="certificate" type="net.odyssi.certserv.dao.hibernate.model.X509CertificateUserType"&amp;gt;&lt;br /&gt;   &amp;lt;column null="true" type="date" name="start_date"&amp;gt;&lt;br /&gt;   &amp;lt;column null="true" type="time" name="expiration_date"&amp;gt;&lt;br /&gt;   &amp;lt;column null="true" type="integer" name="serial_number"&amp;gt;&lt;br /&gt;   &amp;lt;column null="true" type="blob" name="certificate_data"&amp;gt;&lt;br /&gt;&amp;lt;/property&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all there is to it!  Hibernate can now store and retrieve X509Certificate objects with no problem, and queries can reference properties of that certificate.  To see the finished product, take a look at &lt;a href="http://odyssica.cvs.sourceforge.net/odyssica/OdyssiCS/odyssics-dao-hibernate/src/net/odyssi/certserv/dao/hibernate/model/X509CertificateUserType.java?revision=1.5&amp;amp;view=markup" target="_blank"&gt;X509CertificateUserType&lt;/a&gt; in the &lt;a href="http://odyssica.cvs.sourceforge.net/odyssica/OdyssiCS/" target="_blank"&gt;OdyssiCS CVS repository&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-8964723289432135204?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/8964723289432135204/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=8964723289432135204' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/8964723289432135204'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/8964723289432135204'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/04/often-when-working-with-hibernate-as.html' title='Hibernate CompositeUserType and X509Certificate'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-6648716127300170143</id><published>2007-03-30T10:49:00.000-05:00</published><updated>2007-04-26T10:59:08.093-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='databases'/><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='MAC'/><category scheme='http://www.blogger.com/atom/ns#' term='PostgreSQL'/><category scheme='http://www.blogger.com/atom/ns#' term='SELinux'/><title type='text'>Security-Enhanced PostgreSQL</title><content type='html'>Recently, I came across &lt;a href="http://marc.info/?l=selinux&amp;m=117309829931217&amp;amp;w=2"&gt;a posting&lt;/a&gt; announcing the release of a &lt;a href="http://www.nsa.gov/selinux/"&gt;SELinux&lt;/a&gt;-enhanced version of PostgreSQL.  &lt;a href="http://www.kaigai.gr.jp/index.php?sepgsql"&gt;SE-PostgreSQL&lt;/a&gt; makes use of the labeling and &lt;a href="http://en.wikipedia.org/wiki/Mandatory_access_control"&gt;Mandatory Access Control (MAC)&lt;/a&gt; features that SELinux provides.  This is a tremendous addition to enterprise security, particularly in the government and financial fields.&lt;br /&gt;&lt;br /&gt;What SE-PostgreSQL provides is a mechanism for controlling access to information in a database in a very fine-graned manner.  The example provided in &lt;a href="http://marc.info/?l=selinux&amp;m=117309829931217&amp;amp;w=2"&gt;the announcement&lt;/a&gt; demonstrates this using a table containing beverage information.  In this example, the table contains several columns related to different types of beverages (name, price, type of beverage, quantity on hand, etc.).  The table is then altered to require a SE-Linux security context to access specific parts of this data.  For example, one security context is required to access the table at all.  An additional, higher-security context is required to access rows where the beverage is not a soft-drink.  If you look at the output provided in the sample queries, you will see that these rules are applied based on the user's security context.  The amount of customization doesn't end there, however.  You can apply access control based on rows, columns, tables, databases, etc.  While this level of customization may be intimidating to smaller enterprises, larger enterprises will welcome the flexibility it gives them.&lt;br /&gt;&lt;br /&gt;This type of capability has huge ramifications for the use of PostgreSQL, Linux, and open-source in general within the government and financial sectors.  Data of different government classification levels can now reside in the same database.  A unified database can be used in financial institutions with assurance that the information cannot be modified by unprivileged or unauthorized users.  My small amount of SQL experience has largely revolved around open-source databases, such as &lt;a href="http://www.mysql.com/"&gt;MySQL&lt;/a&gt; and &lt;a href="http://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;.  My day job does not require much SQL work, so as a result I only play around with it on my free time.  While MySQL seems to be the most widely used, my personal preference is for PostgreSQL.  True, it lacks some of the features MySQL has, such as clustering.  However, it more than makes up for it in other ways, such as its strict transaction handling abilities.  I've always felt PostgreSQL's security architecture was superior as well, with its ability to use external authentication sources (Kerberos, LDAP, etc.) for users.  The addition of SELinux support to this mix has put PostgreSQL in a class unmatched by any other open-source database (and many closed-source ones as well).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-6648716127300170143?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/6648716127300170143/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=6648716127300170143' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6648716127300170143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6648716127300170143'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/04/security-enhanced-postgresql.html' title='Security-Enhanced PostgreSQL'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-2597289623729911880</id><published>2007-03-15T10:51:00.000-05:00</published><updated>2007-07-24T13:18:10.162-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='centos'/><title type='text'>CentOS 5-beta -- First Impressions</title><content type='html'>Last night, I installed the &lt;a target="_blank" href="http://beta.centos.org/centos/4.92"&gt;beta release of CentOS 5&lt;/a&gt; to take it for a test drive.  For those of you looking for screenshots, there are several screenshots of Red Hat Enterprise Linux (RHEL) 5 &lt;a target="_blank" href="http://www.linuxformat.co.uk/modules.php?op=modload&amp;name=News&amp;amp;file=article&amp;sid=511"&gt;available here&lt;/a&gt;.  Although the artwork is different in CentOS, everything else is the same.  These screenshots should give you a general idea of what the experience is like.  While I haven't had a chance to play with all of the new and enhanced features, I've been quite impressed with what I have seen so far.  I started by installing the beta on my HP Pavilion zt3000 laptop.  My biggest criticism of those who claim that "Linux is ready for the desktop" has been issues with running it on a laptop.  From ACPI support, poor hardware support, and an inability to function the way a laptop should, I have been less than thrilled with several Linux distributions.&lt;br /&gt;&lt;br /&gt;For starters, I was pleasantly surprised with the updates to &lt;a target="_blank" href="http://www.linuxformat.co.uk/images/rhel5/xeninsta-small.jpg"&gt;Anaconda&lt;/a&gt;, the system installer.  The entire install process has been streamlined, requiring far less user intervention, even for more advanced configurations.  One of the things I like most about Windows XP installations is that, for the most part, it is "set it, and forget it".  You answer a few questions about networking and such, and the rest of the install is automated.  CentOS 5 follows this philosophy with the updated installer.&lt;br /&gt;&lt;br /&gt;One thing that was obvious from the first time I booted into CentOS 5, is that it is FAST.  &lt;span style="font-style: italic;"&gt;Really&lt;/span&gt; fast.  In CentOS 4.4, it took approximately 10 seconds from login to GNOME desktop.  With CentOS 5, this time has been reduced to 2 seconds.  While this is a pretty trivial achievement, it goes a long way to improving the usability of the system.  Speaking of usability, CentOS won me over during the boot process.  Why?  Because X Windows, by default, was set to my laptop's optimum resolution:  1680x1050.  In every other Linux distribution I have used, it defaulted to some other non-widescreen resolution, and I was forced to manually change the configuration file.  Not any more.  In addition, when I later plugged the laptop into its docking station to use my external 19" LCD, it immediately changed its resolution to the appropriate 1280x1024.  This is such a long overdue change in system behavior.&lt;br /&gt;&lt;br /&gt;Once I was logged in, I began taking a quick tour of the system.  The new &lt;a href="http://clearlooks.sourceforge.net/" target="_blank"&gt;Clearlooks theme&lt;/a&gt; looks great, and the crispness of screen fonts is incredible.  I've never seen a Linux distribution that displays fonts this clearly.  This release also includes &lt;a href="http://www.go-compiz.org/" target="_blank"&gt;Compiz&lt;/a&gt;, the compositing engine for X Windows.  Compiz provides all the nifty (yet pointless) &lt;a href="http://www.go-compiz.org/index.php?title=Screenshots" target="_blank"&gt;visual effects&lt;/a&gt; for the desktop, similar to what Mac OS X is capable of.  This was one of the things I was most curious about, as I have not had a chance to work with a Compiz-enabled system.  My impressions?  Not bad.  The wobbly windows are definitely a neat trick, but I don't like that it takes a split second for the windows to snap into place and regain their focus.  One thing I didn't get a chance to investigate was the load on the CPU with Compiz enabled.  Compiz is OpenGL accelerated, and I wanted to make sure that the extra load was forced off to the graphics card instead of the CPU.  I'll take a look at this tonight.&lt;br /&gt;&lt;br /&gt;This evening, I plan to investigate some of the server and developer-oriented features of CentOS 5.  Since I spend most of my time in Linux doing Java development work, I'm curious as to the performance of Eclipse, Intellij Idea, MySQL/PostgreSQL, and Tomcat.  If the server performance has increased in the same way as desktop performance, I think I will find myself spending a lot more time in Linux, and a lot less time using Windows XP.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-2597289623729911880?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/2597289623729911880/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=2597289623729911880' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/2597289623729911880'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/2597289623729911880'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/04/centos-5-beta-first-impressions.html' title='CentOS 5-beta -- First Impressions'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-964571996827384630</id><published>2007-03-14T10:52:00.000-05:00</published><updated>2007-04-26T10:59:50.846-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='disney'/><category scheme='http://www.blogger.com/atom/ns#' term='smartcards'/><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='pki'/><category scheme='http://www.blogger.com/atom/ns#' term='disneyworld'/><category scheme='http://www.blogger.com/atom/ns#' term='disneyland'/><category scheme='http://www.blogger.com/atom/ns#' term='kerberos'/><category scheme='http://www.blogger.com/atom/ns#' term='biometrics'/><title type='text'>Smartcards at Disneyland?</title><content type='html'>I stumbled upon &lt;a target="_blank" href="http://jacksonshaw.blogspot.com/2007/01/it-of-pki-at-eurodisney.html"&gt;this blog entry&lt;/a&gt;, showing a picture of the new hotel room keys in use at &lt;a target="_blank" href="http://www.disneylandparis.com/"&gt;Disneyland Paris&lt;/a&gt;.  You're not mistaken, they look like smartcards.  It seems every year in recent memory has been called, "The Year of the Smartcard" or, "The Year of PKI".  I can certainly imagine some innovative uses for these smartcards.  Since they can be used for payments at all of the restaurants and stores within the park, what about a &lt;a target="_blank" href="http://www-128.ibm.com/developerworks/wireless/library/wi-satsa/"&gt;Kerberos-enabled payment system&lt;/a&gt;?  Or, load the cards with credentials enabling a user to download photos from their vacation when they get home (so long as they have a reader attached to their PC).  While I'm not sure what the backend technology behind the cards does, it's not the first time that Disney has used cutting-edge security technologies at their parks and resorts.  In 2005, Disney implemented &lt;a target="_blank" href="http://thedisneyblog.typepad.com/tdb/2005/01/biometric_scann.html"&gt;biometric finger scans&lt;/a&gt; for all guests at &lt;a target="_blank" href="http://www.disneyworld.com/"&gt;Walt Disney World&lt;/a&gt;.  However, they used similar technology for annual passholders even before that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-964571996827384630?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/964571996827384630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=964571996827384630' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/964571996827384630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/964571996827384630'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/04/smartcards-at-disneyland.html' title='Smartcards at Disneyland?'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-6880679017761554041</id><published>2006-10-26T11:00:00.000-05:00</published><updated>2007-04-26T11:00:31.781-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='redhat'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='centos'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>Oracle Linux Download</title><content type='html'>Yesterday, &lt;a href="http://www.oracle.com/technologies/linux/index.html?pageregion=ocom_hp_a_main_1_Linux_102506" target="_blank"&gt;Oracle announced&lt;/a&gt; that they will be supporting &lt;a href="http://www.redhat.com/" target="_blank"&gt;Red Hat Linux&lt;/a&gt;, as well as releasing their own clone of the OS, just like &lt;a href="http://www.centos.org/" target="_blank"&gt;CentOS&lt;/a&gt; and others have done.  There had been speculation for weeks that Oracle was going to get into the Linux business, either by creating their own distribution, or purchasing a Linux company.  While there have been some &lt;a href="http://www.linux-watch.com/news/NS7753065928.html" target="_blank"&gt;negative reactions&lt;/a&gt; to this announcement, I view it as a great opportunity, and a means of forcing more innovation into the market.  The download itself is &lt;a href="http://edelivery.oracle.com/linux" target="_blank"&gt;available here&lt;/a&gt;.  You simply need to fill out a form to gain access to the files. &lt;br /&gt;&lt;br /&gt;Those of you who have read &lt;a href="http://jroller.com/page/$entry.website.user.userName/?anchor=ifolder_client_on_centos_red" target="_blank"&gt;previous entries&lt;/a&gt; in my blog may realize that I'm a fan of the Red Hat clone, &lt;a href="http://www.centos.org/" target="_blank"&gt;CentOS&lt;/a&gt;.  I've used it on laptops, desktops, and servers, and have had very few problems.  The stability it provides is far better than I've experienced with Fedora, SuSE, or other distributions.  However, if I wanted to install it in a corporate environment, my support options would be limited.  Red Hat charges a great deal for support, even rivaling Microsoft in terms of pricing.  With Oracle now offering support, the cost of deployment is greatly reduced.  Some have argued that this is a low-blow by Oracle, as they are simply repackaging Red Hat's work and distributing it as their own.  That is exactly what they're doing.  However, that is the risk with open source software.  Just because Red Hat is "loved" by the open source community doesn't mean it should be treated any differently.&lt;br /&gt;&lt;br /&gt;For the past several years, open source advocates have argued that it is a superior business model.  The thought that releasing a product as free, open-source software and charging support will give you a lock on a given market is laughable, at best.  A company like Red Hat cannot compete with Oracle in terms of size and resources.  As a result, they are vulnerable to having their prices undercut by a company that is able to deliver better support for less money.  &lt;a href="http://www.mysql.com/" target="_blank"&gt;MySQL&lt;/a&gt;, &lt;a href="http://www.alfresco.com/" target="_blank"&gt;Alfresco&lt;/a&gt;, and all other open-source companies are vulnerable to this as well.  The key is not just providing support, but providing some additional value that your competitors are incapable of providing.  And, currently, Red Hat is not taking these measures.&lt;br /&gt;&lt;br /&gt;Red Hat provides no value-added resources on top of their existing OS distribution.  For those individuals who don't need support, there is no incentive to purchase a commercial distribution of the product.  They can simply download CentOS or one of the other clones that are available.  Now that Oracle is offering cheaper support, there is even less of an incentive for a company to purchase support from Red Hat.  If, however, Red Hat began to provide offerings that only they were capable of, they would regain their advantage in this area.  Oracle has learned this lesson the hard way.  After growing tired of being "just a database company", they began developing and acquiring applications that were designed to run on top of their database as a complete stack.  By providing an all-in-one solution, companies were able to get all of their products in one place, from one vendor, with one support contract.  Red Hat does have some closed-source applications in their product library, such as their &lt;a target="_blank" href="http://www.redhat.com/software/rha/certificate/"&gt;Certificate System&lt;/a&gt; which was purchased from Netscape some time ago.  However, this is such a niche product that it is not important to the majority of Red Hat's customers.  Providing other applications, however, may provide them with an advantage.  For example, an identity management system similar to Active Directory, using their &lt;a target="_blank" href="http://directory.fedora.redhat.com/"&gt;LDAP directory server&lt;/a&gt; and Kerberos for single sign-on.  Or, tightly integrated management applications for administering Apache, LDAP, MySQL/PostgreSQL, and other system services.&lt;br /&gt;&lt;br /&gt;I am not surprised by Red Hat's sudden drop in share price following this announcement.  For existing Oracle customers, there is now a very good reason to drop support contracts currently offered by Red Hat.  For those not running Oracle on Linux, they now have a very good reason to think about it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-6880679017761554041?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/6880679017761554041/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=6880679017761554041' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6880679017761554041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6880679017761554041'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2006/10/oracle-linux-download.html' title='Oracle Linux Download'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-1934439750360603242</id><published>2006-10-09T11:01:00.000-05:00</published><updated>2007-04-26T11:01:55.189-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='replication'/><category scheme='http://www.blogger.com/atom/ns#' term='databses'/><title type='text'>MySQL Master-Master Replication</title><content type='html'>I came across a great article detailing how to accomplish &lt;a target="_blank" href="http://www.howtoforge.com/mysql_master_master_replication"&gt;Master-Master Replication on MySQL 5&lt;/a&gt;.  While there are other technologies that could be used to create high availability MySQL instances, such as MySQL Cluster, these often provide a less-than-ideal solution because of the additional requirements they impose (e.g. the use of in-memory databases or a different storage engine).  The solution provided in the article is to use circular replication.  That is, if we have 2 databases, A and B:  A is a master to B, while B is a master to A.  Because each node acts as both a master and a slave, changes to either database get propigated to the other.  Take a look at the article to find out more.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-1934439750360603242?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/1934439750360603242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=1934439750360603242' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1934439750360603242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1934439750360603242'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/04/mysql-master-master-replication.html' title='MySQL Master-Master Replication'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-1411349353736122086</id><published>2006-10-06T11:02:00.000-05:00</published><updated>2007-04-26T11:02:29.432-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='redbull'/><category scheme='http://www.blogger.com/atom/ns#' term='cubicle'/><category scheme='http://www.blogger.com/atom/ns#' term='pixar'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><category scheme='http://www.blogger.com/atom/ns#' term='offices'/><category scheme='http://www.blogger.com/atom/ns#' term='google'/><category scheme='http://www.blogger.com/atom/ns#' term='workspaces'/><title type='text'>Great Workspaces</title><content type='html'>I came across a great article depicting some of the &lt;a target="_blank" href="http://positivesharing.com/2006/10/10-seeeeeriously-cool-workplaces"&gt;best workspaces in companies around the world&lt;/a&gt;.  &lt;a target="_blank" href="http://www.pixar.com/"&gt;Pixar&lt;/a&gt;, &lt;a target="_blank" href="http://www.google.com/"&gt;Google&lt;/a&gt;, &lt;a target="_blank" href="http://www.redbull.com/"&gt;Red Bull&lt;/a&gt;, etc.  The pictures show just how creative these offices can be, and show that this creativity can lead to creativity in their employees.  As someone who has worked in a private office, cubicle, and now a shared workspace, I can certainly understand how &lt;a target="_blank" href="http://joelonsoftware.com/articles/BionicOffice.html"&gt;offices like this&lt;/a&gt; can make the daily 9-5 grind a much more pleasant experience.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-1411349353736122086?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/1411349353736122086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=1411349353736122086' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1411349353736122086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/1411349353736122086'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2006/10/great-workspaces.html' title='Great Workspaces'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-3081820043365621818</id><published>2006-09-21T10:56:00.005-05:00</published><updated>2008-09-23T10:36:23.720-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='databases'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='relations'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><title type='text'>Complex Many-to-Many Relations in Hibernate</title><content type='html'>SQL makes querying structured data very easily, especially when the data in tables is related.  Using simple joins, it's possible to query for data in two separate tables that shares a common element.  For example, imagine if we have a table &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;users_tbl&lt;/span&gt;&lt;/span&gt; that contains our user account information, and a table&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt; roles_tbl&lt;/span&gt;&lt;/span&gt; that contains our roles information.  Additionally, we make use of a join table &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;users_roles_tbl&lt;/span&gt;&lt;/span&gt; that allows us to have many-to-many relationships between our users and roles.  To create our tables, we could use the following commands (on &lt;a target="_blank" href="http://www.mysql.org/"&gt;MySQL&lt;/a&gt; 5):&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;create table users_tbl (&lt;br /&gt;   id INT AUTO_INCREMENT NOT NULL,&lt;br /&gt;   username VARCHAR(32) NOT NULL,&lt;br /&gt;   PRIMARY KEY (id)&lt;br /&gt;) TYPE=INNODB;&lt;br /&gt;&lt;br /&gt;create table roles_tbl (&lt;br /&gt;     id INT AUTO_INCREMENT NOT NULL,&lt;br /&gt;     rolename VARCHAR(32) NOT NULL,&lt;br /&gt;     PRIMARY KEY (id)&lt;br /&gt;) TYPE=INNODB;&lt;br /&gt;&lt;br /&gt;create table users_roles_tbl (&lt;br /&gt;    user_id INT NOT NULL,&lt;br /&gt;    role_id INT NOT NULL,&lt;br /&gt;    FOREIGN KEY (user_id) REFERENCES users_tbl (id),&lt;br /&gt;    FOREIGN KEY (role_id) REFERENCES roles_tbl (id),&lt;br /&gt;    PRIMARY KEY (user_id, role_id)&lt;br /&gt;) TYPE=INNODB;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Suppose we have created 3 users:  &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;user_a&lt;/span&gt;&lt;/span&gt;, &lt;span style=";font-family:Courier New,Courier,mono;font-size:100%;"  &gt;user_b&lt;/span&gt;, and &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;user_c&lt;/span&gt;&lt;/span&gt;.  Additionally, we have created 2 roles:  &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_a&lt;/span&gt;&lt;/span&gt;, and &lt;span style=";font-family:Courier New,Courier,mono;font-size:100%;"  &gt;role_b&lt;/span&gt;.  Our first user, &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;user_a&lt;/span&gt;&lt;/span&gt;, is a member of &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_a&lt;/span&gt;&lt;/span&gt;.  Our second user, &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;user_b&lt;/span&gt;&lt;/span&gt;, is a member of &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_b&lt;/span&gt;&lt;/span&gt;.  And, our third user, &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;user_c&lt;/span&gt;&lt;/span&gt;, is a member of both &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_a&lt;/span&gt;&lt;/span&gt; and &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_b&lt;/span&gt;&lt;/span&gt;.  We can see these users and roles displayed with the following SQL SELECT statement:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;SELECT DISTINCT u.username, r.rolename FROM users_tbl u&lt;br /&gt;   INNER JOIN users_roles_tbl j ON u.id = j.user_id&lt;br /&gt;   INNER JOIN roles_tbl r ON r.id = j.role_id;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There are many instances in which we would like to find members of specific role, such as for access control.  If we wanted to find all members of &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_a&lt;/span&gt;&lt;/span&gt;, a simple modification to the previous SELECT statement will do the trick:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;SELECT DISTINCT u.username, r.rolename FROM users_tbl u&lt;br /&gt;   INNER JOIN users_roles_tbl j ON u.id = j.user_id&lt;br /&gt;   INNER JOIN roles_tbl r ON r.id = j.role_id WHERE r.rolename = 'role_a';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this example, &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;user_a&lt;/span&gt;&lt;/span&gt; and &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;user_c&lt;/span&gt;&lt;/span&gt; are returned, since they are the only members of &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_a&lt;/span&gt;&lt;/span&gt;.  But what happens when we want to perform a more complex query?  For example, what if we only wanted to return users who are members of &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_a&lt;/span&gt;&lt;/span&gt; AND &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_b&lt;/span&gt;&lt;/span&gt;?  This is where things become a bit more complicated.  The IN statement allows us to perform a logical ORing of values.  This is what we would use if wanted to find users who are members of &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_a&lt;/span&gt;&lt;/span&gt; OR &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_b&lt;/span&gt;&lt;/span&gt;.  But what if we want users who are members of BOTH roles?&lt;br /&gt;&lt;br /&gt;In this case, our SQL SELECT statement becomes a bit more complicated.  Our SELECT statement would now look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;SELECT DISTINCT u.username FROM users_tbl u&lt;br /&gt;   INNER JOIN( roles_tbl r INNER JOIN users_roles_tbl j&lt;br /&gt;   ON ( ( r.id = j.role_id ) AND ( ( r.rolename = 'role_a' )&lt;br /&gt;   OR ( r.rolename = 'role_b' ) ) )) ON ( u.id = j.user_id )&lt;br /&gt;   GROUP BY u.id HAVING COUNT( j.user_id ) = 2;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, this SELECT statement gets very complex, very quickly.  However, it accomplishes exactly what we like.  Running this statement provides us with only &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;user_c&lt;/span&gt;&lt;/span&gt;, the only user who is a member of both &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_a&lt;/span&gt;&lt;/span&gt; and &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_b&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Our question now becomes, how do we accomplish the same thing using &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt;?  For example, we have our core domain model objects, &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;UserModel&lt;/span&gt;&lt;/span&gt; and &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;RoleModel&lt;/span&gt;&lt;/span&gt; which have the appropriate &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; mappings configured.  One way we could accomplish our goal is to create a many-to-many relation in &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; for our &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;UserModel&lt;/span&gt;&lt;/span&gt; object that contains all the roles a user is a member of.  We could then query on &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_a&lt;/span&gt;&lt;/span&gt;, query on &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;role_b&lt;/span&gt;&lt;/span&gt;, and then determine the intersection of those two Collections that are returned.  However, this is a very expensive and time-consuming operation.&lt;br /&gt;&lt;br /&gt;There is a much better way to accomplish our goal.  We can create an additional domain model object that represents the data stored in our join table, &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;users_roles_tbl&lt;/span&gt;&lt;/span&gt;.  This object can be called &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;UserRoleRelation&lt;/span&gt;&lt;/span&gt;.  For these examples, I will be using &lt;a target="_blank" href="http://xdoclet.sf.net/"&gt;XDoclet&lt;/a&gt; to generate our &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; mapping classes.  Our &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;UserModel&lt;/span&gt;&lt;/span&gt; class would look like the following:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;/**&lt;br /&gt; * @hibernate.class table="users_tbl" lazy="true"&lt;br /&gt; */&lt;br /&gt;public class UserModel {&lt;br /&gt;&lt;br /&gt;    private Serializable identifier = null;&lt;br /&gt;    private String username = null;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * @hibernate.id column="id"&lt;br /&gt;    *     generator-class="native"&lt;br /&gt;    *     type="java.lang.Integer"&lt;br /&gt;     */&lt;br /&gt;    public Serializable getIdentifier() {&lt;br /&gt;        return identifier;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void setIdentifier(Serializable identifier) {&lt;br /&gt;        this.identifier = identifier;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * @hibernate.property column="username" not-null="true"&lt;br /&gt;     */&lt;br /&gt;    public String getUsername() {&lt;br /&gt;       return username;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void setUsername(String username) {&lt;br /&gt;        this.username = username;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Our &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;RoleModel&lt;/span&gt;&lt;/span&gt; object would be configured similarly, but with the &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; table as &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;roles_tbl&lt;/span&gt;&lt;/span&gt;.  As for our &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;UserRoleRelation&lt;/span&gt;&lt;/span&gt; object, it would be defined as:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;/**&lt;br /&gt; * @hibernate.class table="users_roles_tbl" lazy="true"&lt;br /&gt; */&lt;br /&gt;public class UserRoleRelation {&lt;br /&gt;    &lt;br /&gt;    private UserRoleRelationID   identifier = null;&lt;br /&gt;    private UserModel user = null;&lt;br /&gt;    private RoleModel role = null;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * @hibernate.id unsaved-value="any"&lt;br /&gt;     */&lt;br /&gt;    public UserRoleRelationID getIdentifier() {&lt;br /&gt;        return identifier;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void setIdentifier(UserRoleRelationID identifier) {&lt;br /&gt;        this.identifier = identifier;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * @hibernate.many-to-one column="user_id"&lt;br /&gt;     *            class="UserModel"&lt;br /&gt;     *            not-null="true" cascade="all"&lt;br /&gt;     *            insert="false"&lt;br /&gt;     *            update="false"&lt;br /&gt;     */&lt;br /&gt;    public UserModel getUser() {&lt;br /&gt;        ...&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;      * @hibernate.many-to-one column="role_id"&lt;br /&gt;      *            class="RoleModel"&lt;br /&gt;      *            not-null="true" cascade="all"&lt;br /&gt;      *            insert="false"&lt;br /&gt;      *            update="false"&lt;br /&gt;      */&lt;br /&gt;     public RoleModel getRole() {&lt;br /&gt;         ...&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     ...&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You will notice the user of a class called &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;UserRoleRelationID&lt;/span&gt;&lt;/span&gt; as our &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; identifier.  This is due to the fact that &lt;a target="_blank" href="http://xdoclet.sf.net/"&gt;XDoclet&lt;/a&gt; does not currently support composite primary keys.  However, by defining our &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;UserRoleRelationID&lt;/span&gt;&lt;/span&gt; class in the following way, we are able to get around this limitation and use our composite primary key:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;public class UserRoleRelationID implements Serializable {&lt;br /&gt;&lt;br /&gt;    private Serializable userID = null;&lt;br /&gt;    private Serializable roleID = null;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * @hibernate.property column="user_id"&lt;br /&gt;     *    type="java.lang.Integer" not-null="true"&lt;br /&gt;     */&lt;br /&gt;    public Serializable getUserID() {&lt;br /&gt;        return userID;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;      * @hibernate.property column="role_id"&lt;br /&gt;      *    type="java.lang.Integer" not-null="true"&lt;br /&gt;      */&lt;br /&gt;     public Serializable getRoleID() {&lt;br /&gt;         return roleID;&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;    public int hashCode() {&lt;br /&gt;        ...&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean equals(Object o) {&lt;br /&gt;        ...&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now that our &lt;a href="http://www.hibernate.org/" target="_self"&gt;Hibernate&lt;/a&gt; objects are defined, we need to create a &lt;a href="http://www.hibernate.org/" target="_blank"&gt;Hibernate&lt;/a&gt; &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/reference/en/html/queryhql.html"&gt;Query&lt;/a&gt; that accomplishes the same thing as our SELECT statement does.  However, by working with &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; and utilizing our &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;UserRoleRelation&lt;/span&gt;&lt;/span&gt; class, you will find the complexity of our query is greatly reduced.  Our &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/reference/en/html/queryhql.html"&gt;Query&lt;/a&gt; can be defined as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;SELECT DISTINCT j.user AS p FROM UserRoleRelation j&lt;br /&gt;   INNER JOIN j.user AS urj&lt;br /&gt;   WHERE j.role.rolename IN (:roleList)&lt;br /&gt;   GROUP BY j.user HAVING COUNT (j.role) = :roleCount&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To execute this query, our Java command would look something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;/*&lt;br /&gt;  * The query we defined above&lt;br /&gt;  */&lt;br /&gt; String queryString = ...&lt;br /&gt;&lt;br /&gt;/*&lt;br /&gt; * Declare a List containning the names of the roles we&lt;br /&gt; * wish to match on&lt;br /&gt; */&lt;br /&gt;ArrayList roleList = new ArrayList();&lt;br /&gt;roleList.add("role_a");&lt;br /&gt;roleList.add("role_b");&lt;br /&gt;&lt;br /&gt;Session s = ....&lt;br /&gt;Query q = s.createQuery(queryString);&lt;br /&gt;q.setParameterList("roleList", roleList);&lt;br /&gt;q.setInteger("roleCount", roleList.size());&lt;br /&gt;&lt;br /&gt;List users = q.list();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You will see that the results returned by our &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; &lt;a target="_blank" href="http://www.hibernate.org/hib_docs/reference/en/html/queryhql.html"&gt;Query&lt;/a&gt; are exactly the same as those returned by our SQL SELECT statement, but with a greatly reduced amount of complexity.&lt;br /&gt;&lt;br /&gt;This type of scenario is very common when dealing with many-to-many relations.  For example, a site that maintains user profiles and keywords describing those profiles.  We could modify our code slightly to allow a user to search for profiles that contain the keywords "jazz" and "hamburgers".  Another example would be an online shopping site.  It's very easy to search for the keyword "dogs", or search for the keyword "books".  Our approach now allows us to search for books about dogs.  Additionally, we can modify our approach to allow for less refined matches.  For example, modifying the last line in our query to read:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;GROUP BY j.user HAVING COUNT (j.role) &gt;= :roleCount&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;will allow us to match on a variable amount of roles. If we have a list of 25 roles, we could search for those users that are members of 10 or more by changing the &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;roleCount&lt;/span&gt;&lt;/span&gt; variable to 10 instead of &lt;span style="font-size:100%;"&gt;&lt;span style="font-family:Courier New,Courier,mono;"&gt;roleList.size()&lt;/span&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;As you can see, using &lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; in our Java application makes performing these complex queries much easier than coding in raw JDBC.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-3081820043365621818?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/3081820043365621818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=3081820043365621818' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/3081820043365621818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/3081820043365621818'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2007/04/complex-many-to-many-relations-in.html' title='Complex Many-to-Many Relations in Hibernate'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-142459400550998444</id><published>2006-08-09T11:02:00.000-05:00</published><updated>2007-04-26T11:03:10.806-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Junit'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><title type='text'>JUnit Tests for Spring and Hibernate</title><content type='html'>There are two big things that I love about the &lt;a target="_blank" href="http://www.springframework.org/"&gt;Spring Framework&lt;/a&gt;:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It has excellent Hibernate support through the use of its &lt;a target="_blank" href="http://www.springframework.org/docs/api/org/springframework/orm/hibernate3/HibernateTemplate.html"&gt;HibernateTemplate&lt;/a&gt; class&lt;/li&gt;&lt;li&gt;It makes writing JUnit tests much simpler, yielding better code&lt;/li&gt;&lt;/ol&gt;However, up until now I hadn't been able to find a good way to combine these two beneftis and JUnit test Hibernate-based code that I had written using Spring as my foundataion.  (Mostly because I'd never really bothered putting any effort into figuring out the best way to do it)  Up until now, I had my JUnit tests create a Spring &lt;a target="_blank" href="http://www.springframework.org/docs/api/org/springframework/context/ApplicationContext.html"&gt;ApplicationContext&lt;/a&gt;, and simply obtained my Hibernate DAO and ran my tests.  While this did accomplish my goal of determining if my Hibernate-based code was working properly, it was poor design, and was terribly inefficient.&lt;br /&gt;&lt;br /&gt;Recently, however, I came across a great article explaining how to &lt;a target="_blank" href="http://today.java.net/pub/a/today/2005/10/11/testing-hibernate-mapping.html"&gt;unit test Hibernate mapping configurations&lt;/a&gt;.  The article goes into how to create your session factory, and wire up your DAO with a new HibernateTemplate.  It even gives tips on how to setup an in-memory &lt;a target="_blank" href="http://www.hsqldb.org/"&gt;HSQLDB&lt;/a&gt; database for testing.  This reduces the overhead needed to perform your tests, as you don't need a full-fledged SQL database to run your tests against.&lt;br /&gt;&lt;br /&gt;If you write any Hibernate-based code and are looking for a means of testing your Hibernate configuration, this is article is a great referrence.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-142459400550998444?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/142459400550998444/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=142459400550998444' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/142459400550998444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/142459400550998444'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2006/08/junit-tests-for-spring-and-hibernate.html' title='JUnit Tests for Spring and Hibernate'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2823669911800907659.post-6132033870733401105</id><published>2006-07-28T11:03:00.000-05:00</published><updated>2007-04-26T11:03:53.658-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Security'/><category scheme='http://www.blogger.com/atom/ns#' term='pki'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='release'/><category scheme='http://www.blogger.com/atom/ns#' term='odyssi'/><title type='text'>OdyssiCS v0.1 Released</title><content type='html'>At long last there is something coming out of my Odyssi CS project.  Version 0.1 has just been posted on the &lt;a href="http://www.sourceforge.net/projects/odyssica" target="_blank"&gt;project page&lt;/a&gt;.  It is important to note, however, that this release should be considered &lt;span style="font-weight: bold; font-style: italic;"&gt;alpha quality at best&lt;/span&gt;.  A great deal of functionality has been left out of this release, and virtually all of the installation and configuration must be done manually.  The main reason behind this release was just to get something out of the door.  Some initial screenshots can be found &lt;a target="_blank" href="https://sourceforge.net/project/screenshots.php?group_id=145808"&gt;here&lt;/a&gt;.  The interface is pretty rough, and I've nearly completed a much more user-friendly interface that will be included in the next version.  Version 0.2 will represent a pretty large redesign of the application, and should have much more functionality included.&lt;br /&gt;&lt;br /&gt;At this point, the following is included in version 0.1:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Submit client certificate requests via a web browser (IE or Mozilla-based browsers)&lt;/li&gt;&lt;li&gt;Download an approved certificate into the client browser&lt;/li&gt;&lt;li&gt;Registration Authorities may approve or reject pending certificate requests&lt;/li&gt;&lt;/ul&gt;As you can see, the functionality is pretty minimal.  In fact, the following are things that are not included in this release and are important to make note of:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Registration Authorities authenticate to the CA using a username/password combination managed by Tomcat (or whatever application server you have deployed to) -- NOT client certificates&lt;/li&gt;&lt;li&gt;Registration Authorities must have the "registrationAuthority" role in order to access the RA administration pages&lt;/li&gt;&lt;li&gt;No certificate extension support is included for any certificate generated by the CA, although this will be implemented very shortly.  (There are a couple of bugs that need to be worked out)&lt;/li&gt;&lt;li&gt;No certificate revocation or CRL support is included&lt;/li&gt;&lt;li&gt;You may not currently specify which Java cryptography provider you wish to use&lt;/li&gt;&lt;li&gt;All installation and configuration must be done manually&lt;/li&gt;&lt;/ul&gt;The following libraries were used for this version of the project:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a target="_blank" href="http://struts.apache.org/"&gt;Struts&lt;/a&gt; 1.2.x for the web interface&lt;/li&gt;&lt;li&gt;&lt;a target="_self" href="http://www.springframework.org/"&gt;Spring&lt;/a&gt; for dependancy injection and IoC&lt;/li&gt;&lt;li&gt;&lt;a target="_blank" href="http://www.hibernate.org/"&gt;Hibernate&lt;/a&gt; for data persistence&lt;/li&gt;&lt;li&gt;&lt;a target="_blank" href="http://xmlbeans.apache.org/"&gt;XMLBeans&lt;/a&gt; for XML-&gt;Java mappings&lt;/li&gt;&lt;/ul&gt;Version 0.2 will maintain its use of Spring and Hibernate, but Struts may be replaced by another web application framework.  In addition, I plan to use more of the design patterns found in &lt;a target="_blank" href="http://www.corej2eepatterns.com/"&gt;Core J2EE Patterns&lt;/a&gt; and &lt;a target="_blank" href="http://www.coresecuritypatterns.com/"&gt;Core Security Patterns&lt;/a&gt; to create a more robust and secure application.  I'll also be writing some articles about these patterns, outlining how they are being used in the application.&lt;br /&gt;&lt;br /&gt;I have also released version 0.1 of the Odyssi ASN.1 library.  This library provides the ASN.1 encoding for certificate and certificate extensions.  It is extracted from the BouncyCastle JCE library, and has been modified to work with any Java crypto provider.  A pre-build JAR is already included in the Odyssi CS distribution, so you will not need to build it separately.  However, it is provided as a separate download for you, if you wish.&lt;br /&gt;&lt;br /&gt;For those of you who are brave enough, Version 0.1 can be &lt;a target="_blank" href="https://sourceforge.net/project/showfiles.php?group_id=145808"&gt;downloaded here&lt;/a&gt;.  Make sure you read the INSTALL.txt and README.txt files for information on how to configure and install the web application.  And, as always, please make use of the &lt;a href="https://sourceforge.net/forum/?group_id=145808" target="_blank"&gt;forums&lt;/a&gt; for any questions, comments, or criticisms you may have. Good luck!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2823669911800907659-6132033870733401105?l=odyssi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://odyssi.blogspot.com/feeds/6132033870733401105/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2823669911800907659&amp;postID=6132033870733401105' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6132033870733401105'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2823669911800907659/posts/default/6132033870733401105'/><link rel='alternate' type='text/html' href='http://odyssi.blogspot.com/2006/07/odyssics-v01-released.html' title='OdyssiCS v0.1 Released'/><author><name>WhoAmI?</name><uri>http://www.blogger.com/profile/15300881906495920716</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
