Friday, October 24, 2008

It's a girl!


Samantha Jane was born Oct 24 at 3:27 PM, 6lbs. 6oz., 19" long. Everyone is healthy and doing well.

More photos.

Monday, October 13, 2008

Command history in Windows



I was brushing some dust off my keyboard and accidentally hit F7 while in a Windows command prompt window. That brings up list of your command history. Cool. You learn something new every day.

Automatic Timestamping for Entity Framework

I was looking for a way to get ADO.NET Entity Framework to do magic timestamping à la Ruby on Rails ActiveRecord. I couldn't find any built in mechanism to accomplish this. Here's what I came up with:


public partial class MyEntityContext
{
partial void OnContextCreated()
{
SavingChanges += OnSavingChanges;
}

private void OnSavingChanges(object sender, EventArgs e)
{
var now = DateTime.Now;
foreach (var entry in ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Modified))
{
if (!entry.IsRelationship)
{
var timestampedEntity = entry.Entity as ITimestampedEntity;
if (timestampedEntity != null)
{
timestampedEntity.DateModified = now;
var entity = entry.Entity as EntityObject;
if (entity != null && entity.EntityState == EntityState.Added)
{
timestampedEntity.DateCreated = now;
}
}
}
}
}
}

internal interface ITimestampedEntity
{
DateTime DateCreated { get; set; }
DateTime DateModified { get; set; }
}


Now for every entity that implements the ITimestampedEntity interface, the DateCreated and DateModified fields will automagically be set/updated.

The above code is covered under the Logos Code Blog License.

Saturday, September 20, 2008

Apparently I Look Like A Stoner

Yesterday I was walking out of the office after work. There were these three kids about seventeen years old walking down the sidewalk. They stared at me for about five seconds, sizing me up. Then one asked, "Hey, do you know where we can get some chronic?" I guess I look like a guy who would know where to score some drugs. It must be the 'fro.

Posted with LifeCast

Monday, September 15, 2008

RefTagger Bookmarklet

Here's a test of a RefTagger bookmarklet. Drag this link to your bookmark bar: Run RefTagger.

Browse to any page with a Bible reference, e.g. Grace Baptist Fellowship, and click the Run RefTagger bookmark. RefTagger will run on the page you are on and highlight the Bible references.

Monday, August 18, 2008

Prov-a-Day 2.0

Back when I was in junior high, my dad got something called Visual Basic at work, which made it very easy to write Windows programs. I started playing around with it quite a bit. When Visual Basic 3.0 came out, it added an embedded database, so I started playing with that. One of the first programs I wrote with VB3 was Prov-a-Day, which just displayed a random verse that I had typed in from the book of Proverbs. That was 1993. I am please to announce, 15 years later, Prov-a-Day 2.0. This time around, it's a widget that you can put on your blog or other web page and it pulls random verses from Proverbs from Bible.Logos.com, the project I've been working on at work.

BTW, I love my new job.

Thursday, July 3, 2008

Big Changes

So a lot of stuff has happened in the last few days. This weekend Davey started walking. He had been able to take a few steps before, but now he's making fully controlled two-footed journeys across the room. A day or two after he started walking, he started crawling in the normal style instead of what I can best describe as the breast-stroke across the floor.

On Monday I interviewed with Logos Research Systems, which is the number one Bible software company out there. I got an offer that night and immediately accepted. I'm excited to start work in two weeks. During my interview, I asked Bob, the company owner and president, about his vision for the company. To summarize, he wants the Bible and supporting texts to be ubiquitous so that nobody has an excuse for not knowing the truth. That's a vision I can get behind and be excited about. I'll be working on the content delivery team, which will be working on bringing data to the Internet.

Also on Monday, Tamara had an ultrasound. We're having a girl. It's a bit scary for me; I'm not sure why, but I'm afraid of little girls. Tam and I and most everyone else who weighed in thought we were going to have another little boy. I was excited to have a couple little guys growing up together, but this will be fun too, I'm sure.

