1 13 14 package mondrian.rolap; 15 import java.io.*; 16 import java.lang.ref.SoftReference ; 17 import java.lang.reflect.Constructor ; 18 import java.lang.reflect.InvocationTargetException ; 19 import java.lang.reflect.Modifier ; 20 import java.net.URL ; 21 import java.security.MessageDigest ; 22 import java.security.NoSuchAlgorithmException ; 23 import java.util.*; 24 25 import javax.sql.DataSource ; 26 27 import mondrian.olap.Access; 28 import mondrian.olap.Category; 29 import mondrian.olap.Cube; 30 import mondrian.olap.Dimension; 31 import mondrian.olap.Exp; 32 import mondrian.olap.Formula; 33 import mondrian.olap.FunTable; 34 import mondrian.olap.Hierarchy; 35 import mondrian.olap.Level; 36 import mondrian.olap.Member; 37 import mondrian.olap.MondrianDef; 38 import mondrian.olap.MondrianProperties; 39 import mondrian.olap.NamedSet; 40 import mondrian.olap.Parameter; 41 import mondrian.olap.Role; 42 import mondrian.olap.Schema; 43 import mondrian.olap.SchemaReader; 44 import mondrian.olap.Syntax; 45 import mondrian.olap.Util; 46 import mondrian.olap.fun.*; 47 import mondrian.olap.type.MemberType; 48 import mondrian.olap.type.NumericType; 49 import mondrian.olap.type.StringType; 50 import mondrian.olap.type.Type; 51 import mondrian.resource.MondrianResource; 52 import mondrian.rolap.aggmatcher.AggTableManager; 53 import mondrian.rolap.aggmatcher.JdbcSchema; 54 import mondrian.rolap.sql.SqlQuery; 55 import mondrian.spi.UserDefinedFunction; 56 import mondrian.spi.DataSourceChangeListener; 57 58 import org.apache.log4j.Logger; 59 import org.apache.commons.vfs.*; 60 61 import org.eigenbase.xom.DOMWrapper; 62 import org.eigenbase.xom.Parser; 63 import org.eigenbase.xom.XOMException; 64 import org.eigenbase.xom.XOMUtil; 65 66 76 public class RolapSchema implements Schema { 77 private static final Logger LOGGER = Logger.getLogger(RolapSchema.class); 78 79 private static final Set<Access> schemaAllowed = 80 Util.enumSetOf(Access.NONE, Access.ALL, Access.ALL_DIMENSIONS); 81 82 private static final Set<Access> cubeAllowed = 83 Util.enumSetOf(Access.NONE, Access.ALL); 84 85 private static final Set<Access> dimensionAllowed = 86 Util.enumSetOf(Access.NONE, Access.ALL); 87 88 private static final Set<Access> hierarchyAllowed = 89 Util.enumSetOf(Access.NONE, Access.ALL, Access.CUSTOM); 90 91 private static final Set<Access> memberAllowed = 92 Util.enumSetOf(Access.NONE, Access.ALL); 93 94 private String name; 95 96 99 private final RolapConnection internalConnection; 100 103 private final Map<String , RolapCube> mapNameToCube; 104 108 private final Map<String , MemberReader> mapSharedHierarchyToReader; 109 110 114 private final Map<String , RolapHierarchy> mapSharedHierarchyNameToHierarchy; 115 118 private Role defaultRole; 119 120 private final String md5Bytes; 121 122 125 private AggTableManager aggTableManager; 126 127 131 private String key; 132 135 private final Map<String , Role> mapNameToRole; 136 139 private final Map<String , NamedSet> mapNameToSet = new HashMap<String , NamedSet>(); 140 144 private FunTable funTable; 145 146 private MondrianDef.Schema xmlSchema; 147 148 final List<RolapSchemaParameter > parameterList = 149 new ArrayList<RolapSchemaParameter >(); 150 151 private Date schemaLoadDate; 152 153 private DataSourceChangeListener dataSourceChangeListener; 154 155 164 private RolapSchema( 165 final String key, 166 final Util.PropertyList connectInfo, 167 final DataSource dataSource, 168 final String md5Bytes) { 169 this.key = key; 170 this.md5Bytes = md5Bytes; 171 this.defaultRole = createDefaultRole(); 173 this.internalConnection = 174 new RolapConnection(connectInfo, this, dataSource); 175 176 this.mapSharedHierarchyNameToHierarchy = new HashMap<String , RolapHierarchy>(); 177 this.mapSharedHierarchyToReader = new HashMap<String , MemberReader>(); 178 this.mapNameToCube = new HashMap<String , RolapCube>(); 179 this.mapNameToRole = new HashMap<String , Role>(); 180 this.aggTableManager = new AggTableManager(this); 181 this.dataSourceChangeListener = createDataSourceChangeListener(connectInfo); 182 } 183 184 185 194 private RolapSchema( 195 final String key, 196 final String md5Bytes, 197 final String catalogUrl, 198 final String catalogStr, 199 final Util.PropertyList connectInfo, 200 final DataSource dataSource) 201 { 202 this(key, connectInfo, dataSource, md5Bytes); 203 load(catalogUrl, catalogStr); 204 } 205 206 private RolapSchema( 207 final String key, 208 final String catalogUrl, 209 final Util.PropertyList connectInfo, 210 final DataSource dataSource) 211 { 212 this(key, connectInfo, dataSource, null); 213 load(catalogUrl, null); 214 } 215 216 protected void finalCleanUp() { 217 if (aggTableManager != null) { 218 aggTableManager.finalCleanUp(); 219 aggTableManager = null; 220 } 221 } 222 223 protected void finalize() throws Throwable { 224 finalCleanUp(); 225 } 226 227 public boolean equals(Object o) { 228 if (!(o instanceof RolapSchema)) { 229 return false; 230 } 231 RolapSchema other = (RolapSchema) o; 232 return other.key.equals(key); 233 } 234 235 public int hashCode() { 236 return key.hashCode(); 237 } 238 239 protected Logger getLogger() { 240 return LOGGER; 241 } 242 243 250 protected void load(String catalogUrl, String catalogStr) { 251 try { 252 final Parser xmlParser = XOMUtil.createDefaultParser(); 253 254 final DOMWrapper def; 255 if (catalogStr == null) { 256 FileSystemManager fsManager = VFS.getManager(); 260 if (fsManager == null) { 261 throw Util.newError("Cannot get virtual file system manager"); 262 } 263 264 if (catalogUrl.startsWith("file://localhost")) { 266 catalogUrl = catalogUrl.substring("file://localhost".length()); 267 } 268 if (catalogUrl.startsWith("file:")) { 269 catalogUrl = catalogUrl.substring("file:".length()); 270 } 271 272 File userDir = new File("").getAbsoluteFile(); 273 FileObject file = fsManager.resolveFile(userDir, catalogUrl); 274 if (!file.isReadable()) { 275 throw Util.newError("Virtual file is not readable: " + 276 catalogUrl); 277 } 278 279 FileContent fileContent = file.getContent(); 280 if (fileContent == null) { 281 throw Util.newError("Cannot get virtual file content: " + 282 catalogUrl); 283 } 284 def = xmlParser.parse(fileContent.getInputStream()); 285 } else { 286 def = xmlParser.parse(catalogStr); 287 } 288 289 xmlSchema = new MondrianDef.Schema(def); 290 291 if (getLogger().isDebugEnabled()) { 292 StringWriter sw = new StringWriter(4096); 293 PrintWriter pw = new PrintWriter(sw); 294 pw.println("RolapSchema.load: dump xmlschema"); 295 xmlSchema.display(pw, 2); 296 pw.flush(); 297 getLogger().debug(sw.toString()); 298 } 299 300 load(xmlSchema); 301 302 } catch (XOMException e) { 303 throw Util.newError(e, "while parsing catalog " + catalogUrl); 304 } catch (FileSystemException e) { 305 throw Util.newError(e, "while parsing catalog " + catalogUrl); 306 } 307 308 aggTableManager.initialize(); 309 setSchemaLoadDate(); 310 } 311 312 private void setSchemaLoadDate() { 313 schemaLoadDate = new Date(); 314 } 315 316 public Date getSchemaLoadDate() { 317 return schemaLoadDate; 318 } 319 320 Role getDefaultRole() { 321 return defaultRole; 322 } 323 324 MondrianDef.Schema getXMLSchema() { 325 return xmlSchema; 326 } 327 328 public String getName() { 329 Util.assertPostcondition(name != null, "return != null"); 330 Util.assertPostcondition(name.length() > 0, "return.length() > 0"); 331 return name; 332 } 333 334 340 public SqlQuery.Dialect getDialect() { 341 DataSource dataSource = getInternalConnection().getDataSource(); 342 return SqlQuery.Dialect.create(dataSource); 343 } 344 345 private void load(MondrianDef.Schema xmlSchema) { 346 this.name = xmlSchema.name; 347 if (name == null || name.equals("")) { 348 throw Util.newError("<Schema> name must be set"); 349 } 350 351 final Map<String , UserDefinedFunction> mapNameToUdf = 355 new HashMap<String , UserDefinedFunction>(); 356 for (MondrianDef.UserDefinedFunction udf : xmlSchema.userDefinedFunctions) { 357 defineFunction(mapNameToUdf, udf.name, udf.className); 358 } 359 final RolapSchemaFunctionTable funTable = 360 new RolapSchemaFunctionTable(mapNameToUdf.values()); 361 funTable.init(); 362 this.funTable = funTable; 363 364 for (MondrianDef.Dimension xmlDimension : xmlSchema.dimensions) { 366 if (xmlDimension.foreignKey != null) { 367 throw MondrianResource.instance() 368 .PublicDimensionMustNotHaveForeignKey.ex( 369 xmlDimension.name); 370 } 371 } 372 373 Set<String > parameterNames = new HashSet<String >(); 375 for (MondrianDef.Parameter xmlParameter : xmlSchema.parameters) { 376 String name = xmlParameter.name; 377 if (!parameterNames.add(name)) { 378 throw MondrianResource.instance().DuplicateSchemaParameter.ex( 379 name); 380 } 381 Type type; 382 if (xmlParameter.type.equals("String")) { 383 type = new StringType(); 384 } else if (xmlParameter.type.equals("Numeric")) { 385 type = new NumericType(); 386 } else { 387 type = new MemberType(null, null, null, null); 388 } 389 final String description = xmlParameter.description; 390 final boolean modifiable = xmlParameter.modifiable; 391 String defaultValue = xmlParameter.defaultValue; 392 RolapSchemaParameter param = 393 new RolapSchemaParameter( 394 this, name, defaultValue, description, type, modifiable); 395 Util.discard(param); 396 } 397 398 for (MondrianDef.Cube xmlCube : xmlSchema.cubes) { 400 if (xmlCube.isEnabled()) { 401 RolapCube cube = new RolapCube(this, xmlSchema, xmlCube, true); 402 cube.validate(); 403 } 404 } 405 406 for (MondrianDef.VirtualCube xmlVirtualCube : xmlSchema.virtualCubes) { 408 if (xmlVirtualCube.isEnabled()) { 409 RolapCube cube = 410 new RolapCube(this, xmlSchema, xmlVirtualCube, true); 411 Util.discard(cube); 412 } 413 } 414 415 for (MondrianDef.NamedSet xmlNamedSet : xmlSchema.namedSets) { 417 mapNameToSet.put(xmlNamedSet.name, createNamedSet(xmlNamedSet)); 418 } 419 420 for (MondrianDef.Role xmlRole : xmlSchema.roles) { 422 Role role = createRole(xmlRole); 423 mapNameToRole.put(xmlRole.name, role); 424 } 425 426 if (xmlSchema.defaultRole != null) { 428 Role role = lookupRole(xmlSchema.defaultRole); 429 if (role == null) { 430 throw Util.newError("Role '" + xmlSchema.defaultRole + "' not found"); 431 } 432 defaultRole = role; 433 } 434 } 435 436 private NamedSet createNamedSet(MondrianDef.NamedSet xmlNamedSet) { 437 final String formulaString = xmlNamedSet.getFormula(); 438 final Exp exp; 439 try { 440 exp = getInternalConnection().parseExpression(formulaString); 441 } catch (Exception e) { 442 throw MondrianResource.instance().NamedSetHasBadFormula.ex( 443 xmlNamedSet.name, e); 444 } 445 final Formula formula = new Formula( 446 new String [] {xmlNamedSet.name}, 447 exp); 448 return formula.getNamedSet(); 449 } 450 451 private Role createRole(MondrianDef.Role xmlRole) { 452 Role role = new Role(); 453 for (MondrianDef.SchemaGrant schemaGrant : xmlRole.schemaGrants) { 454 role.grant(this, getAccess(schemaGrant.access, schemaAllowed)); 455 for (MondrianDef.CubeGrant cubeGrant : schemaGrant.cubeGrants) { 456 RolapCube cube = lookupCube(cubeGrant.cube); 457 if (cube == null) { 458 throw Util.newError("Unknown cube '" + cube + "'"); 459 } 460 role.grant(cube, getAccess(cubeGrant.access, cubeAllowed)); 461 final SchemaReader schemaReader = cube.getSchemaReader(null); 462 for (MondrianDef.DimensionGrant dimensionGrant : cubeGrant.dimensionGrants) { 463 Dimension dimension = (Dimension) 464 schemaReader.lookupCompound( 465 cube, Util.explode(dimensionGrant.dimension), true, 466 Category.Dimension); 467 role.grant( 468 dimension, 469 getAccess(dimensionGrant.access, dimensionAllowed)); 470 } 471 for (MondrianDef.HierarchyGrant hierarchyGrant : cubeGrant.hierarchyGrants) { 472 Hierarchy hierarchy = (Hierarchy) 473 schemaReader.lookupCompound( 474 cube, Util.explode(hierarchyGrant.hierarchy), true, 475 Category.Hierarchy); 476 final Access hierarchyAccess = 477 getAccess(hierarchyGrant.access, hierarchyAllowed); 478 Level topLevel = null; 479 if (hierarchyGrant.topLevel != null) { 480 if (hierarchyAccess != Access.CUSTOM) { 481 throw Util.newError( 482 "You may only specify 'topLevel' if access='custom'"); 483 } 484 topLevel = (Level) schemaReader.lookupCompound( 485 cube, Util.explode(hierarchyGrant.topLevel), true, 486 Category.Level); 487 } 488 Level bottomLevel = null; 489 if (hierarchyGrant.bottomLevel != null) { 490 if (hierarchyAccess != Access.CUSTOM) { 491 throw Util.newError( 492 "You may only specify 'bottomLevel' if access='custom'"); 493 } 494 bottomLevel = (Level) schemaReader.lookupCompound( 495 cube, Util.explode(hierarchyGrant.bottomLevel), 496 true, Category.Level); 497 } 498 role.grant( 499 hierarchy, hierarchyAccess, topLevel, bottomLevel); 500 for (MondrianDef.MemberGrant memberGrant : hierarchyGrant.memberGrants) { 501 if (hierarchyAccess != Access.CUSTOM) { 502 throw Util.newError( 503 "You may only specify <MemberGrant> if <Hierarchy> has access='custom'"); 504 } 505 Member member = schemaReader.getMemberByUniqueName( 506 Util.explode(memberGrant.member), true); 507 if (member.getHierarchy() != hierarchy) { 508 throw Util.newError( 509 "Member '" + 510 member + 511 "' is not in hierarchy '" + 512 hierarchy + 513 "'"); 514 } 515 role.grant( 516 member, 517 getAccess(memberGrant.access, memberAllowed)); 518 } 519 } 520 } 521 } 522 role.makeImmutable(); 523 return role; 524 } 525 526 private Access getAccess(String accessString, Set<Access> allowed) { 527 final Access access = Access.valueOf(accessString.toUpperCase()); 528 if (allowed.contains(access)) { 529 return access; } 531 throw Util.newError("Bad value access='" + accessString + "'"); 532 } 533 534 public Dimension createDimension(Cube cube, String xml) { 535 MondrianDef.CubeDimension xmlDimension; 536 try { 537 final Parser xmlParser = XOMUtil.createDefaultParser(); 538 final DOMWrapper def = xmlParser.parse(xml); 539 final String tagName = def.getTagName(); 540 if (tagName.equals("Dimension")) { 541 xmlDimension = new MondrianDef.Dimension(def); 542 } else if (tagName.equals("DimensionUsage")) { 543 xmlDimension = new MondrianDef.DimensionUsage(def); 544 } else { 545 throw new XOMException("Got <" + tagName + 546 "> when expecting <Dimension> or <DimensionUsage>"); 547 } 548 } catch (XOMException e) { 549 throw Util.newError(e, "Error while adding dimension to cube '" + 550 cube + "' from XML [" + xml + "]"); 551 } 552 return ((RolapCube) cube).createDimension(xmlDimension); 553 } 554 555 public Cube createCube(String xml) { 556 557 RolapCube cube; 558 try { 559 final Parser xmlParser = XOMUtil.createDefaultParser(); 560 final DOMWrapper def = xmlParser.parse(xml); 561 final String tagName = def.getTagName(); 562 if (tagName.equals("Cube")) { 563 final MondrianDef.Schema xmlSchema = new MondrianDef.Schema(); 566 MondrianDef.Cube xmlDimension = new MondrianDef.Cube(def); 567 cube = new RolapCube(this, xmlSchema, xmlDimension, false); 568 } else if (tagName.equals("VirtualCube")) { 569 MondrianDef.Schema xmlSchema = getXMLSchema(); 571 MondrianDef.VirtualCube xmlDimension = 572 new MondrianDef.VirtualCube(def); 573 cube = new RolapCube(this, xmlSchema, xmlDimension, false); 574 } else { 575 throw new XOMException("Got <" + tagName + 576 "> when expecting <Cube>"); 577 } 578 } catch (XOMException e) { 579 throw Util.newError(e, "Error while creating cube from XML [" + 580 xml + "]"); 581 } 582 return cube; 583 } 584 585 609 610 616 static class Pool { 617 private final MessageDigest md; 618 619 private static Pool pool = new Pool(); 620 621 private Map<String , SoftReference <RolapSchema>> mapUrlToSchema = 622 new HashMap<String , SoftReference <RolapSchema>>(); 623 624 625 private Pool() { 626 try { 628 md = MessageDigest.getInstance("MD5"); 629 } catch (NoSuchAlgorithmException e) { 630 throw new RuntimeException (e); 631 } 632 } 633 634 static Pool instance() { 635 return pool; 636 } 637 638 644 private synchronized String encodeMD5(final String value) { 645 md.reset(); 646 final byte[] bytes = md.digest(value.getBytes()); 647 return (bytes != null) ? new String (bytes) : null; 648 } 649 650 synchronized RolapSchema get( 651 final String catalogUrl, 652 final String connectionKey, 653 final String jdbcUser, 654 final String dataSourceStr, 655 final Util.PropertyList connectInfo) 656 { 657 return get( 658 catalogUrl, 659 connectionKey, 660 jdbcUser, 661 dataSourceStr, 662 null, 663 connectInfo); 664 } 665 666 synchronized RolapSchema get( 667 final String catalogUrl, 668 final DataSource dataSource, 669 final Util.PropertyList connectInfo) 670 { 671 return get( 672 catalogUrl, 673 null, 674 null, 675 null, 676 dataSource, 677 connectInfo); 678 } 679 680 private RolapSchema get( 681 final String catalogUrl, 682 final String connectionKey, 683 final String jdbcUser, 684 final String dataSourceStr, 685 final DataSource dataSource, 686 final Util.PropertyList connectInfo) 687 { 688 String key = (dataSource == null) ? 689 makeKey(catalogUrl, connectionKey, jdbcUser, dataSourceStr) : 690 makeKey(catalogUrl, dataSource); 691 692 RolapSchema schema = null; 693 boolean hadDynProc = false; 694 695 String dynProcName = connectInfo.get( 696 RolapConnectionProperties.DynamicSchemaProcessor.name()); 697 698 String catalogStr = connectInfo.get( 702 RolapConnectionProperties.CatalogContent.name()); 703 if (catalogStr != null) { 704 dynProcName = null; 705 key = catalogStr; 708 } 709 710 if ( ! Util.isEmpty(dynProcName)) { 715 assert catalogStr == null; 716 717 try { 718 final URL url = new URL (catalogUrl); 719 720 final Class <DynamicSchemaProcessor> clazz = 721 (Class <DynamicSchemaProcessor>) Class.forName(dynProcName); 722 final Constructor <DynamicSchemaProcessor> ctor = 723 clazz.getConstructor(); 724 final DynamicSchemaProcessor dynProc = ctor.newInstance(); 725 catalogStr = dynProc.processSchema(url, connectInfo); 726 hadDynProc = true; 727 728 } catch (Exception e) { 729 throw Util.newError(e, "loading DynamicSchemaProcessor " 730 + dynProcName); 731 } 732 733 if (LOGGER.isDebugEnabled()) { 734 String msg = "Pool.get: create schema \"" + 735 catalogUrl + 736 "\" using dynamic processor"; 737 LOGGER.debug(msg); 738 } 739 } 740 741 boolean useContentChecksum = Boolean.parseBoolean( 742 connectInfo.get( 743 RolapConnectionProperties.UseContentChecksum.name())); 744 if (useContentChecksum) { 745 751 String md5Bytes = null; 752 try { 753 if (catalogStr == null) { 754 catalogStr = Util.readURL(catalogUrl); 755 } 756 md5Bytes = encodeMD5(catalogStr); 757 758 } catch (Exception ex) { 759 ex.printStackTrace(); 764 } 765 766 if (md5Bytes != null) { 767 SoftReference <RolapSchema> ref = 768 mapUrlToSchema.get(md5Bytes); 769 if (ref != null) { 770 schema = ref.get(); 771 if (schema == null) { 772 mapUrlToSchema.remove(key); 774 mapUrlToSchema.remove(md5Bytes); 775 } 776 } 777 } 778 779 if ((schema == null) || 780 md5Bytes == null || 781 schema.md5Bytes == null || 782 ! schema.md5Bytes.equals(md5Bytes)) { 783 784 schema = new RolapSchema( 785 key, 786 md5Bytes, 787 catalogUrl, 788 catalogStr, 789 connectInfo, 790 dataSource); 791 792 SoftReference <RolapSchema> ref = new SoftReference <RolapSchema>(schema); 793 if (md5Bytes != null) { 794 mapUrlToSchema.put(md5Bytes, ref); 795 } 796 mapUrlToSchema.put(key, ref); 797 798 if (LOGGER.isDebugEnabled()) { 799 String msg = "Pool.get: create schema \"" + 800 catalogUrl + 801 "\" with MD5"; 802 LOGGER.debug(msg); 803 } 804 805 } else if (LOGGER.isDebugEnabled()) { 806 String msg = "Pool.get: schema \"" + 807 catalogUrl + 808 "\" exists already with MD5"; 809 LOGGER.debug(msg); 810 } 811 812 } else if (hadDynProc) { 813 schema = new RolapSchema( 816 key, 817 null, 818 catalogUrl, 819 catalogStr, 820 connectInfo, 821 dataSource); 822 823 } else { 824 SoftReference <RolapSchema> ref = mapUrlToSchema.get(key); 825 if (ref != null) { 826 schema = ref.get(); 827 if (schema == null) { 828 mapUrlToSchema.remove(key); 830 } 831 } 832 833 if (schema == null) { 834 if (catalogStr == null) { 835 schema = new RolapSchema( 836 key, 837 catalogUrl, 838 connectInfo, 839 dataSource); 840 } else { 841 schema = new RolapSchema( 842 key, 843 null, 844 catalogUrl, 845 catalogStr, 846 connectInfo, 847 dataSource); 848 } 849 850 mapUrlToSchema.put(key, new SoftReference <RolapSchema>(schema)); 851 852 if (LOGGER.isDebugEnabled()) { 853 String msg = "Pool.get: create schema \"" + 854 catalogUrl + 855 "\""; 856 LOGGER.debug(msg); 857 } 858 859 } else if (LOGGER.isDebugEnabled()) { 860 String msg = "Pool.get: schema \"" + 861 catalogUrl + 862 "\" exists already "; 863 LOGGER.debug(msg); 864 } 865 866 } 867 868 return schema; 869 } 870 871 synchronized void remove( 872 final String catalogUrl, 873 final String connectionKey, 874 final String jdbcUser, 875 final String dataSourceStr) 876 { 877 final String key = makeKey( 878 catalogUrl, 879 connectionKey, 880 jdbcUser, 881 dataSourceStr); 882 if (LOGGER.isDebugEnabled()) { 883 String msg = "Pool.remove: schema \"" + 884 catalogUrl + 885 "\" and datasource string \"" + 886 dataSourceStr + 887 "\""; 888 LOGGER.debug(msg); 889 } 890 891 remove(key); 892 } 893 894 synchronized void remove( 895 final String catalogUrl, 896 final DataSource dataSource) 897 { 898 final String key = makeKey(catalogUrl, dataSource); 899 if (LOGGER.isDebugEnabled()) { 900 String msg = "Pool.remove: schema \"" + 901 catalogUrl + 902 "\" and datasource object"; 903 LOGGER.debug(msg); 904 } 905 906 remove(key); 907 } 908 909 private void remove(String key) { 910 SoftReference <RolapSchema> ref = mapUrlToSchema.get(key); 911 if (ref != null) { 912 RolapSchema schema = ref.get(); 913 if (schema != null) { 914 if (schema.md5Bytes != null) { 915 mapUrlToSchema.remove(schema.md5Bytes); 916 } 917 schema.finalCleanUp(); 918 } 919 } 920 mapUrlToSchema.remove(key); 921 } 922 923 synchronized void clear() { 924 if (LOGGER.isDebugEnabled()) { 925 String msg = "Pool.clear: clearing all RolapSchemas"; 926 LOGGER.debug(msg); 927 } 928 929 for (SoftReference <RolapSchema> ref : mapUrlToSchema.values()) { 930 if (ref != null) { 931 RolapSchema schema = ref.get(); 932 if (schema != null) { 933 schema.finalCleanUp(); 934 } 935 } 936 937 } 938 mapUrlToSchema.clear(); 939 JdbcSchema.clearAllDBs(); 940 } 941 942 947 synchronized Iterator<RolapSchema> getRolapSchemas() { 948 List<RolapSchema> list = new ArrayList<RolapSchema>(); 949 for (Iterator<SoftReference <RolapSchema>> it = mapUrlToSchema.values().iterator(); 950 it.hasNext(); ) { 951 SoftReference <RolapSchema> ref = it.next(); 952 RolapSchema schema = ref.get(); 953 if (schema != null) { 955 list.add(schema); 956 } else { 957 try { 959 it.remove(); 960 } catch (Exception ex) { 961 LOGGER.warn(ex); 964 } 965 } 966 } 967 return list.iterator(); 968 } 969 970 synchronized boolean contains(RolapSchema rolapSchema) { 971 return mapUrlToSchema.containsKey(rolapSchema.key); 972 } 973 974 975 978 private static String makeKey( 979 final String catalogUrl, 980 final String connectionKey, 981 final String jdbcUser, 982 final String dataSourceStr) 983 { 984 final StringBuilder buf = new StringBuilder (100); 985 986 appendIfNotNull(buf, catalogUrl); 987 appendIfNotNull(buf, connectionKey); 988 appendIfNotNull(buf, jdbcUser); 989 appendIfNotNull(buf, dataSourceStr); 990 991 final String key = buf.toString(); 992 return key; 993 } 994 995 998 private static String makeKey( 999 final String catalogUrl, 1000 final DataSource dataSource) 1001 { 1002 final StringBuilder buf = new StringBuilder (100); 1003 1004 appendIfNotNull(buf, catalogUrl); 1005 buf.append('.'); 1006 buf.append("external#"); 1007 buf.append(System.identityHashCode(dataSource)); 1008 1009 final String key = buf.toString(); 1010 return key; 1011 } 1012 1013 private static void appendIfNotNull(StringBuilder buf, String s) { 1014 if (s != null) { 1015 if (buf.length() > 0) { 1016 buf.append('.'); 1017 } 1018 buf.append(s); 1019 } 1020 } 1021 } 1022 1023 1026 public static void flushSchema( 1027 final String catalogUrl, 1028 final String connectionKey, 1029 final String jdbcUser, 1030 String dataSourceStr) 1031 { 1032 Pool.instance().remove( 1033 catalogUrl, 1034 connectionKey, 1035 jdbcUser, 1036 dataSourceStr); 1037 } 1038 1039 1042 public static void flushSchema( 1043 final String catalogUrl, 1044 final DataSource dataSource) 1045 { 1046 Pool.instance().remove(catalogUrl, dataSource); 1047 } 1048 1049 1052 public static void clearCache() { 1053 Pool.instance().clear(); 1054 } 1055 1056 public static Iterator<RolapSchema> getRolapSchemas() { 1057 return Pool.instance().getRolapSchemas(); 1058 } 1059 1060 public static boolean cacheContains(RolapSchema rolapSchema) { 1061 return Pool.instance().contains(rolapSchema); 1062 } 1063 1064 public Cube lookupCube(final String cube, final boolean failIfNotFound) { 1065 RolapCube mdxCube = lookupCube(cube); 1066 if (mdxCube == null && failIfNotFound) { 1067 throw MondrianResource.instance().MdxCubeNotFound.ex(cube); 1068 } 1069 return mdxCube; 1070 } 1071 1072 1076 protected RolapCube lookupCube(final String cubeName) { 1077 return mapNameToCube.get(Util.normalizeName(cubeName)); 1078 } 1079 1080 1085 protected MondrianDef.CalculatedMember lookupXmlCalculatedMember( 1086 final String calcMemberName, 1087 final String cubeName) { 1088 String nameParts[] = Util.explode(calcMemberName); 1089 for (final MondrianDef.Cube cube : xmlSchema.cubes) { 1090 if (Util.equalName(cube.name, cubeName)) { 1091 for (final MondrianDef.CalculatedMember calculatedMember : cube.calculatedMembers) { 1092 if (Util.equalName( 1093 calculatedMember.dimension, nameParts[0]) && 1094 Util.equalName( 1095 calculatedMember.name, 1096 nameParts[nameParts.length - 1])) { 1097 return calculatedMember; 1098 } 1099 } 1100 } 1101 } 1102 return null; 1103 } 1104 1105 public List<RolapCube> getCubesWithStar(RolapStar star) { 1106 List<RolapCube> list = new ArrayList<RolapCube>(); 1107 for (RolapCube cube : mapNameToCube.values()) { 1108 if (star == cube.getStar()) { 1109 list.add(cube); 1110 } 1111 } 1112 return list; 1113 } 1114 1115 1119 protected void addCube(final RolapCube cube) { 1120 mapNameToCube.put( 1121 Util.normalizeName(cube.getName()), 1122 cube); 1123 } 1124 1125 public boolean removeCube(final String cubeName) { 1126 final RolapCube cube = 1127 mapNameToCube.remove(Util.normalizeName(cubeName)); 1128 return cube != null; 1129 } 1130 1131 public Cube[] getCubes() { 1132 Collection<RolapCube> cubes = mapNameToCube.values(); 1133 return cubes.toArray(new RolapCube[cubes.size()]); 1134 } 1135 1136 public List<RolapCube> getCubeList() { 1137 return new ArrayList<RolapCube>(mapNameToCube.values()); 1138 } 1139 1140 public Hierarchy[] getSharedHierarchies() { 1141 Collection<RolapHierarchy> hierarchies = 1142 mapSharedHierarchyNameToHierarchy.values(); 1143 return hierarchies.toArray(new RolapHierarchy[hierarchies.size()]); 1144 } 1145 1146 RolapHierarchy getSharedHierarchy(final String name) { 1147 1162 return mapSharedHierarchyNameToHierarchy.get(name); 1163 } 1164 1165 public NamedSet getNamedSet(String name) { 1166 return mapNameToSet.get(name); 1167 } 1168 1169 public Role lookupRole(final String role) { 1170 return mapNameToRole.get(role); 1171 } 1172 1173 public Set<String > roleNames() { 1174 return mapNameToRole.keySet(); 1175 } 1176 1177 public FunTable getFunTable() { 1178 return funTable; 1179 } 1180 1181 public Parameter[] getParameters() { 1182 return parameterList.toArray( 1183 new Parameter[parameterList.size()]); 1184 } 1185 1186 1196 private void defineFunction( 1197 Map<String , UserDefinedFunction> mapNameToUdf, 1198 String name, 1199 String className) { 1200 final Class <?> klass; 1202 try { 1203 klass = Class.forName(className); 1204 } catch (ClassNotFoundException e) { 1205 throw MondrianResource.instance().UdfClassNotFound.ex(name, 1206 className); 1207 } 1208 Constructor <?> constructor; 1210 Object [] args = {}; 1211 try { 1213 constructor = klass.getConstructor(String .class); 1214 if (Modifier.isPublic(constructor.getModifiers())) { 1215 args = new Object [] {name}; 1216 } else { 1217 constructor = null; 1218 } 1219 } catch (NoSuchMethodException e) { 1220 constructor = null; 1221 } 1222 if (constructor == null) { 1224 try { 1225 constructor = klass.getConstructor(); 1226 if (Modifier.isPublic(constructor.getModifiers())) { 1227 args = new Object [] {}; 1228 } else { 1229 constructor = null; 1230 } 1231 } catch (NoSuchMethodException e) { 1232 constructor = null; 1233 } 1234 } 1235 if (constructor == null) { 1237 throw MondrianResource.instance().UdfClassWrongIface.ex(name, 1238 className, UserDefinedFunction.class.getName()); 1239 } 1240 final UserDefinedFunction udf; 1242 try { 1243 udf = (UserDefinedFunction) constructor.newInstance(args); 1244 } catch (InstantiationException e) { 1245 throw MondrianResource.instance().UdfClassWrongIface.ex(name, 1246 className, UserDefinedFunction.class.getName()); 1247 } catch (IllegalAccessException e) { 1248 throw MondrianResource.instance().UdfClassWrongIface.ex(name, 1249 className, UserDefinedFunction.class.getName()); 1250 } catch (ClassCastException e) { 1251 throw MondrianResource.instance().UdfClassWrongIface.ex(name, 1252 className, UserDefinedFunction.class.getName()); 1253 } catch (InvocationTargetException e) { 1254 throw MondrianResource.instance().UdfClassWrongIface.ex(name, 1255 className, UserDefinedFunction.class.getName()); 1256 } 1257 validateFunction(udf); 1259 UserDefinedFunction existingUdf = mapNameToUdf.get(name); 1261 if (existingUdf != null) { 1262 throw MondrianResource.instance().UdfDuplicateName.ex(name); 1263 } 1264 mapNameToUdf.put(name, udf); 1265 } 1266 1267 1271 private void validateFunction(final UserDefinedFunction udf) { 1272 final String udfName = udf.getName(); 1274 if (udfName == null || udfName.equals("")) { 1275 throw Util.newInternal("User-defined function defined by class '" + 1276 udf.getClass() + "' has empty name"); 1277 } 1278 final String description = udf.getDescription(); 1280 Util.discard(description); 1281 final Type[] parameterTypes = udf.getParameterTypes(); 1282 for (int i = 0; i < parameterTypes.length; i++) { 1283 Type parameterType = parameterTypes[i]; 1284 if (parameterType == null) { 1285 throw Util.newInternal("Invalid user-defined function '" + 1286 udfName + "': parameter type #" + i + 1287 " is null"); 1288 } 1289 } 1290 final String [] reservedWords = udf.getReservedWords(); 1292 Util.discard(reservedWords); 1293 final Type returnType = udf.getReturnType(parameterTypes); 1297 if (returnType == null) { 1298 throw Util.newInternal("Invalid user-defined function '" + 1299 udfName + "': return type is null"); 1300 } 1301 final Syntax syntax = udf.getSyntax(); 1302 if (syntax == null) { 1303 throw Util.newInternal("Invalid user-defined function '" + 1304 udfName + "': syntax is null"); 1305 } 1306 } 1307 1308 1315 synchronized MemberReader createMemberReader( 1316 final String sharedName, 1317 final RolapHierarchy hierarchy, 1318 final String memberReaderClass) { 1319 1320 MemberReader reader; 1321 if (sharedName != null) { 1322 reader = mapSharedHierarchyToReader.get(sharedName); 1323 if (reader == null) { 1324 reader = createMemberReader(hierarchy, memberReaderClass); 1325 if (false) mapSharedHierarchyToReader.put(sharedName, reader); 1327 1340 if (! mapSharedHierarchyNameToHierarchy.containsKey(sharedName)) { 1341 mapSharedHierarchyNameToHierarchy.put(sharedName, hierarchy); 1342 } 1343 } else { 1345 } 1355 } else { 1356 reader = createMemberReader(hierarchy, memberReaderClass); 1357 } 1358 return reader; 1359 } 1360 1361 1364 private MemberReader createMemberReader( 1365 final RolapHierarchy hierarchy, 1366 final String memberReaderClass) { 1367 1368 if (memberReaderClass != null) { 1369 Exception e2; 1370 try { 1371 Properties properties = null; 1372 Class <?> clazz = Class.forName(memberReaderClass); 1373 Constructor <?> constructor = clazz.getConstructor( 1374 RolapHierarchy.class, 1375 Properties.class); 1376 Object o = constructor.newInstance(hierarchy, properties); 1377 if (o instanceof MemberReader) { 1378 return (MemberReader) o; 1379 } else if (o instanceof MemberSource) { 1380 return new CacheMemberReader((MemberSource) o); 1381 } else { 1382 throw Util.newInternal("member reader class " + clazz + 1383 " does not implement " + MemberSource.class); 1384 } 1385 } catch (ClassNotFoundException e) { 1386 e2 = e; 1387 } catch (NoSuchMethodException e) { 1388 e2 = e; 1389 } catch (InstantiationException e) { 1390 e2 = e; 1391 } catch (IllegalAccessException e) { 1392 e2 = e; 1393 } catch (InvocationTargetException e) { 1394 e2 = e; 1395 } 1396 throw Util.newInternal(e2, 1397 "while instantiating member reader '" + memberReaderClass); 1398 } else { 1399 SqlMemberSource source = new SqlMemberSource(hierarchy); 1400 1401 1406 1408 int memberCount; 1409 if (false) { 1410 memberCount = source.getMemberCount(); 1411 } else { 1412 memberCount = Integer.MAX_VALUE; 1413 } 1414 int largeDimensionThreshold = 1415 MondrianProperties.instance().LargeDimensionThreshold.get(); 1416 1417 return (memberCount > largeDimensionThreshold) 1418 ? new SmartMemberReader(source) 1419 : new CacheMemberReader(source); 1420 } 1421 } 1422 1423 public SchemaReader getSchemaReader() { 1424 return new RolapSchemaReader(defaultRole, this) { 1425 public Cube getCube() { 1426 throw new UnsupportedOperationException (); 1427 } 1428 }; 1429 } 1430 1431 1434 private DataSourceChangeListener createDataSourceChangeListener( 1435 Util.PropertyList connectInfo) { 1436 1437 DataSourceChangeListener changeListener = null; 1438 1439 String dataSourceChangeListenerStr = connectInfo.get( 1443 RolapConnectionProperties.DataSourceChangeListener.name()); 1444 1445 if ( ! Util.isEmpty(dataSourceChangeListenerStr)) { 1446 try { 1447 1448 Class <?> clazz = Class.forName(dataSourceChangeListenerStr); 1449 Constructor <?> constructor = clazz.getConstructor(); 1450 changeListener = (DataSourceChangeListener)constructor.newInstance(); 1451 1452 1457 1458 } catch (Exception e) { 1459 throw Util.newError(e, "loading DataSourceChangeListener " 1460 + dataSourceChangeListenerStr); 1461 } 1462 1463 if (LOGGER.isDebugEnabled()) { 1464 String msg = "RolapSchema.createDataSourceChangeListener: create datasource change listener \"" + 1465 dataSourceChangeListenerStr; 1466 1467 LOGGER.debug(msg); 1468 } 1469 } 1470 return changeListener; 1471 } 1472 1473 1474 1478 public RolapConnection getInternalConnection() { 1479 return internalConnection; 1480 } 1481 1482 private Role createDefaultRole() { 1483 Role role = new Role(); 1484 role.grant(this, Access.ALL); 1485 role.makeImmutable(); 1486 return role; 1487 } 1488 1489 private RolapStar makeRolapStar(final MondrianDef.Relation fact) { 1490 DataSource dataSource = getInternalConnection().getDataSource(); 1491 return new RolapStar(this, dataSource, fact); 1492 } 1493 1494 1497 class RolapStarRegistry { 1498 private final Map<String , RolapStar> stars = new HashMap<String , RolapStar>(); 1499 1500 RolapStarRegistry() { 1501 } 1502 1503 1508 synchronized RolapStar getOrCreateStar(final MondrianDef.Relation fact) { 1509 String factTableName = fact.toString(); 1510 RolapStar star = stars.get(factTableName); 1511 if (star == null) { 1512 star = makeRolapStar(fact); 1513 stars.put(factTableName, star); 1514 } 1515 return star; 1516 } 1517 synchronized RolapStar getStar(final String factTableName) { 1518 return stars.get(factTableName); 1519 } 1520 synchronized Collection<RolapStar> getStars() { 1521 return stars.values(); 1522 } 1523 } 1524 1525 private RolapStarRegistry rolapStarRegistry = new RolapStarRegistry(); 1526 1527 public RolapStarRegistry getRolapStarRegistry() { 1528 return rolapStarRegistry; 1529 } 1530 1531 1535 class RolapSchemaFunctionTable extends FunTableImpl { 1536 private final List<UserDefinedFunction> udfList; 1537 1538 RolapSchemaFunctionTable(Collection<UserDefinedFunction> udfs) { 1539 udfList = new ArrayList<UserDefinedFunction>(udfs); 1540 } 1541 1542 protected void defineFunctions() { 1543 final FunTable globalFunTable = GlobalFunTable.instance(); 1544 for (String reservedWord : globalFunTable.getReservedWords()) { 1545 defineReserved(reservedWord); 1546 } 1547 for (Resolver resolver : globalFunTable.getResolvers()) { 1548 define(resolver); 1549 } 1550 for (UserDefinedFunction udf : udfList) { 1551 define(new UdfResolver(udf)); 1552 } 1553 } 1554 1555 1556 public List<FunInfo> getFunInfoList() { 1557 return Collections.unmodifiableList(this.funInfoList); 1558 } 1559 } 1560 1561 public RolapStar getStar(final String factTableName) { 1562 return getRolapStarRegistry().getStar(factTableName); 1563 } 1564 1565 public Collection<RolapStar> getStars() { 1566 return getRolapStarRegistry().getStars(); 1567 } 1568 1569 1572 public void flushRolapStarCaches(boolean forced) { 1573 for (RolapStar star : getStars()) { 1574 star.clearCachedAggregations(forced); 1578 } 1579 } 1580 1583 public void checkAggregateModifications() { 1584 for (RolapStar star : getStars()) { 1585 star.checkAggregateModifications(); 1586 } 1587 } 1588 1592 public void pushAggregateModificationsToGlobalCache() { 1593 for (RolapStar star : getStars()) { 1594 star.pushAggregateModificationsToGlobalCache(); 1595 } 1596 } 1597 1598 1601 public static void flushAllRolapStarCachedAggregations() { 1602 for (Iterator<RolapSchema> itSchemas = RolapSchema.getRolapSchemas(); 1603 itSchemas.hasNext(); ) { 1604 1605 RolapSchema schema = itSchemas.next(); 1606 schema.flushRolapStarCaches(true); 1607 } 1608 } 1609 1610 final RolapNativeRegistry nativeRegistry = new RolapNativeRegistry(); 1611 1612 RolapNativeRegistry getNativeRegistry() { 1613 return nativeRegistry; 1614 } 1615 1616 1617 private int nextDimensionOrdinal = 1; 1619 public synchronized int getNextDimensionOrdinal() { 1620 return nextDimensionOrdinal++; 1621 } 1622 1623 1624 1627 public DataSourceChangeListener getDataSourceChangeListener() { 1628 return dataSourceChangeListener; 1629 } 1630 1631 1632 1635 public void setDataSourceChangeListener( 1636 DataSourceChangeListener dataSourceChangeListener) { 1637 this.dataSourceChangeListener = dataSourceChangeListener; 1638 } 1639 1640} 1641 1642 | Popular Tags |