Thursday, June 23, 2011

GWT RPC + Apache Jackrabbit + OCM – extended example

In my previously article, I wrote about how to pass Apache Jackrabbit OCM and pass the nodes to the client side. It works really well, so I wanted to go for more extended example, for example using some collections or aggregation like shown in the OCM example page. Very briefly overview of what you should do to get all this to work:

NOTE: Read this article to see how I set-up the project fist.

  • Do not forget to implement IsSerializable – every time you have new node you should also implement the IsSerializable interface to make GWT able to pass the values to the client side
  • Do not forget to add the new nodes to the AnnotationMapperImpl
  • Because you change the schema you have to remove the old schema and restart the application, otherwise you may get some null pointer exceptions

Here the steps in details:

  • Copy the Author.java, Url.java and PressRelease.java into your project and inherit the IsSerializable.
1 @Node
2 public class Author implements IsSerializable {
3
4 @Field
5 private String firstName;
6 @Field
7 private String lastName;
8
9 /**
10 * no arg constructor required by Jackrabbit OCM
11 */
12 public Author() {
13 }
14
15 public Author(String firstName, String lastName) {
16
17 this.firstName = firstName;
18 this.lastName = lastName;
19 }
20
21 public String getFirstName() {
22 return firstName;
23 }
24
25 public void setFirstName(String firstName) {
26 this.firstName = firstName;
27 }
28
29 public String getLastName() {
30 return lastName;
31 }
32
33 public void setLastName(String lastName) {
34 this.lastName = lastName;
35 }
36
37 }
38

and here the Url.java


1 @Node
2 public class Url implements IsSerializable{
3
4 @Field(path = true)
5 String path;
6 @Field
7 String url;
8 @Field
9 String description;
10 @Field
11 String caption;
12
13 /**
14 * no arg constructor required by the OCM
15 */
16 public Url() {
17 super();
18
19 }
20
21 public Url(String url, String description, String caption) {
22 super();
23 this.url = url;
24 this.description = description;
25 this.caption = caption;
26 }
27
28 public String getPath() {
29 return path;
30 }
31
32 public void setPath(String path) {
33 this.path = path;
34 }
35
36 public String getUrl() {
37 return url;
38 }
39
40 public void setUrl(String url) {
41 this.url = url;
42 }
43
44 public String getDescription() {
45 return description;
46 }
47
48 public void setDescription(String description) {
49 this.description = description;
50 }
51
52 public String getCaption() {
53 return caption;
54 }
55
56 public void setCaption(String caption) {
57 this.caption = caption;
58 }
59
60 }
61

and PressRelease.java


1 @Node
2 public class PressRelease implements IsSerializable {
3 @Field(path = true)
4 private String path;
5
6 @Field
7 private String title;
8
9 @Field
10 private Date pubDate;
11
12 @Field
13 private String content;
14
15 @Bean
16 private Author author;
17
18 @Collection
19 private List<Url> urls;
20
21 // if you want a map instead of a list, use the following declaration
22 @Collection
23 private Map<String, Url> map;
24
25 public PressRelease(){
26 }
27
28 public String getPath() {
29 return path;
30 }
31
32 public void setPath(String path) {
33 this.path = path;
34 }
35
36 public String getContent() {
37 return content;
38 }
39
40 public void setContent(String content) {
41 this.content = content;
42 }
43
44 public Date getPubDate() {
45 return pubDate;
46 }
47
48 public void setPubDate(Date pubDate) {
49 this.pubDate = pubDate;
50 }
51
52 public String getTitle() {
53 return title;
54 }
55
56 public void setTitle(String title) {
57 this.title = title;
58 }
59
60 public Author getAuthor() {
61 return author;
62 }
63
64 public void setAuthor(Author author) {
65 this.author = author;
66 }
67
68 public List<Url> getUrls() {
69 return urls;
70 }
71
72 public void setUrls(List<Url> urls) {
73 this.urls = urls;
74 }
75
76 public Map<String, Url> getMap() {
77 return map;
78 }
79
80 public void setMap(Map<String, Url> map) {
81 this.map = map;
82 }
83 }
84



  • Go into your remote service implementation and add the new classes to the AnnotationMapperImpl class and initialize some values into your getPress RPC, so that your class will look like this on the end

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



  • Now we can proceed with the call from the client side like 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 String author = result.getAuthor().getLastName();
12
13 serverResponseLabel.setHTML(str + "<br/><b>" + author + "</b>");
14
15
16
17 dialogBox.center();
18 closeButton.setFocus(true);
19 }
20
21 @Override
22 public void onFailure(Throwable caught) {
23 // Show the RPC error message to the user
24 dialogBox
25 .setText("Remote Procedure Call - Failure");
26 serverResponseLabel
27 .addStyleName("serverResponseLabelError");
28 serverResponseLabel.setHTML(SERVER_ERROR);
29 dialogBox.center();
30 closeButton.setFocus(true);
31 }
32 });
33

