1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.web.jcr.rest.client.json;
25
26 import java.io.File;
27 import java.io.FileFilter;
28 import java.net.HttpURLConnection;
29 import java.net.URL;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.LinkedList;
35 import java.util.List;
36 import java.util.Map;
37 import org.codehaus.jettison.json.JSONArray;
38 import org.codehaus.jettison.json.JSONObject;
39 import org.modeshape.common.util.Base64;
40 import org.modeshape.common.util.CheckArg;
41 import org.modeshape.common.util.Logger;
42 import org.modeshape.web.jcr.rest.client.IJcrConstants;
43 import org.modeshape.web.jcr.rest.client.IRestClient;
44 import org.modeshape.web.jcr.rest.client.RestClientI18n;
45 import org.modeshape.web.jcr.rest.client.Status;
46 import org.modeshape.web.jcr.rest.client.Status.Severity;
47 import org.modeshape.web.jcr.rest.client.domain.QueryRow;
48 import org.modeshape.web.jcr.rest.client.domain.Repository;
49 import org.modeshape.web.jcr.rest.client.domain.Server;
50 import org.modeshape.web.jcr.rest.client.domain.Workspace;
51 import org.modeshape.web.jcr.rest.client.http.HttpClientConnection;
52 import org.modeshape.web.jcr.rest.client.json.IJsonConstants.RequestMethod;
53
54
55
56
57
58 public final class JsonRestClient implements IRestClient {
59
60
61
62
63
64
65
66
67 private static final Logger LOGGER = Logger.getLogger(JsonRestClient.class);
68
69
70
71
72
73
74
75
76
77
78
79
80 private HttpClientConnection connect( Server server,
81 URL url,
82 RequestMethod method ) throws Exception {
83 LOGGER.trace("connect: url={0}, method={1}", url, method);
84 return new HttpClientConnection(server, url, method);
85 }
86
87
88
89
90
91
92
93
94
95 private void createFileNode( Workspace workspace,
96 String path,
97 File file ) throws Exception {
98 LOGGER.trace("createFileNode: workspace={0}, path={1}, file={2}", workspace.getName(), path, file.getAbsolutePath());
99 FileNode fileNode = new FileNode(workspace, path, file);
100 HttpClientConnection connection = connect(workspace.getServer(), fileNode.getUrl(), RequestMethod.POST);
101
102 try {
103 LOGGER.trace("createFileNode: create node={0}", fileNode);
104 connection.write(fileNode.getContent());
105
106
107 int responseCode = connection.getResponseCode();
108
109 if (responseCode != HttpURLConnection.HTTP_CREATED) {
110
111 LOGGER.error(RestClientI18n.connectionErrorMsg, responseCode, "createFileNode");
112 String msg = RestClientI18n.createFileFailedMsg.text(file.getName(), path, workspace.getName(), responseCode);
113 throw new RuntimeException(msg);
114 }
115 } finally {
116 if (connection != null) {
117 LOGGER.trace("createFileNode: leaving");
118 connection.disconnect();
119 }
120 }
121 }
122
123
124
125
126
127
128
129
130 private void createFolderNode( Workspace workspace,
131 String path ) throws Exception {
132 LOGGER.trace("createFolderNode: workspace={0}, path={1}", workspace.getName(), path);
133 FolderNode folderNode = new FolderNode(workspace, path);
134
135 HttpClientConnection connection = connect(workspace.getServer(), folderNode.getUrl(), RequestMethod.POST);
136
137 try {
138 LOGGER.trace("createFolderNode: create node={0}", folderNode);
139 connection.write(folderNode.getContent());
140
141
142 int responseCode = connection.getResponseCode();
143
144 if (responseCode != HttpURLConnection.HTTP_CREATED) {
145
146 LOGGER.error(RestClientI18n.connectionErrorMsg, responseCode, "createFolderNode");
147 String msg = RestClientI18n.createFolderFailedMsg.text(path, workspace.getName(), responseCode);
148 throw new RuntimeException(msg);
149 }
150 } finally {
151 if (connection != null) {
152 LOGGER.trace("createFolderNode: leaving");
153 connection.disconnect();
154 }
155 }
156 }
157
158
159
160
161
162
163
164
165 private void ensureFolderExists( Workspace workspace,
166 String folderPath ) throws Exception {
167 LOGGER.trace("ensureFolderExists: workspace={0}, path={1}", workspace.getName(), folderPath);
168 FolderNode folderNode = new FolderNode(workspace, folderPath);
169
170 if (!pathExists(workspace.getServer(), folderNode.getUrl())) {
171 StringBuilder path = new StringBuilder();
172
173 for (char c : folderPath.toCharArray()) {
174 if (c == '/') {
175 if (path.length() > 1) {
176 folderNode = new FolderNode(workspace, path.toString());
177
178 if (!pathExists(workspace.getServer(), folderNode.getUrl())) {
179 createFolderNode(workspace, folderNode.getPath());
180 }
181 }
182
183 path.append(c);
184 } else {
185 path.append(c);
186
187 if (path.length() == folderPath.length()) {
188 folderNode = new FolderNode(workspace, path.toString());
189
190 if (!pathExists(workspace.getServer(), folderNode.getUrl())) {
191 createFolderNode(workspace, folderNode.getPath());
192 }
193 }
194 }
195 }
196 }
197 }
198
199
200
201
202
203
204 public Collection<Repository> getRepositories( Server server ) throws Exception {
205 CheckArg.isNotNull(server, "server");
206 LOGGER.trace("getRepositories: server={0}", server);
207
208 ServerNode serverNode = new ServerNode(server);
209 HttpClientConnection connection = connect(server, serverNode.getFindRepositoriesUrl(), RequestMethod.GET);
210
211 try {
212 int responseCode = connection.getResponseCode();
213
214 if (responseCode == HttpURLConnection.HTTP_OK) {
215 return serverNode.getRepositories(connection.read());
216 }
217
218
219 LOGGER.error(RestClientI18n.connectionErrorMsg, responseCode, "getRepositories");
220 String msg = RestClientI18n.getRepositoriesFailedMsg.text(server.getName(), responseCode);
221 throw new RuntimeException(msg);
222 } finally {
223 if (connection != null) {
224 LOGGER.trace("getRepositories: leaving");
225 connection.disconnect();
226 }
227 }
228 }
229
230
231
232
233
234
235
236 public URL getUrl( File file,
237 String path,
238 Workspace workspace ) throws Exception {
239 CheckArg.isNotNull(file, "file");
240 CheckArg.isNotNull(path, "path");
241 CheckArg.isNotNull(workspace, "workspace");
242
243
244 if (file.isDirectory()) {
245 throw new IllegalArgumentException();
246 }
247
248 return new FileNode(workspace, path, file).getUrl();
249 }
250
251
252
253
254
255
256 public Collection<Workspace> getWorkspaces( Repository repository ) throws Exception {
257 CheckArg.isNotNull(repository, "repository");
258 LOGGER.trace("getWorkspaces: repository={0}", repository);
259
260 RepositoryNode repositoryNode = new RepositoryNode(repository);
261 HttpClientConnection connection = connect(repository.getServer(), repositoryNode.getUrl(), RequestMethod.GET);
262
263 try {
264 int responseCode = connection.getResponseCode();
265
266 if (responseCode == HttpURLConnection.HTTP_OK) {
267 return repositoryNode.getWorkspaces(connection.read());
268 }
269
270
271 LOGGER.error(RestClientI18n.connectionErrorMsg, responseCode, "getWorkspaces");
272 String msg = RestClientI18n.getWorkspacesFailedMsg.text(repository.getName(),
273 repository.getServer().getName(),
274 responseCode);
275 throw new RuntimeException(msg);
276 } finally {
277 if (connection != null) {
278 LOGGER.trace("getWorkspaces: leaving");
279 connection.disconnect();
280 }
281 }
282 }
283
284
285
286
287
288
289
290
291
292
293 String getFileContents( Workspace workspace,
294 String path,
295 File file ) throws Exception {
296 FileNode fileNode = new FileNode(workspace, path, file);
297 HttpClientConnection connection = connect(workspace.getServer(), fileNode.getFileContentsUrl(), RequestMethod.GET);
298 int responseCode = connection.getResponseCode();
299
300 if (responseCode == HttpURLConnection.HTTP_OK) {
301 String result = connection.read();
302 return fileNode.getFileContents(result);
303 }
304
305 return null;
306 }
307
308
309
310
311
312
313
314 private boolean pathExists( Server server,
315 URL url ) throws Exception {
316 LOGGER.trace("pathExists: url={0}", url);
317 HttpClientConnection connection = connect(server, url, RequestMethod.GET);
318
319 try {
320 int responseCode = connection.getResponseCode();
321 LOGGER.trace("pathExists: responseCode={0}", responseCode);
322 return (responseCode == HttpURLConnection.HTTP_OK);
323 } finally {
324 if (connection != null) {
325 LOGGER.trace("pathExists: leaving");
326 connection.disconnect();
327 }
328 }
329 }
330
331
332
333
334
335
336
337
338 public boolean pathExists( Workspace workspace,
339 String path,
340 File file ) throws Exception {
341 FileNode fileNode = new FileNode(workspace, path, file);
342 return pathExists(workspace.getServer(), fileNode.getUrl());
343 }
344
345
346
347
348
349
350
351 public Status publish( Workspace workspace,
352 String path,
353 File file ) {
354 CheckArg.isNotNull(workspace, "workspace");
355 CheckArg.isNotNull(path, "path");
356 CheckArg.isNotNull(file, "file");
357 LOGGER.trace("publish: workspace={0}, path={1}, file={2}", workspace.getName(), path, file.getAbsolutePath());
358
359 try {
360
361 if (pathExists(workspace, path, file)) {
362 unpublish(workspace, path, file);
363 } else {
364
365 ensureFolderExists(workspace, path);
366 }
367
368
369 createFileNode(workspace, path, file);
370 } catch (Exception e) {
371 String msg = RestClientI18n.publishFailedMsg.text(file.getAbsolutePath(), path, workspace.getName());
372 return new Status(Severity.ERROR, msg, e);
373 }
374
375 return Status.OK_STATUS;
376 }
377
378
379
380
381
382
383
384 public Status unpublish( Workspace workspace,
385 String path,
386 File file ) {
387 CheckArg.isNotNull(workspace, "workspace");
388 CheckArg.isNotNull(path, "path");
389 CheckArg.isNotNull(file, "file");
390 LOGGER.trace("unpublish: workspace={0}, path={1}, file={2}", workspace.getName(), path, file.getAbsolutePath());
391
392 HttpClientConnection connection = null;
393
394 try {
395 FileNode fileNode = new FileNode(workspace, path, file);
396 connection = connect(workspace.getServer(), fileNode.getUrl(), RequestMethod.DELETE);
397 int responseCode = connection.getResponseCode();
398 LOGGER.trace("responseCode={0}", responseCode);
399
400 if (responseCode != HttpURLConnection.HTTP_NO_CONTENT) {
401
402 if (!pathExists(workspace.getServer(), fileNode.getUrl())) {
403 String msg = RestClientI18n.unpublishNeverPublishedMsg.text(file.getAbsolutePath(), workspace.getName(), path);
404 return new Status(Severity.INFO, msg, null);
405 }
406
407
408 LOGGER.error(RestClientI18n.connectionErrorMsg, responseCode, "unpublish");
409 String msg = RestClientI18n.unpublishFailedMsg.text(file.getName(), workspace.getName(), path);
410 throw new RuntimeException(msg);
411 }
412
413 return Status.OK_STATUS;
414 } catch (Exception e) {
415 String msg = RestClientI18n.unpublishFailedMsg.text(file.getAbsolutePath(), workspace.getName(), path);
416 return new Status(Severity.ERROR, msg, e);
417 } finally {
418 if (connection != null) {
419 LOGGER.trace("unpublish: leaving");
420 connection.disconnect();
421 }
422 }
423 }
424
425 @Override
426 public List<QueryRow> query( Workspace workspace,
427 String language,
428 String statement ) throws Exception {
429 return query(workspace, language, statement, 0, -1);
430 }
431
432 @SuppressWarnings( "unchecked" )
433 @Override
434 public List<QueryRow> query( Workspace workspace,
435 String language,
436 String statement,
437 int offset,
438 int limit ) throws Exception {
439 CheckArg.isNotNull(workspace, "workspace");
440 CheckArg.isNotNull(language, "language");
441 CheckArg.isNotNull(statement, "statement");
442 LOGGER.trace("query: workspace={0}, language={1}, file={2}, offset={3}, limit={4}",
443 workspace.getName(),
444 language,
445 statement,
446 offset,
447 limit);
448
449 HttpClientConnection connection = null;
450
451 try {
452 WorkspaceNode workspaceNode = new WorkspaceNode(workspace);
453 StringBuilder url = new StringBuilder(workspaceNode.getQueryUrl().toString());
454
455
456 boolean hasOffset = offset > 0;
457 if (hasOffset) {
458 url.append("?offset=").append(offset);
459 }
460
461 if (limit >= 0) {
462 if (hasOffset) {
463 url.append("&");
464 } else {
465 url.append("?");
466 }
467
468 url.append("limit=").append(limit);
469 }
470
471 connection = connect(workspace.getServer(), new URL(url.toString()), RequestMethod.POST);
472 connection.setContentType(contentTypeFor(language));
473 connection.write(statement.getBytes());
474
475 int responseCode = connection.getResponseCode();
476 LOGGER.trace("responseCode={0}", responseCode);
477
478 String response = connection.read();
479 JSONObject result = new JSONObject(response);
480 Map<String, String> columnTypes = new HashMap<String, String>();
481 if (result.has("types")) {
482 JSONObject types = (JSONObject)result.get("types");
483
484 for (Iterator<String> iter = types.keys(); iter.hasNext();) {
485 String columnName = iter.next();
486 columnTypes.put(columnName, types.getString(columnName));
487 }
488 }
489
490 Map<String, String> types = Collections.unmodifiableMap(columnTypes);
491
492 System.out.println(result);
493
494 JSONArray rows = (JSONArray)result.get("rows");
495 List<QueryRow> queryRows = new LinkedList<QueryRow>();
496 for (int i = 0; i < rows.length(); i++) {
497 JSONObject row = (JSONObject)rows.get(i);
498 Map<String, Object> values = new HashMap<String, Object>();
499
500 for (Iterator<String> valueIter = row.keys(); valueIter.hasNext();) {
501 String valueName = valueIter.next();
502 if (valueName.endsWith(IJsonConstants.BASE64_SUFFIX)) {
503 byte[] data = Base64.decode(row.getString(valueName));
504 valueName = valueName.substring(0, valueName.length() - IJsonConstants.BASE64_SUFFIX.length());
505 values.put(valueName, data);
506 } else {
507 values.put(valueName, row.getString(valueName));
508 }
509 }
510
511 queryRows.add(new QueryRow(types, values));
512 }
513
514 return queryRows;
515 } finally {
516 if (connection != null) {
517 LOGGER.trace("query: leaving");
518 connection.disconnect();
519 }
520 }
521 }
522
523 private String contentTypeFor( String language ) throws Exception {
524 if (IJcrConstants.XPATH.equalsIgnoreCase(language)) {
525 return "application/jcr+xpath";
526 }
527 if (IJcrConstants.JCR_SQL.equalsIgnoreCase(language)) {
528 return "application/jcr+sql";
529 }
530 if (IJcrConstants.JCR_SQL2.equalsIgnoreCase(language)) {
531 return "application/jcr+sql2";
532 }
533 if (IJcrConstants.JCR_SEARCH.equalsIgnoreCase(language)) {
534 return "application/jcr+search";
535 }
536
537 throw new IllegalStateException(
538 RestClientI18n.invalidQueryLanguageMsg.text(language, IJcrConstants.VALID_QUERY_LANGUAGES));
539 }
540
541 private static final String SERVER_PARM = "--server";
542 private static final String REPO_PARM = "--repo";
543 private static final String WORKSPACENAME_PARM = "--workspacename";
544 private static final String WORKSPACEPATH_PARM = "--workspacepath";
545 private static final String FILE_PARM = "--file";
546 private static final String DIR_PARM = "--dir";
547 private static final String USERNAME_PARM = "--username";
548 private static final String PWD_PARM = "--pwd";
549 private static final String UNPUBLISH = "--unpublish";
550 private static final String HELP_PARM = "--help";
551
552
553
554
555 @SuppressWarnings( "null" )
556 public static void main( String[] args ) {
557
558 if (args == null || args.length == 0 || args[0].equals(HELP_PARM)) {
559 System.out.println("Running the ModeShape Rest Client");
560 System.out.println(" required arguments are:");
561 System.out.println(" " + SERVER_PARM);
562 System.out.println(" " + FILE_PARM + " or " + DIR_PARM);
563 System.out.println(" " + WORKSPACEPATH_PARM);
564 System.out.println(" " + REPO_PARM);
565 System.out.println(" optional arguments are:");
566 System.out.println(" " + WORKSPACENAME_PARM + " (default=default)");
567 System.out.println(" " + USERNAME_PARM + "(default=admin");
568 System.out.println(" " + PWD_PARM + " (default=admin");
569 System.out.println(" " + UNPUBLISH + " with no parameter, will activate");
570
571 System.exit(0);
572 }
573
574 String server_name = null;
575 String workspace_name = "default";
576 String workspace_path = null;
577 String file_name = null;
578 String dir_loc = null;
579 String user = "admin";
580 String pwd = "admin";
581 String repo_name = null;
582
583 boolean publish = true;
584
585 int pos = 0;
586 for (String arg : args) {
587 arg = arg.trim();
588 if (arg.equals(SERVER_PARM)) {
589 server_name = args[pos + 1];
590 } else if (arg.equals(REPO_PARM)) {
591 repo_name = args[pos + 1];
592 } else if (arg.equals(WORKSPACENAME_PARM)) {
593 workspace_name = args[pos + 1];
594 } else if (arg.equals(WORKSPACEPATH_PARM)) {
595 workspace_path = args[pos + 1];
596 } else if (arg.equals(FILE_PARM)) {
597 file_name = args[pos + 1];
598 } else if (arg.equals(DIR_PARM)) {
599 dir_loc = args[pos + 1];
600 } else if (arg.equals(USERNAME_PARM)) {
601 user = args[pos + 1];
602 } else if (arg.equals(PWD_PARM)) {
603 pwd = args[pos + 1];
604 } else if (arg.equals(UNPUBLISH)) {
605 publish = false;
606 }
607
608 ++pos;
609 }
610
611 String errparm = null;
612 if (server_name == null) {
613 errparm = SERVER_PARM;
614 } else if (repo_name == null) {
615 errparm = REPO_PARM;
616 } else if (file_name == null && dir_loc == null) {
617 errparm = "[" + FILE_PARM + " | " + DIR_PARM + "]";
618 } else if (user == null) {
619 errparm = USERNAME_PARM;
620 } else if (pwd == null) {
621 errparm = PWD_PARM;
622 } else if (workspace_path == null) {
623 errparm = WORKSPACEPATH_PARM;
624 }
625
626 if (errparm != null) {
627 LOGGER.error(RestClientI18n.nullArgumentMsg, errparm);
628 System.exit(-1);
629 }
630
631 Server server = new Server(server_name, user, pwd);
632 Repository repository = new Repository(repo_name, server);
633 Workspace workspace = new Workspace(workspace_name, repository);
634
635 JsonRestClient client = new JsonRestClient();
636
637 try {
638 client.getRepositories(server);
639 } catch (Exception e) {
640 e.printStackTrace();
641 System.exit(-1);
642 }
643
644 try {
645 client.getWorkspaces(repository);
646 } catch (Exception e) {
647 e.printStackTrace();
648 System.exit(-1);
649 }
650
651 if (publish) {
652
653 if (file_name != null) {
654 Status status = client.publish(workspace, workspace_path, new File(file_name));
655 if (checkStatus(status)) {
656 LOGGER.info(RestClientI18n.publishSucceededMsg, file_name, workspace_path, workspace_name);
657 }
658
659 } else {
660 File[] files = findAllFilesInDirectory(dir_loc);
661 for (File f : files) {
662 Status status = client.publish(workspace, workspace_path, f);
663 if (checkStatus(status)) {
664 LOGGER.info(RestClientI18n.publishSucceededMsg, f.getName(), workspace_path, workspace_name);
665 }
666 }
667 }
668 } else {
669 if (file_name != null) {
670 Status status = client.unpublish(workspace, workspace_path, new File(file_name));
671 if (checkStatus(status)) {
672 LOGGER.info(RestClientI18n.unpublishSucceededMsg, file_name, workspace_path, workspace_name);
673 }
674
675 } else {
676 File[] files = findAllFilesInDirectory(dir_loc);
677 for (File f : files) {
678 Status status = client.unpublish(workspace, workspace_path, f);
679 if (checkStatus(status)) {
680 LOGGER.info(RestClientI18n.unpublishSucceededMsg, f.getName(), workspace_path, workspace_name);
681 }
682 }
683 }
684 }
685
686 }
687
688 private static boolean checkStatus( Status status ) {
689 if (status.isError()) {
690 System.err.println(status.getMessage());
691 status.getException().printStackTrace(System.err);
692 return false;
693 }
694 return true;
695 }
696
697
698
699
700
701
702
703 private static File[] findAllFilesInDirectory( String dir ) {
704
705
706 File modelsDirFile = new File(dir);
707 FileFilter fileFilter = new FileFilter() {
708
709 public boolean accept( File file ) {
710 if (file.isDirectory()) {
711 return false;
712 }
713
714 String fileName = file.getName();
715
716 if (fileName == null || fileName.length() == 0) {
717 return false;
718 }
719
720 return true;
721
722 }
723 };
724
725 File[] modelFiles = modelsDirFile.listFiles(fileFilter);
726
727 return modelFiles;
728
729 }
730
731 }