1 16 17 package org.apache.cocoon.acting.modular; 18 19 import java.io.IOException ; 20 import java.sql.Connection ; 21 import java.sql.PreparedStatement ; 22 import java.sql.SQLException ; 23 import java.util.Map ; 24 25 import org.apache.avalon.excalibur.datasource.DataSourceComponent; 26 import org.apache.avalon.framework.activity.Disposable; 27 import org.apache.avalon.framework.configuration.Configurable; 28 import org.apache.avalon.framework.configuration.Configuration; 29 import org.apache.avalon.framework.configuration.ConfigurationException; 30 import org.apache.avalon.framework.parameters.Parameters; 31 import org.apache.avalon.framework.service.ServiceException; 32 import org.apache.avalon.framework.service.ServiceManager; 33 import org.apache.avalon.framework.service.ServiceSelector; 34 import org.apache.avalon.framework.thread.ThreadSafe; 35 36 import org.apache.cocoon.Constants; 37 import org.apache.cocoon.ProcessingException; 38 import org.apache.cocoon.acting.AbstractComplementaryConfigurableAction; 39 import org.apache.cocoon.components.modules.database.AutoIncrementModule; 40 import org.apache.cocoon.components.modules.input.InputModule; 41 import org.apache.cocoon.components.modules.output.OutputModule; 42 import org.apache.cocoon.environment.Redirector; 43 import org.apache.cocoon.environment.SourceResolver; 44 import org.apache.cocoon.util.HashMap; 45 import org.apache.cocoon.util.JDBCTypeConversions; 46 import org.apache.commons.lang.BooleanUtils; 47 48 96 public abstract class DatabaseAction extends AbstractComplementaryConfigurableAction implements Configurable, Disposable, ThreadSafe { 97 98 102 static final Integer MODE_AUTOINCR = new Integer (0); 103 static final Integer MODE_OTHERS = new Integer (1); 104 static final Integer MODE_OUTPUT = new Integer (2); 105 106 static final String ATTRIBUTE_KEY = "org.apache.cocoon.action.modular.DatabaseAction.outputModeName"; 107 108 static final String inputHint = "request-param"; static final String outputHint = "request-attr"; static final String databaseHint = "manual"; 113 static final String INPUT_MODULE_SELECTOR = InputModule.ROLE + "Selector"; 114 static final String OUTPUT_MODULE_SELECTOR = OutputModule.ROLE + "Selector"; 115 static final String DATABASE_MODULE_SELECTOR = AutoIncrementModule.ROLE + "Selector"; 116 117 118 122 protected ServiceSelector dbselector; 123 protected Map defaultModeNames = new HashMap( 3 ); 124 protected final HashMap cachedQueryData = new HashMap(); 125 protected String pathSeparator = "."; 126 protected int firstRow = 0; 127 protected boolean failOnEmpty = true; 128 129 133 136 protected static class Column { 137 boolean isKey = false; 138 boolean isSet = false; 139 boolean isAutoIncrement = false; 140 String mode = null; 141 Configuration modeConf = null; 142 Configuration columnConf = null; 143 } 144 145 149 protected static class CacheHelper { 150 153 public String queryString = null; 154 158 public int setMaster = -1; 159 public boolean isSet = false; 160 public int noOfKeys = 0; 161 public Column[] columns = null; 162 163 public CacheHelper( int cols ) { 164 this(0,cols); 165 } 166 167 public CacheHelper( int keys, int cols ) { 168 noOfKeys = keys; 169 columns = new Column[cols]; 170 for ( int i=0; i<cols; i++ ) { 171 columns[i] = new Column(); 172 } 173 } 174 } 175 176 181 protected static class LookUpKey { 182 public Configuration tableConf = null; 183 public Map modeTypes = null; 184 185 public LookUpKey( Configuration tableConf, Map modeTypes ) { 186 this.tableConf = tableConf; 187 this.modeTypes = modeTypes; 188 } 189 190 193 public boolean equals(Object obj) { 194 boolean result = false; 195 if (obj != null && obj instanceof LookUpKey) { 196 LookUpKey luk = (LookUpKey) obj; 197 result = true; 198 result = result && (luk.tableConf == null ? 199 this.tableConf == null : luk.tableConf.equals(this.tableConf)); 200 result = result && (luk.modeTypes == null ? 201 this.modeTypes == null : luk.modeTypes.equals(this.modeTypes)); 202 } 203 204 return result; 205 } 206 207 210 public int hashCode() { 211 return (this.tableConf != null ? 212 this.tableConf.hashCode() : 213 (this.modeTypes != null ? this.modeTypes.hashCode() : super.hashCode())); 214 } 215 } 216 217 public void configure(Configuration conf) throws ConfigurationException { 225 super.configure(conf); 226 if (this.settings != null) { 227 this.defaultModeNames.put(MODE_OTHERS, this.settings.get("input", inputHint)); 228 this.defaultModeNames.put(MODE_OUTPUT, this.settings.get("output", outputHint)); 229 this.defaultModeNames.put(MODE_AUTOINCR, this.settings.get("autoincrement", databaseHint)); 230 this.pathSeparator = (String )this.settings.get("path-separator", this.pathSeparator); 231 String tmp = (String )this.settings.get("first-row",null); 232 if (tmp != null) { 233 try { 234 this.firstRow = Integer.parseInt(tmp); 235 } catch (NumberFormatException nfe) { 236 if (getLogger().isWarnEnabled()) 237 getLogger().warn("problem parsing first row option "+tmp+" using default instead."); 238 } 239 } 240 tmp = (String ) this.settings.get("fail-on-empty",String.valueOf(this.failOnEmpty)); 241 this.failOnEmpty = BooleanUtils.toBoolean(tmp); 242 } 243 } 244 245 249 252 public void service(ServiceManager manager) throws ServiceException { 253 super.service(manager); 254 this.dbselector = (ServiceSelector) manager.lookup(DataSourceComponent.ROLE + "Selector"); 255 } 256 257 260 public void dispose() { 261 this.manager.release(dbselector); 262 } 263 264 268 271 protected DataSourceComponent getDataSource( Configuration conf, Parameters parameters ) 272 throws ServiceException { 273 274 String sourceName = parameters.getParameter( "connection", (String ) settings.get( "connection" ) ); 275 if ( sourceName == null ) { 276 Configuration dsn = conf.getChild("connection"); 277 return (DataSourceComponent) this.dbselector.select(dsn.getValue("")); 278 } else { 279 if (getLogger().isDebugEnabled()) 280 getLogger().debug("Using datasource: "+sourceName); 281 return (DataSourceComponent) this.dbselector.select(sourceName); 282 } 283 } 284 285 288 protected final boolean isLargeObject (String type) { 289 if ("ascii".equals(type)) return true; 290 if ("binary".equals(type)) return true; 291 if ("image".equals(type)) return true; 292 return false; 293 } 294 295 299 protected void setOutputAttribute(Map objectModel, String outputMode, String key, Object value) { 300 301 ServiceSelector outputSelector = null; 302 OutputModule output = null; 303 try { 304 outputSelector = (ServiceSelector) this.manager.lookup(OUTPUT_MODULE_SELECTOR); 305 if (outputMode != null && outputSelector != null && outputSelector.isSelectable(outputMode)) { 306 output = (OutputModule) outputSelector.select(outputMode); 307 } 308 if (output != null) { 309 output.setAttribute(null, objectModel, key, value); 310 } else if (getLogger().isWarnEnabled()) { 311 getLogger().warn("Could not select output mode " + outputMode); 312 } 313 } catch (Exception e) { 314 if (getLogger().isWarnEnabled()) { 315 getLogger().warn("Could not select output mode " + outputMode + ":" + e.getMessage()); 316 } 317 } finally { 318 if (outputSelector != null) { 319 if (output != null) 320 outputSelector.release(output); 321 this.manager.release(outputSelector); 322 } 323 } 324 } 325 326 334 protected int processTable( Configuration table, Connection conn, Map objectModel, 335 Map results, Map modeTypes ) 336 throws SQLException , ConfigurationException, Exception { 337 338 PreparedStatement statement = null; 339 int rows = 0; 340 try { 341 LookUpKey luk = new LookUpKey(table, modeTypes); 342 CacheHelper queryData = null; 343 344 if (getLogger().isDebugEnabled()) 345 getLogger().debug("modeTypes : "+ modeTypes); 346 347 synchronized (this.cachedQueryData) { 352 queryData = (CacheHelper) this.cachedQueryData.get(luk,null); 353 if (queryData == null) { 354 queryData = this.getQuery( table, modeTypes, defaultModeNames ); 355 this.cachedQueryData.put(luk,queryData); 356 } 357 } 358 359 if (getLogger().isDebugEnabled()) 360 getLogger().debug("query: "+queryData.queryString); 361 statement = conn.prepareStatement(queryData.queryString); 362 363 Object [][] columnValues = this.getColumnValues( table, queryData, objectModel ); 364 365 int setLength = 1; 366 if ( queryData.isSet ) { 367 if ( columnValues[ queryData.setMaster ] != null ) { 368 setLength = columnValues[ queryData.setMaster ].length; 369 } else { 370 setLength = 0; 371 } 372 } 373 374 for ( int rowIndex = 0; rowIndex < setLength; rowIndex++ ) { 375 if (getLogger().isDebugEnabled()) { 376 getLogger().debug( "====> row no. " + rowIndex ); 377 } 378 rows += processRow( objectModel, conn, statement, (String ) modeTypes.get(MODE_OUTPUT), table, queryData, columnValues, rowIndex, results ); 379 } 380 } finally { 381 try { 382 if (statement != null) { 383 statement.close(); 384 } 385 } catch (SQLException e) {} 386 } 387 return rows; 388 } 389 390 399 protected Configuration getMode( Configuration conf, String type ) 400 throws ConfigurationException { 401 402 String modeAll = "all"; 403 Configuration[] modes = conf.getChildren("mode"); 404 Configuration modeConfig = null; 405 406 for ( int i=0; i<modes.length; i++ ) { 407 String modeType = modes[i].getAttribute("type", "others"); 408 if ( modeType.equals(type) || modeType.equals(modeAll)) { 409 if (getLogger().isDebugEnabled()) 410 getLogger().debug("requested mode was \""+type+"\" returning \""+modeType+"\""); 411 modeConfig = modes[i]; 412 break; 413 } 414 } 415 return modeConfig; 416 } 417 418 421 protected String getOutputName ( Configuration tableConf, Configuration columnConf ) { 422 423 return getOutputName( tableConf, columnConf, -1 ); 424 } 425 426 436 protected String getOutputName ( Configuration tableConf, Configuration columnConf, int rowIndex ) { 437 438 if ( rowIndex != -1 && this.settings.containsKey("append-row") && 439 (this.settings.get("append-row").toString().equalsIgnoreCase("false") || 440 this.settings.get("append-row").toString().equalsIgnoreCase("0")) ) { 441 rowIndex = -1; 442 } else { 443 rowIndex = rowIndex + this.firstRow; 444 } 445 if ( this.settings.containsKey("append-table-name") && 446 (this.settings.get("append-table-name").toString().equalsIgnoreCase("false") || 447 this.settings.get("append-table-name").toString().equalsIgnoreCase("0")) ) 448 { 449 return ( columnConf.getAttribute("name",null) 450 + ( rowIndex == -1 ? "" : "[" + rowIndex + "]" ) ); 451 } else { 452 return ( tableConf.getAttribute("alias", tableConf.getAttribute("name", null) ) 453 + this.pathSeparator + columnConf.getAttribute("name",null) 454 + ( rowIndex == -1 ? "" : "[" + rowIndex + "]" ) ); 455 } 456 } 457 458 467 protected Object [] getColumnValue(Configuration tableConf, Column column, Map objectModel) 468 throws ConfigurationException, ServiceException { 469 470 if (column.isAutoIncrement) { 471 return new Object [1]; 472 } else { 473 Object [] values; 474 String cname = getOutputName( tableConf, column.columnConf ); 475 476 ServiceSelector inputSelector = null; 478 InputModule input = null; 479 try { 480 inputSelector = (ServiceSelector) this.manager.lookup(INPUT_MODULE_SELECTOR); 481 if (column.mode != null && inputSelector != null && inputSelector.isSelectable(column.mode)){ 482 input = (InputModule) inputSelector.select(column.mode); 483 } 484 485 if (column.isSet) { 486 if (getLogger().isDebugEnabled()) { 487 getLogger().debug( "Trying to set column " + cname + " from " + column.mode + " using getAttributeValues method"); 488 } 489 values = input.getAttributeValues( cname, column.modeConf, objectModel ); 490 } else { 491 if (getLogger().isDebugEnabled()) { 492 getLogger().debug( "Trying to set column " + cname + " from " + column.mode + " using getAttribute method"); 493 } 494 values = new Object [1]; 495 values[0] = input.getAttribute( cname, column.modeConf, objectModel ); 496 } 497 498 if (values != null) { 499 for ( int i = 0; i < values.length; i++ ) { 500 if (getLogger().isDebugEnabled()) { 501 getLogger().debug( "Setting column " + cname + " [" + i + "] " + values[i] ); 502 } 503 } 504 } 505 } finally { 506 if (inputSelector != null) { 507 if (input != null) { 508 inputSelector.release(input); 509 } 510 this.manager.release(inputSelector); 511 } 512 } 513 return values; 514 } 515 } 516 517 520 protected void fillModes ( Configuration[] conf, boolean isKey, Map defaultModeNames, 521 Map modeTypes, CacheHelper set ) 522 throws ConfigurationException { 523 524 String setMode = null; 525 int offset = (isKey ? 0: set.noOfKeys); 526 527 for (int i = offset; i < conf.length + offset; i++) { 528 if (getLogger().isDebugEnabled()) { 529 getLogger().debug("i=" + i); 530 } 531 set.columns[i].columnConf = conf[ i - offset ]; 532 set.columns[i].isSet = false; 533 set.columns[i].isKey = isKey; 534 set.columns[i].isAutoIncrement = false; 535 if (isKey & this.honourAutoIncrement()) { 536 set.columns[i].isAutoIncrement = set.columns[i].columnConf.getAttributeAsBoolean("autoincrement",false); 537 } 538 set.columns[i].modeConf = getMode(set.columns[i].columnConf, 539 selectMode(set.columns[i].isAutoIncrement, modeTypes)); 540 set.columns[i].mode = (set.columns[i].modeConf != null ? 541 set.columns[i].modeConf.getAttribute("name", selectMode(isKey, defaultModeNames)) : 542 selectMode(isKey, defaultModeNames)); 543 setMode = set.columns[i].columnConf.getAttribute("set", null); if (setMode == null && set.columns[i].modeConf != null) { 546 setMode = set.columns[i].modeConf.getAttribute("set", null); 548 } 549 if (setMode != null) { 550 set.columns[i].isSet = true; 551 set.isSet = true; 552 if (setMode.equals("master")) { 553 set.setMaster = i; 554 } 555 } 556 } 557 } 558 559 564 protected void setOutput( Map objectModel, String outputMode, Map results, 565 Configuration table, Configuration column, int rowIndex, Object value ) { 566 567 String param = this.getOutputName( table, column, rowIndex ); 568 if (getLogger().isDebugEnabled()) { 569 getLogger().debug( "Setting column " + param + " to " + value ); 570 } 571 this.setOutputAttribute(objectModel, outputMode, param, value); 572 if (results != null) { 573 results.put( param, String.valueOf( value ) ); 574 } 575 } 576 577 581 protected void setColumn (PreparedStatement statement, int position, Configuration entry, Object value) throws Exception { 582 JDBCTypeConversions.setColumn(statement, position, value, 583 (Integer )JDBCTypeConversions.typeConstants.get(entry.getAttribute("type"))); 584 } 585 586 593 protected void setColumn (Map objectModel, String outputMode, Map results, 594 Configuration table, Configuration column, int rowIndex, 595 Object value, PreparedStatement statement, int position) throws Exception { 596 597 if (results != null) { 598 this.setOutput(objectModel, outputMode, results, table, column, rowIndex, value); 599 } 600 this.setColumn( statement, position, column, value ); 601 } 602 603 607 612 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, 613 String source, Parameters param) throws Exception { 614 615 DataSourceComponent datasource = null; 616 Connection conn = null; 617 Map results = new HashMap(); 618 int rows = 0; 619 boolean failed = false; 620 621 boolean reloadable = Constants.DESCRIPTOR_RELOADABLE_DEFAULT; 623 624 String outputMode = param.getParameter("output", (String ) defaultModeNames.get(MODE_OUTPUT)); 627 628 if (this.settings.containsKey("reloadable")) { 629 reloadable = Boolean.valueOf((String ) this.settings.get("reloadable")).booleanValue(); 630 } 631 try { 633 Configuration conf = 634 this.getConfiguration(param.getParameter("descriptor", (String ) this.settings.get("descriptor")), 635 resolver, 636 param.getParameterAsBoolean("reloadable",reloadable)); 637 638 datasource = this.getDataSource(conf, param); 640 conn = datasource.getConnection(); 641 if (conn.getAutoCommit() == true) { 642 try { 643 conn.setAutoCommit(false); 644 } catch (Exception ex) { 645 String tmp = param.getParameter("use-transactions",(String ) this.settings.get("use-transactions",null)); 646 if (tmp != null && (tmp.equalsIgnoreCase("no") || tmp.equalsIgnoreCase("false") || tmp.equalsIgnoreCase("0"))) { 647 if (getLogger().isErrorEnabled()) 648 getLogger().error("This DB connection does not support transactions. If you want to risk your data's integrity by continuing nonetheless set parameter \"use-transactions\" to \"no\"."); 649 throw ex; 650 } 651 } 652 } 653 654 Configuration[] tables = conf.getChildren("table"); 656 String tablesetname = param.getParameter("table-set", (String ) this.settings.get("table-set")); 657 658 Map modeTypes = null; 659 660 if (tablesetname == null) { 661 modeTypes = new HashMap(6); 662 modeTypes.put( MODE_AUTOINCR, "autoincr" ); 663 modeTypes.put( MODE_OTHERS, "others" ); 664 modeTypes.put( MODE_OUTPUT, outputMode ); 665 for (int i = 0; i < tables.length; i++) { 666 rows += processTable(tables[i], conn, objectModel, results, modeTypes); 667 } 668 } else { 669 671 Map tableIndex = new HashMap(2*tables.length); 673 String tableName = null; 674 Object result = null; 675 for (int i=0; i<tables.length; i++) { 676 tableName = tables[i].getAttribute("alias",tables[i].getAttribute("name","")); 677 result = tableIndex.put(tableName,new Integer (i)); 678 if (result != null) { 679 throw new IOException ("Duplicate table entry for "+tableName+" at positions "+result+" and "+i); 680 } 681 } 682 683 Configuration[] tablesets = conf.getChildren("table-set"); 684 String setname = null; 685 boolean found = false; 686 687 int j = 0; 689 for (j = 0; j < tablesets.length; j++) { 690 setname = tablesets[j].getAttribute ("name", ""); 691 if (tablesetname.trim().equals (setname.trim ())) { 692 found = true; 693 break; 694 } 695 } 696 if (!found) { 697 throw new IOException (" given set " + tablesetname + " does not exists in a description file."); 698 } 699 700 Configuration[] set = tablesets[j].getChildren("table"); 701 702 for (int i = 0; i < set.length; i++) { 703 modeTypes = new HashMap(6); 705 modeTypes.put( MODE_AUTOINCR, set[i].getAttribute( "autoincr-mode", "autoincr" ) ); 706 modeTypes.put( MODE_OTHERS, set[i].getAttribute( "others-mode", "others" ) ); 707 modeTypes.put( MODE_OUTPUT, outputMode ); 708 tableName=set[i].getAttribute("name",""); 709 if (tableIndex.containsKey(tableName)) { 710 j = ((Integer )tableIndex.get(tableName)).intValue(); 711 rows += processTable( tables[j], conn, objectModel, results, modeTypes ); 712 } else { 713 throw new IOException (" given table " + tableName + " does not exists in a description file."); 714 } 715 } 716 } 717 718 if (conn.getAutoCommit() == false) { 719 conn.commit(); 720 } 721 722 ServiceSelector outputSelector = null; 724 OutputModule output = null; 725 try { 726 outputSelector = (ServiceSelector) this.manager.lookup(OUTPUT_MODULE_SELECTOR); 727 if (outputMode != null && outputSelector != null && outputSelector.isSelectable(outputMode)){ 728 output = (OutputModule) outputSelector.select(outputMode); 729 } 730 if (output != null) { 731 output.commit(null, objectModel); 732 } else if (getLogger().isWarnEnabled()) { 733 getLogger().warn("Could not select output mode " + outputMode); 734 } 735 } catch (ServiceException e) { 736 if (getLogger().isWarnEnabled()) { 737 getLogger().warn("Could not select output mode " + outputMode + ":" + e.getMessage()); 738 } 739 } finally { 740 if (outputSelector != null) { 741 if (output != null) { 742 outputSelector.release(output); 743 } 744 this.manager.release(outputSelector); 745 } 746 } 747 } catch (Exception e) { 748 failed = true; 749 if ( conn != null ) { 750 try { 751 if (getLogger().isDebugEnabled()) { 752 getLogger().debug( "Rolling back transaction. Caused by " + e.getMessage() ); 753 e.printStackTrace(); 754 } 755 conn.rollback(); 756 results = null; 757 758 ServiceSelector outputSelector = null; 760 OutputModule output = null; 761 try { 762 outputSelector = (ServiceSelector) this.manager.lookup(OUTPUT_MODULE_SELECTOR); 763 if (outputMode != null && outputSelector != null && outputSelector.isSelectable(outputMode)){ 764 output = (OutputModule) outputSelector.select(outputMode); 765 } 766 if (output != null) { 767 output.rollback( null, objectModel, e); 768 } else if (getLogger().isWarnEnabled()) { 769 getLogger().warn("Could not select output mode " + outputMode); 770 } 771 } catch (ServiceException e2) { 772 if (getLogger().isWarnEnabled()) { 773 getLogger().warn("Could not select output mode " + outputMode + ":" + e2.getMessage()); 774 } 775 } finally { 776 if (outputSelector != null) { 777 if (output != null) { 778 outputSelector.release(output); 779 } 780 this.manager.release(outputSelector); 781 } 782 } 783 } catch (SQLException se) { 784 if (getLogger().isDebugEnabled()) 785 getLogger().debug("There was an error rolling back the transaction", se); 786 } 787 } 788 789 791 793 String throwException = (String ) this.settings.get( "throw-exception", 794 param.getParameter( "throw-exception", null ) ); 795 if ( throwException != null && BooleanUtils.toBoolean(throwException)) { 796 throw new ProcessingException("Cannot process the requested SQL statement ",e); 797 } 798 } finally { 799 if (conn != null) { 800 try { 801 conn.close(); 802 } catch (SQLException sqe) { 803 getLogger().warn("There was an error closing the datasource", sqe); 804 } 805 } 806 807 if (datasource != null) 808 this.dbselector.release(datasource); 809 } 810 if (results != null) { 811 if (rows>0 || (!failed && !this.failOnEmpty)) { 812 results.put("row-count",new Integer (rows)); 813 } else { 814 results = null; 815 } 816 } else { 817 if (rows>0) { 818 results = new HashMap(1); 819 results.put("row-count",new Integer (rows)); 820 } 821 } 822 823 return results; } 825 826 830 837 protected abstract int processRow( Map objectModel, Connection conn, PreparedStatement statement, String outputMode, 838 Configuration table, CacheHelper queryData, Object [][] columnValues, 839 int rowIndex, Map results ) 840 throws SQLException , ConfigurationException, Exception ; 841 842 848 protected abstract String selectMode( boolean isAutoIncrement, Map modes ); 849 850 857 protected abstract boolean honourAutoIncrement(); 858 859 866 abstract Object [][] getColumnValues( Configuration tableConf, CacheHelper queryData, Map objectModel ) 867 throws ConfigurationException, ServiceException; 868 869 880 protected abstract CacheHelper getQuery( Configuration table, Map modeTypes, Map defaultModeNames ) 881 throws ConfigurationException, ServiceException; 882 883 } 884 | Popular Tags |