Java
Exception cause matcher for JUnit 4.7
Oct 25th
Since the introduction of @Rule and ExpectedException in JUnit, I’ve moved to using pretty much just that mechanism (instead of the expected= in the @Test annotation).
One thing sorely missing is a way to check the cause of an exception (which is useful when throwing an exception wrapped in a custom one).
I therefore wrote a little class to do just that.
public class ExceptionCauseMatcher extends BaseMatcher {
private Class<? extends Exception> expectedClass;
public ExceptionCauseMatcher(Class<? extends Exception> expectedClass) {
if (expectedClass == null) {
throw new IllegalArgumentException("Can't build with null argument");
}
this.expectedClass = expectedClass;
}
@Override
public void describeTo(Description description) {
description.appendText("Exception cause should be : " + expectedClass.toString());
}
@Override
public boolean matches(Object obj) {
if (obj instanceof Exception == false) {
return false;
}
Exception e = (Exception) obj;
if (e.getCause() == null) {
return false;
}
return expectedClass.equals(e.getCause().getClass());
}
}
And here’s how you’d use it
thrown.expect(new ExpectionCauseMatcher(IOException.class));
Test that camel route
May 22nd
You might want to get a bit of context by reading the 2 first parts of this series.
This time, I’ll show you how a route is tested by using mock producers and consumers and setting up the expectations. As I always remind the team, this is unit testing, not integration testing. At this point, we need to trust that the Camel team has properly tested all it’s components. What we want to do is check that 1) our route is routing messages to the proper endpoints, and 2) our enrichers or filters are kicking in properly.
To better understand the rest of this post, reading through this might be usefull.
Route Builder modifications
In the previous example, I had hard coded all route and processor information directly into the RouteBuilder implementation.
Obviously, this won’t cut it if we want to test. So the first thing we want to do is externalize the routeconfiguration information to a class (backed by an interface. You’ll se why later).
class MyRouteBuilder extends RouteBuilder {
private IRouteBuilderConiguration config;
@Override
public void configure() throws Exception {
errorHandler(
deadLetterChannel(config.getDlqEndpointUrl()).
delay(config.getErrorHandlerDelay()).
maximumRedeliveries(config.getErrorHandlerRetries())
);
from(config.getSourceEndpointUrl()).
choice().
when(config.getMessageFilter()).
process(config.getMessageEnricher()).to(config.getMessageEndpointUrl()).
otherwise().to(config.getErrorEndpointUrl());
}
}
What’s missing here is a concrete implementation of the IRouteBuilderConfiguration interface. It would look something like:
public class MyRouteBuilderConfiguration implements IRouteBuilderConfiguration {
public String getMessageEndpointUrl() {
return ....
}
....
}
I used spring to inject the configuration into the builder, but you can use whatever you want.
<bean id="myRouteBuilder" class="default.MyRouteBuilder">
<property name="configuration">
<bean class="default.MyRouteBuilderConfiguration">
<property name="messageFilter" ref="myFilter"/>
<property name="messageEnricher" ref="myEnricher"/>
</bean>
</property>
</bean>
Test class
Once that’s done, then it’s simply a case of following what’s in the Camel testing example page with a few variations.
- Overide the createRouteBuilder() method, but make sure to send back your own implementation
- Inject a different IRouteBuilderConfiguration implementation which uses mock and direct endpoints.
For example:
public class TestMessageRouter extends CamelTestSupport {
private static final String SOURCE_COMPONENT_URL = "direct:start";
private static final String DLQ_COMPONENT_URL = "mock:dlq";
private static final String INVALID_COMPONENT_URL = "mock:invalid";
private static final String ENDPOINT_URL = "mock:endpoint";
@EndpointInject(uri = DLQ_COMPONENT_URL)
protected MockEndpoint dlqEndpoint;
@EndpointInject(uri = INVALID_COMPONENT_URL)
protected MockEndpoint invalidEndpoint;
@EndpointInject(uri = ENDPOINT_URL)
protected MockEndpoint endpoint;
@Produce(uri= SOURCE_COMPONENT_URL)
protected ProducerTemplate producerTemplate;
public void testMessageOk() throws Exception {
producerTemplate.sendBodyAndHeaders("a", getValidHeaders());
invalidEndpoint.expectedMessageCount(0);
dlqEndpoint.expectedMessageCount(0);
endpoint.expectedMessageCount(1);
dlqEndpoint.assertIsSatisfied();
invalidEndpoint.assertIsSatisfied();
endpoint.assertIsSatisfied();
Message inMessage = endpoint.getReceivedExchanges().get(0).getIn();
Assert.assertEquals("a", inMessage.getBody());
}
public void testWithInvalidBodyType() throws Exception {
Map<String, Object> headers =createValidHeaders();
producerTemplate.sendBodyAndHeaders(Integer.valueOf(1), headers);
invalidEndpoint.expectedMessageCount(1);
endpoint.expectedMessageCount(0);
dlqEndpoint.expectedMessageCount(0);
dlqEndpoint.assertIsSatisfied();
invalidEndpoint.assertIsSatisfied();
endpoint.assertIsSatisfied();
}
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
MyRouteBuilder builder = new MyRouteBuilder();
builder.setConfiguration(new TestRunnerRouteConfiguration());
return builder;
}
class TestRunnerRouteConfiguration implements IRouteBuilderConfiguration {
public Endpoint getMessageEndpoint() {
return endpoint;
}
public String getDLQCompomentUrl() {
return DLQ_COMPONENT_URL;
}
public String getInvalidMessageComponentUrl() {
return INVALID_COMPONENT_URL;
}
public String getSourceComponentUrl() {
return SOURCE_COMPONENT_URL;
}
....
}
}
The example isn’t complete, but it does give a good idea of what can be done. The learning curve isn’t extremely steep with Camel, but it’s there and you most likely won’t pickup all the concepts immediately. We’re not talking weeks and days, just a few hours.
I guess that covers it for now. I’ll be looking to setup a Component that can publish to Tumblr, just as an excersise, so if I get around to that, I’ll post the results here.
A bit more meat: Camel applied : JMS to File
May 13th
As mentionned before, here is the use case:
- Listen on a queue for new messages
- Validate that the message is properly formed (has a body and has the proper headers)
- Save the content to a file (file name is in one of the headers)
Spring configuration
<bean id="myRouteBuilder" class="default.MyRouteBuilder"/>
<bean id="myFilter" class="default.MyMessageFilter"/>
<bean id="myEnricher" class="default.MyMessageEnricher"/>
<camel:camelContext id="myCamelContext">
<camel:routeBuilder ref="myRouteBuilder"/>
</camel:camelContext>
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616"/>
</bean>
</property>
</bean>
The important bean here is the camelContext bean. The RouteBuilder refered to will be initialized (by calling up the configure() method on it) when the spring container gets initialized.
The ‘jms’ bean is used to configure the JMS component which can be refered to by name inside the route builder.
The 2 other beans (myEnricher and myFilter) are simply used by the route builder. A spring bean lookup is used to get a handle on these from the router.
1 gotcha: My ide (Eclipse) was giving me a lot of greif over the suggested namespace setup. Save yourself some trouble, and save the camel-spring.xsd offline and change your namesapce declaration to something like:
<beans xmlns:camel="http://camel.apache.org/schema/spring" xsi:schemaLocation=" http://camel.apache.org/schema/spring classpath:META-INF/camel-spring.xsd">
The RouteBuilder
This is where you build the Came routing for any messages. In this case, remember that we want to route incoming queue messages to a file (1 file per message). Ideally, any messages we can’t deal with and where there is an error in processing the message would be put into a DLQ, and any invalid messages would be put onto another queue.
public class MyRouteBuilder extends RouteBuilder implements ApplicationContextAware {
private ApplicationContext context;
@Override
public void configure() throws Exception {
Predicate messageFilter = (Predicate) context.getBean("myFilter");
Processor contentEnricher = (Processor) context.getBean("myEnricher");
errorHandler(
deadLetterChannel("jms:queue:my.dlq").
delay(2000).
maximumRedeliveries(5)
);
from("jms:queue:my.queue").
choice().when(messageFilter).process(contentEnricher).to("file://var/tmp").
otherwise().to("jms:queue:my.queue.invalid");
}
Explanation:
- The first section is a simple error handler that will force a message to a DLQ in the event of an exception. A lot more can be done, but this is all I needed
- Second we define the source compoment: “jms:” refers to the “jms” bean in the spring containter. Had we named that bean “activemqconnection” we would have used “activemqconnection:queue:my.queue” instead.
- We then apply a filter (implements Predicate) to determine if our message is valid (in my case, I check for specific header values). If the Predicate returns false, then we route the message to another queue.
- If the predicate passes, the message is stored in the /var/tmp directory. Specifically, for the Camel File component, the myEnricher bean takes care of setting a special header called CamelFileName which tells the component which name to give the saved file. Had that header not been present, the file would have been saved with a defaulting mechanism. An alternative would have been to use the ?fileName option in the component url (file://var/tmp?fileName=xxxxxx). See the docs for the File2 compoment.
Other classes
This isn’t super interesting, but it gives an idea how simple it is to setup a content enricher (which is really jsut a process) and a filter (Predicate)
Enricher
A note: This could have been done using the Camel DSL language, but I prefered to bring it out.
public class MyMessageEnricher implements Processor {
private static final Logger logger = LogManager.getLogger(MyMessageEnricher.class);
public void process(Exchange exchange) throws Exception {
if (logger.isTraceEnabled()) {
logger.trace("Enriching received message");
}
String fileName = (String) exchange.getIn().getHeader("fileName");
exchange.getIn().setHeader(Exchange.FILE_NAME, fileName);
if (logger.isTraceEnabled()) {
logger.trace("Enriched content header: " + exchange.getIn().getHeader(Exchange.FILE_NAME).toString());
}
}
}
Filter
(This also could have been implemented using the DSL)
public class MyMessageFilter implements Predicate {
private static final Logger logger = LogManager.getLogger(MyMessageFilter.class);
public boolean matches(Exchange exchange) {
if (logger.isTraceEnabled()) {
logger.trace("Excecuting message filter");
}
Message in = exchange.getIn();
for (String header : MyMessageHeaderKeys.MANDATORY_HEADERS) {
if (in.getHeader(header) == null) {
logger.warn("Filtering out message because of missing header: " + header);
return false;
}
}
if (in.getBody() == null) {
logger.warn("Filtering out message because there is no content");
return false;
}
if (logger.isTraceEnabled()) {
logger.trace("Message was found to be valid. Continuing route");
}
return true;
}
}
Next post, I’ll explain what brings all this together. The test cases.
Apache Camel. 3 Words: Wow
May 11th
Here’s my current use case:
- Receive a message on a queue
- Save the body of the message to a file, where the name is determined by a header value
Easy enough, right? Agreed. But to get something solid and reliable, you’re going to do a lot of plumbing:
- Setup a message listener that recovers from disconnects
- Make sure all message consumption is transacted
- Apply some sort of retry mechanism
- Put messages you can’t read or deal with onto a deal letter queue
- Handle all io locking issues that can arise
Enter Camel: With a bit of XML configuration and a simple RouteBuilder class, I can do most of this work in a few simple lines.
Obviously, having read the EIP book will help, but the documentation is stellar. You get examples for all the different patterns, as well as some amazing documentation for all components.
That being said, the full power of Camel (for this guy, and at this moment in time) comes from:
- How easy it is to add different steps in your route, without breaking anything else.
- Testability
- The amount of components you can route from and to.
- Spring integration
- Component
In my above use case, I already know that the messages will eventually need to be aggregated before getting writen to file. Check. I already know that eventually, I won’t be writing to file, but most likely to FTP. Check. I won’t have much to do short of changing a bit of code in my route, recompile, re-test and voila, all done!
(I’ve always said — ok, for the last few years — that a good senior programmer knows how to find the tools, api’s and frameworks to make his job easier, but more importantly, allow him/her to concentrate on the core buisness code. This is an example of that.)
Unit testing ehcache (Take #2)
Jan 23rd
My solution works, but there was something smelly in there. I wasn’t satisfied with the way I was checking to ensure that the cache had actually been accessed. Given that the notification interface doesn’t define a method to find that out, I had sort of tricked it ….
Well, I obviously couldn’t leave it at that. So the solution to a better smelling cache unit test lies below (tip: use the Statistics information that’s right there on the cache, but I hadn’t noticed)
import junit.framework.Assert;
@Test
public void testCache() throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
DataFacade dataFacade = (DataFacade) context.getBean("cacheInjectedDataFacade");
TestCacheListener listener = new TestCacheListener();
Cache cache = dataFacade.getCache();
cache.getCacheEventNotificationService().registerListener(listener);
dataFacade.getData();
Assert.assertEquals(ListenerStatus.ELEMENT_ADDED, listener.getLastStatus());
Assert.assertNotNull(cache.get(DataFacade.CACHE_KEY));
listener.resetLastStatus();
Assert.assertEquals(listener.getLastStatus(), ListenerStatus.RESET);
dataFacade.getData();
Assert.assertEquals(1, cache.getStatistics().getCacheHits());
Thread.sleep(2500);
dataFacade.getData();
Assert.assertEquals(ListenerStatus.ELEMENT_ADDED, listener.getLastStatus());
}
Also, remember to reset your cache through cache.removeAll() between tests (through the @BeforeTest) or else your cache hits won’t jive.
Using TumblrJ to write posts, read credentials and running the unit tests
Jan 19th
Some more progress with the api. This post will be about writing a post using the API, then reading a users credential information and I’ll finish off with the specifics on running the unit tests.
All code examples will focus on using the code through Spring, but this can all be done without Spring very easily (i.e.: Instead of building your instances through DI, just build them in the code yourself).
Writing
Same as with reading Tumblr information, you’ll need to:
- Setup an instance of the TumblrService class
- That class in turn needs a properly configured ITumblrReader instance. Since there’s only one implementation of that interfae, you’ll use TumblrHttpReader
- That in turns needs a TumblrConnectionOptions object with at the very minimum the name of your Tumblr log.
The small difference with here is that the write() method needs to be given a Credentials object to work.
Therefore, the Spring configuration would look something like:
<beans>
<bean id="tumblrJProperties" class="org.mikem.tumblr.api.util.TumblrJProperties"/>
<bean id="connectionOptions" class="org.mikem.tumblr.api.http.TumblrConnectionOptions">
<property name="name" value="${logname}"/>
</bean>
<bean id="reader" class="org.mikem.tumblr.api.http.TumblrHttpReader">
<property name="tumblrConnectionOptions" ref="connectionOptions"/>
<property name="properties" ref="tumblrJProperties"/>
</bean>
<bean id="credentials" class="org.mikem.tumblr.api.util.Credentials">
<property name="email" value="${email}"/>
<property name="password" value="${password}"/>
</bean>
<bean id="service" class="org.mikem.tumblr.api.TumblrService">
<property name="reader" ref="reader"/>
</bean>
</beans>
In the above configuration, you’ll need to replace the ${} variables with your own values.
A test case to go with this configuration would then look something like:
@Test
public void testWrite() throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("test-spring-context.xml");
TumblrService service = (TumblrService) context.getBean("service");
Credentials credentials = (Credentials) context.getBean("credentials");
RegularPost post = new RegularPost();
post.setBody("Test post");
post.setPrivatePost(true);
post.setTitle("Test post title");
TumblePost savedPost = service.write(post, credentials);
Assert.assertNotNull(savedPost);
Assert.assertNotNull(savedPost.getId());
}
Simple enough! Notice that for now, only RegularPost has been tested.
Reading credentials
The Tumblr api offers a way to pull out all the relevant information for a user. To get that information, and using the same Spring configuration outlined above, you’d do something like:
@Test
public void testReadAuthenticationInformaiton() {
ApplicationContext context = new ClassPathXmlApplicationContext("test-spring-context.xml");
TumblrService service = (TumblrService) context.getBean("service");
Credentials credentials = (Credentials) context.getBean("credentials");
User user = service.getUserInformation(credentials);
Assert.assertNotNull(user);
Assert.assertTrue(user.getUserTumblelogs().size() > 0);
}
Check out the User object to see the wealth of information returned by Tumblr.
Running the tests
The unit tests found in the source code are tightly coupled to Tumblr itself, and by extension a users log, email and password. Therefore, so that everyone may run the tests on their own log and with their own credentials, all tests are devised to be configured against a file called private-test-config.properties. That file should be found on the classpath.
The contents is straightforward enough:
logname=mylogname
email=myemail
password=mypassword
If you look at the @Before method of the BaseServiceTest class, you’ll notice the call to that file. Again, if the file isn’t present on the classpath, the tests will fail miserably. I haven’t checked that file in for obvious reasons!
Unit testing ehcache
Jan 17th
I just had to write some unit tests to make sure my caching mechanism was working properly. Obviously, there are better ways to do this, but for what I needed, it was ample. Any suggestions are definitely welcome.
Setup
We’ll start with a simple service class in which all caching manipulations are done from. The class is kept simple so as to not obfuscate what I’m really trying to show.
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;
public class DataFacade {
private static final String CACHE_KEY = "mykey";
private Cache cache;
public String getData() {
Element element = cache.get(CACHE_KEY);
if (element != null && element.getValue() != null) {
return (String) element.getValue();
} else {
String data = "lama";
cache.put(new Element(CACHE_KEY, data));
return data;
}
}
public Cache getCache() {
return cache;
}
public void setCache(Cache cache) {
this.cache = cache;
}
}
Spring Setup
Now, we need to write a test to ensure that the cache is being hit and expired properly. To do this, we’ll need to setup a test listener that will be injected into the Cache object used by the service class. Using spring, this is pretty easy, but we can also hand code it.
We’ll need a spring context configuration file somewhere in the classpath.
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="cacheInjectedDataFacade" class="org.mikem.DataFacade">
<property name="cache" ref="cache"/>
</bean>
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache-config.xml"/>
</bean>
<bean id="cache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="timeToIdle" value="2"/>
<property name="timeToLive" value="2"/>
<property name="eternal" value="false"/>
<property name="diskPersistent" value="false"/>
<property name="cacheName" value="org.mikem.lamacache"/>
</bean>
</beans>
The things to watch out for here are:
- We’re not using a ehcache.xml confguration file. All config is done through spring.
- The package names should be changed to reflect where your own DataFacade is
- Notice the timeToIdel and timeToExpire properties on the cache bean. More on that later
Unit test and the cache event listener
Now we’re ready to write the unit test related code. We’ll first need an implementation of the net.sf.ehcache.event.CacheEventListener interface. That implementation should look like the following:
public class TestCacheListener implements CacheEventListener {
public static enum ListenerStatus {
RESET,
DISPOSED, ELEMENT_EVICTED, ELEMENT_EXPIRED, ELEMENT_ADDED,
ELEMENT_REMOVED, ELEMENT_UPDATED, REMOVED_ALL,
};
public ListenerStatus lastStatus;
public ListenerStatus getLastStatus() {
return this.lastStatus;
}
public void resetLastStatus() {
this.lastStatus = ListenerStatus.RESET;
}
public void dispose() {
this.lastStatus = ListenerStatus.DISPOSED;
}
public void notifyElementEvicted(Ehcache arg0, Element arg1) {
this.lastStatus = ListenerStatus.ELEMENT_EVICTED;
}
public void notifyElementExpired(Ehcache arg0, Element arg1) {
this.lastStatus = ListenerStatus.ELEMENT_EXPIRED;
}
public void notifyElementPut(Ehcache arg0, Element arg1) throws CacheException {
this.lastStatus = ListenerStatus.ELEMENT_ADDED;
}
public void notifyElementRemoved(Ehcache arg0, Element arg1) throws CacheException {
this.lastStatus = ListenerStatus.ELEMENT_REMOVED;
}
public void notifyElementUpdated(Ehcache arg0, Element arg1) throws CacheException {
this.lastStatus = ListenerStatus.ELEMENT_UPDATED;
}
public void notifyRemoveAll(Ehcache arg0) {
this.lastStatus = ListenerStatus.REMOVED_ALL;
}
}
What this gives us is a listener class which we’ll inject into the cache. On each cache event, the last status member will be updated.
Now for the finale:
import junit.framework.Assert;
import net.sf.ehcache.Cache;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.stw.sandbox.TestCacheListener.ListenerStatus;
public class TestDataFacade {
@Test
public void testCache() throws Exception {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
DataFacade dataFacade = (DataFacade) context.getBean("cacheInjectedDataFacade");
TestCacheListener listener = new TestCacheListener();
Cache cache = dataFacade.getCache();
cache.getCacheEventNotificationService().registerListener(listener);
dataFacade.getData();
Assert.assertEquals(ListenerStatus.ELEMENT_ADDED, listener.getLastStatus());
Assert.assertNotNull(cache.get(DataFacade.CACHE_KEY));
listener.resetLastStatus();
Assert.assertEquals(listener.getLastStatus(), ListenerStatus.RESET);
dataFacade.getData();
Assert.assertEquals(ListenerStatus.RESET, listener.getLastStatus());
Thread.sleep(2500);
dataFacade.getData();
Assert.assertEquals(ListenerStatus.ELEMENT_ADDED, listener.getLastStatus());
}
}
Explanation:
- Lines 14 and 15 are the spring access and retrieval of the DataFacade instance
- Lines 17 through 19 take care of creating a new instance of the TestCacheListener and injecting it into the data facade instance
- Line 21 runs the method where we have some cache access going on
- Line 22: Because our last line actually put something in the cache, our cache listeners status will have changed. Here we’re making sure the status has been changed accordingly.
- Line 23: Make sure we have data in the cache (we could also check that the content is right)
- Line 25: Reset the last status on the listener
- Line 28: Fetch the data once again. At this point, the listeners last status will remain to RESET since it won’t have been put into the cache anew.
- Line 31: Because we configured our timeToIdle and timeToExpire to 2 seconds (see the spring config), we know that by sleeping 2.5 seconds, the next hit to our method will force a renewal of the cache content
Voila! Obviously, you could and should expand on this, but it’s start.
[EDIT] I’ve posted an update, mainly about how to access cache hits.
Using TumblrJ to read
Jan 9th
With the current implementation of TumblrJ working properly for straight reads of different types, I figured I’d show a quick demo of the api usage (without Spring – I’ll post something about that later).
The api revolves around a single class called TumblrService. The class pretty much acts like a facade hiding the implementation details. The TumblrService needs a couple things to work properly:
- A TumblrJProperties instance
- An implementation of the ITumblrReader interface. The api includes the only one possible at this point which is an HTTP specific implementation.
Configuration
The TumblrJProperties class automatically initializes itself by looking for a file called tumblrj-config.properties in the classpath. You can override the name by using the 1 arg constructor. For now, the file needs to be found in the classpath.
The file contains basic information about paths, url’s, … The contents should look something exactly like:
base.url=http://{0}.tumblr.com
path.read=/api/read
path.authenticate=/api/authenticate
path.delete=/api/delete
Since most of these properties are pretty much standard, I’ll implement a defaults mechanism so that they aren’t mandatory.
Connection options
The other object needed to use the TumblrService and TumblrHttpReader classes is a TumblrConnectionOptions instance. At a very minimum, you’ll need to enter the name of your tumblr log (usualy, the first part in your tumblr.com url). If you want to access private posts, you’ll want to put in your email and password.
And for those who want fine grained control over the connection options, the object can also accept a org.apache.commons.httpclient.params.HttpClientParams instance which will be used on every http call.
Full Example
TumblrConnectionOptions connectionOptions = new TumblrConnectionOptions();
connectionOptions.setName("lama");
TumblrHttpReader reader = new TumblrHttpReader();
TumblrJProperties properties = new TumblrJProperties();
reader.setProperties(properties);
reader.setTumblrConnectionOptions(connectionOptions);
TumblrService service = new TumblrService();
service.setReader(reader);
TumblrReadOptions readOptions = new TumblrReadOptions();
readOptions.setStart(1);
readOptions.setNum(1);
TumbleLog log = service.read(readOptions);
Explanation
Lines 1 through 11 were pretty much explaines. Lines 12, 13 and 14 are interesting in that they let you configure the read options by letting you filter the data. The available options include:
- Wether or not to include private posts
- The start and number of posts to return (for paging and limiting results)
- The id of the post
- The type of post
Check out the TumblrReadOptions class for details.
Line 13 is the line where the read is executed and a TumbleLog object returned. That object will include a List of TumblePost objects as well as a gamut of other information concerning the returned results. As mentionned before, the test package contains a lot of usefull unit tests showing off how to use the different read options.
Arrays, what’s their point. Dito.
Dec 26th
When I first saw the post on Reddit, I found the reaction harsh (I still think the original poster meant “why use arrays instead of lists”).
As with Erlang, in Java arrays get in the way of readability. I understand arrays and the different usages, but I still prefer a List which is inifinitely more usable.
Hibernate session load vs. get gotcha
Aug 21st
The Hibernate documentation (3.2) for the session.get() and session.load() methods state:
load Returns: the persistent instance or proxy
get Returns: a persistent instance or nul
Notice “or proxy” in the load documentation. This last part took me an hour to figure out while debugging an application. In my case, for whatever reason (which I haven’t figured out), the load method was returning a proxy instead of an full instance, resulting in a state exception (closed session). For some unknown reason, this was only happening while running inside the web container and access the method through a transactional service. Test cases ran fine.
So changing my dao method from:
return (User) getSession().load(User.class, id)
to
return (User) getSession().get(User.class, id);
fixed everything.