Tamara wants to buy a different house too, but I don't think with a new kid and a job change, a new house is a bit much. The extra space, both inside and outside, would be nice, but I'm content where we are. Tam is too, but we can't entertain a very large group in our current house. Oh well, maybe next year.

Friday, June 27, 2008

RefTagger

I've added Logos RefTagger to this blog. I have to say from a user standpoint it's pretty cool. It auto-links Bible references like John 3:16 or Gen 1:1-10. From a technical standpoint, it has some of the most impressive regular expressions I've ever seen. Once you drill into the regular expressions, it's not super hard to figure out what's going on, but at first glance they look like monsters. Kudos to whoever had the patience to work those out.

One thing I noticed is that the CSS for the tooltip has a rule:
#tooltipBody p {
display:inline;
}

which causes multi-lined verses to run together without a space between words, e.g. Prov. 1:2 or Isaiah 54:17. The "display:inline" should be removed or there should be a space between the <p> tags for each line in the verse.

Sunday, June 22, 2008

Open Source Software

Here's my philosophy on open source software: all software has bugs; at least with open source, you can debug it yourself instead of waiting for the author to do it.

Of course there are other benefits to releasing your software as open source and using open source, but that's a pretty big one for me. The early web service stacks had some real problems. I could work through the JBoss and Apache Axis bugs by fixing them myself, but for closed platforms, I was at the mercy of Microsoft, BEA and friends.

Saturday, June 21, 2008

Events and Threads

Ed Ball over at Logos Bible Software has written an interested series of articles on events and threads. In part 3, Ed discusses locking around adding and removing delegates and firing the event. There is somewhat of a parallel in the Java world.

Java doesn't have delegates but by convention uses event listeners and the observer pattern. The observable keeps a list of listeners and iterates through them when firing an event. The same issue of locking the list comes up, but if consequences of not locking can be even worse. Most list implementations in Java do not handle the case where a list is changed while it is being iterated over. The iterator is invalidated and will throw a ConcurrentModificationException when hasNext() or next() is called.

One solution is to lock the list while adding and removing listeners and firing the event. This has the same problems that Ed points out in C# that it's scary from a deadlock perspective calling arbitrary code while holding a lock. Another solution in Java is to use the CopyOnWriteArrayList from the java.util.concurrent package or Doug Lea's util.concurrent package. It does exactly what it sounds like it does. This works perfectly for the observer pattern because its iterator is guaranteed to not throw a ConcurrentModificationException. It's expensive to add or remove listeners, but that typically isn't done very often. Before the concurrency tools were available, I would sometimes lock the list, copy it, unlock the list and iterate over the copy to fire the event. This is less performant in most situations because you'll probably be firing events much more often than adding or removing listeners.

Thanks again Ed for the interesting series.

Friday, June 20, 2008

Groovy DOMBuilder update

I finally got around to submitting my NSDOMBuilder code to the Groovy dev list. I got the "why didn't you just fix the existing class instead of creating a new one" response that I sort of expected, which made me go back and figure out what that didn't work when I tried it. Someone in that thread did point out StreamingDOMBuilder, which would probably work for the situations I need, except default namespace handling is currently broken in the releases, but now fixed in svn. It's not documented anywhere that I could find, but it's not too hard to figure out.

Very un-groovy

I really like Groovy as a language, but it does have its flaws. My colleague and I came across some very strange syntax and weird behavior surrounding GPathResult. GPathResult is fine and dandy for a read-only view of an object graph, but it does support some write operations. The documentation says you should only use it for read operations and then gives examples of how to write.

Unfortunately, those write operations are a bit broken. You can add nodes but the changes won't be reflected by the iterators or size() method. There is not method to remove a node, but you can effectively do so by replacing it with an empty closure. Unfortunately there are some downsides that as well because in some cases the nodes act like a linked list and if you replace one in the middle, you lose all the nodes after it.

