001/* 002 * ModeShape (http://www.modeshape.org) 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.modeshape.web.server; 017 018import java.io.File; 019import java.io.FileInputStream; 020import java.io.FileOutputStream; 021import java.math.BigDecimal; 022import java.security.Principal; 023import java.util.ArrayList; 024import java.util.Calendar; 025import java.util.Collection; 026import javax.jcr.Node; 027import javax.jcr.NodeIterator; 028import javax.jcr.PathNotFoundException; 029import javax.jcr.Property; 030import javax.jcr.PropertyType; 031import javax.jcr.Repository; 032import javax.jcr.RepositoryException; 033import javax.jcr.Session; 034import javax.jcr.Value; 035import javax.jcr.nodetype.NodeType; 036import javax.jcr.nodetype.NodeTypeIterator; 037import javax.jcr.nodetype.NodeTypeManager; 038import javax.jcr.nodetype.PropertyDefinition; 039import javax.jcr.query.Query; 040import javax.jcr.query.QueryManager; 041import javax.jcr.query.QueryResult; 042import javax.jcr.query.Row; 043import javax.jcr.query.RowIterator; 044import javax.jcr.security.AccessControlEntry; 045import javax.jcr.security.AccessControlList; 046import javax.jcr.security.AccessControlManager; 047import javax.jcr.security.AccessControlPolicy; 048import javax.jcr.security.Privilege; 049import org.modeshape.common.logging.Logger; 050import org.modeshape.web.client.JcrService; 051import org.modeshape.web.shared.RemoteException; 052import org.modeshape.web.shared.Acl; 053import org.modeshape.web.shared.JcrNode; 054import org.modeshape.web.shared.JcrNodeType; 055import org.modeshape.web.shared.JcrPermission; 056import org.modeshape.web.shared.JcrProperty; 057import org.modeshape.web.shared.JcrRepositoryDescriptor; 058import org.modeshape.web.shared.Policy; 059import org.modeshape.web.shared.RepositoryName; 060import org.modeshape.web.shared.ResultSet; 061import com.google.gwt.user.server.rpc.RemoteServiceServlet; 062import java.io.IOException; 063import java.util.Arrays; 064import java.util.Date; 065import javax.jcr.AccessDeniedException; 066import javax.jcr.InvalidItemStateException; 067import javax.jcr.ItemExistsException; 068import javax.jcr.ReferentialIntegrityException; 069import javax.jcr.lock.LockException; 070import javax.jcr.nodetype.ConstraintViolationException; 071import javax.jcr.nodetype.NoSuchNodeTypeException; 072import javax.jcr.version.VersionException; 073import javax.servlet.ServletContext; 074import javax.servlet.http.HttpServletRequest; 075import org.modeshape.jcr.JcrI18n; 076import org.modeshape.jcr.JcrRepository; 077import org.modeshape.jcr.RepositoryStatistics; 078import org.modeshape.jcr.api.monitor.DurationMetric; 079import org.modeshape.jcr.api.monitor.History; 080import org.modeshape.jcr.api.monitor.Statistics; 081import org.modeshape.jcr.api.monitor.ValueMetric; 082import org.modeshape.jcr.api.monitor.Window; 083import org.modeshape.web.server.impl.MsDurationMetric; 084import org.modeshape.web.server.impl.MsValueMetric; 085import org.modeshape.web.server.impl.TimeUnit; 086import org.modeshape.web.shared.BackupParams; 087import org.modeshape.web.shared.RestoreParams; 088import org.modeshape.web.shared.Stats; 089 090/** 091 * The server side implementation of the RPC service. 092 */ 093@SuppressWarnings( "serial" ) 094public class JcrServiceImpl extends RemoteServiceServlet implements JcrService { 095 096 private final static String CONNECTOR_CLASS_PARAMETER = "connector-class"; 097 private final static String DEFAULT_CONNECTOR_CLASS = 098 "org.modeshape.web.server.impl.ConnectorImpl"; 099 100 private final static Logger logger = Logger.getLogger(JcrServiceImpl.class); 101 102 103 private Connector connector() throws RemoteException { 104 Connector connector = (Connector)getThreadLocalRequest().getSession(true).getAttribute("connector"); 105 if (isUnknown(connector)) { 106 String clsName = getConnectorClassName(); 107 if (isUnknown(clsName)) { 108 clsName = DEFAULT_CONNECTOR_CLASS; 109 } 110 connector = loadConnector(clsName, getServletContext()); 111 getThreadLocalRequest().getSession(true).setAttribute("connector", connector); 112 } 113 return connector; 114 } 115 116 private String getConnectorClassName() { 117 return getServletContext().getInitParameter(CONNECTOR_CLASS_PARAMETER); 118 } 119 120 @Override 121 public String getRequestedURI() { 122 String uri = (String)getThreadLocalRequest().getSession().getAttribute("initial.uri"); 123 if (uri != null) { 124 logger.debug("Requested URI " + uri); 125 return uri; 126 } 127 128 uri = getThreadLocalRequest().getRequestURI(); 129 String servletPath = getThreadLocalRequest().getServletPath(); 130 131 String res = uri.substring(0, uri.indexOf(servletPath)); 132 logger.debug("Requested URI " + uri); 133 return res; 134 } 135 136 @Override 137 public String getUserName() throws RemoteException { 138 ServletContext context = getServletContext(); 139 140 //get user's credentials from servlet context 141 String uname = (String)context.getAttribute("uname"); 142 String passwd = (String)context.getAttribute("password"); 143 144 //login to the repositories 145 if (uname != null) { 146 connector().login(uname, passwd); 147 } 148 149 //return user's name 150 String res = connector().userName(); 151 return res; 152 } 153 154 @Override 155 public Collection<RepositoryName> getRepositories() throws RemoteException { 156 return connector().getRepositories(); 157 } 158 159 @Override 160 public Collection<RepositoryName> findRepositories( String criteria ) throws RemoteException { 161 return connector().search(criteria); 162 } 163 164 @Override 165 public String[] getWorkspaces( String repositoryName ) throws RemoteException { 166 return connector().find(repositoryName).getWorkspaces(); 167 } 168 169 @Override 170 public void login( String userName, 171 String password ) throws RemoteException { 172 connector().login(userName, password); 173 } 174 175 @Override 176 public String logout() { 177 try { 178 connector().logout(); 179 } catch (RemoteException e) { 180 //nothing to do here in case of the exception 181 } finally { 182 //clean up session 183 getThreadLocalRequest().getSession().invalidate(); 184 185 //clean up context 186 getServletContext().removeAttribute("uname"); 187 getServletContext().removeAttribute("password"); 188 189 //redirect to initial page. it will trigger login form 190 return getThreadLocalRequest().getContextPath() + "/Console.html"; 191 } 192 } 193 194 @Override 195 public JcrNode node( String repository, 196 String workspace, 197 String path ) throws RemoteException { 198 logger.debug("Requested node in repository '" + repository + "', workspace '" + workspace + "', path '" + path + "'"); 199 200 if (repository == null || workspace == null) { 201 return null; 202 } 203 try { 204 Session session = connector().find(repository).session(workspace); 205 Node n = session.getNode(path); 206 207 // convert into value object 208 JcrNode node = new JcrNode(repository, workspace, n.getName(), n.getPath(), n.getPrimaryNodeType().getName()); 209 node.setMixins(mixinTypes(n)); 210 node.setProperties(getProperties(repository, workspace, path, n)); 211 212 node.setPropertyDefs(propertyDefs(n)); 213 214 try { 215 node.setAcl(getAcl(repository, workspace, path)); 216 } catch (AccessDeniedException e) { 217 node.setAcl(null); 218 } 219 220 NodeIterator it = n.getNodes(); 221 node.setChildCount(it.getSize()); 222 return node; 223 } catch (RepositoryException e) { 224 logger.error(e, JcrI18n.unexpectedException, e.getMessage()); 225 throw new RemoteException(e.getMessage()); 226 } 227 } 228 229 @Override 230 public Collection<JcrNode> childNodes(String repository, String workspace, String path, int index, int count) throws RemoteException { 231 if (repository == null || workspace == null) { 232 return null; 233 } 234 try { 235 Session session = connector().find(repository).session(workspace); 236 Node n = session.getNode(path); 237 238 NodeIterator it = n.getNodes(); 239 240 ArrayList<JcrNode> res = new ArrayList(); 241 int i = 0; 242 while (it.hasNext()) { 243 Node child = it.nextNode(); 244 if (i >= index && i < (index + count)) { 245 res.add(new JcrNode(repository, workspace, child.getName(), child.getPath(), child.getPrimaryNodeType().getName())); 246 } 247 i++; 248 } 249 return res; 250 } catch (RepositoryException e) { 251 logger.error(e, JcrI18n.unexpectedException, e.getMessage()); 252 throw new RemoteException(e.getMessage()); 253 } 254 } 255 256 private String[] mixinTypes( Node node ) throws RepositoryException { 257 NodeType[] values = node.getMixinNodeTypes(); 258 String[] res = new String[values.length]; 259 for (int i = 0; i < res.length; i++) { 260 res[i] = values[i].getName(); 261 } 262 return res; 263 } 264 265 /** 266 * Gets the list of properties available to the given node. 267 * 268 * @param node the node instance. 269 * @return list of property names. 270 * @throws RepositoryException 271 */ 272 private String[] propertyDefs( Node node ) throws RepositoryException { 273 ArrayList<String> list = new ArrayList<>(); 274 275 NodeType primaryType = node.getPrimaryNodeType(); 276 PropertyDefinition[] defs = primaryType.getPropertyDefinitions(); 277 278 for (PropertyDefinition def : defs) { 279 if (!def.isProtected()) { 280 list.add(def.getName()); 281 } 282 } 283 284 NodeType[] mixinType = node.getMixinNodeTypes(); 285 for (NodeType type : mixinType) { 286 defs = type.getPropertyDefinitions(); 287 for (PropertyDefinition def : defs) { 288 if (!def.isProtected()) { 289 list.add(def.getName()); 290 } 291 } 292 } 293 294 String[] res = new String[list.size()]; 295 list.toArray(res); 296 297 return res; 298 } 299 300 private Acl getAcl( String repository, 301 String workspace, 302 String path ) throws RepositoryException, RemoteException { 303 Session session = connector().find(repository).session(workspace); 304 305 AccessControlManager acm = session.getAccessControlManager(); 306 AccessControlList accessList = findAccessList(acm, path); 307 308 if (accessList == null) { 309 return null; 310 } 311 312 Acl acl = new Acl(); 313 314 AccessControlEntry[] entries = accessList.getAccessControlEntries(); 315 for (AccessControlEntry entry : entries) { 316 Policy policy = new Policy(); 317 policy.setPrincipal(entry.getPrincipal().getName()); 318 Privilege[] privileges = entry.getPrivileges(); 319 for (Privilege privilege : privileges) { 320 policy.enable(privilege.getName()); 321 } 322 acl.addPolicy(policy); 323 } 324 return acl; 325 } 326 327 /** 328 * Searches access list for the given node. 329 * 330 * @param acm access manager 331 * @param path the path to the node 332 * @return access list representation. 333 * @throws RepositoryException 334 */ 335 private AccessControlList findAccessList( AccessControlManager acm, 336 String path ) throws RepositoryException { 337 AccessControlPolicy[] policy = acm.getPolicies(path); 338 339 if (policy != null && policy.length > 0) { 340 return (AccessControlList)policy[0]; 341 } 342 343 policy = acm.getEffectivePolicies(path); 344 if (policy != null && policy.length > 0) { 345 return (AccessControlList)policy[0]; 346 } 347 348 return null; 349 } 350 351 /** 352 * Reads properties of the given node. 353 * 354 * @param repository the repository name 355 * @param workspace the workspace name 356 * @param path the path to the node 357 * @param node the node instance 358 * @return list of node's properties. 359 * @throws RepositoryException 360 */ 361 private Collection<JcrProperty> getProperties( String repository, String workspace, String path, Node node ) throws RepositoryException { 362 ArrayList<PropertyDefinition> names = new ArrayList<>(); 363 364 NodeType primaryType = node.getPrimaryNodeType(); 365 PropertyDefinition[] defs = primaryType.getPropertyDefinitions(); 366 367 names.addAll(Arrays.asList(defs)); 368 369 NodeType[] mixinType = node.getMixinNodeTypes(); 370 for (NodeType type : mixinType) { 371 defs = type.getPropertyDefinitions(); 372 names.addAll(Arrays.asList(defs)); 373 } 374 375 ArrayList<JcrProperty> list = new ArrayList<>(); 376 for (PropertyDefinition def : names) { 377 378 String name = def.getName(); 379 String type = PropertyType.nameFromValue(def.getRequiredType()); 380 381 Property p = null; 382 try { 383 p = node.getProperty(def.getName()); 384 } catch (PathNotFoundException e) { 385 } 386 387 String display = values(def, p); 388 String value = def.isMultiple() ? multiValue(p) 389 : singleValue(p, def, repository, workspace, path); 390 list.add(new JcrProperty(name, type, value, display)); 391 392 } 393 return list; 394 } 395 396 private String singleValue(Property p, PropertyDefinition def, 397 String repository, String workspace, String path) throws RepositoryException { 398 switch (def.getRequiredType()) { 399 case PropertyType.BINARY: 400 HttpServletRequest request = this.getThreadLocalRequest(); 401 String context = request.getContextPath(); 402 return String.format("%s/binary/node?repository=%s&workspace=%s&path=%s&property=%s", 403 context, repository, workspace, path, def.getName()); 404 default: 405 return p == null ? "N/A" : p.getValue() != null ? p.getValue().getString() : "N/A"; 406 } 407 } 408 409 private String multiValue(Property p) throws RepositoryException { 410 if (p == null) { 411 return ""; 412 } 413 414 Value[] values = p.getValues(); 415 416 if (values == null || values.length == 0) { 417 return ""; 418 } 419 420 if (values.length == 1) { 421 return values[0].getString(); 422 } 423 424 String s = values[0].getString(); 425 for (int i = 1; i < values.length; i++) { 426 s += "," + values[i].getString(); 427 } 428 429 return s; 430 } 431 432 /** 433 * Displays property value as string 434 * 435 * @param pd the property definition 436 * @param p the property to display 437 * @return property value as text string 438 * @throws RepositoryException 439 */ 440 private String values( PropertyDefinition pd, Property p ) throws RepositoryException { 441 if (p == null) { 442 return "N/A"; 443 } 444 445 if (pd.getRequiredType() == PropertyType.BINARY) { 446 return "BINARY"; 447 } 448 449 if (!p.isMultiple()) { 450 return p.getString(); 451 } 452 453 return multiValue(p); 454 } 455 456 @Override 457 public JcrRepositoryDescriptor repositoryInfo( String repository ) throws RemoteException { 458 JcrRepositoryDescriptor desc = new JcrRepositoryDescriptor(); 459 460 try { 461 Repository repo = connector().find(repository).repository(); 462 String keys[] = repo.getDescriptorKeys(); 463 for (int i = 0; i < keys.length; i++) { 464 Value value = repo.getDescriptorValue(keys[i]); 465 desc.add(keys[i], value != null ? value.getString() : "N/A"); 466 } 467 } catch (RemoteException | IllegalStateException | RepositoryException e) { 468 throw new RemoteException(e); 469 } 470 return desc; 471 } 472 473 @Override 474 public ResultSet query( String repository, 475 String workspace, 476 String text, 477 String lang ) throws RemoteException { 478 ResultSet rs = new ResultSet(); 479 try { 480 Session session = connector().find(repository).session(workspace); 481 482 QueryManager qm = session.getWorkspace().getQueryManager(); 483 Query q = qm.createQuery(text, lang); 484 485 QueryResult qr = q.execute(); 486 487 rs.setColumnNames(qr.getColumnNames()); 488 ArrayList<String[]> rows = new ArrayList<>(); 489 RowIterator it = qr.getRows(); 490 while (it.hasNext()) { 491 Row row = it.nextRow(); 492 String[] list = new String[qr.getColumnNames().length]; 493 494 for (int i = 0; i < qr.getColumnNames().length; i++) { 495 Value v = row.getValue(qr.getColumnNames()[i]); 496 list[i] = v != null ? v.getString() : "null"; 497 } 498 499 rows.add(list); 500 } 501 502 rs.setRows(rows); 503 logger.debug("Query result: " + rs.getRows().size()); 504 return rs; 505 } catch (RemoteException | RepositoryException | IllegalStateException e) { 506 throw new RemoteException(e); 507 } 508 } 509 510 @Override 511 public String[] supportedQueryLanguages( String repository, 512 String workspace ) throws RemoteException { 513 try { 514 Session session = connector().find(repository).session(workspace); 515 QueryManager qm = session.getWorkspace().getQueryManager(); 516 return qm.getSupportedQueryLanguages(); 517 } catch (RepositoryException e) { 518 throw new RemoteException(e.getMessage()); 519 } 520 } 521 522 @Override 523 public JcrNode addNode( String repository, 524 String workspace, 525 String path, 526 String name, 527 String primaryType ) throws RemoteException { 528 Session session = connector().find(repository).session(workspace); 529 try { 530 Node parent = (Node)session.getItem(path); 531 Node n = parent.addNode(name, primaryType); 532 JcrNode node = new JcrNode(repository, workspace, n.getName(), n.getPath(), n.getPrimaryNodeType().getName()); 533 node.setMixins(mixinTypes(n)); 534 node.setProperties(getProperties(repository, workspace, path, n)); 535 node.setPropertyDefs(propertyDefs(n)); 536 return node; 537 } catch (RepositoryException e) { 538 logger.debug("Could not add node: " + e.getMessage()); 539 throw new RemoteException(e.getMessage()); 540 } 541 } 542 543 @Override 544 public void removeNode( String repository, 545 String workspace, 546 String path ) throws RemoteException { 547 Session session = connector().find(repository).session(workspace); 548 try { 549 Node node = (Node)session.getItem(path); 550 node.remove(); 551 } catch (PathNotFoundException e) { 552 logger.debug(e.getLocalizedMessage()); 553 } catch (RepositoryException e) { 554 throw new RemoteException(e.getMessage()); 555 } 556 } 557 558 @Override 559 public void addMixin( String repository, 560 String workspace, 561 String path, 562 String mixin ) throws RemoteException { 563 Session session = connector().find(repository).session(workspace); 564 try { 565 Node node = (Node)session.getItem(path); 566 node.addMixin(mixin); 567 } catch (PathNotFoundException e) { 568 logger.debug(e.getLocalizedMessage()); 569 } catch (RepositoryException e) { 570 throw new RemoteException(e.getMessage()); 571 } 572 } 573 574 @Override 575 public void removeMixin( String repository, 576 String workspace, 577 String path, 578 String mixin ) throws RemoteException { 579 Session session = connector().find(repository).session(workspace); 580 try { 581 Node node = (Node)session.getItem(path); 582 node.removeMixin(mixin); 583 } catch (PathNotFoundException e) { 584 logger.debug(e.getLocalizedMessage()); 585 } catch (RepositoryException e) { 586 throw new RemoteException(e.getMessage()); 587 } 588 } 589 590 @Override 591 public void setProperty(JcrNode node, String name, String value) throws RemoteException { 592 Session session = connector().find(node.getRepository()).session(node.getWorkspace()); 593 try { 594 Node n = session.getNode(node.getPath()); 595 n.setProperty(name, value); 596 } catch (Exception e) { 597 throw new RemoteException(e.getMessage()); 598 } 599 } 600 601 @Override 602 public void setProperty(JcrNode node, String name, Boolean value) throws RemoteException { 603 Session session = connector().find(node.getRepository()).session(node.getWorkspace()); 604 try { 605 Node n = session.getNode(node.getPath()); 606 n.setProperty(name, value); 607 } catch (Exception e) { 608 throw new RemoteException(e.getMessage()); 609 } 610 } 611 612 @Override 613 public void setProperty(JcrNode node, String name, Date value) throws RemoteException { 614 Session session = connector().find(node.getRepository()).session(node.getWorkspace()); 615 try { 616 Node n = session.getNode(node.getPath()); 617 Calendar calendar = Calendar.getInstance(); 618 calendar.setTime(value); 619 n.setProperty(name, calendar); 620 } catch (Exception e) { 621 throw new RemoteException(e.getMessage()); 622 } 623 } 624 625 public void setProperty( String repository, 626 String workspace, 627 String path, 628 String name, 629 Object value ) throws RemoteException { 630 Session session = connector().find(repository).session(workspace); 631 try { 632 Node node = (Node)session.getItem(path); 633 switch (type(node, name)) { 634 case PropertyType.BOOLEAN: 635 node.setProperty(name, (Boolean)value); 636 break; 637 case PropertyType.DATE: 638 node.setProperty(name, (Calendar)value); 639 break; 640 case PropertyType.DECIMAL: 641 node.setProperty(name, BigDecimal.valueOf(Double.parseDouble((String)value))); 642 break; 643 case PropertyType.DOUBLE: 644 node.setProperty(name, Double.parseDouble((String)value)); 645 break; 646 case PropertyType.LONG: 647 node.setProperty(name, Long.parseLong((String)value)); 648 break; 649 case PropertyType.NAME: 650 node.setProperty(name, (String)value); 651 break; 652 case PropertyType.PATH: 653 node.setProperty(name, (String)value); 654 break; 655 case PropertyType.STRING: 656 node.setProperty(name, (String)value); 657 break; 658 case PropertyType.URI: 659 node.setProperty(name, (String)value); 660 break; 661 662 } 663 } catch (PathNotFoundException e) { 664 logger.debug(e.getLocalizedMessage()); 665 } catch (Exception e) { 666 logger.debug("Unexpected error", e); 667 throw new RemoteException(e.getMessage()); 668 } 669 } 670 671 private int type( Node node, 672 String propertyName ) throws RepositoryException { 673 PropertyDefinition[] defs = node.getPrimaryNodeType().getPropertyDefinitions(); 674 for (PropertyDefinition def : defs) { 675 if (def.getName().equals(propertyName)) { 676 return def.getRequiredType(); 677 } 678 } 679 680 NodeType[] mixins = node.getMixinNodeTypes(); 681 for (NodeType type : mixins) { 682 defs = type.getPropertyDefinitions(); 683 for (PropertyDefinition def : defs) { 684 if (def.getName().equals(propertyName)) { 685 return def.getRequiredType(); 686 } 687 } 688 } 689 690 return -1; 691 } 692 693 @Override 694 public void addAccessList( String repository, 695 String workspace, 696 String path, 697 String principal ) throws RemoteException { 698 Session session = connector().find(repository).session(workspace); 699 try { 700 AccessControlManager acm = session.getAccessControlManager(); 701 Privilege allPermissions = acm.privilegeFromName(Privilege.JCR_ALL); 702 703 AccessControlList acl = (AccessControlList)acm.getApplicablePolicies(path).nextAccessControlPolicy(); 704 acl.addAccessControlEntry(new SimplePrincipal(principal), new Privilege[] {allPermissions}); 705 acm.setPolicy(path, acl); 706 // session.save(); 707 } catch (PathNotFoundException e) { 708 logger.debug(e.getLocalizedMessage()); 709 } catch (RepositoryException e) { 710 throw new RemoteException(e.getMessage()); 711 } 712 } 713 714 @Override 715 public void removeAccessList( String repository, 716 String workspace, 717 String path, 718 String principal ) throws RemoteException { 719 Session session = connector().find(repository).session(workspace); 720 try { 721 AccessControlManager acm = session.getAccessControlManager(); 722 AccessControlList acl = this.findAccessList(acm, path); 723 724 AccessControlEntry entry = pick(acl, principal); 725 acl.removeAccessControlEntry(entry); 726 acm.setPolicy(path, acl); 727 // session.save(); 728 } catch (PathNotFoundException e) { 729 logger.debug(e.getLocalizedMessage()); 730 } catch (RepositoryException e) { 731 throw new RemoteException(e.getMessage()); 732 } 733 } 734 735 @Override 736 public String[] getPrimaryTypes( String repository, 737 String workspace, String superType, 738 boolean allowAbstract ) throws RemoteException { 739 Session session = connector().find(repository).session(workspace); 740 ArrayList<String> list = new ArrayList<>(); 741 try { 742 NodeTypeManager mgr = session.getWorkspace().getNodeTypeManager(); 743 NodeTypeIterator it = mgr.getPrimaryNodeTypes(); 744 while (it.hasNext()) { 745 NodeType nodeType = it.nextNodeType(); 746 if (nodeType.isAbstract() && !allowAbstract) { 747 continue; 748 } 749 if (superType != null 750 && !isInset(superType, nodeType.getDeclaredSupertypeNames())) { 751 continue; 752 } 753 list.add(nodeType.getName()); 754 } 755 String[] res = new String[list.size()]; 756 list.toArray(res); 757 return res; 758 } catch (RepositoryException e) { 759 throw new RemoteException(e.getMessage()); 760 } 761 } 762 763 private boolean isInset(String name, String[] list) { 764 for (int i = 0; i < list.length; i++) { 765 if (name.equals(list[i])) return true; 766 } 767 return false; 768 } 769 770 @Override 771 public String[] getMixinTypes( String repository, 772 String workspace, 773 boolean allowAbstract ) throws RemoteException { 774 Session session = connector().find(repository).session(workspace); 775 ArrayList<String> list = new ArrayList<>(); 776 try { 777 NodeTypeManager mgr = session.getWorkspace().getNodeTypeManager(); 778 NodeTypeIterator it = mgr.getMixinNodeTypes(); 779 while (it.hasNext()) { 780 NodeType nodeType = it.nextNodeType(); 781 if (!nodeType.isAbstract() || allowAbstract) { 782 list.add(nodeType.getName()); 783 } 784 } 785 String[] res = new String[list.size()]; 786 list.toArray(res); 787 return res; 788 } catch (RepositoryException e) { 789 throw new RemoteException(e.getMessage()); 790 } 791 } 792 793 @Override 794 public void save( String repository, 795 String workspace ) throws RemoteException { 796 Session session = connector().find(repository).session(workspace); 797 try { 798 session.save(); 799 } catch (AccessDeniedException ex) { 800 throw new RemoteException("Access violation: " + ex.getMessage()); 801 } catch (ItemExistsException ex) { 802 throw new RemoteException("Item exist: " + ex.getMessage()); 803 } catch (ReferentialIntegrityException ex) { 804 throw new RemoteException("Referential integrity violation: " + ex.getMessage()); 805 } catch (ConstraintViolationException ex) { 806 throw new RemoteException("Constraint violation: " + ex.getMessage()); 807 } catch (InvalidItemStateException ex) { 808 throw new RemoteException("Invalid item state: " + ex.getMessage()); 809 } catch (VersionException ex) { 810 throw new RemoteException("version error: " + ex.getMessage()); 811 } catch (LockException ex) { 812 throw new RemoteException("Lock violation: " + ex.getMessage()); 813 } catch (NoSuchNodeTypeException ex) { 814 throw new RemoteException("Node type problem: " + ex.getMessage()); 815 } catch (RepositoryException ex) { 816 throw new RemoteException("Generic error: " + ex.getMessage()); 817 } 818 } 819 820 @Override 821 public void updateAccessList( String repository, 822 String workspace, 823 String path, 824 String principal, 825 JcrPermission permission, 826 boolean enabled ) throws RemoteException { 827 Session session = connector().find(repository).session(workspace); 828 try { 829 AccessControlManager acm = session.getAccessControlManager(); 830 AccessControlList acl = this.findAccessList(acm, path); 831 832 AccessControlEntry entry = pick(acl, principal); 833 acl.removeAccessControlEntry(entry); 834 835 Privilege[] privs = enabled ? includePrivilege(acm, entry.getPrivileges(), permission) : excludePrivilege(entry.getPrivileges(), 836 permission); 837 acl.addAccessControlEntry(entry.getPrincipal(), privs); 838 acm.setPolicy(path, acl); 839 } catch (Exception e) { 840 throw new RemoteException(e.getMessage()); 841 } 842 } 843 844 /** 845 * Picks access entry for the given principal. 846 * 847 * @param acl 848 * @param principal 849 * @return the ACL entry 850 * @throws RepositoryException 851 */ 852 private AccessControlEntry pick( AccessControlList acl, 853 String principal ) throws RepositoryException { 854 for (AccessControlEntry entry : acl.getAccessControlEntries()) { 855 if (entry.getPrincipal().getName().equals(principal)) { 856 return entry; 857 } 858 } 859 return null; 860 } 861 862 /** 863 * Excludes given privilege. 864 * 865 * @param privileges 866 * @param permission 867 * @return the privileges 868 */ 869 private Privilege[] excludePrivilege( Privilege[] privileges, 870 JcrPermission permission ) { 871 ArrayList<Privilege> list = new ArrayList<>(); 872 873 for (Privilege privilege : privileges) { 874 if (!privilege.getName().equalsIgnoreCase(permission.getName())) { 875 list.add(privilege); 876 } 877 } 878 879 Privilege[] res = new Privilege[list.size()]; 880 list.toArray(res); 881 return res; 882 } 883 884 /** 885 * Includes given privilege. 886 * 887 * @param acm the access control manager 888 * @param privileges 889 * @param permission 890 * @return the privileges 891 * @throws RepositoryException 892 */ 893 private Privilege[] includePrivilege( AccessControlManager acm, 894 Privilege[] privileges, 895 JcrPermission permission ) throws RepositoryException { 896 ArrayList<Privilege> list = new ArrayList<>(); 897 898 for (Privilege privilege : privileges) { 899 if (!privilege.getName().equalsIgnoreCase(permission.getName())) { 900 list.add(privilege); 901 } 902 } 903 904 list.add(acm.privilegeFromName(permission.getJcrName())); 905 Privilege[] res = new Privilege[list.size()]; 906 list.toArray(res); 907 return res; 908 } 909 910 @Override 911 public Collection<JcrNodeType> nodeTypes( String repository, 912 String workspace ) throws RemoteException { 913 Session session = connector().find(repository).session(workspace); 914 ArrayList<JcrNodeType> list = new ArrayList<>(); 915 try { 916 NodeTypeManager mgr = session.getWorkspace().getNodeTypeManager(); 917 918 NodeTypeIterator it = mgr.getAllNodeTypes(); 919 while (it.hasNext()) { 920 NodeType type = it.nextNodeType(); 921 JcrNodeType jcrType = new JcrNodeType(); 922 jcrType.setName(type.getName()); 923 jcrType.setAbstract(type.isAbstract()); 924 jcrType.setPrimary(!type.isMixin()); 925 jcrType.setMixin(type.isMixin()); 926 list.add(jcrType); 927 } 928 } catch (Exception e) { 929 throw new RemoteException(e.getMessage()); 930 } 931 return list; 932 } 933 934 @Override 935 public void renameNode( String repository, 936 String workspace, 937 String path, 938 String name ) throws RemoteException { 939 Session session = connector().find(repository).session(workspace); 940 try { 941 Node node = session.getNode(path); 942 session.move(path, node.getParent().getPath() + "/" + name); 943 } catch (Exception e) { 944 throw new RemoteException(e.getMessage()); 945 } 946 } 947 948 @Override 949 public void backup( String repository, 950 String name, 951 BackupParams params) throws RemoteException { 952 connector().find(repository).backup(name, params); 953 } 954 955 @Override 956 public void restore( String repository, 957 String name, 958 RestoreParams params) throws RemoteException { 959 connector().find(repository).restore(name, params); 960 } 961 962 @Override 963 public void export( String repository, 964 String workspace, 965 String path, 966 String location, 967 boolean skipBinary, 968 boolean noRecurse ) throws RemoteException { 969 File file = new File(location); 970 try { 971 FileOutputStream fout = new FileOutputStream(file); 972 connector().find(repository).session(workspace).exportSystemView(path, fout, skipBinary, noRecurse); 973 } catch (RemoteException | IOException | RepositoryException e) { 974 throw new RemoteException(e); 975 } 976 } 977 978 @Override 979 public void importXML( String repository, 980 String workspace, 981 String path, 982 String location, 983 int option ) throws RemoteException { 984 File file = new File(location); 985 try { 986 FileInputStream fin = new FileInputStream(file); 987 connector().find(repository).session(workspace).getWorkspace().importXML(path, fin, option); 988 } catch (RemoteException | IOException | RepositoryException e) { 989 throw new RemoteException(); 990 } 991 } 992 993 @Override 994 public void refreshSession(String repository, String workspace, 995 boolean keepChanges) throws RemoteException { 996 try { 997 connector().find(repository).session(workspace).refresh(keepChanges); 998 } catch (RemoteException | RepositoryException e) { 999 throw new RemoteException(e); 1000 } 1001 } 1002 1003 @Override 1004 public Collection<Stats> getValueStats(String repository, String param, String tu) throws RemoteException { 1005 ArrayList<Stats> stats = new ArrayList<>(); 1006 1007 try { 1008 Window w = TimeUnit.find(tu).window(); 1009 ValueMetric m = MsValueMetric.find(param).metric(); 1010 1011 JcrRepository repo = (JcrRepository) connector().find(repository).repository(); 1012 1013 RepositoryStatistics repositoryStatistics = repo.getRepositoryStatistics(); 1014 History history = repositoryStatistics.getHistory(m, w); 1015 1016 Statistics[] s = history.getStats(); 1017 for (int i = 0; i < s.length; i++) { 1018 double min = s[i] != null ? s[i].getMinimum() : 0; 1019 double max = s[i] != null ? s[i].getMaximum() : 0; 1020 double avg = s[i] != null ? s[i].getMean() : 0; 1021 stats.add(new Stats(min, max, avg)); 1022 } 1023 } catch (Exception e) { 1024 throw new RemoteException(e); 1025 } 1026 return stats; 1027 } 1028 1029 @Override 1030 @SuppressWarnings("unchecked") 1031 public Collection<Stats> getDurationStats(String repository, String param, String tu) throws RemoteException { 1032 ArrayList<Stats> stats = new ArrayList(); 1033 1034 try { 1035 Window w = TimeUnit.find(tu).window(); 1036 DurationMetric m = MsDurationMetric.find(param).metric(); 1037 1038 JcrRepository repo = (JcrRepository) connector().find(repository).repository(); 1039 1040 RepositoryStatistics repositoryStatistics = repo.getRepositoryStatistics(); 1041 History history = repositoryStatistics.getHistory(m, w); 1042 1043 Statistics[] s = history.getStats(); 1044 for (int i = 0; i < s.length; i++) { 1045 double min = s[i] != null ? s[i].getMinimum() : 0; 1046 double max = s[i] != null ? s[i].getMaximum() : 0; 1047 double avg = s[i] != null ? s[i].getMean() : 0; 1048 stats.add(new Stats(min, max, avg)); 1049 } 1050 } catch (Exception e) { 1051 throw new RemoteException(e); 1052 } 1053 return stats; 1054 } 1055 1056 @Override 1057 public String[] getValueMetrics() throws RemoteException { 1058 return MsValueMetric.getNames(); 1059 } 1060 1061 @Override 1062 public String[] getDurationMetrics() throws RemoteException { 1063 return MsDurationMetric.getNames(); 1064 } 1065 1066 @Override 1067 public String[] getTimeUnits() throws RemoteException { 1068 return TimeUnit.names(); 1069 } 1070 1071 1072 private class SimplePrincipal implements Principal { 1073 1074 private String name; 1075 1076 protected SimplePrincipal( String name ) { 1077 this.name = name; 1078 } 1079 1080 @Override 1081 public String getName() { 1082 return name; 1083 } 1084 } 1085 1086 private Connector loadConnector(String clsName, ServletContext context) throws RemoteException { 1087 try { 1088 Class<?> cls = getClass().getClassLoader().loadClass(clsName); 1089 Connector connector = (Connector) cls.newInstance(); 1090 connector.start(context); 1091 return connector; 1092 } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | RemoteException e) { 1093 throw new RemoteException(e); 1094 } 1095 } 1096 1097 private boolean isUnknown(Object o) { 1098 return o == null; 1099 } 1100 1101}