<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5552322557063399878</id><updated>2012-01-26T06:35:27.773-08:00</updated><title type='text'>Jeffrey Phillips Blog</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>26</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-558907739337363734</id><published>2012-01-25T15:37:00.000-08:00</published><updated>2012-01-26T06:35:27.788-08:00</updated><title type='text'>Configuring Web Logic 10.3.5 and log4j for your Java Application</title><content type='html'>I recently spent several days trying to track this down why &lt;a href="http://logging.apache.org/log4j/"&gt;Apache log4j &lt;/a&gt;for my Java application was working when deployed to &lt;a href="http://www.oracle.com/technetwork/middleware/weblogic/overview/index.html"&gt;Oracle WebLogic Server 10.3.5&lt;/a&gt;.  I am posting my findings here in the hopes of saving others some time.  I found several blogs that talked about this, but the end result was that log4j was still not working correctly.  There was always one step missing. A co-worker and I finally figured it out for both Linux and Windows.&lt;br /&gt;&lt;br /&gt;1. Copy wllog4j.jar from your WebLogic server/lib directory and place it in your domain_root/lib folder.&lt;br /&gt;2. Copy your version of log4j.jar to your domain_root/lib folder.&lt;br /&gt;3. Copy your log4j.xml to your domain_root folder. &lt;br /&gt;4. Log in to the your WebLogic admin server console.  Click on Servers -&amp;gt; Admin Server -&amp;gt; Logging.  Click on advanced mode, and change the logging implementation from JDK to Log4J.  Save your changes.&lt;br /&gt;&lt;br /&gt;Most of the blogs had these items, but the one critical piece that was missing:&lt;br /&gt;5. (Linux)Edit the setDomainEnv.sh file in the domain_root/bin directory. and find the following code:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;if [ "${LOG4J_CONFIG_FILE}" != "" ] ; then&lt;br /&gt;       JAVA_PROPERTIES="${JAVA_PROPERTIES} -Dlog4j.configuration=file:${LOG4J_CONFIG_FILE}"&lt;br /&gt;       export JAVA_PROPERTIES&lt;br /&gt;fi&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Insert the following above those lines of code(replacing the path with the one for your system's domain_root directory:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;LOG4J_CONFIG_FILE=”/u01/app/oracle/middleware/user_projects/domains/base_domain/log4j.xml”&lt;br /&gt;Export LOG4J_CONFIG_FILE&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;5. (Windows)&lt;br /&gt;Edit the setDomainEnv.cmd file in the domain_root/bin directory. and find the following code:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;if NOT "%LOG4J_CONFIG_FILE%"=="" (                                                          &lt;br /&gt;            set JAVA_PROPERTIES=%JAVA_PROPERTIES% -Dlog4j.configuration=file:%LOG4J_CONFIG_FILE%&lt;br /&gt;)&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Insert the following above those lines of code(replacing the path with the one for your system's domain_root directory:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;set LOG4J_CONFIG_FILE=”C:\Oracle\Middleware\user_projects\domains\base_domain\log4j.xml”&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;6. Activate the changes and restart the admin server.&lt;br /&gt;&lt;br /&gt;Here is a sample log4j.xml file.  Obviously, you will need to update it for a valid location for your file appender, and replace yourcompany and yourproject with valid values.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;br /&gt;&amp;lt;!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"&amp;gt;&lt;br /&gt;&amp;lt;log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false"&amp;gt;&lt;br /&gt;   &amp;lt;!--====================================================================--&amp;gt;&lt;br /&gt;   &amp;lt;!--    Appenders     --&amp;gt;&lt;br /&gt;   &amp;lt;!-- %d{dd-MMM-yy HH:mm:ss} %-5p [%c{1}] %m%n  --&amp;gt;&lt;br /&gt;    &amp;lt;appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender"&amp;gt;&lt;br /&gt;        &amp;lt;param name="Target" value="System.out"/&amp;gt;&lt;br /&gt;        &amp;lt;layout class="org.apache.log4j.PatternLayout"&amp;gt;&lt;br /&gt;            &amp;lt;param name="ConversionPattern" value="[%p] - %m (%F:%M:%L)%n"/&amp;gt;&lt;br /&gt;            &amp;lt;!--param name="ConversionPattern" value="%d{ISO8601} %p [%c{1}] - %m (%F:%M:%L)%n"/--&amp;gt;&lt;br /&gt;        &amp;lt;/layout&amp;gt;&lt;br /&gt;    &amp;lt;/appender&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;appender name="FILE" class="org.apache.log4j.RollingFileAppender"&amp;gt;&lt;br /&gt;        &amp;lt;param name="File" value="/temp/logs/yourproject.log"/&amp;gt;&lt;br /&gt;        &amp;lt;param name="Append" value="true"/&amp;gt;&lt;br /&gt;        &amp;lt;layout class="org.apache.log4j.PatternLayout"&amp;gt;&lt;br /&gt;            &amp;lt;param name="ConversionPattern" value="%d{ISO8601} %t %-5p %c{2} - %m%n"/&amp;gt;&lt;br /&gt;        &amp;lt;/layout&amp;gt;&lt;br /&gt;    &amp;lt;/appender&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;!--====================================================================--&amp;gt;&lt;br /&gt;    &amp;lt;!--    Logging Levels     --&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;category name="com.yourcompany.yourproject"&amp;gt;&lt;br /&gt;      &amp;lt;priority value="DEBUG"/&amp;gt;&lt;br /&gt;    &amp;lt;/category&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;category name="org.apache"&amp;gt;&lt;br /&gt;        &amp;lt;priority value="WARN"/&amp;gt;&lt;br /&gt;    &amp;lt;/category&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;category name="org.springframework"&amp;gt;&lt;br /&gt;        &amp;lt;priority value="WARN"/&amp;gt;&lt;br /&gt;    &amp;lt;/category&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;!--===================================================================--&amp;gt;&lt;br /&gt;    &amp;lt;!--    Appender Assignments     --&amp;gt;&lt;br /&gt;    &amp;lt;root&amp;gt;&lt;br /&gt;        &amp;lt;priority value="ERROR"/&amp;gt;&lt;br /&gt;        &amp;lt;appender-ref ref="FILE"/&amp;gt;&lt;br /&gt;    &amp;lt;/root&amp;gt;&lt;br /&gt;&amp;lt;/log4j:configuration&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Again, I hope that this will save someone the pain I went through to track this down.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-558907739337363734?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/558907739337363734/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=558907739337363734' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/558907739337363734'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/558907739337363734'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2012/01/configuring-web-logic-1035-and-log4j.html' title='Configuring Web Logic 10.3.5 and log4j for your Java Application'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-5686355756004497488</id><published>2011-11-22T14:31:00.000-08:00</published><updated>2011-11-22T17:11:45.360-08:00</updated><title type='text'>Exploring Spring Integration &amp; Spring Web Service, Part 1</title><content type='html'>I have been learning &lt;a href="http://http//www.springsource.org/spring-integration"&gt;Spring Integration&lt;/a&gt; and &lt;a href="http://http://www.springsource.org/spring-web-services"&gt;Spring Web Services&lt;/a&gt; on the project I am on.  The more I learn the more I like the framework, but I have to say it has not been easy to make progress.  The number of resources available is lacking. I thought I would spend the next several blogs covering some of the information I have been learning.&lt;br /&gt;&lt;br /&gt;I needed to do was expose four SOAP Web Services based on a set of WSDL and XSD files provided by the vendor. First thing that had to happen was setting up the web.xml file.  From reading the documentation on the main Spring site, I determined to use MessageDispatcherServlet.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;web.xml&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;&amp;lt;web-app&gt;&lt;br /&gt;   &amp;lt;display-name&gt;My Application Name&amp;lt;/display-name&gt;&lt;br /&gt;   &amp;lt;servlet&gt;&lt;br /&gt;       &amp;lt;servlet-name&gt;si-ws-gateway&amp;lt;/servlet-name&gt;&lt;br /&gt;       &amp;lt;servlet-class&gt;org.springframework.ws.transport.http.MessageDispatcherServlet&amp;lt;/servlet-class&gt;&lt;br /&gt;       &amp;lt;init-param&gt;&lt;br /&gt;           &amp;lt;param-name&gt;contextConfigLocation&amp;lt;/param-name&gt;&lt;br /&gt;           &amp;lt;param-value&gt;classpath:si-ws-gateway-config.xml&amp;lt;/param-value&gt;&lt;br /&gt;       &amp;lt;/init-param&gt;&lt;br /&gt;       &amp;lt;load-on-startup&gt;1&amp;lt;/load-on-startup&gt;&lt;br /&gt;   &amp;lt;/servlet&gt;&lt;br /&gt;   &amp;lt;servlet-mapping&gt;&lt;br /&gt;       &amp;lt;servlet-name&gt;si-ws-gateway&amp;lt;/servlet-name&gt;&lt;br /&gt;       &amp;lt;url-pattern&gt;/*&amp;lt;/url-pattern&gt;    &lt;br /&gt;   &amp;lt;/servlet-mapping&gt;&lt;br /&gt;&amp;lt;/web-app&gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Next, let's take a look at the spring application context file.&lt;br /&gt;&lt;b&gt;si-ws-gateway-config.xml&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;    &amp;lt;bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping"&gt;&lt;br /&gt;        &amp;lt;property name="mappings"&gt;&lt;br /&gt;            &amp;lt;props&gt;&lt;br /&gt;                &amp;lt;prop key="${data.subscriber.url}"&gt;dataGateway&amp;lt;/prop&gt;&lt;br /&gt;                &amp;lt;prop key="${request.status.url}"&gt;statusGateway&amp;lt;/prop&gt;&lt;br /&gt;        &amp;lt;/property&gt;&lt;br /&gt;    &amp;lt;/bean&gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;si:channel id="dataArrivedChannel" /&gt;&lt;br /&gt;    &amp;lt;si-ws:inbound-gateway id="dataGateway" request-channel="dataArrivedChannel" /&gt;&lt;br /&gt;    &amp;lt;stream:stdout-channel-adapter id="stdoutAdaptorWithDefaultCharsetDataArrived" channel="dataArrivedChannel"/&gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;si:channel id="statusChangedChannel" /&gt;&lt;br /&gt;    &amp;lt;si-ws:inbound-gateway id="statusChangedGateway"&lt;br /&gt;                           request-channel="statusChangedChannel"&lt;br /&gt;                           marshaller="marshaller"&lt;br /&gt;                           unmarshaller="marshaller"/&gt;&lt;br /&gt;    &amp;lt;si:service-activator input-channel="statusChangedChannel"&lt;br /&gt;                          ref="statusServiceEndpoint"&lt;br /&gt;                          method="processStatusMessage"/&gt;   &lt;br /&gt;&lt;br /&gt;    &amp;lt;bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"&gt;&lt;br /&gt;        &amp;lt;property name="classesToBeBound"&gt;&lt;br /&gt;            &amp;lt;list&gt;&lt;br /&gt;                &amp;lt;value&gt;com.ourvendor.StatusUpdate&amp;lt;/value&gt;&lt;br /&gt;            &amp;lt;/list&gt;&lt;br /&gt;        &amp;lt;/property&gt;&lt;br /&gt;    &amp;lt;/bean&gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;bean id="StatusDao" class="com.company.StatusDao"/&gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;bean id="statusServiceEndpoint"&lt;br /&gt;          class="com.company.StatusServiceEndpoint"&gt;&lt;br /&gt;        &amp;lt;constructor-arg ref="statusManager"/&gt;&lt;br /&gt;    &amp;lt;/bean&gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;bean id="statusManager"&lt;br /&gt;          class="com.company.StatusManagerImpl"&gt;&lt;br /&gt;        &amp;lt;constructor-arg ref="statusDao"/&gt;&lt;br /&gt;    &amp;lt;/bean&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Well that seems like a lot of spring configuration, but it eliminates a bunch of Java code.  Let's break down this configuration a bit.  First, the UriEndpointMapping bean.  You might notice that there are some values provided from a properties file.  I will be covering properties in more detail in a future blog.  For now, just know spring replaces the ${data.subscriber.url} and ${request.status.url} with a valid URL value from a properties file. This defines which URLs will be routed to the specified spring integration web services inbound-gateway.  The gateway will place the SOAP message onto the specified channel as a DOMSource object. &lt;br /&gt;&lt;br /&gt;As seen above the dataGateway does not specify a marshaller and unmarshaller which is why the DOMSource object is placed on the channel.  The statusChangedGateway does specify the marshaller and unmarshaller, so when it places the object on the channel, it is not a DOMSource object, but a StatusUpdate object.  As you can see it is very easy to leave the input in it's native format or convert it to a Java Object.  In one case the object was only going to be routed(eventually) to a JMS queue, and did not need to be unmarshalled, in the other case it required unmarshalling so that a service activator endpoint could save the object to the database.  As we will see in a future blog, it would have been nice if there was a way to specify the output to the channel as a string, instead of DOMSource.  This would be a great enhancement to spring integration.&lt;br /&gt;&lt;br /&gt;The service activator is an endpoint that allows a specific method to be called, and passes the object from the incoming channel to the method.  It can also place the object, either unmodified or modified, onto an outgoing channel to be further processed by the system.  The rest of the configuration is very straightforward spring beans with dependency injection.  This works very well if each of the web services have a unique URL, but what happens if all calls come into a single URL.  That will be the topic of the next blog in this series.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-5686355756004497488?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/5686355756004497488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=5686355756004497488' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/5686355756004497488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/5686355756004497488'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2011/11/exploring-spring-integration-spring-web.html' title='Exploring Spring Integration &amp; Spring Web Service, Part 1'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-6796330259231506682</id><published>2011-11-08T13:43:00.000-08:00</published><updated>2011-11-08T14:17:15.163-08:00</updated><title type='text'>Accessing External Properties Files in WebLogic</title><content type='html'>It is not only common to do, but is also a very good practice to put values that may need to change into a properties file.  This creates a central location to modify values. Especially for the configuration a system based on the installation location, etc.  Having the properties file inside the .war is very inconvenient when you want to change it as it causes you to recompile your code, even if it hasn't changed.  I was on a project that was able to pull the property file out of the .war and put it in the JBoss AS conf directory.  The application could still access it. Unfortunately the project I am on now uses WebLogic.  After lots of google searching and asking a few co-workers if they new how to do it, I did find the answer.  &lt;br /&gt;&lt;br /&gt;Simply place the properties file in $WEBLOGICHOME/user_projects/domains/mydomain where mydomain is the name of he domain your app uses.&lt;br /&gt;&lt;br /&gt;Works great and now I can change properties all day long without recompiling.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-6796330259231506682?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/6796330259231506682/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=6796330259231506682' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6796330259231506682'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6796330259231506682'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2011/11/accessing-external-properties-files-in.html' title='Accessing External Properties Files in WebLogic'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-6551453841796955683</id><published>2011-06-28T12:17:00.000-07:00</published><updated>2011-06-28T12:23:59.620-07:00</updated><title type='text'>Remote Desktop &amp; Change Password</title><content type='html'>I ran into a problem where I needed to change the password on an account that was on a server half way around the world.  All I could do was Remote Desktop in to that box, and when I did Ctrl + Alt + Delete, it was changing the password on the box I was physically at, not the one in the RD session.  After some searching I found this solution:  Ctrl + Alt + End.  &lt;br /&gt;&lt;br /&gt;Hope this helps someone.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-6551453841796955683?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/6551453841796955683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=6551453841796955683' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6551453841796955683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6551453841796955683'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2011/06/remote-desktop-change-password.html' title='Remote Desktop &amp; Change Password'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-5723703737147254797</id><published>2011-03-28T11:00:00.000-07:00</published><updated>2011-03-31T07:34:58.569-07:00</updated><title type='text'>Java JDBC Performance vs. iBatis</title><content type='html'>While working on a recent project, I worked with a database that contained 23 billion rows in one table, and several hundred million in another table.  Performance retrieving information was critical for success.  I have used &lt;a href="http://www.springsource.org/"&gt;Spring&lt;/a&gt; and &lt;a href="http://www.mybatis.org/"&gt;iBatis&lt;/a&gt; for several years now and only had a couple of weeks to develop a prototype, so implemented the database access layer using iBatis and spring.  It is quick to implement and easy to maintain. We had several use cases we wanted to test.  Some use cases returned as few as one row, some returned as many as 20 million.  Unfortunately, it was taking too much time to retrieve the information for most of the use cases.  One downside of using iBatis, was that I couldn't determine if the time was spent in the database query, returning the results to Java, or in creating the java objects themselves.  I decided to modify the code to use straight JDBC calls so that way I could time the retrieval separately from converting to Java Objects.&lt;br /&gt;&lt;br /&gt;I did not expect to see much performance improvement converting to straight JDBC. However, just converting to straight JDBC was a substantial performance improvement over using iBatis(30-40% faster). Unfortunately this still wasn't enough.  This was a web application being deployed to &lt;a href="http://www.oracle.com/technetwork/middleware/weblogic/downloads/wls-main-097127.html"&gt;WebLogic&lt;/a&gt;.  After some research a co-worker found that the default number of rows retrieved by the WebLogic data source was 10.  Queries whose results were less than 10, performed very well, but most queries returned thousands or millions of rows, so performance was very poor.  We initial bumped that value to 1000, and saw another 40+% performance improvement.   This in turn prompted some investigation of JDBC, and I found that the default value for JDBC against an oracle database was 10.  Inside the code I increased the fetch rows value to 1000, and saw another 30% performance improvement. I concluded that more was better on the fetch rows setting, so I increased it to 50,000 and found that I now had a new problem:  running out of memory.  After some additional modifications to the memory available to the web application, and reducing the fetch count to 5000, I was able to get a reliable result.  Essentially, a higher fetch count, meant less database connections to retrieve the data.&lt;br /&gt;&lt;br /&gt;Using iBatis is a great way to get a lot done in a short amount of time, and for smaller databases I still prefer to use it.  In addition to being easy to implement, it is also easy to maintain.  However, if you have a large database, then using Straight JDBC might be a good solution.  Don't forget to take fetch size into account both in the application server and in the Java JDBC code when dealing with large databases.  The larger the fetch size, the fewer database connections that will be used, but the more memory that will be required.  I did not investigate setting the fetch size in iBatis, so do not know if that is possible.&lt;br /&gt;&lt;br /&gt;In the Java JDBC code setting the fetch size is done as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;            String query = "SELECT * FROM my_table";&lt;br /&gt;            Connection conn = _dataSource.getConnection();            &lt;br /&gt;            PreparedStatement st = conn.prepareStatement(query);&lt;br /&gt;            st.setFetchSize(5000);&lt;br /&gt;            ResultSet rs = st.executeQuery();&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Some other Java improvements that helped with performance, was reducing the number of java objects being created and destroyed.  This includes making any strings that are reused static member variables so they are only created once and reducing the number of java objects that are being created.  If you are outputting to a file or or a message, perform those operations inline to reduce creating intermediate objects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-5723703737147254797?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/5723703737147254797/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=5723703737147254797' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/5723703737147254797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/5723703737147254797'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2011/03/java-jdbc-performance-vs-ibatis.html' title='Java JDBC Performance vs. iBatis'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-5591231995052331636</id><published>2010-07-28T11:00:00.000-07:00</published><updated>2011-03-28T11:36:02.711-07:00</updated><title type='text'>Jersey and Spring Injection</title><content type='html'>On the project I am on we use &lt;a href="http://www.mybatis.org/"&gt;iBatis&lt;/a&gt;, &lt;a href="http://www.springsource.org/"&gt;Spring&lt;/a&gt;, and &lt;a href="https://jersey.dev.java.net/"&gt;Jersey&lt;/a&gt;.  We have been using spring injection for our DAOs since the beginning, but our version of Jersey at the time did not support spring injection.  To get around this we created factories allowing us to inject beans into the factories. Our REST classes then used the factory to get the beans they were interested in.  As the project grew, so did the factory. When we upgraded to Jersey 1.0.3, we were able to take advantage of a Jersey-Spring jar, that would allow us to spring inject beans directly into our REST classes.&lt;br /&gt;&lt;br /&gt;In our Maven pom.xml file we added the following dependency.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;com.sun.jersey.contribs&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jersey-spring&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;1.0.3&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Then in the constructor of the REST Classes we were able to do the following:  (NOTE: as of Jersey 1.4, @Inject becomes @InjectParam)&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;public class CompanyWS {&lt;br /&gt;    private ICompanyManager _companyManager;&lt;br /&gt;    private ICompanyTypeManager _companyTypeManager;&lt;br /&gt;    public CompanyWS (@InjectParam("companyManager") ICompanyManager companyManager,&lt;br /&gt;                      @InjectParam("companyTypeManager") ICompanyTypeManager companyTypeManager) {&lt;br /&gt;        _companyManager= companyManager;&lt;br /&gt;        _companyTypeManager= companyTypeManager;&lt;br /&gt;    }&lt;br /&gt;...&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The web.xml file also needs to be modified to call out the SpringServlet, instead of the ServletContainer:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;    &amp;lt;servlet&amp;gt;&lt;br /&gt;        &amp;lt;servlet-name&amp;gt;REST Application&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;        &amp;lt;servlet-class&gt;com.sun.jersey.spi.spring.container.servlet.SpringServlet&amp;lt;/servlet-class&amp;gt;&lt;br /&gt;        &amp;lt;init-param&amp;gt;&lt;br /&gt;            &amp;lt;param-name&gt;javax.ws.rs.Application&amp;lt;/param-name&amp;gt;&lt;br /&gt;            &amp;lt;param-value&gt;com.accenture.netcds.api.rest.RegisterResources&amp;lt;/param-value&amp;gt;&lt;br /&gt;        &amp;lt;/init-param&amp;gt;&lt;br /&gt;        &amp;lt;load-on-startup&gt;1&amp;lt;/load-on-startup&amp;gt;&lt;br /&gt;    &amp;lt;/servlet&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One downside of the 1.0.3 Spring-Jersey library is that the automatic finding of the REST classes does not work in websphere 6.1.  This has been reported as a &lt;a href="https://jersey.dev.java.net/issues/show_bug.cgi?id=558"&gt;bug&lt;/a&gt; to Jersey and is supposed to be fixed in the more recent versions.  As a result the REST classes need to be registered in an Application class.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;public class RegisterResources extends Application {    &lt;br /&gt;    public Set&amp;lt;Class&amp;lt;?&amp;gt;&amp;gt; getClasses() {&lt;br /&gt;        Set&amp;lt;Class&amp;lt;?&amp;gt;&amp;gt; s = new HashSet&amp;lt;Class&amp;lt;?&amp;gt;&amp;gt;();        &lt;br /&gt;        s.add(CompanyWS.class);        &lt;br /&gt;        return s;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Spring successfully injected the beans into the REST classes, allowing us to delete the factories we had been using that had grown quite large. Another simplification to make it easier to maintain the software over time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-5591231995052331636?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/5591231995052331636/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=5591231995052331636' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/5591231995052331636'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/5591231995052331636'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2010/07/jersey-and-spring-injection.html' title='Jersey and Spring Injection'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-1875051723080974121</id><published>2010-07-28T10:00:00.000-07:00</published><updated>2010-07-29T08:33:41.039-07:00</updated><title type='text'>Nested iBatis Result Mappings</title><content type='html'>Although, this is not an earth shattering &lt;a href="http://ibatis.apache.org/docs/dotnet/datamapper/ch03s05.html#id383575"&gt;iBatis&lt;/a&gt; discovery, it is a time saver, both in development and maintenance so thought I would share it. &lt;br /&gt;&lt;br /&gt;Let's assume we have a class called CompanyType that contains type_id and type name and another class called Company that among other properties has a CompanyType object companyType included.  &lt;br /&gt;&lt;br /&gt;In the iBatis xml file the result map for CompanyType would look something like&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;&amp;lt;sqlMap namespace="CompanyType"&amp;gt;&lt;br /&gt;    &amp;lt;resultMap id="companyTypeResultMap" class="com.sample.CompanyType"&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_type_id" property="typeId"/&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_type_nm" property="typeName"/&amp;gt;&lt;br /&gt;    &amp;lt;/resultMap&amp;gt;&lt;br /&gt;...&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;In the ibatis xml file for Company the result map would look like:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;&amp;lt;sqlMap namespace="Company"&amp;gt;&lt;br /&gt;    &amp;lt;resultMap id="companyResultMap" class="com.sample.Company"&amp;gt;&lt;br /&gt;        &amp;lt;!-- other company properties here --&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_type_id" property="companyType.typeId"/&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_type_nm" property="companyType.typeName"/&amp;gt;&lt;br /&gt;    &amp;lt;/resultMap&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The problem with this is that if something changes in company type, you have to know to change it in two locations.  Makes it harder to maintain.  Instead though you could do the following:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;&amp;lt;sqlMap namespace="Company"&amp;gt;&lt;br /&gt;    &amp;lt;resultMap id="companyResultMap" class="com.sample.Company"&amp;gt;&lt;br /&gt;        &amp;lt;!-- other company properties here --&amp;gt;&lt;br /&gt;        &amp;lt;result property="companyType" resultMap="CompanyType.companyTypeResultMap"/&amp;gt;&lt;br /&gt;    &amp;lt;/resultMap&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, now if the result map is updated in CompanyType, the changes are automatically reflected in the Company ibatis file.  Hope you find this helpful.  I know I did.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-1875051723080974121?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/1875051723080974121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=1875051723080974121' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/1875051723080974121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/1875051723080974121'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2010/07/nested-ibatis-result-mappings.html' title='Nested iBatis Result Mappings'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-3480172144918663061</id><published>2010-03-29T08:40:00.000-07:00</published><updated>2010-03-29T09:40:46.850-07:00</updated><title type='text'>ExtJS store.find vs store.findExact</title><content type='html'>I recently ran into a problem with an &lt;a href="http://www.extjs.com/deploy/dev/docs/"&gt;ExtJS &lt;/a&gt;combo box, specifically using the find method on the store.  In the application, between uses, the last selected value was remembered so it could be selected automatically the next time the user logged in to the system.  The problem was that the previously selected value was no longer a valid value.  In this case, the behavior was supposed to default back to the first value in the list.   However, it did not do that.  &lt;br /&gt;&lt;br /&gt;After some investigation, I found that store.find was doing a partial match, and in this case there were two names very close to each other.  Let's say the last value selected was "War", and there was another value called "War Room".  If the list was unsorted, it is possible that War Room would be earlier in the list than War, and it would get selected over War.  Another scenario, is that between uses, the value of "War" is removed from the system.  The next time the user logs in it would select War Room automatically, instead of the first value in the list. While the second is not as bad of situation as the first, if that behavior is unintended, it is unwanted.&lt;br /&gt;&lt;br /&gt;During the investigation, I found that there was now a store.findExact method for store. I replaced the store.find method with store.findExact and that cleared the problem right up.  Just wanted to make sure that others were aware of this.  I don't remember ExtJS 2.x having this problem.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-3480172144918663061?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/3480172144918663061/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=3480172144918663061' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/3480172144918663061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/3480172144918663061'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2010/03/extjs-storefind-vs-storefindexact.html' title='ExtJS store.find vs store.findExact'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-2351380954048508948</id><published>2010-03-15T09:55:00.000-07:00</published><updated>2010-03-15T10:59:24.865-07:00</updated><title type='text'>Lost Milliseconds using Date and Timestamp</title><content type='html'>While trouble shooting some date discrepancies in our application, we discovered we were losing milliseconds, when converting from &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/sql/Timestamp.html"&gt;java.sql.Timestamp&lt;/a&gt;, to &lt;a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Date.html"&gt;java.util.Date&lt;/a&gt;.  The following test isolated the problem:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;        DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");&lt;br /&gt;&lt;br /&gt;        try {&lt;br /&gt;            Date datea = dfm.parse("2009-05-13 11:11:03.113");&lt;br /&gt;            Date dateb = dfm.parse("2009-05-13 11:11:03.257 ");&lt;br /&gt;&lt;br /&gt;            assertTrue(dateb.after(datea));&lt;br /&gt;        } catch (Exception e) {&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        Timestamp ts1 = new Timestamp(2010, 3, 15, 9, 9, 9, 111000000);&lt;br /&gt;        Timestamp ts2 = new Timestamp(2010, 3, 15, 9, 9, 9, 222000000);&lt;br /&gt;        assertTrue(ts2.after(ts1));&lt;br /&gt;&lt;br /&gt;        Date datea = (Date)ts1;&lt;br /&gt;        Date dateb = (Date)ts2;&lt;br /&gt;       // NOTE this asserts false because it truncates the milliseconds in the cast.&lt;br /&gt;        assertFalse(dateb.after(datea));  &lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;I am surprised that Java did not handle this.  They could have provided a constructor that would take a java.sql.Timestamp as an argument, so they could preserve the milliseconds. Another solution was they could have kept both the milliseconds field and the nanoseconds field updated, so that the cast could have worked.  Either way, hopefully, this saves someone else the time of tracking this down.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-2351380954048508948?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/2351380954048508948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=2351380954048508948' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/2351380954048508948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/2351380954048508948'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2010/03/lost-milliseconds-using-date-and.html' title='Lost Milliseconds using Date and Timestamp'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-6761128785113685170</id><published>2010-03-01T12:30:00.000-08:00</published><updated>2010-03-01T12:38:28.057-08:00</updated><title type='text'>iBatis "Cache Miss", Part 3 Confguring the CacheModel</title><content type='html'>In this final blog of this series we want to analyze the iBatis &lt;a href="http://ibatis.apache.org/docs/dotnet/datamapper/ch03s08.html"&gt;CacheModel&lt;/a&gt;, and how to configure it correctly.  The &lt;a href="http://phillips4jc.blogspot.com/2010/03/ibatis-cache-miss-part-1-validating.html"&gt;first blog&lt;/a&gt; of the series discussed how to use JBoss logging to verify the problem and the &lt;a href="http://phillips4jc.blogspot.com/2010/03/ibatis-cache-miss-part-2-creating.html"&gt;previous blog&lt;/a&gt; demonstrated how to write a test to automatically test caching.&lt;br /&gt;&lt;br /&gt;Let's start with simple example from the other blogs.  The location_type table.  It has no foreign keys to other tables. We only have to worry about changes to this table.  Let's examine the location_type iBatis CacheModel.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;    &amp;lt;cacheModel id="locationType_cache" &lt;br /&gt;                type="LRU" &lt;br /&gt;                readOnly="true" &lt;br /&gt;                serialize="false"&amp;gt;&lt;br /&gt;        &amp;lt;flushInterval hours="24"/&amp;gt;&lt;br /&gt;        &amp;lt;flushOnExecute statement="LocationType.insertLocationType"/&amp;gt;&lt;br /&gt;        &amp;lt;flushOnExecute statement="LocationType.updateLocationType"/&amp;gt;&lt;br /&gt;        &amp;lt;flushOnExecute statement="LocationType.removeLocationType"/&amp;gt;&lt;br /&gt;        &amp;lt;property name="size" value="10"/&amp;gt;&lt;br /&gt;    &amp;lt;/cacheModel&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;There are four attributes defined for the CacheModel.  The first is the id, this is the name you will reference on the select definitions in the iBatis XML file.  The next is, type, which is the type of caching used.  There two choices: LRU - Least Recently Used and FIFO - First-In-First-Out.  We chose LRU so the most frequently used objects would be kept in the cache.  The next two parameters are the two that determine how the cache model works(or doesn't if misconfigured).&lt;br /&gt;&lt;br /&gt;I ran across a &lt;a href="http://www.mail-archive.com/user-cs@ibatis.apache.org/msg02274.html"&gt;blog&lt;/a&gt;, that helped me understand what I was doing wrong.  As Clinton explains how the readOnly and serialize attributes play together.  I will replicate the meat of his analysis here, but have included the link to his post. &lt;br /&gt;&lt;br /&gt;Setting readOnly to false, which means it can be modified, and serialize to false the data is not able to be serialized forces iBatis to limit the caching to the current session request.  This essentially means that a second request from the client, will not use the cache.  This was the situation we found ourselves in.&lt;br /&gt;&lt;br /&gt;There are two possible configurations that will allow caching to work.  You will have to decide which is appropriate for your project.  The first is readOnly=true, serialize=false.  This will cache the objects for in one cache accessed by all users.  This assumes that the objects are readOnly and will not change, or that if they change you will have the appropriate flushOnExecute statements defined.  &lt;br /&gt;&lt;br /&gt;The second configuration you can use is readOnly=false, serialize=true.  This will allow each user to have their own cache. This allows the tables to be updated with out negatively affecting the other users.  The downside of this approach is memory usage.  With each individual having their own cache, you can run yourself out of memory.  &lt;br /&gt;&lt;br /&gt;On our project we went with the single cache shared among all users.  This placed the burden on us to manage the cache model.  How do you do that?  There are some other properties that can be set for the CacheModel.  One of them is size of the cache for a given table.  It will keep the most frequently used objects in the cache up to the quantity specified.  Other parameters, include flushInterval.  This allows you to say if this hasn't been flushed in N hours, flush it anyway.   The finest control is telling the cache model what statements to flush on, using the flushOnExecute properties.  &lt;br /&gt;&lt;br /&gt;How do you know what statements need to cause the cache to flush?  If it caused the database to change, you have to flush the cache.  If you add a new record, modify an existing record, or delete a record you need to flush the cache.  By examining the CacheModel definition above, you will see that it does just that.&lt;br /&gt;&lt;br /&gt;That was the simple case. What if your table joins to other tables in it's select statements? Then cache management becomes more complex.  You need to flush not only when your table contents are modified, but also when the contents of any table you are joined to changes.  Consider the following CacheModel:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;    &amp;lt;cacheModel id="locations_cache" &lt;br /&gt;                type="LRU" &lt;br /&gt;                readOnly="true" &lt;br /&gt;                serialize="false"&gt;&lt;br /&gt;        &amp;lt;flushInterval hours="24"/&amp;gt;&lt;br /&gt;        &amp;lt;flushOnExecute statement="Location.insertLocation"/&amp;gt;&lt;br /&gt;        &amp;lt;flushOnExecute statement="Location.updateLocation"/&amp;gt;&lt;br /&gt;        &amp;lt;flushOnExecute statement="Location.deleteLocation"/&amp;gt;&lt;br /&gt;        &amp;lt;flushOnExecute statement="LocationType.insertLocationType"/&amp;gt;&lt;br /&gt;        &amp;lt;flushOnExecute statement="LocationType.updateLocationType"/&amp;gt;&lt;br /&gt;        &amp;lt;flushOnExecute statement="LocationType.removeLocationType"/&amp;gt;&lt;br /&gt;        &amp;lt;property name="size" value="250"/&amp;gt;&lt;br /&gt;    &amp;lt;/cacheModel&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Location contains a reference to what its location type is, and subsequently all the selects performed on location join to location type to get the type information.  We ran into a problem right after we enabled caching that when locations were cached, and the location_type had the name updated, it would not be reflected in the cache for 24hrs.  After investigating we realized that the location cache was not being cleared out when location types were modified.&lt;br /&gt;&lt;br /&gt;This forced us to start using iBatis namespace so we could reference one iBatis XML statements within another.  In the CacheModel above you can see that the cache will be flushed anytime the location or location type table is modified.&lt;br /&gt;&lt;br /&gt;Our project uses Spring 2.5.6 and iBatis 2.3.4.  As a result of this finding, we also found a problem with iBatis 2.3.4.  It does not support transaction caching.  What this means is that, even though spring rolls back the transactions after each test method, if the cache was updated it is not flushed.  We discovered this when running the automated test suite and they were failing.&lt;br /&gt;&lt;br /&gt;To see this problem you can write a test class that has two tests.  In the first test save a new record, and validate that it was saved.  At the end of the test the database is rolled back. However the cache will still contain the record. in the second test method, do a get all and record the number of records returned.  Let's say it was 2.  Save a new record, which forces the cache to flush and get all the records again.  If all was working as expected, the count would now be 3.  However, the cache still had the record from the first test in it, when the save is done and the cache is flushed, the get will again return 2.&lt;br /&gt;&lt;br /&gt;That's with tests.  Can this happen in a live system?  The answer is yes.  If the saves are complex in nature(ie. more than one dao call required to save the information) and their are select's performed at different points during the save process that update the cache, if an error occurs before the save has completely succeeded and the transaction is rolled back.  The database will be cleaned up, but the cache will still contain the records it attempted to save.&lt;br /&gt;&lt;br /&gt;iBatis 3 is supposed to solve that problem, however, there is no Spring support for iBatis 3 yet.  They have it planned for Spring version 3.1.  For now, you must be very careful using cache on complex save operations.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-6761128785113685170?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/6761128785113685170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=6761128785113685170' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6761128785113685170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6761128785113685170'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2010/03/ibatis-cache-miss-part-3-confguring.html' title='iBatis &quot;Cache Miss&quot;, Part 3 Confguring the CacheModel'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-7974482115751282381</id><published>2010-03-01T12:25:00.000-08:00</published><updated>2010-03-01T12:33:25.440-08:00</updated><title type='text'>iBatis "Cache Miss", Part 2 Creating automated tests</title><content type='html'>In &lt;a href="http://phillips4jc.blogspot.com/2010/03/ibatis-cache-miss-part-1-validating.html"&gt;part 1&lt;/a&gt;, of this series we discussed how to set up the JBoss logging so that you can verify whether or not your iBatis caching is working.  Once you get caching working, how do you prevent it from being broken by future modifications.  It is important to create some automated integration tests, that will fail if the caching is broken.  &lt;br /&gt;&lt;br /&gt;You may ask does this make sense, after all iBatis is a third party Jar.  Why would I want to test their code?  If you can't trust third party jar files, where does the testing end.  This is a valid argument.  After all you would never write a test to verify that Java set/get methods work as expected.  However, in the case of iBatis caching you are not so much testing third party software, although it will certainly do that, as you are testing that you configured the third party software correctly.  As an added benefit, if you updated iBatis and they had broken the cacheModel, you would know immediately.&lt;br /&gt;&lt;br /&gt;Spring provides a autowire capability for injecting beans into your test classes for integration tests.  So we wanted to simulate the client behavior in an integration test.  We injected the dao bean for a simple table in the database.  We wrote two tests.&lt;br /&gt;&lt;br /&gt;In the first test, we performed a get immediately that should have set up the cache.  Next, making use of the &lt;a href="http://forum.springsource.org/showthread.php?t=62366"&gt;Spring SimpleJdbcInsert&lt;/a&gt; class we bypassed our iBatis bean, to do an insert into the table.  We did this so that iBatis would not flush it's cache on insert.  Now if the cacheModel is set up correctly, when you go get the list of records again, it will retrieve it from the cache and it will not include this new record.  Next, using the iBatis dao bean, we saved another record to this table.  Now when the get all command is executed it should grow by 2 records(the one inserted through spring, and the one inserted through iBatis). Here is the test method.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;    public void testDatabaseCaching() {&lt;br /&gt;        List&amp;lt;LocationType&amp;gt; cachedTypes = _locationTypeDao.getAll();&lt;br /&gt;        int numTypes = cachedTypes.size();&lt;br /&gt;&lt;br /&gt;        SimpleJdbcInsert lJdbcInsert = new simpleJdbcInsert(_dataSource)&lt;br /&gt;         .withTableName("location_type")&lt;br /&gt;         .usingGeneratedKeyColumns("location_type_id");&lt;br /&gt;        Map&amp;lt;String, Object&amp;gt; lParameters = new HashMap&amp;lt;String, Object&amp;gt;();&lt;br /&gt;        lParameters.put("location_type_nm", "cacheTest");&lt;br /&gt;        lParameters.put("location_type_desc", "verify ibatis caching working");&lt;br /&gt;        lParameters.put("message_resource_key", "location.type.cacheTest");&lt;br /&gt;        lParameters.put("active_flag", 1);&lt;br /&gt;        lParameters.put("create_user", "ibatisCacheVerification@daoTest.com");&lt;br /&gt;        lParameters.put("create_ts", Calendar.getInstance().getTime());&lt;br /&gt;&lt;br /&gt;        // verify that the straight JDBC insert works&lt;br /&gt;        Number lNewId = lJdbcInsert.executeAndReturnKey(lParameters);&lt;br /&gt;        assertNotNull("autogen key shouldn't be null", lNewId);&lt;br /&gt;&lt;br /&gt;        // ibatis cache should be unaware that we added a new &lt;br /&gt;        // location type since we did it with straight JDBC&lt;br /&gt;        cachedTypes = _locationTypeDao.getAll();&lt;br /&gt;        assertEquals(numTypes, cachedTypes.size());&lt;br /&gt;&lt;br /&gt;        LocationType newType = new LocationType("cache2", "verify caching working part 2");&lt;br /&gt;        newType.setCreateUserName("ibatisCacheVerification@daoTest.com");&lt;br /&gt;        _locationTypeDao.save(newType);&lt;br /&gt;&lt;br /&gt;        // insert causes flush, so cache should now have both new types&lt;br /&gt;        cachedTypes = _locationTypeDao.getAll();&lt;br /&gt;        assertEquals(numTypes + 2, cachedTypes.size());&lt;br /&gt;    }&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Running this first test prior to fixing the cache model resulted in assert failures, which validated that our caching was broken.&lt;br /&gt;&lt;br /&gt;The second test was to validate a more complex caching problem.  In this example, you have a location table with a foreign key to the location_type table. Performing a select on location will result in returning the location information as well as information about the location type.  Suppose that the location select is cached, and then a modification occurs where some of the location type information is updated.  If the cacheModel is configured incorrectly, it will not flush the cache and the location select cache will still contain the old values from the location_type table.  Here is what that test looked like.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;    public void testIbatisCacheWithJoins() {&lt;br /&gt;        // get location types&lt;br /&gt;        List&amp;lt;LocationType&amp;gt; cachedTypes = _locationTypeDao.getAll();&lt;br /&gt;&lt;br /&gt;        // Add new location type&lt;br /&gt;        LocationType locType = null;&lt;br /&gt;        for(LocationType type : cachedTypes) {&lt;br /&gt;            if (type.getId() == 1) {&lt;br /&gt;                locType = type;&lt;br /&gt;                break;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        // get locations to set the cache initially&lt;br /&gt;        _locationsDao.getLocationsByTypeId(null, locType.getId(), null);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        locType.setUpdateUserName("ibatisCacheVerification@daoTest.com");&lt;br /&gt;        locType.setName("New Type Name");&lt;br /&gt;        _locationTypeDao.save(locType);&lt;br /&gt;        List&amp;lt;Location&amp;gt;cachedLocations =  &lt;br /&gt;           _locationsDao.getLocationsByTypeId(null, locType.getId(), null);&lt;br /&gt;&lt;br /&gt;        assertEquals("New Type Name", cachedLocations.get(0).getLocType());&lt;br /&gt;      }&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Running this test case with the cacheModel set up incorrectly resulted in an assert failure as well.  Now that we have the two main tests in place we needed to fix the CacheModel definition, which is discussed in the final blog, &lt;a href="http://phillips4jc.blogspot.com/2010/03/ibatis-cache-miss-part-3-confguring.html"&gt;iBatis "Cache Miss", Part 3&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-7974482115751282381?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/7974482115751282381/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=7974482115751282381' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/7974482115751282381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/7974482115751282381'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2010/03/ibatis-cache-miss-part-2-creating.html' title='iBatis &quot;Cache Miss&quot;, Part 2 Creating automated tests'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-6778813672446005396</id><published>2010-03-01T12:20:00.000-08:00</published><updated>2010-03-01T12:43:13.526-08:00</updated><title type='text'>iBatis "Cache Miss", Part  1 - Validating the problem</title><content type='html'>There is a lot of information to cover on this topic, so this will be a series of three blogs.  First a little background.  The project I am on, noticed some performance issues as we had more and more users.  As a result, we decided to cache the heavily used, infrequently modified iBatis selects.  At the time a lot of investigation was done to verify that the cache models were set up correctly and working.&lt;br /&gt;&lt;br /&gt;Unfortunately, all the testing was done manually.  So as fate would have it, subsequent changes ended up breaking the caching.  However, due to upgrades and other optimizations in the hardware and software it was not immediately obvious that this had occurred. As a result, the project progressed for nearly a year with caching broken.  &lt;br /&gt;&lt;br /&gt;In this first blog I want to discuss how we were able to verify that caching was broken through JBoss logging.  The &lt;a href="http://phillips4jc.blogspot.com/2010/03/ibatis-cache-miss-part-2-creating.html"&gt;second blog&lt;/a&gt; will discuss how to write an integration test that can be automated using maven, to prevent the caching from being broken in the future. The &lt;a href="http://phillips4jc.blogspot.com/2010/03/ibatis-cache-miss-part-3-confguring.html"&gt;final blog&lt;/a&gt;, will cover a discussion of the &lt;a href="http://ibatis.apache.org/docs/dotnet/datamapper/ch03s08.html"&gt;iBatis cacheModel&lt;/a&gt; and how to configure it.&lt;br /&gt;&lt;br /&gt;First, you will need to modify the jboss-log4j.xml file in the $JBOSS_HOME/server/default/conf directory.  You will need to add a new appender definition as well as set up the categories you want to be output to that file. This information was found in this &lt;a href="http://stackoverflow.com/questions/2314667/log4j-different-category-priority-than-appender-threshold"&gt;post&lt;/a&gt;. Here are the exerts from that file. &lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;   &amp;lt;appender name="SQL_FILE" &lt;br /&gt;      class="org.jboss.logging.appender.DailyRollingFileAppender"&amp;gt;&lt;br /&gt;      &amp;lt;errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/&amp;gt;&lt;br /&gt;      &amp;lt;param name="File" value="${jboss.server.log.dir}/sql.log"/&amp;gt;&lt;br /&gt;      &amp;lt;param name="Append" value="false"/&amp;gt;&lt;br /&gt;      &amp;lt;param name="DatePattern" value="'.'yyyy-MM-dd"/&amp;gt;&lt;br /&gt;      &amp;lt;layout class="org.apache.log4j.PatternLayout"&amp;gt;&lt;br /&gt;         &amp;lt;param name="ConversionPattern" value="%d %-5p [%c] %m%n"/&amp;gt;&lt;br /&gt;      &amp;lt;/layout&gt;&lt;br /&gt;   &amp;lt;/appender&amp;gt;&lt;br /&gt;&lt;br /&gt;   &amp;lt;category name="java.sql" additivity="false"&amp;gt;&lt;br /&gt;     &amp;lt;priority value="DEBUG" /&amp;gt;&lt;br /&gt;     &amp;lt;appender-ref ref="SQL_FILE"/&amp;gt;&lt;br /&gt;   &amp;lt;/category&amp;gt;&lt;br /&gt;&lt;br /&gt;   &amp;lt;category name="com.ibatis" additivity="false"&amp;gt;&lt;br /&gt;     &amp;lt;priority value="DEBUG" /&amp;gt;&lt;br /&gt;     &amp;lt;appender-ref ref="SQL_FILE"/&amp;gt;&lt;br /&gt;   &amp;lt;/category&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;When verifying this we started with our simplest example.  A single table that did not join to other tables.  From the client we brought up our admin interface for that table.  This performed the initial select of everything which set up the cache.  Refreshing, without having performed an insert, update, or delete should have returned the same results from the cacheModel.  Much to our surprise, what we saw was "Cache Miss".&lt;br /&gt;&lt;pre&gt;&lt;b&gt;&lt;br /&gt;2010-03-01 13:43:56,515 DEBUG [com.ibatis.sqlmap.engine.cache.CacheModel] Cache&lt;br /&gt;                   'LocationType.locationType_cache': cache miss&lt;br /&gt;2010-03-01 13:59:41,921 DEBUG [java.sql.Connection] {conn-102346} &lt;br /&gt;                   Preparing Statement:          SELECT ...&lt;br /&gt;2010-03-01 13:59:41,921 DEBUG [java.sql.PreparedStatement] {pstm-102347} &lt;br /&gt;                   Executing Statement:          SELECT ...&lt;br /&gt;2010-03-01 13:59:41,921 DEBUG [java.sql.PreparedStatement] {pstm-102347} &lt;br /&gt;                   Parameters: []&lt;br /&gt;&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;Now that we have been able to manually verify that the cacheModel is broken, we need to write some automated tests so this doesn't happen again.  That is the subject of of the next blog, iBatis "Cache Miss", Part 2.&lt;br /&gt;&lt;br /&gt;After fixing the cacheModel the result was quite different.&lt;br /&gt;&lt;pre&gt;&lt;b&gt;&lt;br /&gt;2010-03-01 14:01:28,109 DEBUG [java.sql.Connection] {conn-102351} Connection&lt;br /&gt;2010-03-01 14:01:28,109 DEBUG [com.ibatis.sqlmap.engine.cache.CacheModel] Cache &lt;br /&gt;    'LocationType.locationType_cache': retrieved object 'LocationType name: ...'&lt;br /&gt;&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;As you can see from the above log entries, it retrieved the results from the cache.  Now when you do an insert, this is the output you will see:&lt;br /&gt;&lt;pre&gt;&lt;b&gt;&lt;br /&gt;2010-03-01 14:06:20,421 DEBUG [java.sql.Connection] {conn-102352} Connection&lt;br /&gt;2010-03-01 14:06:20,421 DEBUG [java.sql.Connection] {conn-102352} &lt;br /&gt;       Preparing Statement:          INSERT ...&lt;br /&gt;2010-03-01 14:06:20,421 DEBUG [java.sql.PreparedStatement] {pstm-102353} &lt;br /&gt;       Executing Statement:          INSERT ...&lt;br /&gt;2010-03-01 14:06:20,421 DEBUG [java.sql.PreparedStatement] {pstm-102353} &lt;br /&gt;       Parameters: [Weather, null, location.type.weather, 1, jp1@test.com]&lt;br /&gt;2010-03-01 14:06:20,421 DEBUG [java.sql.PreparedStatement] {pstm-102353} &lt;br /&gt;       Types: [java.lang.String, null, java.lang.String, java.lang.Integer, java.lang.String]&lt;br /&gt;2010-03-01 14:06:20,703 DEBUG [com.ibatis.sqlmap.engine.cache.CacheModel] &lt;br /&gt;       Cache 'Location.new_locations_cache': flushed&lt;br /&gt;2010-03-01 14:06:20,703 DEBUG [com.ibatis.sqlmap.engine.cache.CacheModel] &lt;br /&gt;       Cache 'LocationType.locationType_cache': flushed&lt;br /&gt;&lt;/b&gt;&lt;/pre&gt;&lt;br /&gt;Reviewing these log statements you can see that both the Location and LocationType cache was flushed, when the insert was performed.  The next select that is done will hit the database again and cache the values returned.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-6778813672446005396?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/6778813672446005396/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=6778813672446005396' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6778813672446005396'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6778813672446005396'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2010/03/ibatis-cache-miss-part-1-validating.html' title='iBatis &quot;Cache Miss&quot;, Part  1 - Validating the problem'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-3300105217395902927</id><published>2010-02-28T23:08:00.000-08:00</published><updated>2010-03-11T10:05:03.006-08:00</updated><title type='text'>Bulk saving and updating using iBatis</title><content type='html'>Recently I had the need to save records to an association table.  All the object values were the same except one.  I was looping through the array list saving them one at a time, but kept getting unable to get database connection errors.  I lamented that I couldn't do a bulk save.  A co-worker thought that iBatis supported what I wanted to do, so after some investigation we were able to get both a bulk save and a bulk update to work.  &lt;br /&gt;&lt;br /&gt;For our example let us assume we have a table that contains a list of companies and another table that contains a list of Locations.   From the client when you edit a company it allows you to multi-select a list of locations to associate with that company. On the back end you could loop through the locations and store each separately, but iBatis allows you to save them all at once.  Let's examine how to do that.&lt;br /&gt;&lt;br /&gt;Here are the Company and Location classes, minus getters/setters and imports;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;package com.projectA;&lt;br /&gt;public class Company {&lt;br /&gt;    private int companyId;&lt;br /&gt;    private String name;&lt;br /&gt;    private List&lt;int&gt; locations;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;package com.projectA;&lt;br /&gt;public class Location {&lt;br /&gt;    private int locationId;&lt;br /&gt;    private String address;&lt;br /&gt;    private String city;&lt;br /&gt;    private String state;&lt;br /&gt;    private String zipCode;&lt;br /&gt;    private String phoneNumber;&lt;br /&gt;}&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The database contains a table containing all the companies, and a table containing all the locations.  There is also a third table that contains the company - location associations.  To do this we need to take advantage of the iBatis dynamic fields.  Here is the iBaits XML file that contains the bulk save and update methods for the association table:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;&amp;lt;sqlMap namespace="Company"&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;typeAlias alias="company" type="com.projectA.Company"/&amp;gt;&lt;br /&gt;    &amp;lt;typeAlias alias="loation" type="com.projectA.Location"/&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;resultMap id="companyResultMap" class="company"&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_id" property="companyId" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_name" property="name" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="{companyId=company_id}" property="locations"&lt;br /&gt;                   select="Company.getActiveLocations"/&amp;gt;&lt;br /&gt;    &amp;lt;/resultMap&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;resultMap id="locationResultMap" class="location"&amp;gt;&lt;br /&gt;        &amp;lt;result column="location_id" property="locationId" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="location_address" property="address" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="location_city" property="city" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="location_state" property="state" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="location_zip_code" property="zipCode" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="location_phone_number" property="phoneNumber" /&amp;gt;&lt;br /&gt;    &amp;lt;/resultMap&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;sql id="select_company_fragment"&amp;gt;&lt;br /&gt;        SELECT company_id,&lt;br /&gt;               company_name,&lt;br /&gt;        FROM company&lt;br /&gt;    &amp;lt;/sql&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;sql id="select_location_fragment"&amp;gt;&lt;br /&gt;        SELECT cl.location_id,&lt;br /&gt;               loc.location_address,&lt;br /&gt;               loc.location_city,&lt;br /&gt;               loc.location_state,&lt;br /&gt;               loc.location_zip_code,&lt;br /&gt;               loc.location_phone_number&lt;br /&gt;        FROM company_location cl, location loc&lt;br /&gt;        WHERE cl.company_id=#companyId#&lt;br /&gt;          AND cl.location_id = loc.location_id&lt;br /&gt;    &amp;lt;/sql&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;select id="getAllCompanies" &lt;br /&gt;               resultMap="companyResultMap"&amp;gt;&lt;br /&gt;        &amp;lt;include refid="select_company_fragment"/&amp;gt;&lt;br /&gt;        ORDER BY company_name&lt;br /&gt;    &amp;lt;/select&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;select id="getActiveLocations" &lt;br /&gt;               resultMap="int"&amp;gt;&lt;br /&gt;        &amp;lt;include refid="select_location_fragment"/&amp;gt;&lt;br /&gt;          AND active_flag = 1&lt;br /&gt;        ORDER BY location_state, location_city&lt;br /&gt;    &amp;lt;/select&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;insert id="bulkInsertCompanyLocation" parameterClass="map"&amp;gt;&lt;br /&gt;        INSERT INTO company_location&lt;br /&gt;            (company_id,&lt;br /&gt;            location_id,&lt;br /&gt;            active_flag)&lt;br /&gt;        SELECT&lt;br /&gt;            #companyId#,&lt;br /&gt;            #activeFlag#,&lt;br /&gt;            loc.location_id,&lt;br /&gt;        FROM location loc LEFT JOIN company_location cl ON cl.company_id=#companyId# &lt;br /&gt;                                                AND cl.location_id = loc.location_id&lt;br /&gt;        WHERE  cl.company_id IS NULL&lt;br /&gt;        &amp;lt;dynamic&amp;gt;&lt;br /&gt;            &amp;lt;isNotNull property="locations"&amp;gt;&lt;br /&gt;                &amp;lt;iterate property="locations" &lt;br /&gt;                         prepend="AND " open=" loc.location_id IN ("&lt;br /&gt;                         close=")" conjunction=","&amp;gt;&lt;br /&gt;                    #locations[]#&lt;br /&gt;                &amp;lt;/iterate&amp;gt;&lt;br /&gt;            &amp;lt;/isNotNull&amp;gt;&lt;br /&gt;        &amp;lt;/dynamic&amp;gt;&lt;br /&gt;    &amp;lt;/insert&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;update id="bulkUpdateCompanyLocation" parameterClass="map"&amp;gt;&lt;br /&gt;        UPDATE company_location&lt;br /&gt;        SET&lt;br /&gt;           active_flag = #activeFlag#&lt;br /&gt;        WHERE company_id=#companyId#&lt;br /&gt;        &amp;lt;dynamic&gt;&lt;br /&gt;            &amp;lt;isNotNull property="locations"&amp;gt;&lt;br /&gt;                &amp;lt;iterate property="locations" prepend="AND " open=" location_id IN ("&lt;br /&gt;                         close=")" conjunction=","&amp;gt;&lt;br /&gt;                    #locations[]#&lt;br /&gt;                &amp;lt;/iterate&amp;gt;&lt;br /&gt;            &amp;lt;/isNotNull&amp;gt;&lt;br /&gt;        &amp;lt;/dynamic&amp;gt;        &lt;br /&gt;    &amp;lt;/update&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/sqlMap&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The map that is passed into both the insert and update methods, contains the company_id, an active flag, and an array list of locationId's.  These methods save or update multiple records in one shot.  That's all there is to it. By changing to the bulk insert and update methods, the out of database connections errors went away.&lt;br /&gt;&lt;br /&gt;The resulting SQL that iBatis generates looks like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;   INSERT INTO company_location&lt;br /&gt;       (company_id,&lt;br /&gt;        location_id,&lt;br /&gt;        active_flag)&lt;br /&gt;   SELECT&lt;br /&gt;       #companyId#,&lt;br /&gt;       #activeFlag#,&lt;br /&gt;       loc.location_id,&lt;br /&gt;   FROM location loc LEFT JOIN company_location cl ON cl.company_id=#companyId# &lt;br /&gt;                           AND cl.location_id = loc.location_id&lt;br /&gt;   WHERE  cl.company_id IS NULL&lt;br /&gt;     AND loc.location_id IN ("1","3","10")&lt;br /&gt;&lt;br /&gt;   UPDATE company_location&lt;br /&gt;   SET&lt;br /&gt;      active_flag = #activeFlag#&lt;br /&gt;   WHERE company_id=#companyId#&lt;br /&gt;     AND location_id IN ("4","6","12")&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;You can read more about the dynamic and iterate methods in the &lt;a href="http://ibatis.apache.org/docs/dotnet/datamapper/ch03s09.html"&gt;iBatis documentation&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-3300105217395902927?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/3300105217395902927/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=3300105217395902927' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/3300105217395902927'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/3300105217395902927'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2010/02/bulk-saving-and-updating-using-ibatis.html' title='Bulk saving and updating using iBatis'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-1838163469276125892</id><published>2010-02-27T18:00:00.000-08:00</published><updated>2010-03-01T07:44:55.922-08:00</updated><title type='text'>Retrieving complex objects from a database using iBatis</title><content type='html'>We have been using iBatis for 2 years now on the project I am on. We had need to retrieve a complex object from the database.&lt;br /&gt;Basically, we have an object obA that has some fields(string, int, ..., and one that is List&lt;obB&gt; where obB has some fields(String, int, date, ...).  Instead of retrieving all of the obA objects then looping through each of those to get the array of obB objects for each, I wanted to do it in one shot.&lt;br /&gt;&lt;br /&gt;A little digging and I found that &lt;a href="http://ibatis.apache.org/docs/dotnet/datamapper/ch03s05.html"&gt;iBatis&lt;/a&gt; supports this and it really wasn't that hard.  This simple example will generate retrieve an array of Company objects that each contain an array of Employee objects&lt;br /&gt;.  &lt;br /&gt;First here are the two Java Classes for Company and Employee.  To simplify, imports, getter/setter methods, etc have been omitted.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;package com.projectA;&lt;br /&gt;public class Company {&lt;br /&gt;    private int id;&lt;br /&gt;    private String name;&lt;br /&gt;    private String address;&lt;br /&gt;    private String city;&lt;br /&gt;    private String state;&lt;br /&gt;    private String zipCode;&lt;br /&gt;    private List&lt;Employee&gt; employees;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;package com.projectA&lt;br /&gt;public class Employee {&lt;br /&gt;    private int companyId;&lt;br /&gt;    private int id;&lt;br /&gt;    private String firstName;&lt;br /&gt;    private String lastName;&lt;br /&gt;    private String phoneNumber;&lt;br /&gt;    private String ssn;&lt;br /&gt;    private Date dob;&lt;br /&gt;}&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The following is the iBatis XML file that will retrieve a list of all companies sorted by company name, each containing a list of employees sorted alphabetically by last name, then first name.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;&amp;lt;sqlMap namespace="Company"&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;typeAlias alias="company" type="com.projectA.Company"/&amp;gt;&lt;br /&gt;    &amp;lt;typeAlias alias="employee" type="com.projectA.Employee"/&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;resultMap id="companyResultMap" class="company"&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_id" property="id" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_name" property="name" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_address" property="address" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_city" property="city" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_state" property="state" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="company_zip_code" property="zipCode" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="{companyId=company_id}" property="employees"&lt;br /&gt;                   select="Company.getEmployees"/&amp;gt;&lt;br /&gt;    &amp;lt;/resultMap&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;resultMap id="employeeResultMap" class="employee"&amp;gt;&lt;br /&gt;        &amp;lt;result column="employee_company_id" property="companyId" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="employee_id" property="id" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="employee_first_name" property="firstName" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="employee_last_name" property="lastName" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="employee_phone_number" property="phoneNumber" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="employee_ssn" property="ssn" /&amp;gt;&lt;br /&gt;        &amp;lt;result column="employee_dob" property="dob" /&amp;gt;&lt;br /&gt;    &amp;lt;/resultMap&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;sql id="select_company_fragment"&amp;gt;&lt;br /&gt;        SELECT company_id,&lt;br /&gt;               company_name,&lt;br /&gt;               company_address,&lt;br /&gt;               company_city,&lt;br /&gt;               company_state,&lt;br /&gt;               company_zip_code&lt;br /&gt;        FROM company&lt;br /&gt;    &amp;lt;/sql&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;sql id="select_employee_fragment"&amp;gt;&lt;br /&gt;        SELECT employee_company_id,&lt;br /&gt;               employee_id,&lt;br /&gt;               employee_first_name,&lt;br /&gt;               employee_last_name,&lt;br /&gt;               employee_phone_number&lt;br /&gt;               employee_ssn,&lt;br /&gt;               employee_dob&lt;br /&gt;        FROM employee&lt;br /&gt;    &amp;lt;/sql&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;select id="getAllCompanies" &lt;br /&gt;               resultMap="companyResultMap"&amp;gt;&lt;br /&gt;        &amp;lt;include refid="select_company_fragment"/&amp;gt;&lt;br /&gt;        ORDER BY company_name&lt;br /&gt;    &amp;lt;/select&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;select id="getEmployees" &lt;br /&gt;               resultMap="employeeResultMap"&amp;gt;&lt;br /&gt;        &amp;lt;include refid="select_employee_fragment"/&amp;gt;&lt;br /&gt;        WHERE employee_company_id=#companyId#&lt;br /&gt;        ORDER BY employee_last_name, employee_first_name&lt;br /&gt;    &amp;lt;/select&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/sqlMap&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all there is to it.  So when getAllCompanies is called, it will automatically call the getEmployees method to fill in the list of employees for each company. You can see this by looking at the companyResultMap, it has a column called employees that is mapped to a select statement and passes the current company id into that select.&lt;br /&gt;&lt;br /&gt;Some benefits are simplicity and performance.  One dao call retrieves everything I want, instead of getting a list of companies and needing to iterate over each to get the list of employees.&lt;br /&gt;&lt;br /&gt;A nice ibatis feature is the alias.  The benefit of using the alias in the ibatis XML file is that instead of needing to put com.projectA.Company in multiple places in the file, you do it once and reference it.  Then if the package structure is refactored, you only have to change it once.&lt;br /&gt;&lt;br /&gt;Another nice ibatis feature is the sql sections.  This allows you to have SQL fragments, that can be used by more than one select.  So in our example if you wanted to select all the companies for a given city you could add the following:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;b&gt;&lt;br /&gt;    &amp;lt;select id="getAllCompanies" &lt;br /&gt;               parameterClass="String" &lt;br /&gt;               resultMap="companyResultMap"&amp;gt;&lt;br /&gt;        &amp;lt;include refid="select_company_fragment"/&amp;gt;&lt;br /&gt;        WHERE company_city=#city#&lt;br /&gt;        ORDER BY company_name&lt;br /&gt;    &amp;lt;/select&amp;gt;&lt;br /&gt;&lt;/b&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is the same as the select statement above, except it has the additional constraint on the city.&lt;br /&gt;&lt;br /&gt;More iBatis posts to follow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-1838163469276125892?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/1838163469276125892/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=1838163469276125892' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/1838163469276125892'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/1838163469276125892'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2009/04/retrieving-complex-objetcs-from.html' title='Retrieving complex objects from a database using iBatis'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-2028367494872194654</id><published>2009-07-29T13:33:00.001-07:00</published><updated>2009-07-29T14:38:56.580-07:00</updated><title type='text'>JSON Response Encoding Troubles</title><content type='html'>On the project I am currently on, we have had some troubles with some of the JSON responses returned from the server.  Specifically if the strings contained escaped characters.  We are using ExtJs on this project and upon receiving the response on the client, we were doing an Ext.decode(response.responseText);  This was throwing exceptions, like &lt;span style="font-weight:bold;"&gt;error: unterminated string literal&lt;/span&gt;.  &lt;br /&gt;&lt;br /&gt;All the values that caused problems were escaped characters.&lt;br /&gt;\"&lt;br /&gt;\\&lt;br /&gt;\r&lt;br /&gt;\n&lt;br /&gt;\t&lt;br /&gt;&lt;br /&gt;It turns out that those escape sequences in the response are treated as part of the JSON source, not the JSON data.  If your data contains escaped characters, the '\' needs to be encoded so that it becomes part of the data, and the Ext.decode(or eval) will then remove the extra '\'.  The list above becomes:&lt;br /&gt;&lt;br /&gt;\"  --&gt;   \\\"&lt;br /&gt;\\  --&gt;   \\\\&lt;br /&gt;\r  --&gt;   \\r&lt;br /&gt;\n  --&gt;   \\n&lt;br /&gt;\t  --&gt;   \\t&lt;br /&gt;&lt;br /&gt;\\ is particularly bad if it is the last character in the data and it is not encoded, as it will combine with the field terminating ", and cause the JSON to fail.&lt;br /&gt;&lt;br /&gt;\n or \r, cause the JSON source to have a return in the middle of the JSON, thus invalidating it.&lt;br /&gt;&lt;br /&gt;\" will cause the field to prematurely terminate, causing the JSON to fail.&lt;br /&gt;&lt;br /&gt;\t is really pretty harmless, as it actually tabs the data, but at least it doesn't cause the JSON to fail. Other ones that may be of concern, but haven't investigated.&lt;br /&gt;\b - backspace&lt;br /&gt;\f - formfeed&lt;br /&gt;\v - vertical tab&lt;br /&gt;\' - single quote(haven't noticed this one being a problem)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-2028367494872194654?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/2028367494872194654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=2028367494872194654' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/2028367494872194654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/2028367494872194654'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2009/07/json-response-encoding-troubles.html' title='JSON Response Encoding Troubles'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-232255064494161424</id><published>2009-04-13T11:59:00.000-07:00</published><updated>2009-04-13T12:04:11.906-07:00</updated><title type='text'>Grid Panel as a form panel Element</title><content type='html'>We recently ran into a little problem in that we needed a grid panel to be displayed on  a form panel.  It displayed fine but it was left justified and made the form look pretty bad.  One of my co-workers stumbled on to an undocumented feature that solves the problem.  I just wanted to share it and post it for my own future reference.&lt;br /&gt;&lt;br /&gt;For the life of us, we couldn't figure out how to tell the GridPanel to render in the fieldItem column and not the fieldLabel (All the other items let you define a fieldLabel property, but the GridPanel didn't have this option) column of the FormPanel.  We finally found out that there is a property that is not documented anywhere, or anywhere that I can find, that tells the GridPanel that it is a formItem.  So, in order to treat a GridPanel like a formItem all you have to do is set a property "isFormField" to true, slick huh.  Now if you want to, you can also define a fieldLabel for that as well.  This will align your GridPanel with all your other fieldItems.&lt;br /&gt;&lt;br /&gt;Hope this is helpful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-232255064494161424?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/232255064494161424/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=232255064494161424' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/232255064494161424'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/232255064494161424'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2009/04/grid-panel-as-form-panel-element.html' title='Grid Panel as a form panel Element'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-6562740446348784983</id><published>2009-02-25T13:30:00.001-08:00</published><updated>2009-03-05T09:52:54.876-08:00</updated><title type='text'>Manipulating Arrays in JavaScript</title><content type='html'>In my recent work I have had to do a lot of array manipulation in JavaScript.  While doing this I learned a few tricks, that I did not want to forget and thought others might benefit from.&lt;br /&gt;&lt;br /&gt;Some of these things are simple, but I include here for completeness.  I welcome any feedback on any array tricks that you have learned.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Adding to an array&lt;br /&gt; &lt;div&gt;&lt;pre&gt;&lt;span style="font-style: italic; color: rgb(51, 102, 255);"&gt;var array = [];&lt;br /&gt;array.push(5835);&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Deleting from an array&lt;br /&gt; &lt;div&gt;&lt;pre&gt;&lt;span style="font-style: italic; color: rgb(51, 102, 255);"&gt;var array =[];&lt;br /&gt;array.push(5835); // index of 0&lt;br /&gt;array.push(6835); // index of 1&lt;br /&gt;array.push(7835); // index of 2&lt;br /&gt;delete array[1];&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;      The resulting array would be [5835, 7835], but the indexes would still be [0,2].    JavaScript does not reorder the indexes to always be zero based.&lt;br /&gt; &lt;/div&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Array Looping&lt;br /&gt; &lt;div&gt; Although you can loop through an array normally:&lt;br /&gt; &lt;pre&gt;&lt;span style="font-style: italic; color: rgb(51, 102, 255);"&gt;&lt;br /&gt;for(var i = 0; i &lt; array.length; i++) {&lt;br /&gt;  alert("Index was " + i + ", value was " + array[i]);&lt;br /&gt;}&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;You can get into trouble, if you are doing array manipulation like deleting.         Like I stated in #2, there is no more index 1, so you would get a JavaScript         error.                &lt;br /&gt;Because of this I have become a fan of the following loop:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 102, 255);"&gt;for(var i in array) {&lt;br /&gt;  if (typeof array[i] == "function") {&lt;br /&gt;    continue;&lt;br /&gt;  }&lt;br /&gt;  alert("Index was " + i + ", value was " + array[i]);&lt;br /&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;          A couple of things to note, first is the check to make sure the element is not a function.  All arrays have a remove method inherent in them, so you need to skip that.  Second, you will see that the indexes are indeed, 0, 2 and not 0, 1, if you use the dataset from #2.&lt;br /&gt;   &lt;/div&gt;&lt;br /&gt;  &lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;More to come later...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-6562740446348784983?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/6562740446348784983/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=6562740446348784983' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6562740446348784983'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/6562740446348784983'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2009/02/manipulating-arrays-in-javascript.html' title='Manipulating Arrays in JavaScript'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-7335625790831470694</id><published>2008-10-27T13:11:00.000-07:00</published><updated>2008-10-27T13:27:18.949-07:00</updated><title type='text'>Dynamic Size Arrays in javascript</title><content type='html'>Recently I needed to create a dynamic array in javascript.  I didn't want to have two complete arrays defined and choose based off an if statement.  So to do this I used the push() command.  It easily allowed me to tailor the contents of the array based on various boolean flags.  The array I was creating was for the tools param of a new Ext.Panel object.  As you can see in the sample code below, the publish button is only added to the tool array if the isShareable flag is true.&lt;br /&gt;&lt;br /&gt;var tools = [];&lt;br /&gt;&lt;br /&gt;        tools.push({&lt;br /&gt;            id:'toggle',&lt;br /&gt;            handler: this.onToggle.createDelegate(this),&lt;br /&gt;            qtip: "toggle"&lt;br /&gt;        });&lt;br /&gt;        tools.push({&lt;br /&gt;            id:'help',&lt;br /&gt;            handler: this.onHelp.createDelegate(this),&lt;br /&gt;            qtip: "help"&lt;br /&gt;        });&lt;br /&gt;        tools.push({&lt;br /&gt;            id:'gear',&lt;br /&gt;            handler: this.onConfigure.createDelegate(this),&lt;br /&gt;            qtip: "configure"&lt;br /&gt;        });&lt;br /&gt;        tools.push({&lt;br /&gt;            id:'refresh',&lt;br /&gt;            handler: this.onRefresh.createDelegate(this),&lt;br /&gt;            qtip: "refresh"&lt;br /&gt;        });&lt;br /&gt;        if (this.isShareable) {&lt;br /&gt;            tools.push({&lt;br /&gt;                id:'save',&lt;br /&gt;                handler: this.onPublish.createDelegate(this),&lt;br /&gt;                qtip: "publish"&lt;br /&gt;            });&lt;br /&gt;        }&lt;br /&gt;        tools.push({&lt;br /&gt;            id:'close',&lt;br /&gt;            handler: this.onClose.createDelegate(this),&lt;br /&gt;            qtip: "close"&lt;br /&gt;        });&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-7335625790831470694?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/7335625790831470694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=7335625790831470694' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/7335625790831470694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/7335625790831470694'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2008/10/dynamic-size-arrays-in-javascript.html' title='Dynamic Size Arrays in javascript'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-7095959857230636183</id><published>2008-09-22T14:47:00.000-07:00</published><updated>2008-09-24T13:22:03.656-07:00</updated><title type='text'>NoClassDefFound Exceptions tied to PermGen Space</title><content type='html'>Today I spent a good deal of time trying to figure out why I was getting a bunch of NoClassDefFoundErrors.  The changes I made were not significant, but I did make one configuration change to JBoss.  It seems the default is to compile the JSP pages with Java 1.4, so I changed it to 1.5, as a new JSP page used some Java 1.5 generics.&lt;br /&gt;&lt;br /&gt;When I tried to bring the application back up, I got the following different errors each time I restarted JBoss.&lt;br /&gt;&lt;br /&gt;javax.servlet.ServletException: java.lang.NoClassDefFoundError: org/eclipse/jdt/internal/compiler/problem/DefaultProblem&lt;br /&gt;&lt;br /&gt;java.lang.NoClassDefFoundError: org/eclipse/jdt/internal/compiler/ast/Argument&lt;br /&gt;&lt;br /&gt;NoClassDefFoundError: org/eclipse/jdt/internal/compiler/ast/SingleTypeReference&lt;br /&gt;&lt;br /&gt;Finally, I got a PermGen space error.&lt;br /&gt;&lt;br /&gt;A friend suggested increasing my PermGen memory in JBoss, which I did by adding the following line to the run.bat file in the JBoss bin directory:&lt;br /&gt;set JAVA_OPTS=%JAVA_OPTS% -XX:PermSize=512m -XX:MaxPermSize=512m&lt;br /&gt;&lt;br /&gt;This fixed the problem.  So when having weird org/eclipse NoClassDefFoundErrors being thrown, try increasing your PermSize.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-7095959857230636183?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/7095959857230636183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=7095959857230636183' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/7095959857230636183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/7095959857230636183'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2008/09/noclassdeffound-exceptions-tied-to.html' title='NoClassDefFound Exceptions tied to PermGen Space'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-1003843109724009722</id><published>2008-09-04T13:58:00.000-07:00</published><updated>2008-09-04T14:45:01.442-07:00</updated><title type='text'>Custom Events in ExtJS</title><content type='html'>I was kind of new to the idea of event driven applications, but after reading a little about it fell in love with it.  I immediately had some applications in my work where it would solve particular problems I was having.  Having never implemented this before, I didn't know where to start.  Searching the ExtJS forum I uncovered some good information, but not enough to just jump right into it.&lt;br /&gt;&lt;br /&gt;I thought I would blog about it while it was still fresh in my mind. &lt;br /&gt;&lt;br /&gt;First you need to extend the Observable class for the class that you want to add a new custom event to.  Then in a method called when the class is constructed, you need to add the new custom event.  I needed to throw a custom event whenever the tab changed.  As you can see from the code below, that a 'tabchange' event triggered a call to onTabChange function.  This function then fires the 'myCustomEvent' event.   That is all it takes to create a custom event.  Of course, nothing will happen if there are not any listeners registered for that event.&lt;br /&gt;&lt;br /&gt;Example.Events = function(config) {&lt;br /&gt;    this.initialize(config);&lt;br /&gt;};&lt;br /&gt;Ext.extend(Example.Events, Ext.util.Observable, {&lt;br /&gt;    initialize: function(config) {&lt;br /&gt;        this.addEvents('myCustomEvent');&lt;br /&gt;        this.panel = new Ext.TabPanel({&lt;br /&gt;        .....&lt;br /&gt;        };&lt;br /&gt;        this.panel.on('tabchange', this.onTabChange.createDelegate(this));&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;    onTabChange : function(tabPanel, tab) {&lt;br /&gt;        var index = -1;&lt;br /&gt;        if (tab.getIndex) {&lt;br /&gt;           index = tab.getIndex();&lt;br /&gt;        }&lt;br /&gt;        this.fireEvent('myCustomEvent', index);&lt;br /&gt;    },&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;In my case, I had widgets that existed on tabs and they need to know when their tab was activated.  So inside those widgets they needed to obtain a reference to the Example.Events object for our application, and then register as a listener and tell it which function to call back on. I did this in the widget constructor, as shown below.&lt;br /&gt;&lt;br /&gt;in constructor:&lt;br /&gt;&lt;br /&gt;        var event= Example.Parent.getEvent();&lt;br /&gt;        event.on('myCustomEvent',  this.tabSelected.createDelegate(this));&lt;br /&gt;        this.myTabIndex = event.getMyIndex();&lt;br /&gt;&lt;br /&gt;The call back function:&lt;br /&gt;   tabSelected : function(index) {&lt;br /&gt;      if (index == myTabIndex) {&lt;br /&gt;      // Do something if your tab is activated&lt;br /&gt;      }     &lt;br /&gt;   },&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;That's it.  There is nothing more to it.  And it works like a charm.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-1003843109724009722?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/1003843109724009722/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=1003843109724009722' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/1003843109724009722'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/1003843109724009722'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2008/09/custom-events-in-extjs.html' title='Custom Events in ExtJS'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-1568828829099246045</id><published>2008-09-04T07:53:00.000-07:00</published><updated>2008-09-04T08:36:11.408-07:00</updated><title type='text'>JavaScript handling of time zones</title><content type='html'>On the project I am currently working on, we had a problem to solve  regarding time zones with our application.  From the web interface the user was entering time for some of the fields.  The time was being sent to the server and stored in a database.  The problem we ran into was the client time zone was different than the server timezone.   The database on the server was converting all times that were saved to the server time zone.  When the data was retrieved, it was displaying the server time values and presenting it to the user as if it were client local time.  Obviously we were not taking time zones into account.  &lt;br /&gt;&lt;br /&gt;Like most problems, there is usually more than one way to solve it.  The way we solved it, was to make sure that all of the times that were being passed back and forth through the REST web service calls were in GMT (Zulu) time.  The database can convert to local time, but before it is sent to the client, it is converted back to GMT.  Likewise, in the browser, the user can enter time in local time, but before it is sent to the server it is converted to GMT time. &lt;br /&gt;&lt;br /&gt;The rub came in finding the right java script date functions to allow for conversion to and from GMT.  The GMT string representations that were being passed back and forth, needed converted to the clients local time.  This was easily accomplished by using the java script Date constructor.&lt;br /&gt;&lt;br /&gt;var localTimeZoneDate = new Date(gmtTimeZoneDateStr);&lt;br /&gt;&lt;br /&gt;The date components the UI implemented, needed to be initialized with milliseconds from epoch time.  To get that information we simply called Date.parse(localTimeZoneDate);&lt;br /&gt;&lt;br /&gt;On the server it was equally easy to convert the value retrieved from the database to GMT.&lt;br /&gt;        String dateFormatStr = "EEE, dd MMM yyyy HH:mm:ss z";&lt;br /&gt;        SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormatStr ); &lt;br /&gt;        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));&lt;br /&gt;        Date date = dateFormat.parse(timeStr);&lt;br /&gt;&lt;br /&gt;In reviewing what we did, there may have been an easier solution.  Both the Java Date object and the JavaScript Date object, are able to return milliseconds from epoch.  It would have been much simpler to just send that value back and forth, and not worry about the string format.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-1568828829099246045?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/1568828829099246045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=1568828829099246045' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/1568828829099246045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/1568828829099246045'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2008/09/javascript-handling-of-time-zones.html' title='JavaScript handling of time zones'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-8736696728506205705</id><published>2008-04-29T10:34:00.000-07:00</published><updated>2008-04-29T11:05:34.609-07:00</updated><title type='text'>How to add new events types to ExtJS components</title><content type='html'>I needed to be able to click on an Ext.Panel and have a window popup.  The 'click' event is not a supported event for Ext.Panel.   It turns out it is pretty easy to do in ExtJS.  Almost every component has a getEl() method.&lt;br /&gt;&lt;br /&gt;var myPanel = new Ext.Panel({&lt;br /&gt;             html: '&lt;&gt; Click Me &lt; / h2&gt;',&lt;br /&gt;             autoHeight: true&lt;br /&gt;                      });&lt;br /&gt;&lt;br /&gt;Once the panel has been rendered you simply, need to do the following.&lt;br /&gt;var element = myPanel.getEl();&lt;br /&gt;element.on('click', this.handleClick.createDelegate(this));&lt;br /&gt;&lt;br /&gt;Now when ever a mouse clicks on the panel, the handleClick method will be called.  The trick is that you have to wait until the panel has been rendered, to be able to add the event to it.&lt;br /&gt;&lt;br /&gt;You may be tempted to use&lt;br /&gt;myPanel.on({'click':this.handleClick.createDelegate(this), scope: this});&lt;br /&gt;&lt;br /&gt;This method works great for events that the ExtJS Component supports by default, but doesn't work for other events.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-8736696728506205705?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/8736696728506205705/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=8736696728506205705' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/8736696728506205705'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/8736696728506205705'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2008/04/how-to-add-new-events-types-to-extjs.html' title='How to add new events types to ExtJS components'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-3807324362254374957</id><published>2008-04-29T10:15:00.000-07:00</published><updated>2008-04-29T10:31:39.376-07:00</updated><title type='text'>How to add a keymap to an existing FormPanel in ExtJS</title><content type='html'>Recently I needed to add a key map to an existing FormPanel in our ExtJS project.  This sounded like a simple thing to do and in the end the code was simple, but it wasn't obvious how to do it.   So I thought I would try to save you the pain I went through to figure it out.&lt;br /&gt;&lt;br /&gt;In the JavaScript, you might have something like the following.&lt;br /&gt;&lt;br /&gt;this.myForm = new Ext.form.FormPanel({ ...your parameters....});&lt;br /&gt;this.myWindow = new Ext.Window({ ...your parameters....&lt;br /&gt;                                     items: this.myForm,&lt;br /&gt;                                     buttons: [{&lt;br /&gt;                                                        text:  'Save',&lt;br /&gt;                                                        handler: this.onSave.createDelegate(this)&lt;br /&gt;                                                      },{&lt;br /&gt;                                                        text: 'Cancel',&lt;br /&gt;                                                        handler: this.onCancel.createDelegate(this)&lt;br /&gt;                                                      }]&lt;br /&gt;                                    });&lt;br /&gt;&lt;br /&gt;Now how do you go back and add a KeyMap after the fact. &lt;br /&gt;&lt;br /&gt;The panel has to be rendered before you can add the KeyMap to it.  To do that simple call the following.&lt;br /&gt;&lt;br /&gt;this.myWindow.show();&lt;br /&gt;this.myWindow.doLayout();&lt;br /&gt;&lt;br /&gt;Now you can create the KeyMap.&lt;br /&gt;        new Ext.KeyMap(this.myForm.body, [{&lt;br /&gt;                key: Ext.EventObject.ESC,&lt;br /&gt;                fn: this.onCancel.createDelegate(this),&lt;br /&gt;                scope: this&lt;br /&gt;            },{&lt;br /&gt;                key: Ext.EventObject.ENTER,&lt;br /&gt;                fn: this.onSave.createDelegate(this),&lt;br /&gt;                scope: this&lt;br /&gt;            }]);&lt;br /&gt;&lt;br /&gt;Now, the user can either click the cancel button or hit 'esc' to cancel what he was doing and to save what he was doing he can either press the save button or press the 'enter' key on the keyboard.  I hope you find this helpful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-3807324362254374957?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/3807324362254374957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=3807324362254374957' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/3807324362254374957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/3807324362254374957'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2008/04/how-to-add-keymap-to-existing-formpanel.html' title='How to add a keymap to an existing FormPanel in ExtJS'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-3228563036840796270</id><published>2008-04-25T09:28:00.000-07:00</published><updated>2008-04-25T09:41:06.000-07:00</updated><title type='text'>Debugging JavaScript in IE</title><content type='html'>Firebug is so powerful and easy to use,  I almost never need to debug JavaScript in IE.  However, every once in a while I will come across a bug that only occurs in IE.  A co-worker of mine told me about the best free IE JavaScript debugger he was aware of.   It has been a real life saver at times.  I thought I would share the information here so that you too can debug JavaScript in IE, if you must.&lt;br /&gt;&lt;br /&gt;It does take a while to download and install the Mircosoft Visual Web Developer 2008 Express Edition, &lt;span style="font-family:Arial;font-size:85%;"&gt;&lt;span style="font-size: 10pt; font-family: Arial;"&gt;&lt;a title="http://msdn.microsoft.com/vstudio/express/vwd/" href="http://msdn.microsoft.com/vstudio/express/vwd/"&gt;http://msdn.microsoft.com/vstudio/express/vwd/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This blog entry has steps on how to set up and use the tool the first time.&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;&lt;span style="font-size: 10pt; font-family: Arial;"&gt;&lt;a title="http://www.berniecode.com/blog/2007/03/08/how-to-debug-javascript-with-visual-web-developer-express/" href="http://www.berniecode.com/blog/2007/03/08/how-to-debug-javascript-with-visual-web-developer-express/"&gt;http://www.berniecode.com/blog/2007/03/08/how-to-debug-javascript-with-visual-web-developer-express/&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There some special instructions in that blog for getting it to work, if IE is not your default browser.&lt;br /&gt;&lt;br /&gt;Hope you find this helpful&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:Arial;font-size:85%;"&gt;&lt;span style="font-size: 10pt; font-family: Arial;"&gt;&lt;o:p&gt;&lt;br /&gt;&lt;br /&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-3228563036840796270?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/3228563036840796270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=3228563036840796270' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/3228563036840796270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/3228563036840796270'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2008/04/debugging-javascript-in-ie.html' title='Debugging JavaScript in IE'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-731282840225165346</id><published>2008-04-11T10:00:00.000-07:00</published><updated>2008-04-11T07:16:45.312-07:00</updated><title type='text'>Configuration Management: Bugs and Branches</title><content type='html'>Most software when the time comes to make a release will establish a branch.  As the branch is tested bugs will be found that must be fixed.    A very common dilemma is do we fix it on the main branch then merge the fix to the release branch, or do we fix it in the release branch and merge it back to the main branch.  Either way, the changes must be in both locations.&lt;br /&gt;&lt;br /&gt;This is a very old debate, and developers will usually be adamant about which side they support.  I have been involved in projects that have practiced both methods.  I would simply like to share my experiences from both sides.&lt;br /&gt;&lt;br /&gt;Branch to Main:&lt;br /&gt;Pros&lt;br /&gt;&lt;ul&gt;&lt;li&gt;One of the main rationals for this method is that the release is on a time line to get out. The fixes need to be put into the branch first to be able to maintain schedule and insure that the problems are fixed to support the customer.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Mainline code will continue to be enhanced for future releases so it will diverge from the branch code and the fix for the main branch may not be valid for the branch, so you should fix the branch first.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Cons&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The biggest risk is that the fix never makes it back to the main branch so the next release you risk breaking the customer.  Or even worse, an enhancement that was requested at the last minute by the customer, never makes it back to the main line.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Main to Branch:&lt;br /&gt;Pros&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It guarantees that the main branch always has all the fixes and enhancements, so that future releases are not at risk of missing critical bug fixes or minor customer enhancements.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Cons&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It takes longer to update the branch with fixes, potentially causing a delay in the software release.&lt;/li&gt;&lt;li&gt;Risk that the fixes from the main branch don't get merged to the release branch.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The longer the branch lives the more difficult the merges to the branch become.  You only want the bug fixes, not the new development.&lt;/li&gt;&lt;/ul&gt;Experiences:&lt;br /&gt;My experiences have shown me that the testing involved on the release branch is much more rigorous than the testing on the main branch.    No one wants the software to fail for the customer so must of the testing is focused on the release.  Due to new development, the main branch is "broken" more frequently and while that is not desired, it is not entirely unexpected.  It is more difficult to test reliably the main branch because the development my not be finished, and bugs would be written against code that wasn't ready to be tested.  The release branch is usually much more stable with only critical defects being fixed, which makes it easier to test.&lt;br /&gt;&lt;br /&gt;I have been involved in releases of software that went out to the customer only to have bugs reported back, that were fixed in a previous version and are broken again in the version they just received.   Customers are not very understanding of this situation, especially on government contracts.  They will call into question the processes you use, the reliability of your release process, even your ability to write bug free code.   Some very uncomfortable meetings to have to go through.&lt;br /&gt;&lt;br /&gt;One project I was on, the branch lived for a year and a half.  The mainline code had diverged so much that bug fixes were completely different in both places.  In fact we branched 3 times from the original branch without ever going back to the main.  It was truly a nightmare to maintain.  Which ever method you choose, the longer the branch lives, the more difficult the merges become, either direction. &lt;br /&gt;&lt;br /&gt;There are pros and cons to both sides.  From my experience, release branches should be short lived, and bug fixes should be merged in as quickly as possible after you branch.  This negates a lot of the code divergence cons used as arguments for both sides.  Due to the heavy testing on the release branch, it is not very likely that a required bug fix will be missed on the branch. &lt;br /&gt;&lt;br /&gt;For the reasons stated above I am a proponent of fixing in the main branch then merging to the release branch.  That said both methods can work.  Going from the branch to the main requires a well defined process that everyone must follow to guarantee that changes are merged into main once they are verified on the branch.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-731282840225165346?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/731282840225165346/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=731282840225165346' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/731282840225165346'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/731282840225165346'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2008/04/configuration-management-bugs-and.html' title='Configuration Management: Bugs and Branches'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5552322557063399878.post-2207717382732970593</id><published>2008-04-10T16:00:00.000-07:00</published><updated>2008-04-10T14:18:58.605-07:00</updated><title type='text'>Getting Internationalization to work on Windows</title><content type='html'>Recently, I wasted several days trying to resolve an internationalization issue within our web application.  Now I would like to share my experience and the solution I found in order to save others time.&lt;br /&gt;&lt;br /&gt;Our web application has a requirement to support multiple languages, English and Hangul(Korean).   Our application uses &lt;a href="http://www.extjs.com/"&gt;ExtJS&lt;/a&gt; for the front end and &lt;a href="https://jersey.dev.java.net/"&gt;Jersey&lt;/a&gt; for the REST services, and we had difficulties getting i18n working in the javascript.&lt;br /&gt;&lt;br /&gt;Initially all the Korean characters would appear as ??? in our ExtJS widgets.  After some research we added a call to get the UTF-8 bytes, &lt;span style="font-weight: bold;"&gt;new String(bundle.getValue(key).getBytes(“UTF-8”))&lt;/span&gt;.  and setting the content type charset to UTF-8 on the Jersey StringRepresentation.  The issue with this approach was that certain Korean characters would appear in our ExtJS widgets as ?? and others would appear fine, so it was only a partial solution.&lt;br /&gt;&lt;br /&gt;The solution ended up being pretty simple.  Based on some information &lt;a href="http://osdir.com/ml/java.jpos.devel/2005-05/msg00013.html"&gt;here&lt;/a&gt;, you basically have to set the default charset for the JVM to UTF-8.  The default charset on Windows is windows-1252.   To do this set the system property file.encoding=UTF-8 in the JVM, for JBoss you can do this by modifying the run.bat and adding -Dfile.encoding=UTF-8 to the JAVA_OPTS variable.  Please note this is a Windows specific issue, since UTF-8 is the default charset for both Linux and Macs.&lt;br /&gt;&lt;br /&gt;The following is the code before and after the changes to the JVM default charset , that will allow you to properly visualize Hangul(Korean) in your application.&lt;br /&gt;&lt;br /&gt;The following displayed ?? for certain Korean characters.&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;ResourceBundle bundle = ResourceBundle.getBundle(bundleName, locale);&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;   Enumeration&lt;string&gt; keys = bundle.getKeys();&lt;/string&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;   String key = keys.nextElement();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;        String value = bundle.getValue(key);&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;        byte[] bytes = value.getBytes("UTF-8");&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;        String newValue  = new String(bytes);&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;        StringRepresentation representaiton = new StringRepresentation(newValue);&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;        representaiton.setLanguage("ko");&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;   representaiton.setMediaType("text/plain; charset=UTF-8");&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;        return representation;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After configuring the JVM to set the default charset to UTF-8&lt;br /&gt;   &lt;span style="font-weight: bold;"&gt;ResourceBundle bundle = ResourceBundle.getBundle(bundleName, locale);&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;   Enumeration&lt;string&gt; keys = bundle.getKeys();&lt;/string&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;   String key = keys.nextElement();&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;   String value = bundle.getValue(key);&lt;br /&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;        StringRepresentation representaiton = new StringRepresentation(value);&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;   representaiton.setLanguage("ko");&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;   representaiton.setMediaType("text/plain; charset=UTF-8");&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;   return representation;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For those that want some additional details as to why it wasn't working, read on.&lt;br /&gt;bundle.getValue(key) always returns a UTF-8 String, even if the default charset is windows-1252.&lt;br /&gt;&lt;br /&gt;The Jersey REST Service is returning a JSON StringRepresentation back to the client code.  But the StringRepresentation class expects the string you construct it with to be in the default charset .  Setting the media type on the StringRepresentation to "text/plain; charset=UTF-8" will cause it to try to convert the string it was provided from the default charset to UTF-8.&lt;br /&gt;&lt;br /&gt;The first case it was trying to convert a UTF-8 String(thinking it was windows-1252) to a UTF-8 String.  This produces all ?????? on the client for the Hangul(Korean) characters.&lt;br /&gt;&lt;br /&gt;The second case, we get take the UTF-8 string returned from the bundle and convert it to an array of UTF-8 byte codes.  We then take those byte codes and create a new windows-1252 string.  The string representation class basically takes that windows-1252 string and converts it back to a UTF-8 string.  So why didn't this work?  Some byte codes can't be represented in windows-1252, specifically(129, 141, 143, 144, and 157).  So any Korean characters that had one of those specific byte codes, would show up as a '?'.&lt;br /&gt;&lt;br /&gt;The final solution, eliminates all the extra conversions.  Bundle returns a UTF-8 string, StringRepresentation sees that the default charset is UTF-8, and it is sending UTF-8 so it doesn't need to convert anything and the result is the client correctly displays all the Korean characters.&lt;br /&gt;&lt;br /&gt;In summary,  just set the default charset on the JVM to UTF-8.&lt;br /&gt;&lt;br /&gt;To find out what default charset your system is set to you can execute the following Java command:&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;System.out.println(java.nio.charset.Charset.defaultCharset().name());&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5552322557063399878-2207717382732970593?l=phillips4jc.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://phillips4jc.blogspot.com/feeds/2207717382732970593/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5552322557063399878&amp;postID=2207717382732970593' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/2207717382732970593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5552322557063399878/posts/default/2207717382732970593'/><link rel='alternate' type='text/html' href='http://phillips4jc.blogspot.com/2008/04/getting-internationalization-to-work-on.html' title='Getting Internationalization to work on Windows'/><author><name>phillips4jc</name><uri>http://www.blogger.com/profile/11824840333171936017</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='31' height='32' src='http://bp1.blogger.com/_p4DboGKaDqk/R_4nLVq4JTI/AAAAAAAAAAU/mHTYK3Z3kqY/S220/phillips_jeff.jpg'/></author><thr:total>0</thr:total></entry></feed>