Probably the worst thing about GPathResult is the overridden plus operator. I have never seen the plus operator overridden to perform a mutation on the object, but that's what GPathResult does. Take this from a mock Grails plugin for example:

class AGrailsPlugin {
def doWithWebDescriptor = { xml ->
def mappingElement = xml.'servlet'
mappingElement[mappingElement.size() - 1] + {
'servlet' {
'servlet-name'('a')
'servlet-class'('com.discorp.web.AServlet')
}
}
}
}

That actually adds a new servlet node right after the last servlet node in the tree. That has to be to most un-groovy syntax I have seen in Groovy. It's not at all obvious what going on and it's very hackish. Unfortunately I don't think there is any other way to perform that function in Grails, though I have a ticket open for it.

Thursday, June 12, 2008

Google I/O thoughts

I attended the Google I/O conference a couple weeks ago. Here are some thoughts I had from that.

Google has resource constraints, makes mistakes and misses deadlines too. They work in small, focused teams. 40% of Google’s new products in the last year have come out of 20-percent time projects. They do a lot of split A/B testing where they test different versions of the site on small groups and use metrics to determine what’s best. Google’s view is that anything that expands the web is good for Google, so they’re providing all these cool APIs for free.

It will be hard for a small company to keep up with the technology, especially if it is spread thin. Disruptive innovation will always happen before implementation and deployment of a current technology can be completed on a large project.

The web as an application platform is getting much better. It still sucks in many ways, like Javascript as a language, tool support, desktop integration, etc., but there initiatives to address those problems. Gears is a big step forward. SSBs (e.g. Prism and Fluid) and platforms like Adobe Air solve some of these problems. Web applications will never keep up with desktop applications in terms of user interface, performance, and integration.

Javascript still sucks. GWT does solve a lot of the suckiness, but what's the long term viability of it? GWT is pretty cool and is not an all-or-nothing venture, so it would be good to investigate. Javascript 2 is still a long ways off and may not even address the enough of the suckiness. This is a downside of going to a mostly web-based platform.

There seems to be a trend for moving more application logic to the client side Javascript layer of the application. This is necessary if offline support is to be achieved. This does open up security issues unless constraints are also enforced on the server side, creating duplication and therefore maintenance issues.

The web is moving away from RDBMSes as a back end data store. They just don't scale. An emphasis is being placed on what information is "current enough" instead of always the most up to date. In what contexts can this method be used vs. strict transactional, relational data? To what capacity does a project need to scale? That will be the biggest factor in determining whether or not to move to a data store like HBase.

Android is a cool platform, but will still be limited by the wireless companies that use it. No accessory (e.g. printer or signature capture pad) support in the near future and it's unlikely to be able to hack it in because you can't install native apps. It’s an open platform, but can and most likely will be locked down by the wireless companies. Android will have Gears support though.

Targeting WebKit will likely be the best way to get apps to the iPhone and Android phones for the next few years. It may be the only way to target both platforms without significant development overlap. It may be possible to provide specific hooks to the native platforms via browser plugins (currently impossible) or apps that embed the browser (will be possible on Android and iPhone 2.0, though limited).

The best way to develop apps for the web may be to determine if it is possible to deliver 80-90% of the requested features with just the browser and then make up the next 5-10% with optional native hooks. For example, one could deliver a web-based contact manager. If the user has installed Gears, the user can get offline lookup of some subset of contacts and perform some, but not all, actions on those contacts. Providing offline support will require architectural overhead, which may be minor or very significant – I can’t really say at this point.

Monday, March 3, 2008

Wisdom: the forgotten context

In high school and college I was really big on seeking wisdom. I figured the facts and stuff would follow. Somewhere along the line, I forgot all that and became content with whatever wisdom I already had. I also forgot the source of the wisdom that had served me so well in the past.

I was reading through Proverbs last week and came across what was a favorite verse of mine. Proverbs 24:3-4 says,
By wisdom a house is built,
And by understanding it is established;
And by knowledge the rooms are filled
With all precious and pleasant riches.


