<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearch/1.1/" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" gd:etag="W/&quot;CU4MRXczeSp7ImA9WhRaE0o.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134</id><updated>2012-02-15T22:33:04.981-08:00</updated><category term="initWithContentURL" /><category term="Python" /><category term="AOP" /><category term="IE7" /><category term="FlashPlayer" /><category term="head request" /><category term="iPhone SDK" /><category term="CSS" /><category term="MethodInvocation" /><category term="Spring Framework" /><category term="ActiveRecord" /><category term="ActionScript" /><category term="initWithURL" /><category term="Caching" /><category term="MPMoviePlayerController" /><category term="Java" /><category term="alias_method" /><category term="MethodInterceptor" /><category term="STI" /><category term="Mootools" /><category term="AudioStreamer" /><category term="Django" /><category term="before_type_cast" /><category term="Pydev" /><category term="ErrorEvent.errorID" /><category term="Redirects" /><category term="Objective-C" /><category term="Eclipse" /><category term="Audio Queue Services" /><category term="HTML" /><category term="accepts_nested_attributes_for" /><category term="redirect" /><category term="Ruby on Rails" /><category term="JavaScript" /><category term="ErrorEvent" /><category term="NSURLConnection" /><category term="Guice" /><title>Atwofer</title><subtitle type="html">A place to talk about Software Development (RoR, J2EE, J2ME, CSS, HTML, JavaScript, Objective-C, etc) and some other stuff</subtitle><link rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml" href="http://www.atwofer.com/feeds/posts/default" /><link rel="alternate" type="text/html" href="http://www.atwofer.com/" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><generator version="7.00" uri="http://www.blogger.com">Blogger</generator><openSearch:totalResults>10</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/Atwofer" /><feedburner:info uri="atwofer" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry gd:etag="W/&quot;CE4BQXc6eip7ImA9Wx9UGEQ.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-3775824916684577166</id><published>2011-01-31T10:26:00.000-08:00</published><updated>2011-02-16T13:49:10.912-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-02-16T13:49:10.912-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Java" /><category scheme="http://www.blogger.com/atom/ns#" term="Guice" /><category scheme="http://www.blogger.com/atom/ns#" term="Caching" /><category scheme="http://www.blogger.com/atom/ns#" term="AOP" /><title>Simple Caching using Guice</title><content type="html">I am at Google now so I decided to use &lt;a href="http://code.google.com/p/google-guice/"&gt;Guice&lt;/a&gt; as the framework for my dependency injections needs. I am a big fan of &lt;a href="http://www.springsource.org/"&gt;Spring&lt;/a&gt; so I was a little concerned that Guice couldn't live up to my expectations. However, after having used it for the last 6 months or so I have to say that I am a fan. Gone are those long spring XML configuration files and replaced with Guice modules.&lt;br /&gt;
&lt;br /&gt;
Recently, while working on my current project we found that some requests against our server were slow. Using my Guice-ified logging framework (maybe another post?) I was able to determine that we were making way too many queries against our persistence layer. Just looking at the queries we could see that a lot of the queries were exactly the same. Immediately, this seems like a problem that could be solved by some caching. Since the main problem was too many persistence store queries per request, I thought that a simple request scoped cache could go a long way and Guice allowed me to do this very easily. The advantage of making this a request scoped cache I really don't need to worry too much about invalidation or thread safety.&lt;br /&gt;
&lt;br /&gt;
The first thing I needed to do was to determine which persistence calls I want to cache. My persistence class is below and I have annotated the cacheable methods with &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@Cache&lt;/span&gt; in addition I have annotated the class with &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@Cacheable&lt;/span&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="line-height: 16px;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span id="l80"&gt;@&lt;span class="stx-class" style="text-decoration: none;"&gt;Cacheable&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span id="l80"&gt;&lt;span class="stx-class" style="text-decoration: none;"&gt;&lt;span class="Apple-style-span" style="color: black;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span id="l81"&gt;@Singleton
&lt;/span&gt;&lt;span id="l82"&gt;&lt;span class="stx-keyword"&gt;public&lt;/span&gt; &lt;span class="stx-keyword"&gt;class&lt;/span&gt; {&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span id="l82"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-left: 10px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@Cache(queryType = QueryType.LOOKUP_BY_KEY)
public Object lookup(Key key)&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-left: 10px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;  // Does lookup against persistence store and returns result.
}&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-left: 10px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-left: 10px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;public Object save(Object resource)&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-left: 10px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;  // Saves to the persistence store.&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-left: 10px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;pre style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span id="l82"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;
The annotations are defined as follows:&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@Retention(RetentionPolicy.RUNTIME)&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@Target({ElementType.PARAMETER, ElementType.TYPE})&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;public @interface Cacheable {&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@Retention(RetentionPolicy.RUNTIME)&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;@Target({ElementType.PARAMETER, ElementType.METHOD})&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;public @interface PersistenceContextCache {&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;/**&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; * Used to give the interceptor a hint on how to cache the&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; * result of the intercepted method.&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;public enum QueryType {&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;FIND_BY_KEY,&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;LIST,&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;LOOKUP_BY_KEY,&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;SEARCH&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;QueryType queryType();&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Because I want to be able easily disable my caching layer and not let my caching logic invade my persistence code, I am using an interceptor to implement my caching logic.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;/**&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;* Intercepts calls to the persistence tier and checks if we&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;* already have the result in the&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;cache. If it is in the cache&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;* then the call to the persistence tier is skipped and the&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;* cached result is returned. Otherwise, we call through and&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;*&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;cache&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;the result.&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;*/&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;public class CacheInterceptor implements&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;MethodInterceptor {&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;/**&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; * Provides the cache which is request scoped so we don't have&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; * to concern ourselves with thread&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;safety.&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; */&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;@Inject&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;private Provider&amp;lt;Cache&amp;lt;CacheKey, Object&amp;gt;&amp;gt; cacheProvider;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;@Override&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;public Object invoke(MethodInvocation invocation)&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;throws Throwable {&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// Construct the cache key&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Cache cacheAnnotation =&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;invocation.getMethod().getAnnotation(&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;PersistenceContextCache.class);&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;CacheKey cacheKey = getCacheKey(&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;cacheAnnotation.queryType(),&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;invocation.getArguments(),&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;invocation.getMethod());&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;Object result = cacheProvider.get().getElement(cacheKey);&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// If the result is null, then we have a cache miss so call&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// through&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;if (null == result) {&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;result = invocation.proceed();&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;br /&gt;
&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;// Now add it to the cache&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp;cacheProvider.get().addElement(&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;cacheKey, result != null ?&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;result : new NullCacheEntry());&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;}&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return result instanceof NullCacheEntry ? null : result;&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Now let's wire things together in a Guice module.&lt;br /&gt;
&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;public class CacheModule extends AbstractModule {&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;@Override&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;protected void configure() {&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;CacheInterceptor cacheInterceptor = new CacheInterceptor();&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// Intercept any calls to methods annotated with @Cache on&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;// a class annotated with @Cacheable.&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;bindInterceptor(&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Matchers.annotatedWith(Cacheable.class),&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Matchers.annotatedWith(Cache.class),&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;cacheInterceptor);&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp; &lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;@Provides&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;@RequestScoped&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;SoftHashMap&amp;lt;CacheKey, Object&amp;gt; provideCache() {&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;return new SoftHaspMap&amp;lt;CacheKey, Object&amp;gt;();&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;
&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Some things to note in the above code:&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Since my interceptor is essentially a singleton but my cache is request scoped, I need to use a &lt;a href="http://google-guice.googlecode.com/svn/trunk/javadoc/com/google/inject/Provider.html"&gt;provider&lt;/a&gt;&amp;nbsp;which allows me to mix scopes.&lt;/li&gt;
&lt;li&gt;I did not show my implementation of&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;getCacheKey.&lt;/span&gt;&amp;nbsp;However, it should be straight-forward to see how you can come up with a Cache Key based on the data I pass in.&lt;/li&gt;
&lt;li&gt;I did not include my cache invalidation code but you should be able to see that you could simply add another interceptor to clear the cache on any write.&lt;/li&gt;
&lt;li&gt;I use a &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;NullCacheEntry&lt;/span&gt; object to represent a "not found" resource since a Map does not allow for null values.&lt;/li&gt;
&lt;li&gt;My cache is actually a SoftHashMap which I do not show the implementation of. &amp;nbsp;The idea is we want to be safe and not let our cache get too big and cause an &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;OutOfMemoryException&lt;/span&gt; so these &lt;a href="http://download.oracle.com/javase/6/docs/api/java/lang/ref/SoftReference.html"&gt;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;SoftReference&lt;/span&gt;s&lt;/a&gt; should be cleaned up if the memory gets low.&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/1690810562053338134-3775824916684577166?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/FhdgeS8IlmuVx7FiseAT1NkUthE/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FhdgeS8IlmuVx7FiseAT1NkUthE/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/FhdgeS8IlmuVx7FiseAT1NkUthE/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/FhdgeS8IlmuVx7FiseAT1NkUthE/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/kSlVh5MKwNM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/3775824916684577166/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2011/01/simple-caching-using-guice.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/3775824916684577166?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/3775824916684577166?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/kSlVh5MKwNM/simple-caching-using-guice.html" title="Simple Caching using Guice" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.atwofer.com/2011/01/simple-caching-using-guice.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0cMQno_fyp7ImA9Wx9VFEQ.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-8932993591531895634</id><published>2010-03-21T10:13:00.000-07:00</published><updated>2011-01-31T10:38:03.447-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-31T10:38:03.447-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="Django" /><category scheme="http://www.blogger.com/atom/ns#" term="Eclipse" /><category scheme="http://www.blogger.com/atom/ns#" term="Python" /><category scheme="http://www.blogger.com/atom/ns#" term="Pydev" /><title>Eclipse + Pydev + Django</title><content type="html">So I decided that I wanted to learn Python. On top of that I wanted to learn &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;. The catch is that I like to do everything from within Eclipse so that I can make use of the debugger. I couldn't find information about how to do all of this so instead I pieced it together here. My overall goal is to run through this &lt;a href="http://docs.djangoproject.com/en/dev/intro/tutorial01/#the-development-server" target="_blank"&gt;Django Tutorial&lt;/a&gt; but this post will only deal with setting up Eclipse/Pydev and Django.&lt;br /&gt;
&lt;br /&gt;
NOTE: Hopefully soon this post will be rendered useless when &lt;a href="http://pydev.blogspot.com/2010/03/django-on-pydev.html" target="_blank"&gt;Pydev includes Django support&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Install Pydev&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt; &lt;/strong&gt;Since I use Eclipse for just about everything from Java to Ruby on Rails to ActionScript I obviously already had it installed so the first thing I needed to do was to install the Pydev Eclipse plugin. Pydev is installed just like any other plugin and instructions on how to install Pydev can be found &lt;a href="http://pydev.org/download.html"&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Install Django&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
Download Django from here then unzip it and navigate to the unzipped folder and run the following command.&lt;br /&gt;
&lt;pre style="padding-left: 30px;"&gt;python setup.py install&lt;/pre&gt;&lt;br /&gt;
I am on a Mac with Python 2.6 installed so Django is installed at&lt;br /&gt;
&lt;pre style="padding-left: 30px;"&gt;/Library/Python/2.6/site-packages&lt;/pre&gt;&lt;br /&gt;
&lt;strong&gt;Create a Django Project&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
Now I should be ready to create my project and the configure it to work with Eclipse/Pydev. First I need to navigate to my folder where I want to create the project and type the following.&lt;br /&gt;
&lt;pre style="padding-left: 30px;"&gt;django-admin.py startproject mysite&lt;/pre&gt;&lt;br /&gt;
Now this results in the following&lt;br /&gt;
&lt;pre style="padding-left: 30px;"&gt;cd mysite/&lt;/pre&gt;&lt;pre style="padding-left: 30px;"&gt;ls&lt;/pre&gt;&lt;pre style="padding-left: 30px;"&gt;__init__.py     manage.py       settings.py     urls.py&lt;/pre&gt;&lt;br /&gt;
As you can see all the project files are created at the root. Generally projects consist of more than just python files. Also, pydev likes the code to be in a "src" folder so lets move the python files to a "mysite" package in the "src" folder.&lt;br /&gt;
&lt;pre style="padding-left: 30px;"&gt;mkdir src
cd src
mkdir mysite
mv *.py src/mysite/.&lt;/pre&gt;&lt;br /&gt;
&lt;strong&gt;Create the Pydev Projec&lt;/strong&gt;t&lt;br /&gt;
&lt;br /&gt;
Now I can launch Eclipse and create a new project by going to&amp;nbsp;&lt;em&gt;File-&amp;gt; New -&amp;gt; Pydev Project&lt;/em&gt;. For the directory I will select the project folder I created earlier.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_RAJECGB1Wk8/TUcA3SluMJI/AAAAAAAAAgA/kquPaXOdCg8/s1600/eclipse%252Bpydev%252Bdjango.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="320" src="http://4.bp.blogspot.com/_RAJECGB1Wk8/TUcA3SluMJI/AAAAAAAAAgA/kquPaXOdCg8/s320/eclipse%252Bpydev%252Bdjango.png" width="304" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Click "Finish" and now I have a Pydev project that looks like the following.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_RAJECGB1Wk8/TUcBImg65AI/AAAAAAAAAgE/ZHpht7CNZSQ/s1600/eclipse%252Bpydev%252Bdjango3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://1.bp.blogspot.com/_RAJECGB1Wk8/TUcBImg65AI/AAAAAAAAAgE/ZHpht7CNZSQ/s1600/eclipse%252Bpydev%252Bdjango3.png" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;strong&gt;Add Django to the Project&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
The first thing I need to do now that I have the project is tell it where to find Django. I can do this by going to &lt;em&gt;Project -&amp;gt; Propertie&lt;/em&gt;s. Here I need to add the installed location of Django as an External Library.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_RAJECGB1Wk8/TUcBOclgaBI/AAAAAAAAAgI/A2BhHDc4tWo/s1600/eclipse%252Bpydev%252Bdjango2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="227" src="http://2.bp.blogspot.com/_RAJECGB1Wk8/TUcBOclgaBI/AAAAAAAAAgI/A2BhHDc4tWo/s320/eclipse%252Bpydev%252Bdjango2.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;strong&gt;Starting My Server From Eclipse&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
In Eclipse I go to &lt;em&gt;Run -&amp;gt; Run Configurations... &lt;/em&gt;and create a new Python Run configuration. Select my project and for the main module provide the path to the manage.py file.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_RAJECGB1Wk8/TUcBTHIUNEI/AAAAAAAAAgM/DwPflZHOxOM/s1600/eclipse%252Bpydev%252Bdjango4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="173" src="http://2.bp.blogspot.com/_RAJECGB1Wk8/TUcBTHIUNEI/AAAAAAAAAgM/DwPflZHOxOM/s320/eclipse%252Bpydev%252Bdjango4.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Switch to the arguments tab and add the "runserver --noreload" argument.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_RAJECGB1Wk8/TUcBYFSpz_I/AAAAAAAAAgQ/oX9Nz6gARVk/s1600/eclipse%252Bpydev%252Bdjango5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="173" src="http://1.bp.blogspot.com/_RAJECGB1Wk8/TUcBYFSpz_I/AAAAAAAAAgQ/oX9Nz6gARVk/s320/eclipse%252Bpydev%252Bdjango5.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
Click "Apply" and now click "Run" and navigate to http://localhost:8000.&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_RAJECGB1Wk8/TUcBgAC3s4I/AAAAAAAAAgU/fr0C31DnsI8/s1600/eclipse%252Bpydev%252Bdjango6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="177" src="http://4.bp.blogspot.com/_RAJECGB1Wk8/TUcBgAC3s4I/AAAAAAAAAgU/fr0C31DnsI8/s320/eclipse%252Bpydev%252Bdjango6.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;This is as far as I have gotten so far. Next I will actually try to code something.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1690810562053338134-8932993591531895634?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/_8cwywKRjtfGUE-TyB9_5I0MuIw/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_8cwywKRjtfGUE-TyB9_5I0MuIw/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/_8cwywKRjtfGUE-TyB9_5I0MuIw/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_8cwywKRjtfGUE-TyB9_5I0MuIw/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/v7azkh7wmlo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/8932993591531895634/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2010/03/eclipse-pydev-django.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/8932993591531895634?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/8932993591531895634?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/v7azkh7wmlo/eclipse-pydev-django.html" title="Eclipse + Pydev + Django" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://4.bp.blogspot.com/_RAJECGB1Wk8/TUcA3SluMJI/AAAAAAAAAgA/kquPaXOdCg8/s72-c/eclipse%252Bpydev%252Bdjango.png" height="72" width="72" /><thr:total>1</thr:total><feedburner:origLink>http://www.atwofer.com/2010/03/eclipse-pydev-django.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEAMRXk4cSp7ImA9Wx9VEUs.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-7212514402240589249</id><published>2009-11-02T09:42:00.000-08:00</published><updated>2011-01-27T13:13:04.739-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-27T13:13:04.739-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ActiveRecord" /><category scheme="http://www.blogger.com/atom/ns#" term="Ruby on Rails" /><category scheme="http://www.blogger.com/atom/ns#" term="accepts_nested_attributes_for" /><category scheme="http://www.blogger.com/atom/ns#" term="STI" /><title>Single Table Inheritance and accepts_nested_attributes_for</title><content type="html">I recently upgraded our web app at &lt;a href="http://www.episodic.com"&gt;Episodic&lt;/a&gt; from Rails 2.0 to Rails 2.3.4.  While this resulted in many issues we had to resolve that I will try to write about in later post, it does allow me to take advantage of some of the Rails 2.3.x features like accepts_nested_attributes_for.  If you aren’t familiar with accepts_nested_attributes_for then check out &lt;a href="http://ryandaigle.com/articles/2009/2/1/what-s-new-in-edge-rails-nested-attributes"&gt;Ryan’s post&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;So I went ahead and tried to make use of this in my object model.  Here are the important parts of the model.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;&lt;br/&gt;&lt;span&gt;class Episode &lt; ActiveRecord::Base&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px"&gt;has_many :field_values, :dependent =&gt; :destroy&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px"&gt;accepts_nested_attributes_for :field_values, :allow_destroy =&gt; true&lt;/span&gt;&lt;br/&gt;&lt;span&gt;end&lt;/span&gt; &lt;br/&gt;&lt;span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span&gt;class FieldValue &lt; ActiveRecord::Base&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px"&gt;:belongs_to :episode&lt;/span&gt;&lt;br/&gt;&lt;span&gt;end&lt;/span&gt;&lt;br/&gt;&lt;span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span&gt;class TextFieldValue &lt; FieldValue&lt;/span&gt;&lt;br/&gt;&lt;span&gt;end&lt;/span&gt;&lt;br/&gt;&lt;span&gt;&lt;/span&gt;&lt;br/&gt;&lt;span&gt;class NumberFieldValue &lt; FieldValue&lt;/span&gt;&lt;br/&gt;&lt;span&gt;end&lt;/span&gt;&lt;br/&gt;&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;Since I am using Single Table Inheritance I end up with a field_values table that has a column named “type” which contain either a value of “TextFieldValue” or “NumberFieldValue”.&lt;br/&gt;&lt;br/&gt;Also, because I enabled accepts_nested_attributes_for on my Episode class I should be able to do something like:&lt;br/&gt;&lt;br/&gt;&lt;code&gt;&lt;br/&gt;&lt;span&gt;episode.field_values_attributes = &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px"&gt;[{:value =&gt; “foo”, :type =&gt; “TextFieldValue”}]&lt;/span&gt;&lt;br/&gt;&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;However, when I look in the DB I see that there is a new row but the “type” column is NULL even though I set it to “TextFieldValue”.  When I look in the logs I see: "Can't mass-assign these protected attributes: type".&lt;br/&gt;&lt;br/&gt;Luckily, there is a way around this.  I added a setter called "value_type" to my FieldValue class.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;&lt;br/&gt;&lt;span&gt;def value_type= value_type&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px"&gt;self.type = value_type&lt;/span&gt;&lt;br/&gt;&lt;span&gt;end&lt;/span&gt;&lt;br/&gt;&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;Now, when I can safely use field_values_attributes and set a type.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;&lt;br/&gt;&lt;span&gt;episode.field_values_attributes = &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px"&gt;[{:value =&gt; “foo”, :value_type =&gt; “TextFieldValue”}]&lt;/span&gt;&lt;br/&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1690810562053338134-7212514402240589249?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/3YNZ3l33O6TR_J30x0jUJLrqOcI/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3YNZ3l33O6TR_J30x0jUJLrqOcI/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/3YNZ3l33O6TR_J30x0jUJLrqOcI/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/3YNZ3l33O6TR_J30x0jUJLrqOcI/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/TK5fzoG_wJ4" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/7212514402240589249/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2009/11/single-table-inheritance-and.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/7212514402240589249?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/7212514402240589249?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/TK5fzoG_wJ4/single-table-inheritance-and.html" title="Single Table Inheritance and accepts_nested_attributes_for" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.atwofer.com/2009/11/single-table-inheritance-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEAMRXs5fSp7ImA9Wx9VEUs.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-129718210125245849</id><published>2009-08-13T03:00:00.000-07:00</published><updated>2011-01-27T13:13:04.525-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-27T13:13:04.525-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="MPMoviePlayerController" /><category scheme="http://www.blogger.com/atom/ns#" term="NSURLConnection" /><category scheme="http://www.blogger.com/atom/ns#" term="head request" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone SDK" /><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><category scheme="http://www.blogger.com/atom/ns#" term="redirect" /><category scheme="http://www.blogger.com/atom/ns#" term="initWithContentURL" /><category scheme="http://www.blogger.com/atom/ns#" term="Redirects" /><category scheme="http://www.blogger.com/atom/ns#" term="initWithURL" /><title>Handling redirects with initWithContentURL</title><content type="html">I keep running into this problem so I thought that I should post my solution.  There are some classes in the iPhone SDK like the MPMoviePlayerController that have an initWithContentURL method. However, the problem is that the URL I have is one that may issue a redirect response when requested.  When I provide such a URL to initWithContentURL the player tells me that it cannot play me file.  To work around this problem I wrote some very simple code.&lt;br/&gt;&lt;br/&gt;The first thing you need to do is to build a HEAD request using NSMutableURLRequest.  Then create an NSURLConnection settings the delegate as the current instance.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;&lt;br/&gt;&lt;span&gt;NSMutableURLRequest *headRequest = &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px"&gt;[NSMutableURLRequest requestWithURL:resourceURL&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px"&gt;cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];&lt;/span&gt;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 0px"&gt;[headRequest setHTTPMethod:@"HEAD"];&lt;/span&gt;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 0px"&gt;connection = [[NSURLConnection alloc] &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px"&gt;initWithRequest:headRequest delegate:self];&lt;/span&gt;&lt;br/&gt;&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;Now we just need to implement the delegate method to play the URL from the response.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;&lt;br/&gt;&lt;span style="padding-left: 0px"&gt;- (void)connection:(NSURLConnection *)connection &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px"&gt;didReceiveResponse:(NSURLResponse *)response {&lt;/span&gt;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px"&gt;// Now we have resolved the URL so play the movie&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px"&gt;thePlayer = [[MPMoviePlayerController alloc] &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px"&gt;initWithContentURL:[response URL]];&lt;/span&gt;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px"&gt;[thePlayer play];&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 0px"&gt;}&lt;/span&gt;&lt;br/&gt;&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;Obviously, this example handles one redirect but you can see that you can easily have it loop and until [response statusCode] == 200.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1690810562053338134-129718210125245849?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/wqIkGzyvso07PHLcl9ro6KnbrOs/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wqIkGzyvso07PHLcl9ro6KnbrOs/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/wqIkGzyvso07PHLcl9ro6KnbrOs/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/wqIkGzyvso07PHLcl9ro6KnbrOs/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/GvzVzVf-CfY" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/129718210125245849/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2009/08/handling-redirects-with.html#comment-form" title="4 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/129718210125245849?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/129718210125245849?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/GvzVzVf-CfY/handling-redirects-with.html" title="Handling redirects with initWithContentURL" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><thr:total>4</thr:total><feedburner:origLink>http://www.atwofer.com/2009/08/handling-redirects-with.html</feedburner:origLink></entry><entry gd:etag="W/&quot;A0EFRno8eSp7ImA9Wx9VFEQ.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-2395650736036084079</id><published>2009-06-30T08:49:00.000-07:00</published><updated>2011-01-31T10:46:57.471-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-31T10:46:57.471-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="MPMoviePlayerController" /><category scheme="http://www.blogger.com/atom/ns#" term="AudioStreamer" /><category scheme="http://www.blogger.com/atom/ns#" term="iPhone SDK" /><category scheme="http://www.blogger.com/atom/ns#" term="Audio Queue Services" /><category scheme="http://www.blogger.com/atom/ns#" term="Objective-C" /><title>Streaming and playing fixed-length MP3s using the iPhone SDK</title><content type="html">&lt;strong&gt;The Problem&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
Lately, I've been doing a fair amount of coding using Objective-C and the iPhone SDK.  Once I got used to the language I really started to like it.  The more I used the SDK the more impressed I became with how much I could do with it.  However, when a customer of ours at &lt;a href="http://www.episodic.com/" target="_blank"&gt;Episodic&lt;/a&gt; needed an iPhone App that could play a list of MP3s (RSS Feed to be exact) with "iPod-like" functionality, I was surprised to find that this is not something I can do with the embedded Quicktime Player.&amp;nbsp; In fact, you can't even just hook into the &lt;em&gt;MPMoviePlayerController&lt;/em&gt;.&amp;nbsp; For example, there is no way to know when one songs ends so that I can start the next song since the &lt;em&gt;MPMoviePlayerPlaybackDidFinishNotification&lt;/em&gt; notification is fired when the song ends or the user hits the done button.&amp;nbsp; So after a lot of searching I came across Matt Gallagher's great post "&lt;a href="http://cocoawithlove.com/2009/06/revisiting-old-post-streaming-and.html" target="_blank"&gt;Revisiting an old post: Streaming and playing an MP3 stream&lt;/a&gt;".&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Modifying the AudioStreamer Class to Play Fixed-Length MP3s&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
So Matt's post got me part of the way there.&amp;nbsp; It at least gave me an understanding of the tools I would need to use to accomplish my task.&amp;nbsp; The main difference between what he had and what I needed is that he was playing an MP3 stream and I wanted to play MP3 files.&amp;nbsp; The first change I made was I added a new constructor to the AudioStreamer class.&amp;nbsp; This allowed me to initialized the class with a URL to a file on the file system (I'll talk about how to get the file on your file system later).&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 20px;"&gt;- (id)initWithFileURL:(NSURL *)aURL {&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;[self initWithURL:aURL];&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;fixedLength = YES;&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;return self;&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
This constructor sets a flag to indicate that we are streaming fixed-length MP3s.&amp;nbsp; I then key off this flag in the &lt;em&gt;openFileStream&lt;/em&gt; method.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 20px;"&gt;if (fixedLength) {&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;stream = CFReadStreamCreateWithFile(&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 80px;"&gt;kCFAllocatorDefault, (CFURLRef)url);&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;} else {&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;CFHTTPMessageRef message = CFHTTPMessageCreateRequest(&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 80px;"&gt;NULL, (CFStringRef)@"GET", (CFURLRef)url,&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 80px;"&gt;kCFHTTPVersion1_1);&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;stream = CFReadStreamCreateForHTTPRequest(NULL, message);&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;CFRelease(message);&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;...&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
The above code simply creates the stream from the file system instead of a remote file.  Because the result is still a CFReadStreamRef object the rest of the class mostly remains unchanged.&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Downloading the MP3&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
In my controller I just need to download the MP3 file to a location on the file system.&amp;nbsp; This can be done very easily by using a combination of &lt;em&gt;NSURLConnection&lt;/em&gt; and &lt;em&gt;NSFileHandle&lt;/em&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 20px;"&gt;audioFile = [[NSFileHandle&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;fileHandleForWritingAtPath:downloadFileName] retain];&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;NSURLRequest *downloadRequest = [NSURLRequest&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;requestWithURL:resourceURL &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;cachePolicy:NSURLRequestUseProtocolCachePolicy&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;timeoutInterval:60.0]; &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;download = [[NSURLConnection alloc]&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;initWithRequest:downloadRequest delegate:self];&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
This code creates the file to write the data to and creates the connection to fetch the data.&amp;nbsp; I know just need to handle the data.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 20px;"&gt;- (void)connection:(NSURLConnection *)connection &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;didReceiveData:(NSData *)data {&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;// Append the data to the file on the file system&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;[audioFile writeData:data];&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;downloadedLength += [data length];&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt; &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;// When we get enough of the file, then just start playing.&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;if (!streamer &amp;amp;&amp;amp; downloadedLength &amp;gt;&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 40px;"&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp; DEFAULT_MIN_LENGTH_TO_PLAY) {&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;NSLog(@"start playback for %@", downloadFileName);&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;[self initAndStartStreamer];&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
The above code is the &lt;em&gt;NSURLConnection&lt;/em&gt; delegate method for receiving the data and writing the data to the file.  Once I have "enough" of the MP3 I can start playing it by create the streamer and passing it the URL to our &lt;em&gt;audioFile&lt;/em&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;NEW: Handling EOF&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
One bug that I ran into since my initial post is that if I actually put this on my phone and went outside, away from my wireless network, the song would sometimes end early.&amp;nbsp; What was happening is that the AudioStreamer was playing the file faster than it was being downloaded.&amp;nbsp; The AudioStreamer would hit the EOF and then stop.&amp;nbsp; To prevent this case I added some "throttling" to the playback loop that checks if we are too close to the end of the file and changes the state to buffering until we have enough to continue.  The following code is in startInternal.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 20px;"&gt;// Flag to indicate that we have gotten too close to the EOF&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 20px;"&gt;// before the entire file has downloaded so we need throttle&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 20px;"&gt;// the playback.&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;BOOL isThrottling = NO;{&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;//&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;// Process the run loop until playback is finished or failed.&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;//&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;BOOL isRunning = YES;&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;do&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;{&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;// If we are playing a fixed-length MP3 make sure we are&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 40px;"&gt;// not too close to the end of the file. This prevents us&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 40px;"&gt;// from hitting the end of the file before it is fully&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 40px;"&gt;// downloaded.  Very useful when not on 3G since the song&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 40px;"&gt;// may be played faster than it is downloaded.&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;if (!fixedLength || self.fileDownloadComplete || &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 80px;"&gt;self.fileDownloadCurrentSize &amp;gt; (fileDownloadBytesRead + &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 80px;"&gt;(kAQBufSize * kNumAQBufs))) {&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;isRunning = [[NSRunLoop currentRunLoop&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 100px;"&gt;runMode:NSDefaultRunLoopMode&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 100px;"&gt;beforeDate:&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 100px;"&gt;&amp;nbsp;&amp;nbsp;[NSDate dateWithTimeIntervalSinceNow:0.25]];&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;if (isThrottling) {&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 80px;"&gt;isThrottling = NO;&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 80px;"&gt;AudioQueueStart(audioQueue, NULL);&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 80px;"&gt;self.state = AS_PLAYING;&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt; &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;//&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;// If there are no queued buffers, we need to check here&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 60px;"&gt;// since the handleBufferCompleteForQueue:buffer: should&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 60px;"&gt;// not change the state(may not enter the synchronized&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style="padding-left: 60px;"&gt;// section).&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;//&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;if (buffersUsed == 0 &amp;amp;&amp;amp; self.state == AS_PLAYING)&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;{&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 80px;"&gt;self.state = AS_BUFFERING;&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;} else if (!isThrottling &amp;amp;&amp;amp; self.state == AS_PLAYING) {&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;NSLog(@"Throttle because we are too close to EOF.");&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt; &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;self.state = AS_BUFFERING;&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt; &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;AudioQueuePause(audioQueue);&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;isThrottling = YES;&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt; &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;// Sleep for a few seconds&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 60px;"&gt;[NSThread sleepForTimeInterval:3.0];&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 40px;"&gt; &lt;/span&gt;&lt;br /&gt;
&lt;span style="padding-left: 20px;"&gt;} while (isRunning &amp;amp;&amp;amp; ![self runLoopShouldExit]);&lt;/span&gt;&lt;br /&gt;
&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Seeking?&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
I was unable to figure out a way to allow for seeking. This would also provide a way for playback to pick up where it left off when the connection during the download phase.&amp;nbsp; If anyone has ideas on how to support this I would love to hear about it.&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
This is still far from ideal and it really seems like this is something that should be a lot easier to do within the SDK.&amp;nbsp; In the meantime, hopefully this is helpful to others.&lt;br /&gt;
&lt;br /&gt;
You can download my source code &lt;a href="https://sites.google.com/site/atwofer/attachments/FixedLengthPlayer-v2.zip?attredirects=0&amp;amp;d=1"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1690810562053338134-2395650736036084079?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/hYCqqlaqiYNylNzeaxN3WcyyTHQ/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hYCqqlaqiYNylNzeaxN3WcyyTHQ/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/hYCqqlaqiYNylNzeaxN3WcyyTHQ/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/hYCqqlaqiYNylNzeaxN3WcyyTHQ/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/JdI_PXFTz98" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/2395650736036084079/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2009/06/streaming-and-playing-fixed-length-mp3s.html#comment-form" title="32 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/2395650736036084079?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/2395650736036084079?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/JdI_PXFTz98/streaming-and-playing-fixed-length-mp3s.html" title="Streaming and playing fixed-length MP3s using the iPhone SDK" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><thr:total>32</thr:total><feedburner:origLink>http://www.atwofer.com/2009/06/streaming-and-playing-fixed-length-mp3s.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEAMQ3k9cSp7ImA9Wx9VEUs.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-3531715193542237656</id><published>2009-02-24T13:15:00.000-08:00</published><updated>2011-01-27T13:13:02.769-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-27T13:13:02.769-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ActionScript" /><category scheme="http://www.blogger.com/atom/ns#" term="ErrorEvent" /><category scheme="http://www.blogger.com/atom/ns#" term="FlashPlayer" /><category scheme="http://www.blogger.com/atom/ns#" term="ErrorEvent.errorID" /><title>Getting the errorID from an ErrorEvent in ActionScript</title><content type="html">Our &lt;a href="http://www.episodic.com/" target="_blank"&gt;Episodic Video Player&lt;/a&gt; is written in ActionScript 3.  I'm still fairly new to ActionScript but came up with a solution to a problem the other day that I thought I should share.&lt;br/&gt;&lt;br/&gt;The player loads various files from the server.  In some cases I want to display an error when one of those files can't be found.  I have an error handler that listens for &lt;a href="http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/ErrorEvent.html" target="_blank"&gt;Error Events&lt;/a&gt; and displays an appropriate error message depending on the type of the error event.  For example, I know that error code 2032 will generally mean in our case that the file could not be found. &lt;br/&gt;&lt;br/&gt;Alright, so this all seems simple enough except for one problem.  When I looked at the &lt;a href="http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/ErrorEvent.html#errorID" target="_blank"&gt;ASDocs for ErrorEvent.errorID&lt;/a&gt; I noticed that that property is not available for Flash Player 9.  Fortunately, ErrorEvent extends &lt;a href="http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/TextEvent.html" target="_blank"&gt;TextEvent&lt;/a&gt; which has text property I can access.  In the case of an ErrorEvent the text property contains a message that includes the error ID.  Therefore, I just wrote my own getter to grab the error ID.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;public function get errorID():int {&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;if ((text != null) &amp;&amp; (text.indexOf("Error #") == 0) &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;&amp;&amp; (text.length &gt; 7)) {&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;// The Error IDs always seem to be 4 digits&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;return parseInt(errorEvent.text.substr(7, errorEvent.text.length - 7));&lt;/span&gt;&lt;br/&gt;}&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1690810562053338134-3531715193542237656?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/g6TR4J9iWiPoN1HzqJJvIQmf8u0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/g6TR4J9iWiPoN1HzqJJvIQmf8u0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/g6TR4J9iWiPoN1HzqJJvIQmf8u0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/g6TR4J9iWiPoN1HzqJJvIQmf8u0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/QchyrPfWUFE" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/3531715193542237656/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2009/02/getting-errorid-from-errorevent-in.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/3531715193542237656?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/3531715193542237656?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/QchyrPfWUFE/getting-errorid-from-errorevent-in.html" title="Getting the errorID from an ErrorEvent in ActionScript" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.atwofer.com/2009/02/getting-errorid-from-errorevent-in.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEAMQXs_cCp7ImA9Wx9VEUs.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-975566856853377509</id><published>2009-02-17T12:45:00.000-08:00</published><updated>2011-01-27T13:13:00.548-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-27T13:13:00.548-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="ActiveRecord" /><category scheme="http://www.blogger.com/atom/ns#" term="Ruby on Rails" /><category scheme="http://www.blogger.com/atom/ns#" term="before_type_cast" /><category scheme="http://www.blogger.com/atom/ns#" term="alias_method" /><title>Override attribute accessor and before_type_cast</title><content type="html">Our &lt;a href="http://www.episodic.com/" target="_blank"&gt;Episodic Video Publishing Web Application&lt;/a&gt; is written in Ruby on Rails.  I recently came across a situation where I had a model where I wanted to override it's attribute accessors in some cases.  Consider a model that looks like this.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;create_table "podcasts", :force =&amp;gt; true do |t|&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;t.string   "name"&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;t.boolean  "use_show_name", :default =&amp;gt; true&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;t.integer   "show_id"&lt;/span&gt;&lt;br/&gt;end&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;As you can see above that each podcast belongs to a show.  I also have a form for this model and in the name field I want to display the name of the podcast unless "use_show_name" is true then I want to display the name of the show that this podcast belongs to.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;&amp;lt;%= text_field :podcast, :name, :class =&amp;gt; "text", :disabled =&amp;gt; @podcast.use_show_name %&amp;gt;&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;I don't want to put this logic in the ERB template but instead I want to put it in the model so that anyone that calls Podcast.name will get the appropriate value depending on the "use_show_name".&lt;br/&gt;&lt;br/&gt;&lt;code&gt;class Podcast &amp;lt; ActiveRecord::Base&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;belongs_to :show&lt;/span&gt;&lt;br/&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;def name&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;return self.use_show_name ? self.show.name : read_attribute(:name)&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;end&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;alias_method :name_before_type_cast, :name&lt;/span&gt;&lt;br/&gt;end&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;So there a few things going on above.&lt;br/&gt;&lt;ul&gt;&lt;br/&gt;	&lt;li&gt;We override the "name" method by simply implementing it.  This works because ActiveRecord using method_missing to implement it's "name" getter but since there is now a "name" method, method_missing will not be invoked.&lt;/li&gt;&lt;br/&gt;	&lt;li&gt;In the method we check "use_show_name" to see what value to return.  If it is true we get the value from the show object.  Otherwise, we call read_attribute to get the value of the name attribute that stored in the DB.&lt;/li&gt;&lt;br/&gt;	&lt;li&gt;Lastly, we alias the name method as "name_before_type_cast" since this is the method that is called by FormHelper.text_field.  ActiveRecord form helpers call the before_type_cast so that they can get the string value of the attribute but we want it to get the value from our "name" method.&lt;/li&gt;&lt;br/&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1690810562053338134-975566856853377509?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/gtaYaXER4mU9e2m-8tYVl70PaVg/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gtaYaXER4mU9e2m-8tYVl70PaVg/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/gtaYaXER4mU9e2m-8tYVl70PaVg/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/gtaYaXER4mU9e2m-8tYVl70PaVg/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/fKYswNEgm7I" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/975566856853377509/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2009/02/override-attribute-accessor-and.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/975566856853377509?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/975566856853377509?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/fKYswNEgm7I/override-attribute-accessor-and.html" title="Override attribute accessor and before_type_cast" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.atwofer.com/2009/02/override-attribute-accessor-and.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEADRnc_fSp7ImA9Wx9VEUs.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-5624120492982399529</id><published>2009-01-02T06:41:00.000-08:00</published><updated>2011-01-27T13:12:57.945-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-27T13:12:57.945-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="Mootools" /><category scheme="http://www.blogger.com/atom/ns#" term="HTML" /><title>Prompt User About Unsaved Changes Using Mootools</title><content type="html">&lt;a href="http://mootools.net/docs/" target="_blank"&gt;Mootools&lt;/a&gt; has a very useful method &lt;a href="http://mootools.net/docs/Element/Element#Element:toQueryString" target="_blank"&gt;toQueryString&lt;/a&gt; on the Form element class.  In short, this method takes all the form fields and builds a query string generally used when sending form data to the server.  However, I found another use for this method that I thought I would share.&lt;br/&gt;&lt;br/&gt;For our &lt;a href="http://www.episodic.com/" target="_blank"&gt;Episodic Video Publishing Web Application&lt;/a&gt; we have a number of pages that include a form.  If the user tries to navigate from a form that has unsaved changes then we want to prompt the user that they have unsaved changes and that these changes will be lost if they navigate away.  There are various ways that you can detect if the user has modified the page's form but the easiest I've found is to make use of &lt;a href="http://mootools.net/docs/Element/Element#Element:toQueryString" target="_blank"&gt;Form.toQueryString&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;First, create a function that stores the form state in a hidden input.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;getOrCreateInitialFormState: function(form) {&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;var formInitialState = null;&lt;/span&gt;,&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;var formInitialState = $('form_initial_state');&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;if (!formInitialState)&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;formInitialState = new Element('input', {&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;id: 'form_initial_state',&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;type: 'hidden',&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;value: form.toQueryString()&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;}).inject(form, 'before');&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;return formInitialState;&lt;/span&gt;&lt;br/&gt;}&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;So when the page loads record the current form state and hook into the click event for any links on your page and check if the form state has changed.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;window.addEvent('domready', function()&lt;br/&gt;{&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;// Grab the form&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;var form = $('my_form');&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;// Store the form state in a hidden input&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;getOrCreateInitialFormState(form);&lt;/span&gt;&lt;br/&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;// Capture any clicks that would cause the user to navigate away&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;$$('a').addEvent('click', function(e) {&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;if (form.toQueryString() != getOrCreateInitialFormState(form) {&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 30px;"&gt;e.stop();&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 30px;"&gt;var result = confirm('You have unsaved changes.' +&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 50px;"&gt;'  Are you sure you want to navigate away from this page?');&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 30px;"&gt;if (result == true) {&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;// The user wants to navigate&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;window.location.href = a.get('href');&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 30px;"&gt;}&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;} &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;});&lt;/span&gt;&lt;br/&gt;});&lt;br/&gt;&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;That's it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1690810562053338134-5624120492982399529?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/v9CUPmpgVrH_joqZDUoy46zarD8/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/v9CUPmpgVrH_joqZDUoy46zarD8/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/v9CUPmpgVrH_joqZDUoy46zarD8/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/v9CUPmpgVrH_joqZDUoy46zarD8/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/MmQX7ftPeCo" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/5624120492982399529/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2009/01/prompt-user-about-unsaved-changes-using.html#comment-form" title="2 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/5624120492982399529?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/5624120492982399529?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/MmQX7ftPeCo/prompt-user-about-unsaved-changes-using.html" title="Prompt User About Unsaved Changes Using Mootools" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><thr:total>2</thr:total><feedburner:origLink>http://www.atwofer.com/2009/01/prompt-user-about-unsaved-changes-using.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEADRno8fyp7ImA9Wx9VEUs.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-2221640420693980137</id><published>2008-12-16T07:39:00.000-08:00</published><updated>2011-01-27T13:12:57.477-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-27T13:12:57.477-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="MethodInterceptor" /><category scheme="http://www.blogger.com/atom/ns#" term="Java" /><category scheme="http://www.blogger.com/atom/ns#" term="Spring Framework" /><category scheme="http://www.blogger.com/atom/ns#" term="AOP" /><category scheme="http://www.blogger.com/atom/ns#" term="MethodInvocation" /><title>Using AOP to Make Calls Asynchronous in Java</title><content type="html">I am working on a project called &lt;a href="http://www.scoreout.com"&gt;ScoreOut&lt;/a&gt; that consists of an application that runs on a phone that interacts with a server via a REST API.  The server is written in Java and makes heavy use of the &lt;a href="http://www.springframework.org/"&gt;Spring Framework&lt;/a&gt;.  I wanted to start tracking some of the actions that a user takes on the phone and recording those actions in our analytics tables.  For example, we want to record when a user logs in or requests a golf course.  However, we do not want to slow down the response to the client since the customer is probably out on a golf course at that time.  Therefore, it seemed to makes sense to perform any auditing asynchronously.&lt;br/&gt;&lt;br/&gt;The server is broken up into services and I already had a service that did auditing.  I added two new methods to it recordClientLogin and recordCourseRetrieval.  I wanted these methods to be run asynchronously but I wanted the asychronousness (if this isn't a word it should be) to be reusable and easily enabled or disabled (easier to unit test when disabled).  This immediately let me to use AOP so I wrote an interceptor.&lt;br/&gt;&lt;br/&gt;&lt;code&gt;&lt;br/&gt;package com.scoreout.service.interceptor;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;import org.aopalliance.intercept.MethodInterceptor;&lt;br/&gt;import org.aopalliance.intercept.MethodInvocation;&lt;br/&gt;import org.apache.commons.logging.Log;&lt;br/&gt;import org.apache.commons.logging.LogFactory;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;/**&lt;br/&gt;&lt;span style="padding-left: 8px;"&gt;* Intecepts a call and makes it asynchronous. If there is an error it is&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 8px;"&gt;* simply logged.&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 8px;"&gt;*/&lt;/span&gt;&lt;br/&gt;public class AsynchronizeInterceptor implements MethodInterceptor&lt;br/&gt;{&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;/**&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 28px;"&gt;* Logger&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 28px;"&gt;*/&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;private static final Log LOG = LogFactory.&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;getLog(AsynchronizeInterceptor.class);&lt;/span&gt;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;/**&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 28px;"&gt;* This method will always return null since the method is&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 28px;"&gt;* invoked asynchronously.&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 28px;"&gt;*&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 28px;"&gt;* @see org.aopalliance.intercept.MethodInterceptor&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 28px;"&gt;*          #invoke(org.aopalliance.intercept.MethodInvocation)&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 28px;"&gt;*/&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;public Object invoke(final MethodInvocation invocation) throws Throwable&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;{&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;Thread thread = new Thread()&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;{&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 60px;"&gt;public void run()&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 60px;"&gt;{&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 80px;"&gt;try&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 80px;"&gt;{&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 100px;"&gt;invocation.proceed();&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 80px;"&gt;} catch (Throwable e)&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 80px;"&gt;{&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 100px;"&gt;LOG.warn("Asynchronous method invocation failed.", e);&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 80px;"&gt;}&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 60px;"&gt;}&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;};&lt;/span&gt;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;thread.start();&lt;/span&gt;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;return null;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;}&lt;/span&gt;&lt;br/&gt;}&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;So this inteceptor will make any method it intercepts asynchronous running the method on a separate thread and returning immediately.  So now I just needed to add the point cut to the spring application context.&lt;br/&gt;&lt;code&gt;&lt;br/&gt;&amp;lt;!-- Turns methods calls into asynchronous method calls --&amp;gt;&lt;br/&gt;&amp;lt;bean id="asynchronizeInterceptor"&lt;br/&gt;&lt;span style="padding-left: 30px;"&gt;class="com.scoreout.service.interceptor.AsynchronizeInterceptor"&amp;gt;&lt;/span&gt;&lt;br/&gt;&amp;lt;/bean&amp;gt;&lt;br/&gt;&lt;span&gt; &lt;/span&gt;&lt;br/&gt;&amp;lt;!-- Intercept caddie event recording method calls --&amp;gt;&lt;br/&gt;&amp;lt;bean id="asynchronizePointCut"&lt;br/&gt;&lt;span style="padding-left: 30px;"&gt;class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;&amp;lt;property name="advice"&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 40px;"&gt;&amp;lt;ref local="asynchronizeInterceptor" /&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;&amp;lt;property name="patterns"&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;&amp;lt;list&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 30px;"&gt;&amp;lt;value&amp;gt;com.scoreout.service.Audit.recordCourseRetrieval*&amp;lt;/value&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 30px;"&gt;&amp;lt;value&amp;gt;com.scoreout.service.Audit.recordClientLogin*&amp;lt;/value&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 20px;"&gt;&amp;lt;/list&amp;gt;&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;&amp;lt;/property&amp;gt;&lt;/span&gt;&lt;br/&gt;&amp;lt;/bean&amp;gt;&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;Now anytime I call com.scoreout.service.Audit.recordCourseRetrieval or com.scoreout.service.Audit.recordClientLogin the call will return immediately and the method will be run in the background.  For testing I can easily make these methods synchronous by either removing them from the application context or providing a new application context that does not include this pointcut (I generally prefer the latter).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1690810562053338134-2221640420693980137?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/_EP4Asz9DFEfixvUcv5nxpcyzt0/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_EP4Asz9DFEfixvUcv5nxpcyzt0/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/_EP4Asz9DFEfixvUcv5nxpcyzt0/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/_EP4Asz9DFEfixvUcv5nxpcyzt0/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/gADU58GWPmM" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/2221640420693980137/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2008/12/using-aop-to-make-calls-asynchronous-in.html#comment-form" title="1 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/2221640420693980137?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/2221640420693980137?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/gADU58GWPmM/using-aop-to-make-calls-asynchronous-in.html" title="Using AOP to Make Calls Asynchronous in Java" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><thr:total>1</thr:total><feedburner:origLink>http://www.atwofer.com/2008/12/using-aop-to-make-calls-asynchronous-in.html</feedburner:origLink></entry><entry gd:etag="W/&quot;CEADRn09fCp7ImA9Wx9VEUs.&quot;"><id>tag:blogger.com,1999:blog-1690810562053338134.post-6392126191080477823</id><published>2008-12-10T03:09:00.000-08:00</published><updated>2011-01-27T13:12:57.364-08:00</updated><app:edited xmlns:app="http://www.w3.org/2007/app">2011-01-27T13:12:57.364-08:00</app:edited><category scheme="http://www.blogger.com/atom/ns#" term="JavaScript" /><category scheme="http://www.blogger.com/atom/ns#" term="Mootools" /><category scheme="http://www.blogger.com/atom/ns#" term="IE7" /><category scheme="http://www.blogger.com/atom/ns#" term="CSS" /><category scheme="http://www.blogger.com/atom/ns#" term="HTML" /><title>IE7 Tricks</title><content type="html">Hey, my first post and I already spent an hour setting up wordpress so I'll keep it short.  Recently, we did an overhaul of our video management web application at &lt;a href="http://www.episodic.com"&gt;Episodic&lt;/a&gt;.  Like any sane web developer I did all my testing in Firefox and then found myself with one day left before pushing to production and I had yet to look at our site in IE7 (IE6 not supported).&lt;br/&gt;&lt;br/&gt;So I RDC'd to our one Windows box and, uh oh, it don't look so good and there are a bunch of JS errors.  Here were a few of the main problems that I had to solve for IE.&lt;br/&gt;&lt;br/&gt;&lt;strong&gt;Trailing Commas&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;IE does not like trailing commas in things like object declarations.  For example, you will get a very general JS error that points to a line that has nothing to do with the problem if you have the following:&lt;br/&gt;&lt;br/&gt;&lt;code&gt;var myObject = {&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;foo: 'bar'&lt;/span&gt;,&lt;br/&gt;&lt;span style="padding-left: 10px;"&gt;baz: anotherObject&lt;span style="color: #800000;"&gt;,&lt;/span&gt;&lt;/span&gt;&lt;br/&gt;};&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;&lt;strong&gt;Positioning and Z-Index&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;Our site has a lot of fancy drop-down menus.  The menu is absolutely positioned within some relatively positioned element and then given a z-index so that it appears overtop of other elements on the page.  However, this was a problem on IE when I noticed that my menu would actually appear behind some elements on the page.  In particular, the menu would appear behind other elements that have "position:relative".&lt;br/&gt;&lt;br/&gt;This took a while for me to figure out.  Eventually it came down to this: If an absolutely positioned element has a z-index then it's containing relatively positioned element must specify a z-index (z-index:1) in order for the absolutely positioned element to be able to appear on top of other relatively positioned elements.&lt;br/&gt;&lt;br/&gt;I did not come up with this on my own but it did take me a while to find this excellent page called "&lt;a href="http://css-class.com/test/css/visformatting/layers/z-index.htm"&gt;Z-Indexing with position relative and absolute&lt;/a&gt;".&lt;br/&gt;&lt;br/&gt;&lt;strong&gt;Mootools Events Targets&lt;/strong&gt;&lt;br/&gt;&lt;br/&gt;I LOVE &lt;a href="http://www.mootools.net"&gt;Mootools&lt;/a&gt;.  Eventually I will do another post about some Mootools tricks I have picked up but for now I'll stay within the context of this post.  I have a lot of JS code that looks like the following:&lt;br/&gt;&lt;br/&gt;&lt;code&gt;someElement.addEvent('click', function(event) {&lt;br/&gt;&lt;span style="padding-left:10px"&gt;if (event.target.get('tag') == 'li'&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left:20px"&gt;event.target.dispose();&lt;/span&gt;&lt;br/&gt;});&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;However, the Mootools documentation says that &lt;span style="text-decoration: underline;"&gt;the event target, is not extended with $ for performance reasons&lt;/span&gt; (which actually only seems to be true in IE and would result in JS errors).  Therefore, the above case should be written as:&lt;br/&gt;&lt;br/&gt;&lt;code&gt;someElement.addEvent('click', function(event) {&lt;br/&gt;&lt;span style="padding-left:10px"&gt;if ($(event.target).get('tag') == 'li'&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left:20px"&gt;$(event.target).dispose();&lt;/span&gt;&lt;br/&gt;});&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;To avoid writing code like this everywhere I extended the Event class as follows:&lt;br/&gt;&lt;br/&gt;&lt;code&gt;Event.implement({&lt;br/&gt;&lt;span style="padding-left:10px"&gt;getTarget: function() {&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left:20px"&gt;if (!this.extendedTarget)&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left:30px"&gt;this.extendedTarget = $(this.target);&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left:20px"&gt;return this.extendedTarget;&lt;/span&gt;&lt;br/&gt;});&lt;/code&gt;&lt;br/&gt;&lt;br/&gt;This left me with:&lt;br/&gt;&lt;br/&gt;&lt;code&gt;someElement.addEvent('click', function(event) {&lt;br/&gt;&lt;span style="padding-left:10px"&gt;if (event.getTarget().get('tag') == 'li'&lt;/span&gt;&lt;br/&gt;&lt;span style="padding-left:20px"&gt;event.getTarget().dispose();&lt;/span&gt;&lt;br/&gt;});&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1690810562053338134-6392126191080477823?l=www.atwofer.com' alt='' /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="http://feedads.g.doubleclick.net/~a/PjxACijjFNVDnDWzhmLA_6aOm0k/0/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PjxACijjFNVDnDWzhmLA_6aOm0k/0/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;br/&gt;
&lt;a href="http://feedads.g.doubleclick.net/~a/PjxACijjFNVDnDWzhmLA_6aOm0k/1/da"&gt;&lt;img src="http://feedads.g.doubleclick.net/~a/PjxACijjFNVDnDWzhmLA_6aOm0k/1/di" border="0" ismap="true"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/Atwofer/~4/XFao1Q64Q_8" height="1" width="1"/&gt;</content><link rel="replies" type="application/atom+xml" href="http://www.atwofer.com/feeds/6392126191080477823/comments/default" title="Post Comments" /><link rel="replies" type="text/html" href="http://www.atwofer.com/2008/12/ie7-tricks.html#comment-form" title="0 Comments" /><link rel="edit" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/6392126191080477823?v=2" /><link rel="self" type="application/atom+xml" href="http://www.blogger.com/feeds/1690810562053338134/posts/default/6392126191080477823?v=2" /><link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/Atwofer/~3/XFao1Q64Q_8/ie7-tricks.html" title="IE7 Tricks" /><author><name>Randy</name><uri>http://www.blogger.com/profile/14854289049802188721</uri><email>noreply@blogger.com</email><gd:image rel="http://schemas.google.com/g/2005#thumbnail" width="32" height="32" src="http://3.bp.blogspot.com/_RAJECGB1Wk8/TUHbPY3q86I/AAAAAAAAAfY/iBuN_iA6LKE/s220/me.jpg" /></author><thr:total>0</thr:total><feedburner:origLink>http://www.atwofer.com/2008/12/ie7-tricks.html</feedburner:origLink></entry></feed>

