0001: /*
0002: * Copyright 2001-2006 C:1 Financial Services GmbH
0003: *
0004: * This software is free software; you can redistribute it and/or
0005: * modify it under the terms of the GNU Lesser General Public
0006: * License Version 2.1, as published by the Free Software Foundation.
0007: *
0008: * This software is distributed in the hope that it will be useful,
0009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0011: * Lesser General Public License for more details.
0012: *
0013: * You should have received a copy of the GNU Lesser General Public
0014: * License along with this library; if not, write to the Free Software
0015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
0016: */
0017:
0018: package de.finix.contelligent.persistence;
0019:
0020: import java.sql.Connection;
0021: import java.sql.PreparedStatement;
0022: import java.sql.ResultSet;
0023: import java.sql.SQLException;
0024: import java.util.HashMap;
0025: import java.util.HashSet;
0026: import java.util.Iterator;
0027: import java.util.Map;
0028: import java.util.Set;
0029:
0030: import javax.sql.DataSource;
0031:
0032: import de.finix.contelligent.ComponentPath;
0033: import de.finix.contelligent.GlobalComponentPath;
0034: import de.finix.contelligent.exception.CircularRelationsException;
0035: import de.finix.contelligent.exception.ComponentPersistenceException;
0036: import de.finix.contelligent.exception.ComponentReadException;
0037: import de.finix.contelligent.exception.ComponentWriteException;
0038: import de.finix.contelligent.logging.LoggingService;
0039: import de.finix.contelligent.util.StringUtils;
0040:
0041: public class RelationsGenericDBAdapter implements
0042: RelationsPersistenceAdapter {
0043: final static org.apache.log4j.Logger log = LoggingService
0044: .getLogger(RelationsGenericDBAdapter.class);
0045:
0046: final private DataSource dataSource;
0047:
0048: final private String name;
0049:
0050: final private String concatOperator;
0051:
0052: public RelationsGenericDBAdapter(String name, DataSource ds)
0053: throws ComponentPersistenceException {
0054: this .name = name;
0055: this .dataSource = ds;
0056: try {
0057: concatOperator = DBDetector.getInstance()
0058: .getConcatOperator(ds);
0059: } catch (SQLException e) {
0060: log
0061: .error("'"
0062: + name
0063: + "'<init> - SQLException while fetching database type from DBDetector: "
0064: + e);
0065: throw new ComponentPersistenceException(e);
0066: }
0067: }
0068:
0069: /**
0070: * Implementation of {@link PersistenceAdapter#destroy}. Drops all tables
0071: * and views this adapter has created.
0072: */
0073: public void destroy() {
0074: }
0075:
0076: public String toString() {
0077: return ("[RelationsGenericDBAdapter[" + name + "]");
0078: }
0079:
0080: public Set getTreeRelations(ComponentPath sourceTree, long sourceCM)
0081: throws ComponentPersistenceException {
0082: Connection con = null;
0083: PreparedStatement pstmt = null;
0084: ResultSet rs = null;
0085: String currentStatement = null;
0086:
0087: try {
0088: con = PersistenceUtils.getConnection(dataSource);
0089: final String sourcePath = sourceTree.getPathWithEndToken()
0090: + "%";
0091:
0092: currentStatement = ("SELECT targetPath FROM "
0093: + TableName.RELATIONS + " WHERE sourcePath like ?" + (sourceCM == Long.MIN_VALUE ? ""
0094: : " AND (sourceCM=? OR sourceCM=?)"));
0095:
0096: pstmt = con.prepareStatement(currentStatement);
0097: pstmt.setString(1, sourcePath);
0098: if (sourceCM > Long.MIN_VALUE) {
0099: pstmt.setLong(2, ComponentPersistenceAdapter.ROOT_ID);
0100: pstmt.setLong(3, sourceCM);
0101: }
0102:
0103: if (log.isDebugEnabled()) {
0104: log
0105: .debug("'"
0106: + this .name
0107: + "':getTreeRelations() - loading relations of component tree with root: '"
0108: + sourceTree
0109: + "' (manager "
0110: + (sourceCM == Long.MIN_VALUE ? "ANY"
0111: : String.valueOf(sourceCM))
0112: + ") ...");
0113: }
0114: final Set relations = new HashSet();
0115: rs = pstmt.executeQuery();
0116: while (rs.next()) {
0117: ComponentPath targetPath = new ComponentPath(rs
0118: .getString("targetPath"));
0119: relations.add(targetPath);
0120: }
0121: if (log.isDebugEnabled()) {
0122: log.debug("'" + this .name
0123: + "':getTreeRelations() - component '"
0124: + sourceTree + "' has referents: " + relations);
0125: }
0126: return relations;
0127: } catch (SQLException e) {
0128: log
0129: .error("'"
0130: + this .name
0131: + "':getTreeRelations() - SQLException while fetching relations of tree '"
0132: + sourceTree + " using statement ["
0133: + currentStatement + "]: " + e);
0134: throw new ComponentReadException(
0135: "[RelationsGenericDBAdapter '"
0136: + this .name
0137: + "']:getTreeRelations() - SQLException while loading relations of tree '"
0138: + sourceTree + "' ", e);
0139: } finally {
0140: PersistenceUtils.closeResources(con, pstmt, rs);
0141: }
0142:
0143: }
0144:
0145: public Set getTreeRelationSources(ComponentPath sourceTree,
0146: long sourceCM) throws ComponentPersistenceException {
0147: Connection con = null;
0148: PreparedStatement pstmt = null;
0149: ResultSet rs = null;
0150: String currentStatement = null;
0151:
0152: try {
0153: con = PersistenceUtils.getConnection(dataSource);
0154: final String baseSourcePath = sourceTree
0155: .getPathWithEndToken()
0156: + "%";
0157:
0158: currentStatement = ("SELECT sourcePath FROM "
0159: + TableName.RELATIONS + " WHERE sourcePath like ?" + (sourceCM == Long.MIN_VALUE ? ""
0160: : " AND (sourceCM=? OR sourceCM=?)"));
0161:
0162: pstmt = con.prepareStatement(currentStatement);
0163: pstmt.setString(1, baseSourcePath);
0164: if (sourceCM > Long.MIN_VALUE) {
0165: pstmt.setLong(2, ComponentPersistenceAdapter.ROOT_ID);
0166: pstmt.setLong(3, sourceCM);
0167: }
0168:
0169: if (log.isDebugEnabled()) {
0170: log
0171: .debug("'"
0172: + this .name
0173: + "':getTreeRelationSources() - loading relations of component tree with root: '"
0174: + sourceTree
0175: + "' (manager "
0176: + (sourceCM == Long.MIN_VALUE ? "ANY"
0177: : String.valueOf(sourceCM))
0178: + ") ...");
0179: }
0180: final Set relations = new HashSet();
0181: rs = pstmt.executeQuery();
0182: while (rs.next()) {
0183: ComponentPath sourcePath = new ComponentPath(rs
0184: .getString("sourcePath"));
0185: relations.add(sourcePath);
0186: }
0187: if (log.isDebugEnabled()) {
0188: log.debug("'" + this .name
0189: + "':getTreeRelationSources() - component '"
0190: + sourceTree + "' has referents: " + relations);
0191: }
0192: return relations;
0193: } catch (SQLException e) {
0194: log
0195: .error("'"
0196: + this .name
0197: + "':getTreeRelationSources() - SQLException while fetching relations of tree '"
0198: + sourceTree + " using statement ["
0199: + currentStatement + "]: " + e);
0200: throw new ComponentReadException(
0201: "[RelationsGenericDBAdapter '"
0202: + this .name
0203: + "']:getTreeRelationSources() - SQLException while loading relations of tree '"
0204: + sourceTree + "' ", e);
0205: } finally {
0206: PersistenceUtils.closeResources(con, pstmt, rs);
0207: }
0208: }
0209:
0210: /**
0211: * Implementation of {@link RelationsPersistenceAdapter#getPathsOfReferents}.
0212: * Note that relations to a category using '//' are currently NOT SUPPORTED!
0213: *
0214: * @see RelationsPersistenceAdapter#getPathsOfReferents
0215: */
0216: public Set getPathsOfReferents(ComponentPath source, long sourceCM)
0217: throws ComponentPersistenceException {
0218: Connection con = null;
0219: PreparedStatement pstmt = null;
0220: ResultSet rs = null;
0221: String currentStatement = null;
0222:
0223: try {
0224: con = PersistenceUtils.getConnection(dataSource);
0225: final String sourcePath = source.getPathWithEndToken();
0226: currentStatement = ("SELECT targetPath FROM "
0227: + TableName.RELATIONS + " WHERE sourcePath=?");
0228: if (sourceCM != Long.MIN_VALUE)
0229: currentStatement += " AND sourceCM=?";
0230:
0231: pstmt = con.prepareStatement(currentStatement);
0232: pstmt.setString(1, sourcePath);
0233: if (sourceCM != Long.MIN_VALUE)
0234: pstmt.setLong(2, sourceCM);
0235:
0236: if (log.isDebugEnabled()) {
0237: log
0238: .debug("'"
0239: + this .name
0240: + "':getPathsOfReferents() - loading referents of component '"
0241: + sourcePath
0242: + "' (manager "
0243: + (sourceCM == Long.MIN_VALUE ? "ANY"
0244: : String.valueOf(sourceCM))
0245: + ") ...");
0246: }
0247: final Set referents = new HashSet();
0248: rs = pstmt.executeQuery();
0249: while (rs.next()) {
0250: ComponentPath targetPath = new ComponentPath(rs
0251: .getString("targetPath"));
0252: referents.add(targetPath);
0253: }
0254: if (log.isDebugEnabled()) {
0255: log.debug("'" + this .name
0256: + "':getPathsOfReferents() - component '"
0257: + source + "' has referents: " + referents);
0258: }
0259: return referents;
0260: } catch (SQLException e) {
0261: log
0262: .error("'"
0263: + this .name
0264: + "':getPathsOfReferents() - SQLException while fetching referents of component '"
0265: + source + " using statement ["
0266: + currentStatement + "]: " + e);
0267: throw new ComponentReadException(
0268: "[RelationsGenericDBAdapter '"
0269: + this .name
0270: + "']:getPathsOfReferents() - SQLException while loading referents of component '"
0271: + source + "' ", e);
0272: } finally {
0273: PersistenceUtils.closeResources(con, pstmt, rs);
0274: }
0275: }
0276:
0277: /**
0278: * Implementation of {@link RelationsPersistenceAdapter#getPathsRelatedTo}.
0279: *
0280: * @see RelationsPersistenceAdapter#getPathsRelatedTo
0281: */
0282: public Set getPathsRelatedTo(ComponentPath target)
0283: throws ComponentPersistenceException {
0284: return getPathsRelatedTo(target, false);
0285: }
0286:
0287: /**
0288: * Implementation of
0289: * {@link RelationsPersistenceAdapter#getGlobalPathsRelatedTo}.
0290: *
0291: * @see RelationsPersistenceAdapter#getGlobalPathsRelatedTo
0292: */
0293: public Set getGlobalPathsRelatedTo(ComponentPath target)
0294: throws ComponentPersistenceException {
0295: return getPathsRelatedTo(target, true);
0296: }
0297:
0298: private Set getPathsRelatedTo(ComponentPath target,
0299: boolean createGlobalPaths)
0300: throws ComponentPersistenceException {
0301: Connection con = null;
0302: PreparedStatement pstmt = null;
0303: ResultSet rs = null;
0304: String currentStatement = null;
0305: try {
0306: if (log.isDebugEnabled()) {
0307: log
0308: .debug("'"
0309: + this .name
0310: + "':getPathsRelatedTo() - loading paths refering to component '"
0311: + target + "' ...");
0312: }
0313: final String targetPath = target.getPathWithEndToken();
0314: currentStatement = ("SELECT sourcePath, sourceCM FROM "
0315: + TableName.RELATIONS + " WHERE targetPath=?");
0316: con = PersistenceUtils.getConnection(dataSource);
0317: pstmt = con.prepareStatement(currentStatement);
0318: pstmt.setString(1, targetPath);
0319:
0320: final Set relatedPaths = new HashSet();
0321: rs = pstmt.executeQuery();
0322: while (rs.next()) {
0323: String sourcePath = rs.getString("sourcePath");
0324: long sourceCM = rs.getLong("sourceCM");
0325: ComponentPath source = (createGlobalPaths ? new GlobalComponentPath(
0326: sourcePath, sourceCM)
0327: : new ComponentPath(sourcePath));
0328: relatedPaths.add(source);
0329: }
0330: if (log.isDebugEnabled()) {
0331: log.debug("'" + this .name
0332: + "':getPathsRelatedTo() - component '"
0333: + target + "' is referred by: " + relatedPaths);
0334: }
0335: return relatedPaths;
0336: } catch (SQLException e) {
0337: log
0338: .error("'"
0339: + this .name
0340: + "':getPathsRelatedTo() - SQLException while loading paths refering to component '"
0341: + target + "' using statement ["
0342: + currentStatement + "]: " + e);
0343: throw new ComponentReadException(
0344: "[RelationsGenericDBAdapter '"
0345: + this .name
0346: + "']:getPathsRelatedTo() - SQLException while loading paths refering to '"
0347: + target + "' ", e);
0348: } finally {
0349: PersistenceUtils.closeResources(con, pstmt, rs);
0350: }
0351: }
0352:
0353: /**
0354: * Implementation of
0355: * {@link RelationsPersistenceAdapter#getPathsRelatedToTree}.
0356: *
0357: * @see RelationsPersistenceAdapter#getPathsRelatedTo
0358: */
0359: public Map getPathsRelatedToTree(ComponentPath targetTree,
0360: boolean includeSelf) throws ComponentPersistenceException {
0361: return getPathsRelatedToTree(targetTree, includeSelf, false);
0362: }
0363:
0364: /**
0365: * Implementation of
0366: * {@link RelationsPersistenceAdapter#getGlobalPathsRelatedToTree}.
0367: *
0368: * @see RelationsPersistenceAdapter#getPathsRelatedTo
0369: */
0370: public Map getGlobalPathsRelatedToTree(ComponentPath targetTree,
0371: boolean includeSelf) throws ComponentPersistenceException {
0372: return getPathsRelatedToTree(targetTree, includeSelf, true);
0373: }
0374:
0375: private Map getPathsRelatedToTree(ComponentPath targetTree,
0376: boolean includeSelf, boolean createGlobalPaths)
0377: throws ComponentPersistenceException {
0378: Connection con = null;
0379: PreparedStatement pstmt = null;
0380: ResultSet rs = null;
0381: boolean debugEnabled = log.isDebugEnabled();
0382: String currentStatement = null;
0383: try {
0384: if (debugEnabled) {
0385: log
0386: .debug("'"
0387: + this .name
0388: + "':getPathsRelatedToTree() - loading paths refering to tree '"
0389: + targetTree + "' ...");
0390: }
0391: if (includeSelf) {
0392: currentStatement = ("SELECT sourcePath, sourceCM, targetPath FROM "
0393: + TableName.RELATIONS + " WHERE targetPath LIKE ?");
0394: } else {
0395: currentStatement = ("SELECT sourcePath, sourceCM, targetPath FROM "
0396: + TableName.RELATIONS + " WHERE targetPath LIKE ? AND sourcePath NOT LIKE ?");
0397: }
0398: con = PersistenceUtils.getConnection(dataSource);
0399: pstmt = con.prepareStatement(currentStatement);
0400: pstmt
0401: .setString(1,
0402: (targetTree.getPathWithEndToken() + '%'));
0403: if (!includeSelf) {
0404: pstmt.setString(2,
0405: (targetTree.getPathWithEndToken() + '%'));
0406: }
0407:
0408: final Map relatedPaths = new HashMap();
0409: rs = pstmt.executeQuery();
0410: while (rs.next()) {
0411: String sourcePath = rs.getString("sourcePath");
0412: long sourceCM = rs.getLong("sourceCM");
0413: String target = rs.getString("targetPath");
0414: ComponentPath source = (createGlobalPaths ? new GlobalComponentPath(
0415: sourcePath, sourceCM)
0416: : new ComponentPath(sourcePath));
0417: if (debugEnabled) {
0418: log
0419: .debug("'"
0420: + this .name
0421: + "':getPathsRelatedToTree() - found source-path '"
0422: + source + "' from manager "
0423: + sourceCM + " related to target '"
0424: + target + "' (target-tree='"
0425: + targetTree + "').");
0426: }
0427:
0428: Set relSet = (relatedPaths.containsKey(target) ? (Set) relatedPaths
0429: .get(target)
0430: : new HashSet());
0431: relSet.add(source);
0432: relatedPaths.put(target, relSet);
0433: }
0434: if (debugEnabled) {
0435: log.debug("'" + this .name
0436: + "':getPathsRelatedToTree() - tree '"
0437: + targetTree + "' is referred by: "
0438: + relatedPaths);
0439: }
0440: return relatedPaths;
0441: } catch (SQLException e) {
0442: log
0443: .error("'"
0444: + this .name
0445: + "':getPathsRelatedToTree() - SQLException while loading paths refering to tree '"
0446: + targetTree + "' using statement ["
0447: + currentStatement + "]: " + e);
0448: throw new ComponentReadException(
0449: "[RelationsGenericDBAdapter '"
0450: + this .name
0451: + "']:getPathsRelatedToTree() - SQLException while loading paths refering to '"
0452: + targetTree + "' ", e);
0453: } finally {
0454: PersistenceUtils.closeResources(con, pstmt, rs);
0455: }
0456: }
0457:
0458: /**
0459: * Implementation of {@link RelationsPersistenceAdapter#updateRelations}.
0460: */
0461: public void updateRelations(ComponentPath sourcePath,
0462: long managerId, Set relatedPaths)
0463: throws ComponentPersistenceException {
0464: Connection con = null;
0465: PreparedStatement pstmt = null;
0466: ResultSet rs = null;
0467: String currentStatement = null;
0468: final boolean debugEnabled = log.isDebugEnabled();
0469: try {
0470: if (debugEnabled) {
0471: log
0472: .debug("'"
0473: + this .name
0474: + "':updateRelations() - removing old relations of source-path '"
0475: + sourcePath + "' from manager '"
0476: + managerId + "' ...");
0477: }
0478: currentStatement = ("DELETE FROM " + TableName.RELATIONS + " WHERE sourcePath=? AND sourceCM=?");
0479: con = PersistenceUtils.getConnection(dataSource);
0480: pstmt = con.prepareStatement(currentStatement);
0481: pstmt.setString(1, sourcePath.getPathWithEndToken());
0482: pstmt.setLong(2, managerId);
0483: int deleted = pstmt.executeUpdate();
0484: if (debugEnabled) {
0485: log.debug("'" + this .name
0486: + "':updateRelations() - removed " + deleted
0487: + " relations of component '" + sourcePath
0488: + "' from manager '" + managerId + "' ...");
0489: }
0490:
0491: try {
0492: if (pstmt != null) {
0493: pstmt.close();
0494: pstmt = null;
0495: }
0496: } catch (Exception e) {
0497: pstmt = null;
0498: }
0499:
0500: if (relatedPaths != null && !(relatedPaths.isEmpty())) {
0501: currentStatement = ("INSERT INTO "
0502: + TableName.RELATIONS + " (sourcePath, sourceCM, targetPath, targetDir, targetName) VALUES(?,?,?,?,?)");
0503: pstmt = con.prepareStatement(currentStatement);
0504: for (Iterator i = relatedPaths.iterator(); i.hasNext();) {
0505: ComponentPath link = (ComponentPath) i.next();
0506: if (debugEnabled) {
0507: log
0508: .debug("'"
0509: + this .name
0510: + "':updateRelations() - inserting relation '"
0511: + sourcePath
0512: + "' from manager '"
0513: + managerId + "' -> '" + link
0514: + "' ...");
0515: }
0516: pstmt
0517: .setString(1, sourcePath
0518: .getPathWithEndToken());
0519: pstmt.setLong(2, managerId);
0520: pstmt.setString(3, link.getPathWithEndToken());
0521: pstmt.setString(4, link.getDir());
0522: pstmt.setString(5, link.getName());
0523: pstmt.executeUpdate();
0524: }
0525: }
0526: } catch (SQLException e) {
0527: log
0528: .error("'"
0529: + this .name
0530: + "':updateRelations() - SQLException while executing statement '"
0531: + currentStatement + "': " + e);
0532: throw new ComponentWriteException(
0533: "[RelationsGenericDBAdapter '"
0534: + this .name
0535: + "':updateRelations() - SQLException while executing statement '"
0536: + currentStatement + "'!", e);
0537: } finally {
0538: PersistenceUtils.closeResources(con, pstmt, rs);
0539: }
0540: }
0541:
0542: /**
0543: * Implementation of {@link RelationsPersistenceAdapter#moveRelationsOfTree}.
0544: */
0545: public void moveRelationsOfTree(ComponentPath path,
0546: long sourceManagerId, long targetManagerId,
0547: boolean recursive) throws ComponentPersistenceException {
0548: if (log.isDebugEnabled()) {
0549: log
0550: .debug("'"
0551: + this .name
0552: + "':moveRelationsOfTree() - changing id of relations of "
0553: + (recursive ? "tree" : "component") + " '"
0554: + path + "' from id " + sourceManagerId
0555: + " to id " + targetManagerId + " ...");
0556: }
0557: Connection con = null;
0558: PreparedStatement pstmt = null;
0559: String currentStatement = null;
0560: try {
0561: if (recursive) {
0562: currentStatement = ("UPDATE " + TableName.RELATIONS + " SET sourceCM=? WHERE sourceCM=? AND sourcePath LIKE ?");
0563: } else {
0564: currentStatement = ("UPDATE " + TableName.RELATIONS + " SET sourceCM=? WHERE sourceCM=? AND sourcePath=?");
0565: }
0566: con = PersistenceUtils.getConnection(dataSource);
0567: pstmt = con.prepareStatement(currentStatement);
0568: pstmt.setLong(1, targetManagerId);
0569: pstmt.setLong(2, sourceManagerId);
0570: if (recursive) {
0571: pstmt.setString(3, path.getPathWithEndToken() + '%');
0572: } else {
0573: pstmt.setString(3, path.getPathWithEndToken());
0574: }
0575: int affected = pstmt.executeUpdate();
0576: if (log.isDebugEnabled()) {
0577: log
0578: .debug("'"
0579: + this .name
0580: + "':moveRelationsOfTree() - ... changed id of "
0581: + affected + " relations from "
0582: + sourceManagerId + " to "
0583: + targetManagerId + ".");
0584: }
0585: } catch (SQLException e) {
0586: log
0587: .error("'"
0588: + this .name
0589: + "':moveRelationsOfTree() - SQLException while executing statement '"
0590: + currentStatement + "': " + e);
0591: throw new ComponentWriteException(
0592: "[RelationsGenericDBAdapter '"
0593: + this .name
0594: + "':moveRelationsOfTree() - SQLException while executing statement '"
0595: + currentStatement + "'!", e);
0596: } finally {
0597: PersistenceUtils.closeResources(con, pstmt);
0598: }
0599: }
0600:
0601: /**
0602: * Implementation of
0603: * {@link de.finix.contelligent.persistence.RelationsPersistenceAdapter#getDeadRelations}.
0604: */
0605: public Map getDeadRelations(ComponentPath path, long[] managerIds,
0606: boolean completeSubtree)
0607: throws ComponentPersistenceException {
0608: final boolean pathIsRoot = ComponentPath.ROOT_PATH.equals(path);
0609: String cmIds = StringUtils.createCSV(managerIds, ',');
0610: if (log.isDebugEnabled()) {
0611: log
0612: .debug("'"
0613: + this .name
0614: + "':getDeadRelations() - searching dead relations "
0615: + (pathIsRoot ? "" : ("for source-tree '"
0616: + path + "' "))
0617: + " where manager-id is in [" + cmIds
0618: + "] ...");
0619: }
0620: Connection con = null;
0621: PreparedStatement pstmt = null;
0622: ResultSet rs = null;
0623: StringBuffer sql = new StringBuffer();
0624: try {
0625: sql.append("SELECT sourcePath, sourceCM, targetPath FROM ")
0626: .append(TableName.RELATIONS);
0627: if (pathIsRoot) {
0628: sql
0629: .append(" WHERE sourceCM IN (")
0630: .append(cmIds)
0631: .append(
0632: ") AND targetPath NOT IN (SELECT r.targetPath FROM ")
0633: .append(TableName.RELATIONS)
0634: .append(" r, ")
0635: .append(TableName.CMAIN)
0636: .append(
0637: " m WHERE r.targetName=m.name AND r.targetDir=m.dir AND m.cm IN (")
0638: .append(cmIds).append("))");
0639: } else if (completeSubtree) {
0640: sql
0641: .append(
0642: " WHERE sourcePath LIKE ? AND sourceCM IN (")
0643: .append(cmIds)
0644: .append(
0645: ") AND targetPath NOT IN (SELECT r.targetPath FROM ")
0646: .append(TableName.RELATIONS)
0647: .append(" r, ")
0648: .append(TableName.CMAIN)
0649: .append(
0650: " m WHERE r.targetName=m.name AND r.targetDir=m.dir AND m.cm IN (")
0651: .append(cmIds).append("))");
0652: } else {
0653: sql
0654: .append(
0655: " WHERE sourcePath = ? AND sourceCM IN (")
0656: .append(cmIds)
0657: .append(
0658: ") AND targetPath NOT IN (SELECT r.targetPath FROM ")
0659: .append(TableName.RELATIONS)
0660: .append(" r, ")
0661: .append(TableName.CMAIN)
0662: .append(
0663: " m WHERE r.targetName=m.name AND r.targetDir=m.dir AND m.cm IN (")
0664: .append(cmIds).append("))");
0665: }
0666: con = PersistenceUtils.getConnection(dataSource);
0667: pstmt = con.prepareStatement(sql.toString());
0668: if (!pathIsRoot) {
0669: if (completeSubtree) {
0670: pstmt
0671: .setString(1,
0672: path.getPathWithEndToken() + '%');
0673: } else {
0674: pstmt.setString(1, path.getPathWithEndToken());
0675: }
0676: }
0677: Map deadLinks = new HashMap();
0678: if (log.isDebugEnabled()) {
0679: log
0680: .debug("'"
0681: + this .name
0682: + "':getDeadRelations() - ... executing statement ["
0683: + sql + "] ...");
0684: }
0685: rs = pstmt.executeQuery();
0686: int number = 0;
0687: while (rs.next()) {
0688: String sourcePathString = rs.getString("sourcePath");
0689: String targetPath = rs.getString("targetPath");
0690:
0691: // trivial relations to null or ROOT target are always legal
0692: if (targetPath == null
0693: || targetPath.equals(ComponentPath.ROOT_PATH
0694: .toPath())) {
0695: continue;
0696: }
0697:
0698: ComponentPath sourcePath = new GlobalComponentPath(
0699: sourcePathString, rs.getLong("sourceCM"));
0700: Set targetSet = (Set) deadLinks.get(sourcePath);
0701: if (targetSet == null) {
0702: targetSet = new HashSet();
0703: deadLinks.put(sourcePath, targetSet);
0704: }
0705: if (log.isDebugEnabled()) {
0706: log
0707: .debug("'"
0708: + this .name
0709: + "':getDeadRelations() - found dead relation from source='"
0710: + sourcePath + "' -> target='"
0711: + targetPath + "'");
0712: }
0713: targetSet.add(targetPath);
0714: number++;
0715: }
0716: if (log.isDebugEnabled()) {
0717: log.debug("'"
0718: + this .name
0719: + "':getDeadRelations() - found "
0720: + number
0721: + " dead relations "
0722: + (pathIsRoot ? "" : ("for source-tree '"
0723: + path + "' "))
0724: + " where manager-id is in [" + cmIds + "].");
0725: }
0726: return deadLinks;
0727: } catch (SQLException e) {
0728: log
0729: .error(
0730: "'"
0731: + this .name
0732: + "':getDeadRelations() - SQLException while fetching dead relations for tree '"
0733: + path + "' (statement was '" + sql
0734: + "'): ", e);
0735: throw new ComponentPersistenceException(e);
0736: } finally {
0737: PersistenceUtils.closeResources(con, pstmt, rs);
0738: }
0739: }
0740:
0741: /**
0742: * <code>searchCircularRelations</code> method finds links which forms
0743: * circles, such a circle causes a deaclock which can kill the server. This
0744: * method starts with linkSet arrary where the path of the modified
0745: * component is stored, then it goes recursively thorough all related
0746: * components.
0747: *
0748: * @param linkSet
0749: * a <code>Set</code> contains path of checked component.
0750: * @exception ComponentPersistenceException
0751: * if an error occurs
0752: */
0753:
0754: // ensure that recursion will finish, maximum after MAX_LINKS_DEPTH steps
0755: final static private int MAX_CIRCULARRELATIONS_DEPTH = 50;
0756:
0757: public void searchCircularRelations(Set linkSet)
0758: throws CircularRelationsException,
0759: ComponentPersistenceException {
0760: HashSet visitedPaths = new HashSet();
0761: searchCircularRelationsRecursion(linkSet, visitedPaths, 0);
0762: }
0763:
0764: protected void searchCircularRelationsRecursion(Set linkSet,
0765: HashSet visitedPaths, int depth)
0766: throws CircularRelationsException,
0767: ComponentPersistenceException {
0768: if (depth > MAX_CIRCULARRELATIONS_DEPTH)
0769: throw new IllegalArgumentException(
0770: "Maximum links depth exceeded!");
0771: if (log.isDebugEnabled()) {
0772: log
0773: .debug("searchDeadLocks(): starting searching with depth: '"
0774: + depth + "'");
0775: log.debug("searchDeadLocks(): visited paths are: '"
0776: + visitedPaths + "'");
0777: }
0778: Iterator links = linkSet.iterator();
0779: while (links.hasNext()) {
0780: String link = (String) links.next();
0781: if (!(link.charAt(link.length() - 1) == ComponentPath.SEPARATOR))
0782: link += ComponentPath.SEPARATOR;
0783: if (log.isDebugEnabled())
0784: log.debug("searchDeadLocks(): checking relation: '"
0785: + link + "'");
0786: if (visitedPaths.contains(link)) {
0787: String message = "searchDeadlocks(): Deadlock found! Circural links are: '"
0788: + visitedPaths + "'";
0789: log.error(message);
0790: throw new CircularRelationsException(visitedPaths);
0791: }
0792: // made String[] out of ComponentPath[]
0793: Set tmp = getPathsRelatedTo(new ComponentPath(link));
0794: Iterator tmpi = tmp.iterator();
0795: Set tmpresult = new HashSet();
0796: while (tmpi.hasNext())
0797: tmpresult.add(((ComponentPath) tmpi.next()).toPath());
0798:
0799: visitedPaths.add(link);
0800: searchCircularRelationsRecursion(tmpresult, visitedPaths,
0801: depth + 1);
0802: visitedPaths.remove(link);
0803: }
0804: }
0805:
0806: /**
0807: * Implementation of {@link RelationsPersistenceAdapter#existRelationsTo}.
0808: *
0809: * @param target
0810: * a <code>ComponentPath</code> value
0811: * @return a <code>boolean</code> value
0812: * @exception ComponentPersistenceException
0813: * if an error occurs
0814: * @see RelationsPersistenceAdapter#existRelationsTo
0815: */
0816: public boolean existRelationsTo(ComponentPath target)
0817: throws ComponentPersistenceException {
0818: Connection con = null;
0819: PreparedStatement pstmt = null;
0820: ResultSet rs = null;
0821: final String targetPath = target.getPathWithEndToken();
0822: String currentStatement = null;
0823: try {
0824: con = PersistenceUtils.getConnection(dataSource);
0825: if (log.isDebugEnabled()) {
0826: log
0827: .debug("'"
0828: + this .name
0829: + "':existRelationsTo() - testing whether any relations to '"
0830: + target + "' exist ...");
0831: }
0832: currentStatement = ("SELECT sourcePath FROM "
0833: + TableName.RELATIONS + " WHERE targetPath=?");
0834: pstmt = con.prepareStatement(currentStatement);
0835: pstmt.setString(1, targetPath);
0836: rs = pstmt.executeQuery();
0837: boolean result = rs.next();
0838: if (log.isDebugEnabled()) {
0839: log.debug("'" + this .name
0840: + "':existRelationsTo() - relations to '"
0841: + targetPath + "' "
0842: + (result ? "exist." : "do not exist."));
0843: }
0844: return result;
0845: } catch (SQLException e) {
0846: log
0847: .error("'"
0848: + this .name
0849: + "':existRelationsTo() - SQLException while testing whether any relations to '"
0850: + target + "' exist using statement '"
0851: + currentStatement + "': " + e);
0852: throw new ComponentReadException(
0853: "[RelationsGenericDBAdapter '"
0854: + this .name
0855: + "':existRelationsTo() - SQLException while executing statement '"
0856: + currentStatement + "'!", e);
0857: } finally {
0858: PersistenceUtils.closeResources(con, pstmt, rs);
0859: }
0860: }
0861:
0862: /**
0863: * Implementation of
0864: * {@link RelationsPersistenceAdapter#existRelationsToTree}.
0865: *
0866: * @param targetTree
0867: * a <code>ComponentPath</code> value
0868: * @return a <code>boolean</code> value
0869: * @exception ComponentPersistenceException
0870: * if an error occurs
0871: * @see RelationsPersistenceAdapter#existRelationsToTree
0872: */
0873: public boolean existRelationsToTree(ComponentPath targetTree)
0874: throws ComponentPersistenceException {
0875: Connection con = null;
0876: PreparedStatement pstmt = null;
0877: ResultSet rs = null;
0878: String currentStatement = null;
0879: try {
0880: con = PersistenceUtils.getConnection(dataSource);
0881: if (log.isDebugEnabled()) {
0882: log
0883: .debug("'"
0884: + this .name
0885: + "':existRelationsToTree() - testing whether relations to tree '"
0886: + targetTree + "' exist ...");
0887: }
0888: currentStatement = ("SELECT sourcePath FROM "
0889: + TableName.RELATIONS + " WHERE targetPath LIKE ?");
0890: pstmt = con.prepareStatement(currentStatement);
0891: pstmt
0892: .setString(1,
0893: (targetTree.getPathWithEndToken() + '%'));
0894: rs = pstmt.executeQuery();
0895: boolean result = false;
0896: while (!result && rs.next()) {
0897: ComponentPath source = new ComponentPath(rs
0898: .getString("sourcePath"));
0899: result = !(source.equals(targetTree) || source
0900: .isSubPathOf(targetTree));
0901: }
0902: if (log.isDebugEnabled()) {
0903: log
0904: .debug("'"
0905: + this .name
0906: + "':existRelationsToTree() - relations to tree '"
0907: + targetTree + "' "
0908: + (result ? "exist." : "do not exist."));
0909: }
0910: return result;
0911: } catch (SQLException e) {
0912: log
0913: .error("'"
0914: + this .name
0915: + "':existRelationsToTree() - SQLException while testing whether relations to tree '"
0916: + targetTree + "' exist using statement '"
0917: + currentStatement + "': " + e);
0918: throw new ComponentReadException(
0919: "[RelationsGenericDBAdapter '"
0920: + this .name
0921: + "':existRelationsToTree() - SQLException while executing statement '"
0922: + currentStatement + "'!", e);
0923: } finally {
0924: PersistenceUtils.closeResources(con, pstmt, rs);
0925: }
0926: }
0927:
0928: /**
0929: * Implementation of {@link RelationsPersistenceAdapter#relationExists}.
0930: *
0931: * @param source
0932: * a <code>ComponentPath</code> value
0933: * @param target
0934: * a <code>ComponentPath</code> value
0935: * @return a <code>boolean</code> value
0936: * @exception ComponentPersistenceException
0937: * if an error occurs
0938: * @see RelationsPersistenceAdapter#relationExists
0939: */
0940: public boolean relationExists(ComponentPath source, long sourceCM,
0941: ComponentPath target) throws ComponentPersistenceException {
0942: Connection con = null;
0943: PreparedStatement pstmt = null;
0944: String currentStatement = null;
0945: ResultSet rs = null;
0946: try {
0947: con = PersistenceUtils.getConnection(dataSource);
0948: final String sourcePath = source.getPathWithEndToken();
0949: final String targetPath = target.getPathWithEndToken();
0950: currentStatement = ("SELECT targetPath FROM "
0951: + TableName.RELATIONS + " WHERE sourcePath=?");
0952: if (sourceCM != Long.MIN_VALUE)
0953: currentStatement += " AND sourceCM=?";
0954:
0955: pstmt = con.prepareStatement(currentStatement);
0956: pstmt.setString(1, sourcePath);
0957: if (sourceCM != Long.MIN_VALUE)
0958: pstmt.setLong(2, sourceCM);
0959:
0960: if (log.isDebugEnabled()) {
0961: log
0962: .debug("'"
0963: + this .name
0964: + "':relationExists() - testing whether a relation between '"
0965: + sourcePath
0966: + "' (manager '"
0967: + (sourceCM == Long.MIN_VALUE ? "ANY"
0968: : String.valueOf(sourceCM))
0969: + ") and '" + targetPath
0970: + "' exists ...");
0971: }
0972:
0973: rs = pstmt.executeQuery();
0974: boolean result = rs.next();
0975: if (log.isDebugEnabled()) {
0976: log.debug("'"
0977: + this .name
0978: + "':relationExists() - relation between '"
0979: + source
0980: + "' (manager '"
0981: + (sourceCM == Long.MIN_VALUE ? "ANY" : String
0982: .valueOf(sourceCM)) + ") and '"
0983: + target + "' "
0984: + (result ? "exists." : "does not exist."));
0985: }
0986: return result;
0987: } catch (SQLException e) {
0988: log
0989: .error("'"
0990: + this .name
0991: + "':relationExists() - SQLException while testing whether a relation between '"
0992: + source + "' and '" + target
0993: + "' exists using statement '"
0994: + currentStatement + "': " + e);
0995: throw new ComponentReadException(
0996: "[RelationsGenericDBAdapter '"
0997: + this .name
0998: + "':relationExists() - SQLException while executing statement '"
0999: + currentStatement + "'!", e);
1000: } finally {
1001: PersistenceUtils.closeResources(con, pstmt, rs);
1002: }
1003: }
1004:
1005: /**
1006: * Implementation of {@link RelationsPersistenceAdapter#removeRelations}.
1007: *
1008: * @param source
1009: * a <code>ComponentPath</code> value
1010: * @exception ComponentPersistenceException
1011: * if an error occurs
1012: * @see RelationsPersistenceAdapter#removeRelations
1013: */
1014: public void removeRelations(ComponentPath source, long sourceCM)
1015: throws ComponentPersistenceException {
1016: Connection con = null;
1017: PreparedStatement pstmt = null;
1018: String currentStatement = null;
1019: try {
1020: con = PersistenceUtils.getConnection(dataSource);
1021: final String sourcePath = source.getPathWithEndToken();
1022: currentStatement = ("DELETE FROM " + TableName.RELATIONS + " WHERE sourcePath=?");
1023: if (sourceCM != Long.MIN_VALUE)
1024: currentStatement += " AND sourceCM=?";
1025:
1026: pstmt = con.prepareStatement(currentStatement);
1027: pstmt.setString(1, sourcePath);
1028: if (sourceCM != Long.MIN_VALUE)
1029: pstmt.setLong(2, sourceCM);
1030:
1031: if (log.isDebugEnabled()) {
1032: log
1033: .debug("'"
1034: + this .name
1035: + "':removeRelations() - removing ALL relations of component '"
1036: + sourcePath
1037: + "' (manager "
1038: + (sourceCM == Long.MIN_VALUE ? "ANY"
1039: : String.valueOf(sourceCM))
1040: + ") ...");
1041: }
1042:
1043: int deleted = pstmt.executeUpdate();
1044: if (log.isDebugEnabled()) {
1045: log.debug("'"
1046: + this .name
1047: + "':removeRelations() - removed "
1048: + deleted
1049: + " relations of component '"
1050: + source
1051: + "' (manager "
1052: + (sourceCM == Long.MIN_VALUE ? "ANY" : String
1053: .valueOf(sourceCM)) + ").");
1054: }
1055: } catch (SQLException e) {
1056: log
1057: .error("'"
1058: + this .name
1059: + "':removeRelations() - SQLException while removing relations from component '"
1060: + source + "' using statement ["
1061: + currentStatement + "]: " + e);
1062: throw new ComponentWriteException(
1063: "[RelationsGenericDBAdapter '"
1064: + this .name
1065: + "':removeRelations() - SQLException while removing relations from component '"
1066: + source + "'!", e);
1067: } finally {
1068: PersistenceUtils.closeResources(con, pstmt);
1069: }
1070: }
1071:
1072: /**
1073: * Implementation of
1074: * {@link RelationsPersistenceAdapter#removeRelationsOfTree}.
1075: *
1076: * @param sourceTree
1077: * a <code>ComponentPath</code> value
1078: * @exception ComponentPersistenceException
1079: * if an error occurs
1080: * @see RelationsPersistenceAdapter#removeRelationsOfTree
1081: */
1082: public void removeRelationsOfTree(ComponentPath sourceTree,
1083: long sourceCM) throws ComponentPersistenceException {
1084: Connection con = null;
1085: PreparedStatement pstmt = null;
1086: final String sourcePath = (sourceTree.getPathWithEndToken() + '%');
1087:
1088: String currentStatement = null;
1089: try {
1090: con = PersistenceUtils.getConnection(dataSource);
1091: currentStatement = ("DELETE FROM " + TableName.RELATIONS + " WHERE sourcePath LIKE ?");
1092: if (sourceCM != Long.MIN_VALUE)
1093: currentStatement += " AND sourceCM=?";
1094:
1095: pstmt = con.prepareStatement(currentStatement);
1096: pstmt.setString(1, sourcePath);
1097: if (sourceCM != Long.MIN_VALUE)
1098: pstmt.setLong(2, sourceCM);
1099:
1100: if (log.isDebugEnabled()) {
1101: log
1102: .debug("'"
1103: + this .name
1104: + "':removeRelationsOfTree() - removing ALL relations of tree '"
1105: + sourcePath
1106: + "' (manager "
1107: + (sourceCM == Long.MIN_VALUE ? "ANY"
1108: : String.valueOf(sourceCM))
1109: + ") ...");
1110: }
1111:
1112: int deleted = pstmt.executeUpdate();
1113: if (log.isDebugEnabled()) {
1114: log.debug("'"
1115: + this .name
1116: + "':removeRelationsOfTree() - removed "
1117: + deleted
1118: + " relations of tree '"
1119: + sourceTree
1120: + "' (manager "
1121: + (sourceCM == Long.MIN_VALUE ? "ANY" : String
1122: .valueOf(sourceCM)) + ").");
1123: }
1124: } catch (SQLException e) {
1125: log
1126: .error("'"
1127: + this .name
1128: + "':removeRelationsOfTree() - SQLException while removing relations from tree '"
1129: + sourceTree + "' using statement ["
1130: + currentStatement + "]: " + e);
1131: throw new ComponentWriteException(
1132: "[RelationsGenericDBAdapter '"
1133: + this .name
1134: + "':removeRelationsOfTree() - SQLException while removing relations from tree '"
1135: + sourceTree + "'!", e);
1136: } finally {
1137: PersistenceUtils.closeResources(con, pstmt);
1138: }
1139: }
1140:
1141: /**
1142: * Implementation of {@link RelationsPersistenceAdapter#removeRelationsTo}.
1143: *
1144: * @param target
1145: * a <code>ComponentPath</code> value
1146: * @exception ComponentPersistenceException
1147: * if an error occurs
1148: * @see RelationsPersistenceAdapter#removeRelationsTo
1149: */
1150: public void removeRelationsTo(ComponentPath target)
1151: throws ComponentPersistenceException {
1152: Connection con = null;
1153: PreparedStatement pstmt = null;
1154: String currentStatement = null;
1155:
1156: try {
1157: con = PersistenceUtils.getConnection(dataSource);
1158: final String targetPath = target.getPathWithEndToken();
1159: currentStatement = ("DELETE FROM " + TableName.RELATIONS + " WHERE targetPath=?");
1160: pstmt = con.prepareStatement(currentStatement);
1161: pstmt.setString(1, targetPath);
1162: int deleted = pstmt.executeUpdate();
1163: if (log.isDebugEnabled()) {
1164: log.debug("'" + this .name
1165: + "':removeRelationsTo() - removed all ("
1166: + deleted + ") relations to '" + target + "'.");
1167: }
1168: } catch (SQLException e) {
1169: log
1170: .error("'"
1171: + this .name
1172: + "':removeRelationsTo() - SQLException while removing relations to component '"
1173: + target + "' using statement ["
1174: + currentStatement + "]: " + e);
1175: throw new ComponentWriteException(
1176: "[RelationsGenericDBAdapter '"
1177: + this .name
1178: + "':removeRelationsTo() - SQLException while removing relations to component '"
1179: + target + "'!", e);
1180: } finally {
1181: PersistenceUtils.closeResources(con, pstmt);
1182: }
1183: }
1184:
1185: /**
1186: * Implementation of
1187: * {@link RelationsPersistenceAdapter#removeRelationsToTree}.
1188: *
1189: * @param targetTree
1190: * a <code>ComponentPath</code> value
1191: * @exception ComponentPersistenceException
1192: * if an error occurs
1193: * @see RelationsPersistenceAdapter#removeRelationsToTree
1194: */
1195: public void removeRelationsToTree(ComponentPath targetTree)
1196: throws ComponentPersistenceException {
1197: Connection con = null;
1198: PreparedStatement pstmt = null;
1199: final String targetPath = (targetTree.getPathWithEndToken() + '%');
1200:
1201: String currentStatement = null;
1202: try {
1203: con = PersistenceUtils.getConnection(dataSource);
1204: currentStatement = ("DELETE FROM " + TableName.RELATIONS + " WHERE targetPath LIKE ?");
1205: pstmt = con.prepareStatement(currentStatement);
1206: pstmt.setString(1, targetPath);
1207: int deleted = pstmt.executeUpdate();
1208: if (log.isDebugEnabled()) {
1209: log.debug("'" + this .name
1210: + "':removeRelationsToTree() - removed all ("
1211: + deleted + ") relations to tree '"
1212: + targetTree + "'.");
1213: }
1214: } catch (SQLException e) {
1215: log
1216: .error("'"
1217: + this .name
1218: + "':removeRelationsToTree() - SQLException while removing relations to tree '"
1219: + targetTree + "' using statement ["
1220: + currentStatement + "]: " + e);
1221: throw new ComponentWriteException(
1222: "[RelationsGenericDBAdapter '"
1223: + this .name
1224: + "':removeRelationsToTree() - SQLException while removing relations to tree '"
1225: + targetTree + "'!", e);
1226: } finally {
1227: PersistenceUtils.closeResources(con, pstmt);
1228: }
1229: }
1230:
1231: }
|