Ya, I learned a lot of facts over the years, but without the context of wisdom those facts are not put to good use. Here's to a renewed goal of seeking wisdom, the context for knowledge.

Tuesday, February 26, 2008

Faith for the little things

I've been going through a pretty rough time the past couple weeks with some stressful situations. The biggest struggle was really with my faith. It wasn't so much a crisis of faith in that I questioned what I believe; it was more an issue of putting into practice what I do believe, particularly that God is in control and I need to trust Him to take care of me.

One of the reasons this problem came up is that I had not been trusting God in the little things. We can get by with a lot on this earth without God. Billions of unbelievers do it every day. Doing things that way seems to work out while circumstances are good. It doesn't lead to a life of joy or peace, but it's not really agony either. But what happens when something comes along that is more than we can handle without God?

If we don't trust Him for the little things, the every day things, if we're not trusting in Him when times are good, how can we expect to trust Him when times are bad?

Tuesday, February 5, 2008

No wonder the church isn't effective

I heard Michael Zimmerman on a radio program last night talking about the compatibility of Christianity and science. Zimmerman is a scientist who founded the
Clergy Letter Project, a statement that basically reduces the Bible to a book of "moral truths" in an effort to reconcile Christianity and science. Now I think that Christianity and science are 100% compatible, but I don't think you need to throw out the Bible to get there. I think it's a mistake for the Christian community to deny that species change over time -- it just makes us look stupid. But I think it's also a mistake to think that current scientific theory trumps God's word. Zimmerman did have one excellent point: science cannot prove or disprove the existence of God. I would add to that that science will never be able to prove or disprove the Genesis creation story because you cannot evaluate the supernatural with natural methods.

Anyway, back to the title point: it's no wonder the church isn't as effective as it should be when you have thousands of Christian leaders willing to throw the idea of the Bible as the authoritative word of God out the window.

Groovy DOMBuilder namespace

I'm building some XML in Groovy and it's pretty rad. However, I had the hardest time figuring out how to make namespaces work the way I thought they should. The examples didn't seem to cover the case I needed. I eventually got it, so in case anyone else is trying to do this, here it is:

Update: this didn't actually work. It looks right when serialized, but the in-memory DOM doesn't have the correct Element implementation. I ended up writing a new NSDOMBuilder class.

import groovy.xml.DOMBuilder
import groovy.xml.QName
import javax.xml.XMLConstants

import org.w3c.dom.Element
import org.joda.time.format.DateTimeFormatter
import org.joda.time.format.ISODateTimeFormat

DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeNoMillis()
def builder = DOMBuilder.newInstance()
def now = new DateTime()

Element el = builder.'ebiz:GetInventoryConsumption'(releaseID:'9.0',
xmlns:[(new QName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, 'xmlns')):'http://www.openapplications.org/oagis/9',
(new QName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, 'xmlns:ebiz')):'http://www.dis-corp.com/xml/ebusiness/2.0']) {
ApplicationArea() {
Sender(InetAddress.getLocalHost().getHostName())
CreationDateTime(dateTimeFormatter.print(now))
BODID(UUID.randomUUID())
}
DataArea() {
Get() {
Expression()
}
InventoryConsumption() {
'ebiz:InventoryConsumptionHeader'() {
// stuff goes here
}
}
}
}


Update 2. Here's the actual code:
HashMapStack.java
package groovy.xml;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

public class HashMapStack
implements Map
{
private LinkedList stack = new LinkedList();

public HashMapStack()
{
stack.add(new HashMap());
}

/**
* Clears the whole structure.
*/
public void clear()
{
stack.clear();
stack.add(new HashMap());
}

public boolean containsKey(Object key)
{
for (ListIterator iter = stack.listIterator(stack.size()); iter.hasPrevious();)
{
Map map = (Map) iter.previous();
if (map.containsKey(key))
{
return true;
}
}
return false;
}

public boolean containsValue(Object value)
{
for (ListIterator iter = stack.listIterator(stack.size()); iter.hasPrevious();)
{
Map map = (Map) iter.previous();
if (map.containsValue(value))
{
return true;
}
}
return false;
}

public Set entrySet()
{
throw new UnsupportedOperationException();
}

public Object get(Object key)
{
for (ListIterator iter = stack.listIterator(stack.size()); iter.hasPrevious();)
{
Map map = (Map) iter.previous();
if (map.containsKey(key))
{
return map.get(key);
}
}
return null;
}

public boolean isEmpty()
{
return false;
}

public Set keySet()
{
HashSet set = new HashSet();
for (Iterator iter = stack.iterator(); iter.hasNext();)
{
Map map = (Map) iter.next();
set.addAll(map.keySet());
}
return set;
}

/**
* Only returns the value of the item replaced in the current frame, not in any lower frames.
*/
public Object put(Object key, Object value)
{
return ((Map) stack.getLast()).put(key, value);
}

public void putAll(Map t)
{
((Map) stack.getLast()).putAll(t);
}

/**
* Only removes the key from the current frame.
*/
public Object remove(Object key)
{
return ((Map) stack.getLast()).remove(key);
}

public int size()
{
return keySet().size();
}

public Collection values()
{
throw new UnsupportedOperationException();
}

public void push()
{
stack.add(new HashMap());
}

public void pop()
{
if (stack.size() > 1)
{
stack.removeLast();
}
else
{
throw new IndexOutOfBoundsException();
}
}
}


NSDOMBuilder.groovy
package groovy.xml

import javax.xml.namespace.QName
import groovy.xml.FactorySupport

import javax.xml.namespace.QName
import javax.xml.parsers.DocumentBuilder
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.parsers.ParserConfigurationException

import org.w3c.dom.Document
import org.w3c.dom.Element
import org.w3c.dom.Node
import org.xml.sax.InputSource
import org.xml.sax.SAXException

class NSDOMBuilder extends BuilderSupport {

private boolean namespaceAware
private HashMapStack nsMap = new HashMapStack()
private Document document
private DocumentBuilder documentBuilder

static NSDOMBuilder newInstance() throws ParserConfigurationException {
return newInstance(false, true)
}

static NSDOMBuilder newInstance(boolean validating, boolean namespaceAware) throws ParserConfigurationException {
DocumentBuilderFactory factory = FactorySupport.createDocumentBuilderFactory()
factory.setNamespaceAware(namespaceAware)
factory.setValidating(validating)
return new NSDOMBuilder(factory.newDocumentBuilder())
}

static Document parse(Reader reader) throws SAXException, IOException, ParserConfigurationException {
return parse(reader, false, true)
}

static Document parse(Reader reader, boolean validating, boolean namespaceAware)
throws SAXException, IOException, ParserConfigurationException {
DocumentBuilderFactory factory = FactorySupport.createDocumentBuilderFactory()
factory.setNamespaceAware(namespaceAware)
factory.setValidating(validating)
DocumentBuilder documentBuilder = factory.newDocumentBuilder()
return documentBuilder.parse(new InputSource(reader))
}

NSDOMBuilder(Document document) {
this(document, true)
}

NSDOMBuilder(Document document, boolean namespaceAware) {
this.document = document
this.namespaceAware = namespaceAware
}

NSDOMBuilder(DocumentBuilder documentBuilder) {
this.documentBuilder = documentBuilder
this.namespaceAware = documentBuilder.namespaceAware
}

protected void setParent(Object parent, Object child) {
Node current = (Node) parent
Node node = (Node) child

current.appendChild(node)
}

protected Object createNode(Object name) {
return createNode(name, [:])
}

protected Document createDocument() {
if (documentBuilder == null) {
throw new IllegalArgumentException("No Document or DOMImplementation available so cannot create Document")
} else {
return documentBuilder.newDocument()
}
}

protected Object createNode(Object name, Object value) {
Element element = (Element) createNode(name, [:])
element.appendChild(document.createTextNode(value.toString()))
return element
}

protected Object createNode(Object name, Map attributes, Object value) {
Element element = (Element) createNode(name, attributes)
element.appendChild(document.createTextNode(value.toString()))
return element
}

protected Object createNode(Object name, Map attributes) {
if (namespaceAware) {
nsMap.push()

for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
def entry = iter.next()
String attrName = entry.key.toString()
if ("xmlns".equals(attrName) || attrName.startsWith("xmlns:")) {
String prefix = ''
if (attrName.startsWith("xmlns:")) {
prefix = attrName.substring(6)
}
nsMap.put(prefix, entry.value)
iter.remove()
}
}
}

if (document == null) {
document = createDocument()
}

Element element

if (namespaceAware) {
def qname = parseQName(name)
element = document.createElementNS(qname.namespaceURI, name)
} else {
element = document.createElement(name)
}

attributes.each { k, value ->
String valueText = (value != null) ? value.toString() : ""
element.setAttribute(k.toString(), valueText)
}

return element
}