Here again the full code from the my client MainEntry class:


 


1 /**
2 * Entry point classes define <code>onModuleLoad()</code>.
3 */
4 public class SampleGWT implements EntryPoint {
5 /**
6 * The message displayed to the user when the server cannot be reached or
7 * returns an error.
8 */
9 private static final String SERVER_ERROR = "An error occurred while "
10 + "attempting to contact the server. Please check your network "
11 + "connection and try again.";
12
13 /**
14 * Create a remote service proxy to talk to the server-side Greeting service.
15 */
16 private final GreetingServiceAsync greetingService = GWT
17 .create(GreetingService.class);
18
19 /**
20 * This is the entry point method.
21 */
22 public void onModuleLoad() {
23 final Button sendButton = new Button("Send");
24 final TextBox nameField = new TextBox();
25 nameField.setText("GWT User");
26 final Label errorLabel = new Label();
27
28 // We can add style names to widgets
29 sendButton.addStyleName("sendButton");
30
31 // Add the nameField and sendButton to the RootPanel
32 // Use RootPanel.get() to get the entire body element
33 RootPanel.get("nameFieldContainer").add(nameField);
34 RootPanel.get("sendButtonContainer").add(sendButton);
35 RootPanel.get("errorLabelContainer").add(errorLabel);
36
37 // Focus the cursor on the name field when the app loads
38 nameField.setFocus(true);
39 nameField.selectAll();
40
41 // Create the popup dialog box
42 final DialogBox dialogBox = new DialogBox();
43 dialogBox.setText("Remote Procedure Call");
44 dialogBox.setAnimationEnabled(true);
45 final Button closeButton = new Button("Close");
46 // We can set the id of a widget by accessing its Element
47 closeButton.getElement().setId("closeButton");
48 final Label textToServerLabel = new Label();
49 final HTML serverResponseLabel = new HTML();
50 VerticalPanel dialogVPanel = new VerticalPanel();
51 dialogVPanel.addStyleName("dialogVPanel");
52 dialogVPanel.add(new HTML("<b>Sending name to the server:</b>"));
53 dialogVPanel.add(textToServerLabel);
54 dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
55 dialogVPanel.add(serverResponseLabel);
56 dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
57 dialogVPanel.add(closeButton);
58 dialogBox.setWidget(dialogVPanel);
59
60 // Add a handler to close the DialogBox
61 closeButton.addClickHandler(new ClickHandler() {
62 public void onClick(ClickEvent event) {
63 dialogBox.hide();
64 sendButton.setEnabled(true);
65 sendButton.setFocus(true);
66 }
67 });
68
69 // Create a handler for the sendButton and nameField
70 class MyHandler implements ClickHandler, KeyUpHandler {
71 /**
72 * Fired when the user clicks on the sendButton.
73 */
74 public void onClick(ClickEvent event) {
75 sendNameToServer();
76 }
77
78 /**
79 * Fired when the user types in the nameField.
80 */
81 public void onKeyUp(KeyUpEvent event) {
82 if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
83 sendNameToServer();
84 }
85 }
86
87 /**
88 * Send the name from the nameField to the server and wait for a response.
89 */
90 private void sendNameToServer() {
91 // First, we validate the input.
92 errorLabel.setText("");
93 String textToServer = nameField.getText();
94 if (!FieldVerifier.isValidName(textToServer)) {
95 errorLabel.setText("Please enter at least four characters");
96 return;
97 }
98
99 // Then, we send the input to the server.
100 sendButton.setEnabled(false);
101 textToServerLabel.setText(textToServer);
102 serverResponseLabel.setText("");
103
104 greetingService.getPress(textToServer, new AsyncCallback<PressRelease>() {
105
106 @Override
107 public void onSuccess(PressRelease result) {
108 dialogBox.setText("Remote Procedure Call");
109 serverResponseLabel
110 .removeStyleName("serverResponseLabelError");
111
112 String str = result.getContent();
113
114 String author = result.getAuthor().getLastName();
115
116 serverResponseLabel.setHTML(str + "<br/><b>" + author + "</b>");
117
118
119
120 dialogBox.center();
121 closeButton.setFocus(true);
122 }
123
124 @Override
125 public void onFailure(Throwable caught) {
126 // Show the RPC error message to the user
127 dialogBox
128 .setText("Remote Procedure Call - Failure");
129 serverResponseLabel
130 .addStyleName("serverResponseLabelError");
131 serverResponseLabel.setHTML(SERVER_ERROR);
132 dialogBox.center();
133 closeButton.setFocus(true);
134 }
135 });
136
137
138 }
139 }
140
141 // Add a handler to send the name to the server
142 MyHandler handler = new MyHandler();
143 sendButton.addClickHandler(handler);
144 nameField.addKeyUpHandler(handler);
145 }
146 }
147

cheers

No comments:

Post a Comment