Friday, June 3, 2011

GWT and Apache Jackrabbit JCR Object Content Mapping

I was playing with Java Content Repository and Apache Jackrabbit and also some examples for Oracle UCM and JCR and since I work a lot with GWT in the last project I thought, why not using GWT with Jackrabbit OCM. There were not really examples in Internet about but several open source projects are using GWT and Apache Jackrabbit together. I wasn’t able to see pure implementation, something like just OCM object and past them via GWT RPC to the client. I thought, I will try it, I didn’t expect that is going to work so easy. Actually I expect that the GWT serialization model will break the OCM notes and I will be not able to pass them to the client, but after a very simple test I was more then surprised to see that this was not the case.

So here is what I did:

  1. Download the 5minutes project from the Apache Jackrabbit page
  2. Create GWT startup project using Eclipse
  3. Download the Apache Jackrabbit Content Repository Implementation and add the libraries to the project. You can use also Maven if you wish I just wanted to make a fast PoC
  4. You do need to download also following libraries in to be able to use the OCM implementation. This libraries are: jcr-2.0.jar, slf4j-api-1.6.1.jar, slf4j-log4j12-1.6.1.jar, log4j-1.2.16.jar, cglib-2.2.2.jar and add them to the GWT Eclipse project you created.

Now let’s get some code from the 5minutes project and use it into our GWT project. First create into the share folder PressRelease class. We need to make one small change to this class, we have to implement from com.google.gwt.user.client.rpc.IsSerializable. Your class will look now like this:

1 import java.util.Date;
2
3 import org.apache.jackrabbit.ocm.mapper.impl.annotation.Field;
4 import org.apache.jackrabbit.ocm.mapper.impl.annotation.Node;
5
6 import com.google.gwt.user.client.rpc.IsSerializable;
7
8 @Node
9 public class PressRelease implements IsSerializable {
10 @Field(path = true)
11 String path;
12 @Field
13 String title;
14 @Field
15 Date pubDate;
16 @Field
17 String content;
18
19 public String getPath() {
20 return path;
21 }
22
23 public void setPath(String path) {
24 this.path = path;
25 }
26
27 public String getContent() {
28 return content;
29 }
30
31 public void setContent(String content) {
32 this.content = content;
33 }
34
35 public Date getPubDate() {
36 return pubDate;
37 }
38
39 public void setPubDate(Date pubDate) {
40 this.pubDate = pubDate;
41 }
42
43 public String getTitle() {
44 return title;
45 }
46
47 public void setTitle(String title) {
48 this.title = title;
49 }
50
51 }

As next I just copied the RepositoryUtil.java class from the 5minutes project into my GWT project into the server namespace. Now we can try to pass the PressRelease class via the GWT-RPC to the client.


Add new RPC method to your GreetingService and GreetingServiceAsync. Now they well look like this:


1 import com.google.gwt.user.client.rpc.RemoteService;
2 import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
3 import com.sample.shared.PressRelease;
4
5 /**
6 * The client side stub for the RPC service.
7 */
8 @RemoteServiceRelativePath("greet")
9 public interface GreetingService extends RemoteService {
10 String greetServer(String name) throws IllegalArgumentException;
11
12 PressRelease getPress(String name) throws IllegalArgumentException;
13 }

and this


1 import com.google.gwt.user.client.rpc.AsyncCallback;
2 import com.sample.shared.PressRelease;
3
4 /**
5 * The async counterpart of <code>GreetingService</code>.
6 */
7 public interface GreetingServiceAsync {
8 void greetServer(String input, AsyncCallback<String> callback)
9 throws IllegalArgumentException;
10
11 void getPress(String name, AsyncCallback<PressRelease> callback);
12 }
13

Next step will be to implement the new service so go into the:


1 @Override
2 public PressRelease getPress(String name) throws IllegalArgumentException {
3
4 System.out.println("Start the tutorial ...");
5 ObjectContentManager ocm;
6 try {
7 ocm = this.getOCM();
8 } catch (IOException e) {
9 // TODO Auto-generated catch block
10 throw new IllegalArgumentException(e.getMessage());
11 }
12
13 // Insert an object
14 System.out.println("Insert a press release in the repository");
15 PressRelease pressRelease = new PressRelease();
16 PressRelease pressReleaseReturn = new PressRelease();
17 pressRelease.setPath("/newtutorial");
18 pressRelease.setTitle("This is the first tutorial on OCM");
19 pressRelease.setPubDate(new Date());
20 pressRelease
21 .setContent("Many Jackrabbit users ask to the dev team to make a tutorial on OCM");
22
23 ocm.insert(pressRelease);
24 ocm.save();
25
26 // Retrieve
27 System.out.println("Retrieve a press release from the repository");
28 pressRelease = (PressRelease) ocm.getObject("/newtutorial");
29 pressReleaseReturn = pressRelease;
30 System.out.println("PressRelease title : " + pressRelease.getTitle());
31
32 // Delete
33 System.out.println("Remove a press release from the repository");
34 ocm.remove(pressRelease);
35 ocm.save();
36
37 return pressReleaseReturn;
38 }
39

Now your GreetingServiceImpl class will look like this:


1 import java.io.IOException;
2 import java.util.ArrayList;
3 import java.util.Date;
4 import java.util.List;
5
6 import javax.jcr.Repository;
7 import javax.jcr.Session;
8
9 import org.apache.jackrabbit.ocm.manager.ObjectContentManager;
10 import org.apache.jackrabbit.ocm.manager.impl.ObjectContentManagerImpl;
11 import org.apache.jackrabbit.ocm.mapper.Mapper;
12 import org.apache.jackrabbit.ocm.mapper.impl.annotation.AnnotationMapperImpl;
13
14 import com.google.gwt.user.server.rpc.RemoteServiceServlet;
15 import com.sample.client.GreetingService;
16 import com.sample.shared.FieldVerifier;
17 import com.sample.shared.PressRelease;
18
19 /**
20 * The server side implementation of the RPC service.
21 */
22 @SuppressWarnings("serial")
23 public class GreetingServiceImpl extends RemoteServiceServlet implements
24 GreetingService {
25
26 public String greetServer(String input) throws IllegalArgumentException {
27 // Verify that the input is valid.
28 if (!FieldVerifier.isValidName(input)) {
29 // If the input is not valid, throw an IllegalArgumentException back
30 // to
31 // the client.
32 throw new IllegalArgumentException(
33 "Name must be at least 4 characters long");
34 }
35
36 String serverInfo = getServletContext().getServerInfo();
37 String userAgent = getThreadLocalRequest().getHeader("User-Agent");
38
39 // Escape data from the client to avoid cross-site script
40 // vulnerabilities.
41 input = escapeHtml(input);
42 userAgent = escapeHtml(userAgent);
43
44 return "Hello, " + input + "!<br><br>I am running " + serverInfo
45 + ".<br><br>It looks like you are using:<br>" + userAgent;
46 }
47
48 /**
49 * Escape an html string. Escaping data received from the client helps to
50 * prevent cross-site script vulnerabilities.
51 *
52 * @param html
53 * the html string to escape
54 * @return the escaped string
55 */
56 private String escapeHtml(String html) {
57 if (html == null) {
58 return null;
59 }
60 return html.replaceAll("&", "&amp;").replaceAll("<", "&lt;")
61 .replaceAll(">", "&gt;");
62 }
63
64 private ObjectContentManager getOCM() throws IOException {
65 // Get a JCR session
66 Session session = getLocalSession();
67
68 // Add persistent classes
69 @SuppressWarnings("rawtypes")
70 List<Class> classes = new ArrayList<Class>();
71 classes.add(com.sample.shared.PressRelease.class);
72
73 Mapper mapper = new AnnotationMapperImpl(classes);
74 return new ObjectContentManagerImpl(session, mapper);
75 }
76
77 private Session getLocalSession() throws IOException {
78
79 Repository repository = RepositoryUtil.getTrancientRepository();
80 Session session = RepositoryUtil.login(repository, "username",
81 "superuser");
82
83 return session;
84 }
85
86 @Override
87 public PressRelease getPress(String name) throws IllegalArgumentException {
88
89 System.out.println("Start the tutorial ...");
90 ObjectContentManager ocm;
91 try {
92 ocm = this.getOCM();
93 } catch (IOException e) {
94 // TODO Auto-generated catch block
95 throw new IllegalArgumentException(e.getMessage());
96 }
97
98 // Insert an object
99 System.out.println("Insert a press release in the repository");
100 PressRelease pressRelease = new PressRelease();
101 PressRelease pressReleaseReturn = new PressRelease();
102 pressRelease.setPath("/newtutorial");
103 pressRelease.setTitle("This is the first tutorial on OCM");
104 pressRelease.setPubDate(new Date());
105 pressRelease
106 .setContent("Many Jackrabbit users ask to the dev team to make a tutorial on OCM");
107
108 ocm.insert(pressRelease);
109 ocm.save();
110
111 // Retrieve
112 System.out.println("Retrieve a press release from the repository");
113 pressRelease = (PressRelease) ocm.getObject("/newtutorial");
114 pressReleaseReturn = pressRelease;
115 System.out.println("PressRelease title : " + pressRelease.getTitle());
116
117 // Delete
118 System.out.println("Remove a press release from the repository");
119 ocm.remove(pressRelease);
120 ocm.save();
121
122 return pressReleaseReturn;
123 }
124
125 }
126

As next to into your main EntryPoint and change the code requesting the RPC into sendNameToServer() function, with this:


1 greetingService.getPress(textToServer, new AsyncCallback<PressRelease>() {
2
3 @Override
4 public void onSuccess(PressRelease result) {
5 dialogBox.setText("Remote Procedure Call");
6 serverResponseLabel
7 .removeStyleName("serverResponseLabelError");
8
9 String str = result.getContent();
10
11 serverResponseLabel.setHTML(str);
12 dialogBox.center();
13 closeButton.setFocus(true);
14 }
15
16 @Override
17 public void onFailure(Throwable caught) {
18 // Show the RPC error message to the user
19 dialogBox
20 .setText("Remote Procedure Call - Failure");
21 serverResponseLabel
22 .addStyleName("serverResponseLabelError");
23 serverResponseLabel.setHTML(SERVER_ERROR);
24 dialogBox.center();
25 closeButton.setFocus(true);
26 }
27 });
28

 


You can start now your GWT project and test it, and you should see this:


image


AmazingZwinkerndes Smiley but it works. I didn’t try to use some more complicated models but I think it should work as well.

No comments:

Post a Comment