protected void nodeCompleted(Object parent, Object node) {
if (namespaceAware) {
nsMap.pop()
}
}

protected QName parseQName(String name) {
String prefix = ''
String localPart = name
int i = name.indexOf(':')
if (i > -1) {
prefix = name.substring(0, i)
localPart = name.substring(i + 1)
}
String namespaceURI = nsMap.get(prefix)
return new QName(namespaceURI, localPart, prefix)
}
}


Update 3: here's an example of how to actually use it.

    Element createGetSupplierPartyMaster(Dealer dealer, Customer customer = null)
{
assert dealer != null
assert dealer.dealerId != null

def builder = NSDOMBuilder.newInstance()

def el = builder.'ebiz:GetSupplierPartyMaster'(releaseID:'9.0',
xmlns:'http://www.openapplications.org/oagis/9',
'xmlns:ebiz':'http://www.dis-corp.com/xml/ebusiness/2.0') {
ApplicationArea() {
Sender(InetAddress.localHost.hostName)
CreationDateTime(dateTimeFormatter.print(new DateTime()))
BODID(UUID.randomUUID())
}
DataArea() {
Get() {
Expression()
}
SupplierPartyMaster() {
PartyIDs() {
ID(schemeURI:BODHelper.DEALER_SCHEME_URI, dealer.dealerId)
if (customer?.store)
{
ID(schemeURI:BODHelper.STORE_SCHEME_URI, customer.store.storeCode)
}
}
if (customer)
{
AccountIDs() {
ID(customer.customerNumber)
}
}
}
}
}

el.ownerDocument.appendChild(el)
return el
}

Saturday, January 26, 2008

"peace is an accident"

Sly was on Rush the other day talking about his new Rambo movie. I haven't seen the movie but he had this to say about a group of idealists portrayed
in the movie:

They actually believe what they're doing is right and that someday there's going to be this mystical moment and we're all going to join hands and sing "We Are the World," and when the truth is, Rambo says, "War is natural. Peace is an accident." That's a fact of life. It's unfortunate that we can start a war in five minutes, but it takes us a hundred years to make peace. So what comes more natural?


Stallone recognizes the human condition. Why is it that most of the rest of Hollywood doesn't?

Wednesday, January 23, 2008

supernatural

xkcd is often pretty entertaining, but I found this one amusing in a different sense:


The obvious logical fallacy here is that you can prove supernatural powers through natural methods. If you could, the powers wouldn't be supernatural. I don't know what inspired that comic, but I think it's interesting when people make science their religion and refuse to think outside the bounds of the physical world. Physics, geology and biology cannot answer a huge class of life's questions. They don't adequately address such philosophical and spiritual issues as love, greed, morals or purpose. It's almost as if you can't prove it with math or observe it with a microscope it isn't important or isn't even real. That seems like a pretty narrow view of the world to me.