1 10 package org.mmbase.core.util; 11 12 import java.util.*; 13 14 import org.mmbase.bridge.Field; 15 import org.mmbase.bridge.NodeQuery; 16 import org.mmbase.cache.*; 17 import org.mmbase.core.CoreField; 18 import org.mmbase.module.corebuilders.*; 19 import org.mmbase.module.core.*; 20 import org.mmbase.storage.*; 21 import org.mmbase.storage.util.Index; 22 import org.mmbase.storage.search.*; 23 import org.mmbase.storage.search.implementation.*; 24 import org.mmbase.util.QueryConvertor; 25 import org.mmbase.util.logging.Logger; 26 import org.mmbase.util.logging.Logging; 27 28 37 public class StorageConnector { 38 39 42 private static final int MAX_QUERY_SIZE = 20000; 43 44 private static final Logger log = Logging.getLoggerInstance(StorageConnector.class); 45 46 51 54 55 82 85 86 89 protected int maxNodesFromQuery = -1; 90 91 94 protected final MMObjectBuilder builder; 95 96 private Map indices = new HashMap(); 98 99 102 public StorageConnector(MMObjectBuilder builder) { 103 this.builder = builder; 104 } 105 106 110 public int size() { 111 try { 112 return builder.getMMBase().getStorageManager().size(builder); 113 } catch (StorageException se) { 114 log.error(se.getMessage()); 115 return -1; 116 } 117 } 118 119 125 public boolean created() { 126 try { 127 return builder.getMMBase().getStorageManager().exists(builder); 128 } catch (StorageException se) { 129 log.error(se.getMessage() + Logging.stackTrace(se)); 130 return false; 131 } 132 } 133 134 public Map getIndices() { 135 return indices; 136 } 137 138 public void addIndex(Index index) { 139 if (index != null && index.getParent() == builder) { 140 indices.put(index.getName(),index); 141 } 142 } 143 144 public void addIndices(List indexList) { 145 if (indexList != null ) { 146 for (Iterator i = indexList.iterator(); i.hasNext(); ) { 147 addIndex((Index)i.next()); 148 } 149 } 150 } 151 152 public Index getIndex(String key) { 153 return (Index)indices.get(key); 154 } 155 156 public synchronized Index createIndex(String key) { 157 Index index = getIndex(key); 158 if (index == null) { 159 index = new Index(builder, key); 160 indices.put(key,index); 161 } 162 return index; 163 } 164 165 public void addToIndex(String key, Field field) { 166 createIndex(key).add(field); 167 } 168 169 public void removeFromIndex(String key, Field field) { 170 Index index = createIndex(key); 171 if (index != null) { 172 index.remove(field); 173 } 174 } 175 176 public boolean isInIndex(String key, Field field) { 177 Index index = getIndex(key); 178 return index != null && index.contains(field); 179 } 180 181 182 183 193 public MMObjectNode getNode(final int number, final boolean useCache) throws StorageException { 194 if (log.isDebugEnabled()) { 195 log.trace("Getting node with number " + number); 196 } 197 if (number < 0) { 198 throw new IllegalArgumentException ("Tried to obtain node from builder '" + builder.getTableName() + "' with an illegal number = " + number); 199 } 200 MMObjectNode node = null; 201 202 Integer numberValue = new Integer (number); 203 node = builder.getNodeFromCache(numberValue); 205 if (node != null) { 206 log.trace("Found in cache!"); 207 if (useCache) { 208 return node; 209 } else { 210 return new MMObjectNode(node); 211 } 212 } 213 214 MMBase mmb = builder.getMMBase(); 215 MMObjectBuilder nodeBuilder = getBuilderForNode(number); 218 log.debug("Getting node from storage"); 220 node = mmb.getStorageManager().getNode(nodeBuilder, number); 221 if (useCache) { 223 if (log.isDebugEnabled()) { 224 log.debug("Caching node from storage" + node); 225 } 226 node = builder.safeCache(numberValue, node); 227 } 228 if (log.isDebugEnabled()) { 229 log.debug("Returning " + node); 230 } 231 if (useCache) { 232 return node; 233 } else { 234 return new MMObjectNode(node); 235 } 236 } 237 238 public MMObjectBuilder getBuilderForNode(final int number) { 239 MMBase mmb = builder.getMMBase(); 240 MMObjectBuilder nodeBuilder = builder; 241 int nodeType = getNodeType(number); 242 if (nodeType < 0) { 243 throw new StorageNotFoundException("Cannot determine node type of node with number =" + number); 245 } 246 if (nodeType != builder.getNumber()) { 248 if (log.isDebugEnabled()) { 249 log.debug(" " + nodeType + "!=" + builder.getNumber()); 250 } 251 String builderName = mmb.getTypeDef().getValue(nodeType); 252 if (builderName == null) { 253 log.error("The nodetype name of node #" + number + " could not be found (nodetype # " + nodeType + "), taking '" + builder.getTableName() + "'"); 254 builderName = builder.getTableName(); 255 } 256 nodeBuilder = mmb.getBuilder(builderName); 257 if (nodeBuilder == null) { 258 log.warn("Node #" + number + "'s builder " + builderName + "(" + nodeType + ") is not loaded, taking 'object'."); 259 nodeBuilder = mmb.getBuilder("object"); 260 } 261 } 262 return nodeBuilder; 263 } 264 265 272 public int getNodeType(int number) throws StorageException { 273 if (number < 0 ) { 274 throw new IllegalArgumentException ("node number was invalid (" + number + " < 0)" ); 275 } else { 276 return builder.getMMBase().getStorageManager().getNodeType(number); 277 } 278 } 279 280 282 292 public List getNodes(Collection virtuals) throws SearchQueryException { 293 List result = new ArrayList(); 294 295 int numbersSize = 0; 296 NodeSearchQuery query = new NodeSearchQuery(builder); 297 BasicStep step = (BasicStep) query.getSteps().get(0); 298 List subResult = new ArrayList(); 299 300 Iterator i = virtuals.iterator(); 301 while(i.hasNext()) { 302 MMObjectNode node = (MMObjectNode) i.next(); 303 304 Integer number = new Integer (node.getNumber()); 307 if(builder.isNodeCached(number)) { 308 result.add(builder.getNodeFromCache(number)); 309 } else { 311 numbersSize += ("," + number).length(); 312 subResult.add(number); 313 step.addNode(number.intValue()); 314 } 315 316 if(numbersSize > MAX_QUERY_SIZE) { 317 addSubResult(query, subResult, result); 318 query = new NodeSearchQuery(builder); 319 step = (BasicStep) query.getSteps().get(0); 320 numbersSize = 0; 321 subResult.clear(); 322 } 323 } 324 325 if(numbersSize > 0) { 328 addSubResult(query, subResult, result); 329 } 331 assert assertSizes(virtuals, result); 333 334 return result; 335 } 336 337 343 protected void addSubResult(final NodeSearchQuery query, final List subResult, final List result) throws SearchQueryException { 344 List rawNodes = getRawNodes(query, true); 345 Map rawMap = new HashMap(); 348 Iterator i = rawNodes.iterator(); 349 while (i.hasNext()) { 350 MMObjectNode n = (MMObjectNode) i.next(); 351 rawMap.put(new Integer (n.getNumber()), n); 353 } 354 Iterator j = subResult.iterator(); 355 while (j.hasNext()) { 356 Integer n = (Integer ) j.next(); 357 result.add(rawMap.get(n)); 358 } 359 } 360 361 364 protected boolean assertSizes(Collection virtuals, Collection result) { 365 if (virtuals.size() != result.size()) { 366 log.error(" virtuals " + virtuals + " result " + result); 367 return false; 368 } else { 369 return true; 370 } 371 } 372 383 public int count(SearchQuery query) throws SearchQueryException { 384 verifyBuilderQuery(query); 386 387 ModifiableQuery modifiedQuery = new ModifiableQuery(query); 389 Step step = (Step) query.getSteps().get(0); 390 CoreField numberField = builder.getField(MMObjectBuilder.FIELD_NUMBER); 391 AggregatedField field = new BasicAggregatedField(step, numberField, AggregatedField.AGGREGATION_TYPE_COUNT); 392 List newFields = new ArrayList(1); 393 newFields.add(field); 394 modifiedQuery.setFields(newFields); 395 396 AggregatedResultCache cache = AggregatedResultCache.getCache(); 397 398 List results = (List) cache.get(modifiedQuery); 399 if (results == null) { 400 results = builder.getMMBase().getSearchQueryHandler().getNodes(modifiedQuery, new ResultBuilder(builder.getMMBase(), modifiedQuery)); 402 cache.put(modifiedQuery, results); 403 } 404 ResultNode result = (ResultNode) results.get(0); 405 return result.getIntValue(MMObjectBuilder.FIELD_NUMBER); 406 } 407 408 private void verifyBuilderQuery(SearchQuery query) throws SearchQueryException { 409 String builderName = null; 410 if (query instanceof NodeQuery) { 411 builderName = ((NodeQuery)query).getNodeManager().getName(); 412 } else if (query instanceof NodeSearchQuery) { 413 builderName = ((NodeSearchQuery)query).getBuilder().getTableName(); 414 } 415 if (builderName != null && !builderName.equals(builder.getTableName())) { 416 throw new IllegalArgumentException ("Query passed runs on '" + builderName + "' but was passed to '" + builder.getTableName() + "'"); 417 } 418 } 419 420 433 protected Map getCache(SearchQuery query) { 434 List steps = query.getSteps(); 435 if (steps.size() == 3) { 436 Step step0 = (Step) steps.get(0); 437 Collection nodes = step0.getNodes(); 438 if (nodes != null && nodes.size() == 1) { 439 return RelatedNodesCache.getCache(); 440 } 441 } 442 return NodeListCache.getCache(); 443 444 } 445 446 460 private List getRawNodes(SearchQuery query, boolean useCache) throws SearchQueryException { 461 verifyBuilderQuery(query); 463 List results = useCache ? (List) getCache(query).get(query) : null; 464 465 if (results == null) { 467 log.debug("result list is null, getting from storage"); 468 results = builder.getMMBase().getSearchQueryHandler().getNodes(query, builder); 469 } else { 470 if (log.isDebugEnabled()) { 471 log.debug("Found from cache" + ((Cache) getCache(query)).getName() + " " + results); 472 } 473 } 474 return results; 475 } 476 477 488 public List getNodes(SearchQuery query) throws SearchQueryException { 489 return getNodes(query, true); 490 } 491 492 504 public List getNodes(SearchQuery query, boolean useCache) throws SearchQueryException { 505 List results = getRawNodes(query, useCache); 506 processSearchResults(results); 509 if (useCache) { 510 getCache(query).put(query, results); 511 } 512 return results; 513 } 514 515 519 public List getNodes() throws SearchQueryException { 520 return getNodes(new NodeSearchQuery(builder)); 521 } 522 523 539 private void processSearchResults(List results) { 540 Map convert = new HashMap(); 541 int convertCount = 0; 542 int convertedCount = 0; 543 int cacheGetCount = 0; 544 int cachePutCount = 0; 545 546 ListIterator resultsIterator = results.listIterator(); 547 while (resultsIterator.hasNext()) { 548 MMObjectNode node = (MMObjectNode) resultsIterator.next(); 549 Integer number = new Integer (node.getNumber()); 550 if(number.intValue() < 0) { 551 log.error("invalid node found, node number was invalid:" + node.getNumber()+", storage invalid?"); 553 resultsIterator.remove(); 556 continue; 557 } 558 559 boolean fromCache = false; 560 int oType = builder.getNumber(); 564 if(oType != -1 && oType != node.getOType()){ 565 MMObjectNode cachedNode = builder.getNodeFromCache(number); 568 if(cachedNode != null) { 569 node = cachedNode; 570 resultsIterator.set(node); 571 fromCache = true; 572 cacheGetCount ++; 573 } else { 574 Integer nodeType = new Integer (node.getOType()); 579 Set nodes = (Set) convert.get(nodeType); 580 if (nodes == null) { 582 nodes = new HashSet(); 583 convert.put(nodeType, nodes); 584 } 585 nodes.add(node); 586 convertCount ++; 587 } 588 603 } 604 605 if(!fromCache && oType == node.getOType()) { 609 node.clearChanged(); node = builder.safeCache(number, node); 613 cachePutCount++; 614 } 615 } 616 617 if ( convert.size() > 0){ 618 Map convertedNodes = new HashMap(); 623 624 Iterator types = convert.entrySet().iterator(); 626 while(types.hasNext()){ 627 Map.Entry typeEntry = (Map.Entry) types.next(); 628 int nodeType = ((Integer )typeEntry.getKey()).intValue(); 629 Set nodes = (Set) typeEntry.getValue(); 630 MMObjectNode typedefNode; 631 try { 632 typedefNode = getNode(nodeType, true); 633 } catch (Exception e) { 634 log.error("Exception during conversion of nodelist to right types. Nodes (" + nodes + ") of current type " + nodeType + " will be skipped. Probably the storage is inconsistent. Message: " + e.getMessage()); 635 636 continue; 637 } 638 if(typedefNode == null) { 639 log.error("Could not find typedef node #" + nodeType); 643 continue; 644 } 645 MMObjectBuilder conversionBuilder = builder.getMMBase().getBuilder(typedefNode.getStringValue("name")); 646 if(conversionBuilder == null) { 647 log.error("Could not find builder with name:" + typedefNode.getStringValue("name") + " refered by node #" + typedefNode.getNumber()+", is it active?"); 651 continue; 652 } 653 try { 654 Iterator converted = conversionBuilder.getStorageConnector().getNodes(nodes).iterator(); 655 while(converted.hasNext()) { 656 MMObjectNode current = (MMObjectNode) converted.next(); 657 convertedNodes.put(new Integer (current.getNumber()), current); 658 } 659 } catch (SearchQueryException sqe) { 660 log.error(sqe.getMessage() + Logging.stackTrace(sqe)); 661 } 663 } 664 665 for(int i = 0; i < results.size(); i++) { 667 MMObjectNode current = (MMObjectNode) results.get(i); 668 Integer number = new Integer (current.getNumber()); 669 if(convertedNodes.containsKey(number)) { 670 results.set(i, convertedNodes.get(number)); 672 convertedCount ++; 673 } 674 current = (MMObjectNode) results.get(i); 675 if(current.getNumber() < 0) { 676 throw new RuntimeException ("invalid node found, node number was invalid:" + current.getNumber()); 678 } 679 } 680 } else if(convert.size() != 0) { 681 log.warn("we still need to convert " + convertCount + " of the " + results.size() + " nodes" 682 + "(number of different types:"+ convert.size() +")"); 683 } 684 if(log.isDebugEnabled()) { 685 log.debug("retrieved " + results.size() + 686 " nodes, converted " + convertedCount + 687 " of the " + convertCount + 688 " invalid nodes(" + convert.size() + 689 " types, " + cacheGetCount + 690 " from cache, " + cachePutCount + " to cache)"); 691 } 692 } 693 694 704 public NodeSearchQuery getSearchQuery(String where) { 705 NodeSearchQuery query; 706 707 if (where != null && where.startsWith("MMNODE ")) { 708 query = convertMMNodeSearch2Query(where); 710 } else { 711 query = new NodeSearchQuery(builder); 712 QueryConvertor.setConstraint(query, where); 713 } 714 715 return query; 716 } 717 718 726 private NodeSearchQuery convertMMNodeSearch2Query(String expr) { 727 NodeSearchQuery query = new NodeSearchQuery(builder); 728 BasicCompositeConstraint constraints = new BasicCompositeConstraint(CompositeConstraint.LOGICAL_AND); 729 String logicalOperator = null; 730 731 StringTokenizer tokenizer = new StringTokenizer(expr.substring(7), "+-\n\r", true); 735 while (tokenizer.hasMoreTokens()) { 736 String fieldExpression = tokenizer.nextToken(); 737 738 int pos = fieldExpression.indexOf('.'); 740 if (pos != -1) { 741 fieldExpression = fieldExpression.substring(pos + 1); 742 } 743 744 pos = fieldExpression.indexOf('='); 747 if (pos != -1 && fieldExpression.length() > pos + 2) { 748 String fieldName = fieldExpression.substring(0, pos); 749 char comparison = fieldExpression.charAt(pos + 1); 750 String value = fieldExpression.substring(pos + 2); 751 752 CoreField field = builder.getField(fieldName); 754 if (field == null) { 755 throw new IllegalArgumentException ( 756 "Invalid MMNODE expression: " + expr); 757 } 758 StepField stepField = query.getField(field); 759 BasicConstraint constraint = parseFieldPart(stepField, comparison, value); 760 constraints.addChild(constraint); 761 762 if (logicalOperator != null && !logicalOperator.equals("+")) { 765 constraint.setInverse(true); 766 } 767 } else { 768 throw new IllegalArgumentException ( 770 "Invalid MMNODE expression: " + expr); 771 } 772 773 if (tokenizer.hasMoreTokens()) { 775 logicalOperator = tokenizer.nextToken(); 776 } 777 } 778 779 List childs = constraints.getChilds(); 780 if (childs.size() == 1) { 781 query.setConstraint((FieldValueConstraint) childs.get(0)); 782 } else if (childs.size() > 1) { 783 query.setConstraint(constraints); 784 } 785 return query; 786 } 787 788 800 private BasicFieldValueConstraint parseFieldPart(StepField field, char comparison, String strValue) { 801 802 Object value = strValue; 803 804 if (field.getType() != Field.TYPE_STRING && 806 field.getType() != Field.TYPE_XML && 807 field.getType() != Field.TYPE_UNKNOWN) { 808 int length = strValue.length(); 810 if (strValue.charAt(0) == '*' && strValue.charAt(length - 1) == '*') { 811 strValue = strValue.substring(1, length - 1); 812 } 813 value = Double.valueOf(strValue); 814 } 815 816 BasicFieldValueConstraint constraint = new BasicFieldValueConstraint(field, value); 817 818 switch (comparison) { 819 case '=': 820 case 'E': 821 if (field.getType() == Field.TYPE_STRING || 823 field.getType() == Field.TYPE_XML) { 824 String str = (String ) value; 827 int length = str.length(); 828 if (str.charAt(0) == '*' && str.charAt(length - 1) == '*') { 829 value = str.substring(1, length - 1); 830 } 831 832 constraint.setValue('%' + (String ) value + '%'); 835 constraint.setCaseSensitive(false); 836 constraint.setOperator(FieldCompareConstraint.LIKE); 837 838 } else { 840 constraint.setOperator(FieldCompareConstraint.EQUAL); 841 } 842 break; 843 844 case 'N': 845 constraint.setOperator(FieldCompareConstraint.NOT_EQUAL); 846 break; 847 848 case 'G': 849 constraint.setOperator(FieldCompareConstraint.GREATER); 850 break; 851 852 case 'g': 853 constraint.setOperator(FieldCompareConstraint.GREATER_EQUAL); 854 break; 855 856 case 'S': 857 constraint.setOperator(FieldCompareConstraint.LESS); 858 break; 859 860 case 's': 861 constraint.setOperator(FieldCompareConstraint.LESS_EQUAL); 862 break; 863 864 default: 865 throw new IllegalArgumentException ("Invalid comparison character: '" + comparison + "'"); 866 } 867 return constraint; 868 } 869 870 871 872 } 873 | Popular Tags |