Last month I did a talk at Expedia on Spring Data JPA, Spring Data REST and Spring HATEOAS for the Seattle Java User’s Group.
Video: http://vimeo.com/66717404
Last month I did a talk at Expedia on Spring Data JPA, Spring Data REST and Spring HATEOAS for the Seattle Java User’s Group.
Video: http://vimeo.com/66717404
Spring in Practice is now available!
It’s also the Deal of the Day today, which means half off if you buy it today (May 8, 2013) from Manning. Use code dotd0508au at www.manning.com/wheeler/.
Josh and I hope that everybody enjoys the book. It was a lot of work, but I’m very happy with the end result.
This is one of those posts where I’m just jotting down some notes for my own future use, but someone else may find this useful.
I’m going to show how to rename a node entity class when using Spring Data Neo4j. I’m talking about the fully-qualified classname here, so it applies when we want to rename a package too.
First, suppose my old classname is org.zkybase.cmdb.api.domain.ApplicationEntity. If I go into the Neo4j shell, I can see that I have a couple of nodes of this type:
neo4j-sh (0)$ start n=node:__types__(className="org.zkybase.cmdb.api.domain.ApplicationEntity") return n
+-----------------------------------------------------------------------------------------------------+
| n |
+-----------------------------------------------------------------------------------------------------+
| Node[36]{__type__->"org.zkybase.cmdb.api.domain.ApplicationEntity",name->"Zkybase"} |
| Node[49]{name->"Spring in Practice Blog",__type__->"org.zkybase.cmdb.api.domain.ApplicationEntity"} |
+-----------------------------------------------------------------------------------------------------+
2 rows, 1 ms
From the above, it looks like you can just go to the nodes themselves and change their __type__ fields:
neo4j-sh (0)$ cd -a 36 neo4j-sh (Zkybase,36)$ ls *__type__ =[org.zkybase.cmdb.api.domain.ApplicationEntity] *name =[Zkybase] neo4j-sh (Zkybase,36)$ set __type__ "org.zkybase.api.domain.entity.ApplicationEntity" neo4j-sh (Zkybase,36)$ ls *__type__ =[org.zkybase.api.domain.entity.ApplicationEntity] *name =[Zkybase]
But then when you try to find the node using the query, it doesn’t show up.
neo4j-sh (Zkybase,36)$ start n=node:__types__(className="org.zkybase.api.domain.entity.ApplicationEntity") return n +---+ | n | +---+ +---+ 0 rows, 1 ms
Moreover, when you re-run the original query, the node whose __type__ we changed still shows up.
The problem is that we need to reindex the nodes. Spring Data Neo4j uses an index called __types__, and we need to replace the old index entries with some new ones.
Let’s see what’s under the old classname, using index -g to get the relevant nodes from the __types__ index:
neo4j-sh (Zkybase,36)$ index -g __types__ className "org.zkybase.cmdb.api.domain.ApplicationEntity" (me) (Spring in Practice Blog,49)
And under the new classname:
neo4j-sh (Zkybase,36)$ index -g __types__ className "org.zkybase.api.domain.entity.ApplicationEntity" neo4j-sh (Zkybase,36)$
We can fix that using index -i, which indexes the current entity:
neo4j-sh (Zkybase,36)$ index -i __types__ className "org.zkybase.api.domain.entity.ApplicationEntity" neo4j-sh (Zkybase,36)$ cd -a 49 neo4j-sh (Spring in Practice Blog,49)$ index -i __types__ className "org.zkybase.api.domain.entity.ApplicationEntity" neo4j-sh (Spring in Practice Blog,49)$ cd -a 36 neo4j-sh (Zkybase,36)$ index -g __types__ className "org.zkybase.api.domain.entity.ApplicationEntity" (me) (Spring in Practice Blog,49)
We still need to clean up the old entries, though, because they’re still there. We use index -r to remove the index entry for the current node:
neo4j-sh (Zkybase,36)$ index -r __types__ className "org.zkybase.cmdb.api.domain.ApplicationEntity" neo4j-sh (Zkybase,36)$ cd -a 49 neo4j-sh (Spring in Practice Blog,49)$ index -r __types__ className "org.zkybase.cmdb.api.domain.ApplicationEntity" neo4j-sh (Spring in Practice Blog,49)$ index -g __types__ className "org.zkybase.cmdb.api.domain.ApplicationEntity" neo4j-sh (Spring in Practice Blog,49)$
That’s it. This was the result of about 15 minutes of investigation, so there’s a good chance there’s more going on than what I’ve described. Let me know and I’ll update the post accordingly.
In previous posts I explained how you can use Spring Data JPA to create repositories that support custom queries, as well as to support paging in your app. You might wonder whether you can use these together.
The answer is yes. It works just like you would expect:
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
public interface IncidentRepo extends JpaRepository<Incident, Long> {
Page<Incident> findByProblemId(Long problemId, Pageable pageable);
}
Here’s a quick tip for you.
Sometimes you need to inject a java.io.File from your classpath into a bean, but you don’t want to have to spell out the absolute path (even in a configuration file). Never fear. It’s easy:
<bean id="tagProviderResource" class="org.springframework.core.io.ClassPathResource">
<constructor-arg value="/htmlcleaner.xml" />
</bean>
<util:property-path id="tagProviderFile" path="tagProviderResource.file" />
<bean id="tagProvider" class="org.htmlcleaner.ConfigFileTagProvider">
<constructor-arg ref="tagProviderFile" />
</bean>
In the configuration above, I used ClassPathResource to find the htmlcleaner.xml resource on the classpath. Then I used the handy <util:property-path> tag to assign the resource’s file property its own ID. Finally, I inject the File using constructor injection.
In an earlier post I introduced Spring Data JPA, which makes it really easy to create a DAO layer. I didn’t get into too much depth, so this time I want to explore a couple of cool features that the DAOs support: pagination and sorting.
Pagination and sorting are useful when you have long lists that you want the user to be able to navigate. Here for example is a UI for a runbook app I’m building. One of the things it allows the user to do is view deployment logs, which we typically want to see in reverse chronological order. Also, since there are lots of logs, we want to page.
There are different ways to design a pagination system from a user experience perspective. Here I’ve done something pretty typical: I have links for first/previous/next/last, and then I show a bounded set of pages around the current page.
How does Spring Data JPA help? Here’s my DeploymentRepo interface:
package com.example.repo;
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.model.Deployment;
public interface DeploymentRepo extends JpaRepository<Deployment, Long> { }
The JpaRepository interface extends Spring Data’s PagingAndSortingRepository interface, so I get some paging/sorting finders for free.
I have a simple service bean that calls the repo:
package com.example.service.impl;
import javax.inject.Inject;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.repo.DeploymentRepo;
import com.example.model.Deployment;
import com.example.service.DeploymentLogService;
@Service
@Transactional
public class DeploymentLogServiceImpl implements DeploymentLogService {
private static final int PAGE_SIZE = 50;
@Inject private DeploymentRepo deploymentRepo;
public Page<Deployment> getDeploymentLog(Integer pageNumber) {
PageRequest request =
new PageRequest(pageNumber - 1, PAGE_SIZE, Sort.Direction.DESC, "startTime");
return deploymentRepo.findAll(pageRequest);
}
}
Spring Data uses 0-indexed pages, but I want my service interface to use 1-indexed pages (they will be user-visible and I want the page numbers to be intuitive), so I make the appropriate adjustment in the request. I specify the page size (50 deployments per page), sort direction, and also one or more property names to act as sort keys. Here I’ve chosen startTime, which is a timestamp for the start of the deployment.
That takes care of the Spring Data JPA part, but just for fun, I’ll show you a simplified version of the controller method and JSP too.
Here’s the controller method:
@RequestMapping(value = "/pages/{pageNumber}", method = RequestMethod.GET)
public String getRunbookPage(@PathVariable Integer pageNumber, Model model) {
Page<Deployment> page = deploymentService.getDeploymentLog(pageNumber);
int current = page.getNumber() + 1;
int begin = Math.max(1, current - 5);
int end = Math.min(begin + 10, page.getTotalPages());
model.addAttribute("deploymentLog", page);
model.addAttribute("beginIndex", begin);
model.addAttribute("endIndex", end);
model.addAttribute("currentIndex", current);
return "deploymentLog";
}
Note again that I’ve adjusted the page numbers to convert Spring Data’s 0-indexing to my app’s 1-indexing.
I’ve precalculated the begin/end indices because JSTL doesn’t have the min and max functions, and anyway, it’s cleaner to do this sort of thing in the controller.
Finally here’s the page navigation in the JSP. It uses the Twitter Bootstrap library for the UI, so that’s where the various CSS elements come from.
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:url var="firstUrl" value="/pages/1" />
<c:url var="lastUrl" value="/pages/${deploymentLog.totalPages}" />
<c:url var="prevUrl" value="/pages/${currentIndex - 1}" />
<c:url var="nextUrl" value="/pages/${currentIndex + 1}" />
<div class="pagination">
<ul>
<c:choose>
<c:when test="${currentIndex == 1}">
<li class="disabled"><a href="#"><<</a></li>
<li class="disabled"><a href="#"><</a></li>
</c:when>
<c:otherwise>
<li><a href="${firstUrl}"><<</a></li>
<li><a href="${prevUrl}"><</a></li>
</c:otherwise>
</c:choose>
<c:forEach var="i" begin="${beginIndex}" end="${endIndex}">
<c:url var="pageUrl" value="/pages/${i}" />
<c:choose>
<c:when test="${i == currentIndex}">
<li class="active"><a href="${pageUrl}"><c:out value="${i}" /></a></li>
</c:when>
<c:otherwise>
<li><a href="${pageUrl}"><c:out value="${i}" /></a></li>
</c:otherwise>
</c:choose>
</c:forEach>
<c:choose>
<c:when test="${currentIndex == deploymentLog.totalPages}">
<li class="disabled"><a href="#">></a></li>
<li class="disabled"><a href="#">>></a></li>
</c:when>
<c:otherwise>
<li><a href="${nextUrl}">></a></li>
<li><a href="${lastUrl}">>></a></li>
</c:otherwise>
</c:choose>
</ul>
</div>
Spring Data JPA makes it very nice and simple. And Twitter Bootstrap looks great too.
The other day I wrote up a post explaining how to use Spring’s constructor namespace, which is new with Spring 3.1. So the following might be a little surprising:
I wanted to like it—honest I did. While I’ve always been a little iffy on the whole idea of introspecting on method parameter names, Juergen Hoeller answered my concerns with respect to @PathVariable and the like. My worries around “least surprise” were admittedly academic since I always compile binaries in debug mode.
So I tried the constructor namespace out, and it bit me in exactly the way I expected.
Just as a quick refresher (or introduction), here’s how the constructor namespace works:
<bean class="com.example.Client"
c:restTemplate-ref-"restTemplate"
c:baseUrl="http://localhost:8080/service" />
The configuration presupposes a constructor that looks like this:
public Client(RestTemplate restTemplate, String baseUrl) { ... }
Here’s the problem. Developers expect changing parameter names to be a local operation. Even in some of the Spring Web MVC cases that Juergen mentions in response to my JIRA issue—@PathVariable, @RequestParam, @RequestHeader and @CookieValue—the annotations in question are colocated with the parameters; e.g.
public String getUser(@PathVariable Long id, Model model) { ... }
so somebody experienced with the framework will know what to do, and somebody less experienced will discover the issue soon enough (the code won’t work) and fix it.
But the constructor namespace is totally different. Changing the constructor parameter name has the potential to break client code, and that client code may not be part of your current project. So you might not know you broke someone else’s code.
This happened to me today, except it was breaking my own code in a separate project. I have a web service client with a constructor like this:
public Client(RestTemplate restTemplate, String basePath) { ... }
and I changed that to
public Client(RestTemplate restTemplate, String baseUrl) { ... }
This broke code in another project that was using the constructor namespace to inject values into the constructor. This is another violation of the principle of least surprise, but this time I don’t think it’s theoretical in the least. It happened to me the very first time.
One solution would be for Spring to simply get rid of this feature, as it leads to brittle code.
Another possibility would be for Spring to do something like this instead:
public Client(@Param RestTemplate restTemplate, @Param String baseUrl) { ... }
and even
public Client(@Param("restTemplate") RestTemplate template, @Param("baseUrl") String url) { ... }
That would make it more explicit that the constructor parameter names were being exposed as part of an API.
If you’re set on using it, you should adopt the practice of documenting your constructors when they support this configuration style. And as an API consumer, you should assume that constructor parameter names are not contractual unless explicitly documented as such. Parameter names in Java have always counted as implementation details, and while I’m all for innovation and challenging the status quo, the benefit that the constructor namespace offers here is far too modest to call for revisiting this particular issue.
This post is for myself more than anything else, just because I keep forgetting the steps involved.
I’m using SpringSource Tool Suite 2.9.1.RELEASE, which is based on Eclipse 3.7.2 (Indigo). I have the egit and m2e Eclipse plugins installed.
You have a brand new, pretty-much-empty GitHub project (other than the README, say—but no Maven stuff yet), and you want to import it into Eclipse as a Maven project.
ssh://git@github.com/williewheeler/sip11.git, for example.To better focus my Java/Spring blogging, I’ve merged my former Wheeler Software blog into this one. There are a few articles that didn’t make the cut, but the majority of them did.
How to run JavaScript from Java
Java 6 comes with the Rhino JavaScript engine, which makes it easy to run JavaScript from inside your Java app. There are different situations in which you might want to do this. Chapter 9 of Spring in Practice affords a good example. There we’re implementing a rich-text comment engine based on the WMD editor that StackOverflow uses. We have a
showdown.jsscript that maps Markdown to HTML, and we want to run it in two places:Here’s how we can run it on the server:
import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; ... public final class RichTextFilter implements TextFilter { @Inject private File showdownJsFile; private String markdownToHtml(String markdown) { try { ScriptEngineManager mgr = new ScriptEngineManager(); ScriptEngine engine = mgr.getEngineByName("JavaScript"); engine.eval(showdownJs); engine.eval("var markdown = '" + markdown + "';"); engine.eval("var converter = new Showdown.converter();"); engine.eval("var html = converter.makeHtml(markdown);"); return (String) engine.get("html"); } catch (ScriptException e) { // Shouldn't happen unless somebody breaks the script throw new RuntimeException(e); } } ... other stuff ... }To see how to inject the File into the class in Spring, see my post Injecting a file from the classpath into a bean.
Share this: