0001: /*
0002: * Created on Feb 23, 2004
0003: */
0004:
0005: package net.sourceforge.orbroker;
0006:
0007: import java.io.IOException;
0008: import java.io.InputStream;
0009: import java.lang.reflect.Member;
0010: import java.lang.reflect.Method;
0011: import java.net.URL;
0012: import java.sql.Connection;
0013: import java.sql.DatabaseMetaData;
0014: import java.sql.ResultSet;
0015: import java.sql.SQLException;
0016: import java.util.Collection;
0017: import java.util.HashMap;
0018: import java.util.Iterator;
0019: import java.util.LinkedList;
0020: import java.util.Map;
0021: import java.util.Properties;
0022: import java.util.Set;
0023: import java.util.WeakHashMap;
0024: import java.util.logging.ConsoleHandler;
0025: import java.util.logging.Handler;
0026: import java.util.logging.Level;
0027: import java.util.logging.LogRecord;
0028: import java.util.logging.Logger;
0029:
0030: import javax.sql.DataSource;
0031:
0032: import net.kildenpedersen.reflect.Reflector;
0033: import net.kildenpedersen.xml.DOMBuilder;
0034: import net.sourceforge.orbroker.exception.DefaultExceptionEvaluator;
0035: import net.sourceforge.orbroker.exception.ExceptionEvaluator;
0036:
0037: import org.w3c.dom.Document;
0038: import org.w3c.dom.Element;
0039: import org.w3c.dom.Node;
0040: import org.w3c.dom.NodeList;
0041:
0042: /**
0043: * The Broker between the JDBC data source and an application. The Broker holds
0044: * the configuration and initiates Queries and Transactions.
0045: *
0046: * @author Nils Kilden-Pedersen
0047: * @version 2.0
0048: * @see net.sourceforge.orbroker.Query
0049: * @see net.sourceforge.orbroker.Transaction
0050: */
0051: public final class Broker {
0052:
0053: private final class ConfigHandler {
0054: private final static String ATTR_CATALOG = "catalog";
0055: private final static String ATTR_CLASS = "class";
0056: private final static String ATTR_DFT_LAZY = "lazy-load";
0057: private final static String ATTR_EXTENDS = "extends";
0058: private final static String ATTR_EXTERNAL = "external-source";
0059: private final static String ATTR_FROM_COLUMN = "from-column";
0060: private final static String ATTR_ID = "id";
0061: private final static String ATTR_ID_SUFFIX = "id-suffix";
0062: private final static String ATTR_KEY_COLUMNS = "key-columns";
0063: private final static String ATTR_LAZY = "lazy-load";
0064: private final static String ATTR_MAPPED_COLUMN = "mapped-column";
0065: private final static String ATTR_NAME = "name";
0066: private final static String ATTR_ON_VALUE = "on-value";
0067: private final static String ATTR_QUERY_COLUMN = "query-column";
0068: private final static String ATTR_RESULT_OBJECT = "result-object";
0069: private final static String ATTR_STATEMENT = "sql-statement";
0070: private final static String ATTR_TRANSACTION_ISOLATION = "transaction-isolation";
0071: private final static String ATTR_USE_COLUMN = "use-column";
0072: private final static String ELEM_ARGUMENT = "argument";
0073: private final static String ELEM_COLUMN = "column";
0074: private final static String ELEM_COLUMN_ALIAS = "column-alias";
0075: private final static String ELEM_CONSTRUCTOR = "constructor";
0076: private final static String ELEM_DELEGATE_MAPPING = "delegate-mapping";
0077: private final static String ELEM_DELEGATE_TO = "delegate-to";
0078: private final static String ELEM_FACTORY_METHOD = "factory-method";
0079: private final static String ELEM_FIELD = "field";
0080: private final static String ELEM_MAP_WITH = "map-with";
0081: private final static String ELEM_PARAMETER = "set-parameter";
0082: private final static String ELEM_PROPERTY = "property";
0083: private final static String ELEM_RESULT_OBJECT = "result-object";
0084: private final static String ELEM_SET_METHOD = "method";
0085: private final static String ELEM_STATEMENT = "sql-statement";
0086: private final static String ELEM_SUB_QUERY = "sub-query";
0087:
0088: private final DOMBuilder builder;
0089: private boolean lazyDefault;
0090: private final String schemaFile = "orbroker-2.0.xsd";
0091:
0092: private ConfigHandler() throws ConfigurationException {
0093: URL schemaURL = getClass().getResource(
0094: "/" + this .schemaFile);
0095: if (schemaURL == null) {
0096: throw new ConfigurationException(this .schemaFile
0097: + " not found.");
0098: }
0099: this .builder = new DOMBuilder(schemaURL);
0100: }
0101:
0102: private void addInstantiator(Element instantiatorElem,
0103: ResultObjectDefinition resultObjectDef)
0104: throws BrokerException {
0105:
0106: String tagName = instantiatorElem.getTagName();
0107: Class type = resultObjectDef.getType();
0108: String methodName = instantiatorElem
0109: .getAttribute(ATTR_NAME);
0110:
0111: NodeList parameters = instantiatorElem
0112: .getElementsByTagName(ELEM_ARGUMENT);
0113: Set candidates = getMethodOrConstructorCandidates(tagName,
0114: type, parameters.getLength(), methodName);
0115: ArgumentList parmList = buildArgumentList(parameters,
0116: candidates);
0117: ResultObjectInstantiator instantiator = null;
0118: if (ELEM_CONSTRUCTOR.equals(tagName)) {
0119: instantiator = new ResultObjectConstructor(type,
0120: parmList);
0121: } else if (ELEM_FACTORY_METHOD.equals(tagName)) {
0122: instantiator = new ResultObjectFactoryMethod(type,
0123: methodName, parmList);
0124: } else {
0125: assert false : "Unknown element passed: "
0126: + instantiatorElem.getTagName();
0127: }
0128:
0129: resultObjectDef.setInstantiator(instantiator);
0130: }
0131:
0132: private void addMappingDelegates(Element delegateMappingElem,
0133: ResultObjectDefinition resultObjectDef)
0134: throws BrokerException {
0135:
0136: String columnName = delegateMappingElem
0137: .getAttribute(ATTR_USE_COLUMN);
0138: Column discriminatorColumn = new Column(columnName,
0139: String.class);
0140: resultObjectDef.setDiscriminatorColumn(discriminatorColumn);
0141:
0142: NodeList delegateList = delegateMappingElem
0143: .getElementsByTagName(ELEM_DELEGATE_TO);
0144: for (int i = 0; i < delegateList.getLength(); i++) {
0145: Element delegateTo = (Element) delegateList.item(i);
0146: String resultObjectId = delegateTo
0147: .getAttribute(ATTR_RESULT_OBJECT);
0148: if (resultObjectId.equals(resultObjectDef.getId())) {
0149: String msg = "A "
0150: + delegateMappingElem.getTagName()
0151: + " cannot reference itself, in this case: "
0152: + resultObjectId;
0153: throw new ConfigurationException(msg);
0154: }
0155: ResultObjectDefinition dynamicDef = Broker.this
0156: .findResultObjectDefinition(resultObjectId);
0157: String dynamicValue = delegateTo
0158: .getAttribute(ATTR_ON_VALUE);
0159: resultObjectDef.addDynamicResultObject(dynamicDef,
0160: dynamicValue);
0161: }
0162: }
0163:
0164: private void addSetField(Element fieldElem,
0165: ResultObjectDefinition resultObjectDef)
0166: throws BrokerException {
0167:
0168: String fieldName = fieldElem.getAttribute(ATTR_NAME);
0169: SetField setField = new SetField(resultObjectDef.getType(),
0170: fieldName);
0171: Element valueTypeElem = DOMBuilder
0172: .getFirstChildElement(fieldElem);
0173: setField.setValueType(buildValueType(valueTypeElem,
0174: setField.getFieldType()));
0175: resultObjectDef.addSetter(setField);
0176: }
0177:
0178: private void addSetMethod(Element setMethodElem,
0179: ResultObjectDefinition resultObjectDef)
0180: throws BrokerException {
0181:
0182: String methodName = setMethodElem.getAttribute(ATTR_NAME);
0183: Class type = resultObjectDef.getType();
0184: NodeList parameters = setMethodElem
0185: .getElementsByTagName(ELEM_ARGUMENT);
0186: Set candidates = getMethodOrConstructorCandidates(
0187: setMethodElem.getTagName(), type, parameters
0188: .getLength(), methodName);
0189: ArgumentList parmList = buildArgumentList(parameters,
0190: candidates);
0191: SetMethod setMethod = new SetMethod(type, methodName,
0192: parmList);
0193: resultObjectDef.addSetter(setMethod);
0194: }
0195:
0196: private void addSetProperty(Element propertyElem,
0197: ResultObjectDefinition resultObjectDef)
0198: throws BrokerException {
0199:
0200: String propertyName = propertyElem.getAttribute(ATTR_NAME);
0201: SetProperty setProperty = new SetProperty(resultObjectDef
0202: .getType(), propertyName);
0203: Element valueTypeElem = DOMBuilder
0204: .getFirstChildElement(propertyElem);
0205: setProperty.setParameter(buildValueType(valueTypeElem,
0206: setProperty.getPropertyType()));
0207: resultObjectDef.addSetter(setProperty);
0208: }
0209:
0210: /**
0211: *
0212: * @param arguments
0213: * @param candidateSet Set of either methods or constructors
0214: * which have the same number of arguments as the argument list.
0215: * @return argument list
0216: * @throws BrokerException
0217: */
0218: private ArgumentList buildArgumentList(NodeList arguments,
0219: Set candidateSet) throws BrokerException {
0220:
0221: Class[] parmTypes = findParameterTypes(candidateSet,
0222: arguments);
0223: ArgumentList argumentList = new ArgumentList(arguments
0224: .getLength());
0225: for (int argumentPos = 0; argumentPos < arguments
0226: .getLength(); argumentPos++) {
0227: Element argument = (Element) arguments
0228: .item(argumentPos);
0229: Element valueTypeElem = DOMBuilder
0230: .getFirstChildElement(argument);
0231: argumentList.add(buildValueType(valueTypeElem,
0232: parmTypes[argumentPos]));
0233: }
0234: argumentList.close();
0235: return argumentList;
0236: }
0237:
0238: private Column buildColumn(Element columnElem,
0239: Class columnToClass, String idAttributeName) {
0240: String columnName = columnElem
0241: .getAttribute(idAttributeName);
0242: return new Column(columnName, columnToClass);
0243: }
0244:
0245: private MapWith buildMapWith(Element mapWithElem,
0246: Class requiredType) throws BrokerException {
0247:
0248: String resultObjID = mapWithElem
0249: .getAttribute(ATTR_RESULT_OBJECT);
0250: ResultObjectDefinition resultObjectDef = findResultObjectDefinition(resultObjID);
0251: if (!requiredType.isAssignableFrom(resultObjectDef
0252: .getType())) {
0253: String msg = "Use of result object '" + resultObjID
0254: + "' is not of required type "
0255: + Reflector.getClassName(requiredType);
0256: throw new ConfigurationException(msg);
0257: }
0258: MapWith mapWith = new MapWith(resultObjectDef);
0259: NodeList aliases = mapWithElem
0260: .getElementsByTagName(ELEM_COLUMN_ALIAS);
0261: for (int i = 0; i < aliases.getLength(); i++) {
0262: Element alias = (Element) aliases.item(i);
0263: String queryColumn = alias
0264: .getAttribute(ATTR_QUERY_COLUMN);
0265: String mappedColumn = alias
0266: .getAttribute(ATTR_MAPPED_COLUMN);
0267: mapWith.addAlias(mappedColumn, queryColumn);
0268: }
0269: return mapWith;
0270: }
0271:
0272: /**
0273: * @param stm
0274: * @param id
0275: * @param extensionElem
0276: * @param extensionsMap
0277: * @return Extended id
0278: */
0279: private String buildStatementExtension(final String stm,
0280: final String id, final Element extensionElem,
0281: final Map extensionsMap) {
0282:
0283: String extensionId = id
0284: + extensionElem.getAttribute(ATTR_ID_SUFFIX);
0285: StringBuffer extendedStm = (StringBuffer) extensionsMap
0286: .get(extensionId);
0287: if (extendedStm == null) {
0288: extendedStm = new StringBuffer(stm);
0289: extensionsMap.put(extensionId, extendedStm);
0290: }
0291: NodeList textNodes = extensionElem.getChildNodes();
0292: for (int i = 0; i < textNodes.getLength(); i++) {
0293: Node node = textNodes.item(i);
0294: if (node.getNodeType() != Node.TEXT_NODE
0295: && node.getNodeType() != Node.CDATA_SECTION_NODE) {
0296: continue;
0297: }
0298: String value = node.getNodeValue();
0299: extendedStm.append(value);
0300: }
0301: return extensionId;
0302: }
0303:
0304: private void buildStatements(Element statementElem)
0305: throws BrokerException {
0306: HashMap extensionStatements = new HashMap();
0307: HashMap extendedResultObjectDefs = new HashMap();
0308: StringBuffer stm = new StringBuffer();
0309: String id = statementElem.getAttribute(ATTR_ID);
0310:
0311: ResultObjectDefinition resultObjectDef = null;
0312: String resultObjId = statementElem
0313: .getAttribute(ATTR_RESULT_OBJECT);
0314: if (resultObjId != null && resultObjId.length() > 0) {
0315: resultObjectDef = findResultObjectDefinition(resultObjId);
0316: }
0317:
0318: final String externalSource = statementElem
0319: .getAttribute(ATTR_EXTERNAL);
0320: if (externalSource != null
0321: && externalSource.trim().length() > 0) {
0322: Statement externalStatement = null;
0323: externalStatement = Statement.newInstance(id,
0324: resultObjectDef, externalSource);
0325: registerStatement(externalStatement);
0326: return;
0327: }
0328:
0329: NodeList nodes = statementElem.getChildNodes();
0330: for (int i = 0; i < nodes.getLength(); i++) {
0331: Node node = nodes.item(i);
0332: if (node.getNodeType() == Node.TEXT_NODE
0333: || node.getNodeType() == Node.CDATA_SECTION_NODE) {
0334: String value = node.getNodeValue();
0335: stm.append(value);
0336: Iterator iter = extensionStatements.values()
0337: .iterator();
0338: while (iter.hasNext()) {
0339: Object obj = iter.next();
0340: StringBuffer extendedStm = (StringBuffer) obj;
0341: extendedStm.append(value);
0342: }
0343: } else if (node.getNodeType() == Node.ELEMENT_NODE) {
0344: Element appendStatement = (Element) node;
0345: String extendedId = buildStatementExtension(stm
0346: .toString(), id, appendStatement,
0347: extensionStatements);
0348: String extResultObjectId = appendStatement
0349: .getAttribute(ATTR_RESULT_OBJECT);
0350: if (extResultObjectId != null
0351: && extResultObjectId.trim().length() > 0) {
0352: extendedResultObjectDefs
0353: .put(
0354: extendedId,
0355: Broker.this
0356: .findResultObjectDefinition(extResultObjectId));
0357: }
0358:
0359: }
0360: }
0361:
0362: Statement statement = Statement.newInstance(id, stm
0363: .toString(), resultObjectDef);
0364: registerStatement(statement);
0365: Iterator extensions = extensionStatements.entrySet()
0366: .iterator();
0367: while (extensions.hasNext()) {
0368: Map.Entry entry = (Map.Entry) extensions.next();
0369: String extendedId = (String) entry.getKey();
0370: StringBuffer extendedStm = (StringBuffer) entry
0371: .getValue();
0372: ResultObjectDefinition extendedDef = (ResultObjectDefinition) extendedResultObjectDefs
0373: .get(extendedId);
0374: if (extendedDef == null) {
0375: extendedDef = resultObjectDef;
0376: }
0377: statement = Statement.newInstance(extendedId,
0378: extendedStm.toString(), extendedDef);
0379: registerStatement(statement);
0380: }
0381: }
0382:
0383: private ValueType buildSubQuery(Element subQueryElem,
0384: Class requiredType) throws BrokerException {
0385:
0386: String statementID = subQueryElem
0387: .getAttribute(ATTR_STATEMENT);
0388: Statement statement = Broker.this
0389: .findStatement(statementID);
0390: if (statement == null) {
0391: String msg = ATTR_STATEMENT + " '" + statementID
0392: + "' does not exist.";
0393: throw new ConfigurationException(msg);
0394: }
0395:
0396: boolean lazy;
0397: String lazyAttr = subQueryElem.getAttribute(ATTR_LAZY);
0398: if (lazyAttr != null && lazyAttr.trim().length() > 0) {
0399: lazy = Boolean.valueOf(lazyAttr).booleanValue();
0400: } else if (Collection.class.isAssignableFrom(requiredType)
0401: && requiredType.isInterface()) {
0402: lazy = this .lazyDefault;
0403: } else {
0404: lazy = false;
0405: }
0406:
0407: SubQuery subQuery = new SubQuery(requiredType, statementID,
0408: Broker.this , lazy);
0409: NodeList parameterList = subQueryElem
0410: .getElementsByTagName(ELEM_PARAMETER);
0411:
0412: for (int i = 0; i < parameterList.getLength(); i++) {
0413: Element parameterElem = (Element) parameterList.item(i);
0414: String parameterName = parameterElem
0415: .getAttribute(ATTR_NAME);
0416: ValueType parameter = buildColumn(parameterElem,
0417: Object.class, ATTR_FROM_COLUMN);
0418: subQuery.addParameterValue(parameterName, parameter);
0419: }
0420:
0421: return subQuery;
0422: }
0423:
0424: private ValueType buildValueType(Element valueTypeElem,
0425: Class type) throws BrokerException {
0426: String tagName = valueTypeElem.getTagName();
0427: if (ELEM_COLUMN.equals(tagName)) {
0428: return buildColumn(valueTypeElem, type, ATTR_NAME);
0429: } else if (ELEM_MAP_WITH.equals(tagName)) {
0430: return buildMapWith(valueTypeElem, type);
0431: } else if (ELEM_SUB_QUERY.equals(tagName)) {
0432: return buildSubQuery(valueTypeElem, type);
0433: } else {
0434: throw newInvalidElement(tagName);
0435: }
0436: }
0437:
0438: private void configure(InputStream configStream)
0439: throws BrokerException {
0440: Document configDoc = this .builder.parse(configStream);
0441: Element root = configDoc.getDocumentElement();
0442: Broker.this .brokerName = root.getAttribute(ATTR_NAME);
0443: Broker.this .catalog = root.getAttribute(ATTR_CATALOG);
0444: if (Broker.this .catalog != null
0445: && Broker.this .catalog.trim().length() == 0) {
0446: Broker.this .catalog = null;
0447: }
0448: this .lazyDefault = !"false".equals(root
0449: .getAttribute(ATTR_DFT_LAZY));
0450:
0451: Broker.this .transactionIsolation = getTransactionIsolation(root
0452: .getAttribute(ATTR_TRANSACTION_ISOLATION));
0453:
0454: /*
0455: * Find all result-objects first. They need to be put in the map
0456: * first, so the sql-statements can include them.
0457: */
0458: NodeList list = configDoc
0459: .getElementsByTagName(ELEM_RESULT_OBJECT);
0460: for (int i = 0; i < list.getLength(); i++) {
0461:
0462: Element roElem = (Element) list.item(i);
0463: String roID = roElem.getAttribute(ATTR_ID);
0464: String roClassName = roElem.getAttribute(ATTR_CLASS);
0465:
0466: String extendsId = roElem.getAttribute(ATTR_EXTENDS);
0467: if (extendsId != null && extendsId.trim().length() == 0) {
0468: extendsId = null;
0469: }
0470:
0471: ResultKeyDef pkDef = null;
0472: String pkColumns = roElem
0473: .getAttribute(ATTR_KEY_COLUMNS);
0474: if (pkColumns != null && pkColumns.trim().length() > 0) {
0475: pkDef = new ResultKeyDef(pkColumns, roID);
0476: }
0477:
0478: Class roType = getClassByName(roClassName);
0479: ResultObjectDefinition resultObjectDef = new ResultObjectDefinition(
0480: roID, roType, extendsId, pkDef, Broker.this );
0481: registerResultObject(resultObjectDef);
0482: }
0483:
0484: // Find all statements
0485: list = configDoc.getElementsByTagName(ELEM_STATEMENT);
0486: for (int i = 0; i < list.getLength(); i++) {
0487: Element statement = (Element) list.item(i);
0488: buildStatements(statement);
0489: }
0490:
0491: // Finish with the result objects
0492: LinkedList unfinished = new LinkedList();
0493: list = configDoc.getElementsByTagName(ELEM_RESULT_OBJECT);
0494: for (int i = 0; i < list.getLength(); i++) {
0495: unfinished.add(list.item(i));
0496: }
0497: while (!unfinished.isEmpty()) {
0498: Element resultObjElem = (Element) unfinished
0499: .removeFirst();
0500: try {
0501: finishResultObject(resultObjElem);
0502: } catch (ParentNotFinishedException e) {
0503: unfinished.addLast(resultObjElem);
0504: }
0505: }
0506: }
0507:
0508: /**
0509: *
0510: * @param candidateSet
0511: * @param arguments
0512: * @return The only candidate member
0513: */
0514: private Class[] findParameterTypes(Set candidateSet,
0515: NodeList arguments) {
0516:
0517: if (candidateSet.size() == 1) {
0518: Member member = (Member) candidateSet.toArray()[0];
0519: return Reflector.getParameterTypes(member);
0520: }
0521:
0522: assert candidateSet.size() > 0 : "List of candidate members is 0.";
0523:
0524: String name;
0525: {
0526: Member example = (Member) candidateSet.toArray()[0];
0527: if (example.getClass() == Method.class) {
0528: name = "method " + example.getName();
0529: } else {
0530: name = "constructor "
0531: + example.getDeclaringClass().getName();
0532: }
0533: }
0534:
0535: Class[] parmTypes = new Class[arguments.getLength()];
0536:
0537: for (int i = 0; i < arguments.getLength(); i++) {
0538: Element argument = (Element) arguments.item(i);
0539: Element childElem = DOMBuilder
0540: .getFirstChildElement(argument);
0541: String argClassName = argument.getAttribute(ATTR_CLASS);
0542:
0543: if (argClassName != null
0544: && argClassName.trim().length() > 0) {
0545: parmTypes[i] = getClassByName(argClassName);
0546: } else if (ELEM_MAP_WITH.equals(childElem.getTagName())) {
0547: String resultObjectId = childElem
0548: .getAttribute(ATTR_RESULT_OBJECT);
0549: if (resultObjectId != null
0550: && resultObjectId.trim().length() > 0) {
0551: parmTypes[i] = findResultObjectDefinition(
0552: resultObjectId).getType();
0553: }
0554: } else {
0555: parmTypes[i] = getArgumentTypeIfAllEqual(
0556: candidateSet, i);
0557: }
0558:
0559: if (parmTypes[i] != null) {
0560: removeNonCompliant(candidateSet, i, parmTypes[i]);
0561: }
0562:
0563: }
0564:
0565: if (candidateSet.size() == 1) {
0566: Member member = (Member) candidateSet.toArray()[0];
0567: return Reflector.getParameterTypes(member);
0568: }
0569:
0570: StringBuffer msg = new StringBuffer();
0571: if (candidateSet.isEmpty()) {
0572: msg.append("Cannot find ");
0573: } else {
0574: msg.append("More than one ");
0575: }
0576: msg.append(name).append("(");
0577: for (int i = 0; i < parmTypes.length; i++) {
0578: if (i > 0) {
0579: msg.append(", ");
0580: }
0581: msg.append(parmTypes[i] != null ? parmTypes[i]
0582: .getName() : "?");
0583: }
0584: msg.append(").");
0585: if (!candidateSet.isEmpty()) {
0586: msg.append(" Please use attribute '")
0587: .append(ATTR_CLASS).append("' to narrow.");
0588: }
0589: throw new ConfigurationException(msg.toString());
0590: }
0591:
0592: /**
0593: * Finish building the {@link ResultObjectDefinition}. All result objects
0594: * have already been constructed to avoid unresolved dependencies. So
0595: * they must be finished by retrieving them from the Map.
0596: * @param resultObjElem
0597: * @throws BrokerException
0598: * @throws ParentNotFinishedException
0599: */
0600: private void finishResultObject(Element resultObjElem)
0601: throws BrokerException, ParentNotFinishedException {
0602: String roID = resultObjElem.getAttribute(ATTR_ID);
0603: ResultObjectDefinition resultObjectDef = findResultObjectDefinition(roID);
0604:
0605: // Initialize with parent, if exist
0606: String extendsId = resultObjectDef.getExtendsId();
0607: if (extendsId != null) {
0608: if (!Broker.this .resultObjectFactories
0609: .containsKey(extendsId)) {
0610: String msg = ELEM_RESULT_OBJECT + " '" + roID
0611: + "' extends '" + extendsId + "', but '"
0612: + extendsId + "' is not defined.";
0613: throw new ConfigurationException(msg);
0614: }
0615: ResultObjectDefinition parentDef = findResultObjectDefinition(extendsId);
0616: if (!parentDef.isClosed()) {
0617: throw new ParentNotFinishedException();
0618: }
0619: resultObjectDef.initializeWith(parentDef);
0620: }
0621:
0622: NodeList roNodes = resultObjElem.getChildNodes();
0623: for (int i = 0; i < roNodes.getLength(); i++) {
0624: Node node = roNodes.item(i);
0625: if (node.getNodeType() != Node.ELEMENT_NODE) {
0626: continue;
0627: }
0628: Element genericElem = (Element) node;
0629: String tagName = genericElem.getTagName();
0630: if (ELEM_DELEGATE_MAPPING.equals(tagName)) {
0631: addMappingDelegates(genericElem, resultObjectDef);
0632: } else if (ELEM_CONSTRUCTOR.equals(tagName)) {
0633: addInstantiator(genericElem, resultObjectDef);
0634: } else if (ELEM_FACTORY_METHOD.equals(tagName)) {
0635: addInstantiator(genericElem, resultObjectDef);
0636: } else if (ELEM_PROPERTY.equals(tagName)) {
0637: addSetProperty(genericElem, resultObjectDef);
0638: } else if (ELEM_SET_METHOD.equals(tagName)) {
0639: addSetMethod(genericElem, resultObjectDef);
0640: } else if (ELEM_FIELD.equals(tagName)) {
0641: addSetField(genericElem, resultObjectDef);
0642: } else {
0643: throw new ConfigurationException(
0644: "Invalid element '" + tagName + "'.");
0645: }
0646: }
0647:
0648: // Set no-arg constructor, if none exist and not abstract/interface
0649: if (!resultObjectDef.hasInstantiator()
0650: && resultObjectDef.isInstantiable()) {
0651: ResultObjectInstantiator instantiator = new ResultObjectConstructor(
0652: resultObjectDef.getType(), ArgumentList.NO_ARG);
0653: resultObjectDef.setInstantiator(instantiator);
0654: }
0655: resultObjectDef.close();
0656: }
0657:
0658: /**
0659: * Return the argument type at the given position if it's the same
0660: * for all members, or <code>null</code> if it's different.
0661: * @param members
0662: * @param argumentPos
0663: * @return type, or null if disagreement
0664: */
0665: private Class getArgumentTypeIfAllEqual(Set members,
0666: int argumentPos) {
0667: Class returnType = null;
0668: Iterator iter = members.iterator();
0669: while (iter.hasNext()) {
0670: Class type = Reflector.getParameterTypes((Member) iter
0671: .next())[argumentPos];
0672: if (returnType == null) {
0673: returnType = type;
0674: } else if (returnType != type) {
0675: return null;
0676: }
0677: }
0678: return returnType;
0679: }
0680:
0681: private Class getClassByName(final String className)
0682: throws ConfigurationException {
0683: Class cls = (Class) Broker.PRIMITIVE_CLASSES.get(className);
0684: if (cls != null) {
0685: return cls;
0686: }
0687: try {
0688: return Class.forName(className);
0689: } catch (ClassNotFoundException e) {
0690: throw new ConfigurationException(e);
0691: }
0692: }
0693:
0694: private Set getMethodOrConstructorCandidates(
0695: final String tagName, final Class type,
0696: final int parmCount, String methodName) {
0697: Set members = null;
0698:
0699: if (ELEM_CONSTRUCTOR.equals(tagName)) {
0700: members = Reflector.getAccessibleConstructors(type,
0701: parmCount);
0702: methodName = null;
0703: } else if (ELEM_SET_METHOD.equals(tagName)) {
0704: members = Reflector.getAccessibleMemberMethods(type,
0705: methodName, parmCount);
0706: } else if (ELEM_FACTORY_METHOD.equals(tagName)) {
0707: members = Reflector.getAccessibleStaticMethods(type,
0708: methodName, parmCount);
0709: } else {
0710: assert false : "Unknown element passed: " + tagName;
0711: }
0712:
0713: if (members == null || members.size() == 0) {
0714: String msg = "No "
0715: + tagName
0716: + (methodName != null ? " named '" + methodName
0717: + "' " : "") + " with " + parmCount
0718: + " argument(s) exist for class '"
0719: + Reflector.getClassName(type) + "'";
0720: throw new ConfigurationException(msg);
0721: }
0722:
0723: return members;
0724: }
0725:
0726: /**
0727: *
0728: * @param isolation
0729: * @return transaction isolation level as Integer, or <code>null</code>
0730: * if it doesn't exist
0731: */
0732: private Integer getTransactionIsolation(String isolation) {
0733: if (isolation == null) {
0734: return null;
0735: }
0736: if (isolation.trim().length() == 0) {
0737: return null;
0738: }
0739: Integer isolationLevel = (Integer) Broker.TRANSACTION_ISOLATIONS
0740: .get(isolation);
0741: if (isolationLevel == null) {
0742: String msg = "Unknown transaction isolation level: "
0743: + isolation;
0744: throw new ConfigurationException(msg);
0745: }
0746: return isolationLevel;
0747: }
0748:
0749: private ConfigurationException newInvalidElement(String tagName) {
0750: String msg = "Invalid element <" + tagName
0751: + "> encountered.";
0752: return new ConfigurationException(msg);
0753: }
0754:
0755: private void registerResultObject(
0756: ResultObjectDefinition resultObjectDef)
0757: throws ConfigurationException {
0758: String id = resultObjectDef.getId();
0759: if (Broker.this .resultObjectFactories.containsKey(id)) {
0760: String msg = ELEM_RESULT_OBJECT + " '" + id
0761: + "' defined multiple times in Broker '"
0762: + Broker.this .getName() + "'";
0763: throw new ConfigurationException(msg);
0764: }
0765: Broker.this .resultObjectFactories.put(id, resultObjectDef);
0766: Broker
0767: .log(Level.CONFIG, "Result-object registrered: "
0768: + id);
0769: }
0770:
0771: private void registerStatement(Statement statement)
0772: throws ConfigurationException {
0773: String id = statement.getId();
0774: if (Broker.this .sqlStatements.containsKey(id)) {
0775: String msg = ELEM_STATEMENT + " '" + id
0776: + "' defined multiple times in Broker '"
0777: + Broker.this .getName() + "'";
0778:
0779: throw new ConfigurationException(msg);
0780: }
0781: Broker.this .sqlStatements.put(id, statement);
0782: Broker.log(Level.CONFIG, "Statement registered: " + id);
0783: }
0784:
0785: private void removeNonCompliant(Set memberCandidates,
0786: int argumentPos, Class mustBeType) {
0787:
0788: Iterator iter = memberCandidates.iterator();
0789: while (iter.hasNext()) {
0790: Member member = (Member) iter.next();
0791: if (Reflector.getParameterTypes(member)[argumentPos] != mustBeType) {
0792: iter.remove();
0793: }
0794: }
0795: }
0796: }
0797:
0798: private final class ParentNotFinishedException extends Exception {
0799: // Nothing to see here. Move along.
0800: }
0801:
0802: private static final WeakHashMap cachedObjects = new WeakHashMap();
0803: private final static Handler DEFAULT_LOG_HANDLER = new ConsoleHandler();
0804: private final static Logger LOGGER = Logger.getLogger(Broker.class
0805: .getName());
0806: private final static HashMap PRIMITIVE_CLASSES = new HashMap();
0807: private static final HashMap TRANSACTION_ISOLATIONS = new HashMap();
0808: static {
0809: TRANSACTION_ISOLATIONS.put("TRANSACTION_NONE", new Integer(
0810: Connection.TRANSACTION_NONE));
0811: TRANSACTION_ISOLATIONS.put("TRANSACTION_READ_UNCOMMITTED",
0812: new Integer(Connection.TRANSACTION_READ_UNCOMMITTED));
0813: TRANSACTION_ISOLATIONS.put("TRANSACTION_READ_COMMITTED",
0814: new Integer(Connection.TRANSACTION_READ_COMMITTED));
0815: TRANSACTION_ISOLATIONS.put("TRANSACTION_REPEATABLE_READ",
0816: new Integer(Connection.TRANSACTION_REPEATABLE_READ));
0817: TRANSACTION_ISOLATIONS.put("TRANSACTION_SERIALIZABLE",
0818: new Integer(Connection.TRANSACTION_SERIALIZABLE));
0819:
0820: Map switched = new HashMap(TRANSACTION_ISOLATIONS.size());
0821: Iterator entries = TRANSACTION_ISOLATIONS.entrySet().iterator();
0822: while (entries.hasNext()) {
0823: Map.Entry entry = (Map.Entry) entries.next();
0824: switched.put(entry.getValue(), entry.getKey());
0825: }
0826: TRANSACTION_ISOLATIONS.putAll(switched);
0827: }
0828:
0829: static {
0830: LOGGER.setUseParentHandlers(false);
0831: DEFAULT_LOG_HANDLER.setFormatter(new BrokerFormatter());
0832: DEFAULT_LOG_HANDLER.setLevel(Level.ALL);
0833: LOGGER.addHandler(DEFAULT_LOG_HANDLER);
0834: }
0835: static {
0836: PRIMITIVE_CLASSES.put("object", Object.class);
0837: PRIMITIVE_CLASSES.put("int", int.class);
0838: PRIMITIVE_CLASSES.put("string", String.class);
0839: PRIMITIVE_CLASSES.put("boolean", boolean.class);
0840: PRIMITIVE_CLASSES.put("long", long.class);
0841: PRIMITIVE_CLASSES.put("short", short.class);
0842: PRIMITIVE_CLASSES.put("byte", byte.class);
0843: PRIMITIVE_CLASSES.put("char", char.class);
0844: PRIMITIVE_CLASSES.put("double", double.class);
0845: PRIMITIVE_CLASSES.put("float", float.class);
0846: PRIMITIVE_CLASSES.put("byte[]", byte[].class);
0847: PRIMITIVE_CLASSES.put("char[]", char[].class);
0848: }
0849:
0850: static Map getCachedObjects(Connection con) {
0851: synchronized (con) {
0852: Map map = (Map) cachedObjects.get(con);
0853: if (map == null) {
0854: map = new HashMap();
0855: cachedObjects.put(con, map);
0856: }
0857: return map;
0858: }
0859: }
0860:
0861: static String getTransactionIsolationLevel(int isolationLevel) {
0862: return (String) TRANSACTION_ISOLATIONS.get(new Integer(
0863: isolationLevel));
0864: }
0865:
0866: /**
0867: * @param scrollableSupport
0868: * @return result set type
0869: */
0870: static int getResultSetType(boolean scrollableSupport) {
0871: if (scrollableSupport) {
0872: return ResultSet.TYPE_SCROLL_SENSITIVE;
0873: }
0874: return ResultSet.TYPE_FORWARD_ONLY;
0875: }
0876:
0877: static boolean isLoggable(Level level) {
0878: return LOGGER.isLoggable(level);
0879: }
0880:
0881: /**
0882: * Add logging handler. When adding a logging handler, the default console
0883: * log handler is automatically removed. Previously added handlers remain.
0884: *
0885: * @param handler Logging handler.
0886: * @see Handler
0887: * @see #addLoggingHandler(Handler, boolean)
0888: */
0889: public static void addLoggingHandler(Handler handler) {
0890: addLoggingHandler(handler, false);
0891: }
0892:
0893: /**
0894: * Add logging handler. Previously added handlers remain.
0895: * @param handler Logging handler.
0896: * @param keepDefaultHandler true, if default console log handler should remain
0897: * @see Handler
0898: * @see #addLoggingHandler(Handler)
0899: */
0900: public static void addLoggingHandler(Handler handler,
0901: boolean keepDefaultHandler) {
0902: LOGGER.addHandler(handler);
0903: if (!keepDefaultHandler) {
0904: LOGGER.removeHandler(DEFAULT_LOG_HANDLER);
0905: }
0906: }
0907:
0908: /**
0909: * Log a message.
0910: *
0911: * @param level Log level
0912: * @param message Log message
0913: */
0914: public static void log(Level level, String message) {
0915: if (LOGGER.isLoggable(level)) {
0916: LOGGER.log(new LogRecord(level, message));
0917: }
0918: }
0919:
0920: /**
0921: * Set the logging level. Log level definitions:
0922: * <ul>
0923: * <li>WARNING: Used for internally caught exceptions not considered severe.</li>
0924: * <li>INFO: Shows transaction boundaries and SQL statements executed.</li>
0925: * <li>CONFIG: Information about driver support and configuration setup</li>
0926: * <li>FINE: Shows dynamic SQL statement after parsing.</li>
0927: * </ul>
0928: *
0929: * @param level Logging level
0930: */
0931: public static void setLoggingLevel(Level level) {
0932: LOGGER.setLevel(level);
0933: }
0934:
0935: /**
0936: * Set the logging level. See log level definitions on {@link #setLoggingLevel(Level)}.
0937: * @param level Logging level
0938: * @see Level
0939: */
0940: public static void setLoggingLevel(String level) {
0941: setLoggingLevel(Level.parse(level));
0942: }
0943:
0944: private String brokerName;
0945: private String catalog;
0946: private DataSource dataSource = null;
0947: private ExceptionEvaluator evaluator = new DefaultExceptionEvaluator();
0948: private final WeakHashMap executableConnections = new WeakHashMap();
0949: private char[] password = null;
0950: private final HashMap resultObjectFactories = new HashMap();
0951: private final HashMap sqlStatements = new HashMap();
0952: private boolean supportsBatch = false;
0953: private boolean supportsScrollable = false;
0954: private TextReplacements textReplacements = new TextReplacements();
0955: private Integer transactionIsolation;
0956: private String username = null;
0957:
0958: /**
0959: * Create Broker without a configuration.
0960: *
0961: * @param dataSource A data source
0962: * @see #addStatement(String, String)
0963: */
0964: public Broker(DataSource dataSource) {
0965: this (dataSource, null, null);
0966: }
0967:
0968: /**
0969: * Create Broker without a configuration.
0970: *
0971: * @param dataSource A data source
0972: * @param username Username to use that is different than on data source
0973: * @param password Password for username
0974: * @see #addStatement(String, String)
0975: */
0976: public Broker(DataSource dataSource, String username,
0977: String password) {
0978: setDataSource(dataSource, username, password);
0979: }
0980:
0981: /**
0982: * The Broker is configured by passing an {@link InputStream} containing an
0983: * XML file and a data source.
0984: *
0985: * @param configuration The XML configuration
0986: * @param dataSource A data source
0987: * @throws BrokerException
0988: */
0989: public Broker(InputStream configuration, DataSource dataSource)
0990: throws BrokerException {
0991: this (configuration, dataSource, null, null);
0992: }
0993:
0994: /**
0995: * The Broker is configured by passing an {@link InputStream} containing an
0996: * XML file and a data source with username and password.
0997: *
0998: * @param configuration The XML configuration
0999: * @param dataSource A data source
1000: * @param username Username to use that is different than on data source
1001: * @param password Password for username
1002: * @throws BrokerException
1003: */
1004: public Broker(InputStream configuration, DataSource dataSource,
1005: String username, String password) throws BrokerException {
1006: ConfigHandler handler = new ConfigHandler();
1007: handler.configure(configuration);
1008: try {
1009: configuration.close();
1010: } catch (IOException e) {
1011: Broker.log(Level.WARNING, e.toString());
1012: }
1013: setDataSource(dataSource, username, password);
1014: }
1015:
1016: private void checkMetaData() {
1017: Connection con = null;
1018: try {
1019: con = getFreshConnection(null);
1020: DatabaseMetaData metaData = con.getMetaData();
1021: this .supportsScrollable = metaData
1022: .supportsResultSetConcurrency(
1023: getResultSetType(true),
1024: ResultSet.CONCUR_READ_ONLY);
1025: this .supportsBatch = metaData.supportsBatchUpdates();
1026: } catch (SQLException e) {
1027: log(Level.WARNING, e.toString());
1028: } finally {
1029: if (con != null) {
1030: try {
1031: con.close();
1032: } catch (SQLException e) {
1033: log(Level.WARNING, e.toString());
1034: }
1035: }
1036: }
1037:
1038: if (!this .supportsScrollable) {
1039: log(Level.CONFIG, "Scrollable result sets not supported.");
1040: }
1041: if (!this .supportsBatch) {
1042: log(Level.CONFIG, "Batch updates not supported.");
1043: }
1044:
1045: }
1046:
1047: ResultObjectDefinition findResultObjectDefinition(
1048: String resultObjectID) throws BrokerException {
1049: ResultObjectDefinition resultObjectDef = (ResultObjectDefinition) this .resultObjectFactories
1050: .get(resultObjectID);
1051: if (resultObjectDef == null) {
1052: throw new BrokerException("Unknown result-object id: "
1053: + resultObjectID);
1054: }
1055: return resultObjectDef;
1056: }
1057:
1058: Statement findStatement(String statementID) throws BrokerException {
1059: Statement stm = (Statement) this .sqlStatements.get(statementID);
1060: if (stm == null) {
1061: String msg = "Statement '" + statementID
1062: + "' unknown to Broker '" + this .brokerName + "'";
1063: throw new BrokerException(msg);
1064: }
1065: return stm;
1066: }
1067:
1068: ExceptionEvaluator getExceptionEvaluator() {
1069: return this .evaluator;
1070: }
1071:
1072: /**
1073: * Get fresh connection from data source.
1074: *
1075: * @param isolationLevel
1076: * @return Fresh connection
1077: */
1078: Connection getFreshConnection(Integer isolationLevel) {
1079: Connection con;
1080: try {
1081:
1082: if (this .username != null && this .password != null) {
1083: con = this .dataSource.getConnection(this .username,
1084: String.valueOf(this .password));
1085: } else {
1086: con = this .dataSource.getConnection();
1087: }
1088:
1089: if (con == null) {
1090: throw new BrokerException(
1091: "Broker '"
1092: + this .brokerName
1093: + "' unable to obtain connection from data source.");
1094: }
1095:
1096: if (this .catalog != null) {
1097: con.setCatalog(this .catalog);
1098: }
1099:
1100: if (isolationLevel != null
1101: && con.getTransactionIsolation() != isolationLevel
1102: .intValue()) {
1103: con.setTransactionIsolation(isolationLevel.intValue());
1104: }
1105:
1106: boolean autoCommit = (con.getTransactionIsolation() == Connection.TRANSACTION_NONE);
1107: if (con.getAutoCommit() != autoCommit) {
1108: con.setAutoCommit(autoCommit);
1109: }
1110:
1111: } catch (SQLException e) {
1112: throw new BrokerException(e.toString());
1113: }
1114:
1115: return con;
1116: }
1117:
1118: /**
1119: * @return Returns the text replacements.
1120: */
1121: TextReplacements getTextReplacements() {
1122: return this .textReplacements;
1123: }
1124:
1125: boolean supportsBatch() {
1126: return this .supportsBatch;
1127: }
1128:
1129: /**
1130: * @return Returns scrollable support.
1131: */
1132: boolean supportsScrollable() {
1133: return this .supportsScrollable;
1134: }
1135:
1136: /**
1137: * Add statement to Broker.
1138: *
1139: * @param id The statement id.
1140: * @param sql The SQL statement
1141: * @throws BrokerException
1142: * @throws ConfigurationException
1143: */
1144: public void addStatement(final String id, final String sql)
1145: throws BrokerException, ConfigurationException {
1146: addStatement(id, sql, null);
1147: }
1148:
1149: /**
1150: * Add statement to Broker.
1151: *
1152: * @param id The statement id.
1153: * @param sql The SQL statement
1154: * @param resultObjectId The <code>result-object</code> id as defined in the XML
1155: * configuration file.
1156: * @throws BrokerException
1157: * @throws ConfigurationException
1158: */
1159: public void addStatement(final String id, final String sql,
1160: String resultObjectId) throws BrokerException,
1161: ConfigurationException {
1162:
1163: ResultObjectDefinition resultObjectDef = null;
1164: if (resultObjectId != null) {
1165: resultObjectDef = findResultObjectDefinition(resultObjectId);
1166: }
1167: Statement stm = Statement.newInstance(id, sql, resultObjectDef);
1168: if (this .sqlStatements.containsKey(id)) {
1169: throw new ConfigurationException("Statement '" + id
1170: + "' already exists.");
1171: }
1172: this .sqlStatements.put(id, stm);
1173: }
1174:
1175: /**
1176: * Get the data source used by Broker.
1177: * @return The datasource used.
1178: */
1179: public DataSource getDataSource() {
1180: return this .dataSource;
1181: }
1182:
1183: /**
1184: * Get brokerName of Broker.
1185: * @return Name of broker
1186: */
1187: public String getName() {
1188: return this .brokerName;
1189: }
1190:
1191: /**
1192: * Obtain an Executable. An Executable instance <i>must</i> be
1193: * released again by calling {@link #releaseExecutable(Executable)},
1194: * or unpredictable behavior can occur.
1195: * @param connection An externally managed connection.
1196: * @return Executable
1197: * @see #releaseExecutable(Executable)
1198: * @see Transaction#obtainExecutable()
1199: */
1200: public Executable obtainExecutable(Connection connection) {
1201: Executable exe = new Executable(this , connection);
1202: this .executableConnections.put(exe, connection);
1203: return exe;
1204: }
1205:
1206: /**
1207: * Release Executable. This will drop the internal Connection and prevent
1208: * other processes, which may hold a reference to the Executable, from
1209: * executing statements on a Connection that may have been returned to a
1210: * DataSource connection pool.
1211: * <p><i>Releasing the Executable will not close the connection.</i></p>
1212: * @param executable The Executable to release
1213: * @throws BrokerException if the Executable was not obtained from this Broker
1214: * @see #obtainExecutable(Connection)
1215: */
1216: public void releaseExecutable(Executable executable) {
1217: executable.release();
1218: Connection con = (Connection) this .executableConnections
1219: .remove(executable);
1220: if (con == null) {
1221: String msg = "Executable was not obtained from Broker '"
1222: + this .brokerName + "'";
1223: throw new BrokerException(msg);
1224: }
1225: getCachedObjects(con).clear();
1226: }
1227:
1228: /**
1229: * Set the catalog.
1230: * @param catalog The catalog name
1231: */
1232: public void setCatalog(String catalog) {
1233: this .catalog = catalog;
1234: }
1235:
1236: /**
1237: * Set the data source. Uses default username and password.
1238: * @param dataSource The data source
1239: */
1240: public void setDataSource(DataSource dataSource) {
1241: setDataSource(dataSource, null, null);
1242: }
1243:
1244: /**
1245: * Set data source and username/password.
1246: *
1247: * @param dataSource The data source
1248: * @param username Username to use that is different than on data source
1249: * @param password Password for username
1250: */
1251: public void setDataSource(DataSource dataSource, String username,
1252: String password) {
1253:
1254: if (dataSource == null) {
1255: throw new NullPointerException("Data source is null.");
1256: }
1257: this .dataSource = dataSource;
1258: this .username = username;
1259: if (password != null) {
1260: this .password = password.toCharArray();
1261: } else {
1262: this .password = null;
1263: }
1264:
1265: checkMetaData();
1266: }
1267:
1268: /**
1269: * Set a new exception evaluator.
1270: * @param evaluator The exception evaluator instance
1271: */
1272: public void setExceptionEvaluator(ExceptionEvaluator evaluator) {
1273: if (evaluator == null) {
1274: throw new NullPointerException(
1275: "Exception evaluator is null.");
1276: }
1277: this .evaluator = evaluator;
1278: }
1279:
1280: /**
1281: * Set a text replacement value. This will replace a <code>{{key}}</code>
1282: * string with the specific parameter value.
1283: * @param key Text replacement key
1284: * @param value Text replacement value
1285: * @see #setTextReplacements(Properties)
1286: */
1287: public void setTextReplacement(String key, String value) {
1288: this .textReplacements.setProperty(key, value);
1289: }
1290:
1291: /**
1292: * Set text replacement values. This will replace all <code>{{key}}</code>
1293: * type properties in an sql-statement with the values.
1294: *
1295: * @param textReplacements The replacement values to set.
1296: * @see #setTextReplacement(String, String)
1297: */
1298: public void setTextReplacements(Properties textReplacements) {
1299: this .textReplacements.setAll(textReplacements);
1300: }
1301:
1302: /**
1303: * Start a read-only query.
1304: *
1305: * @return Query
1306: */
1307: public Query startQuery() {
1308: return new Query(this , this .transactionIsolation);
1309: }
1310:
1311: /**
1312: * Start a read-only query with a given transaction isolation level.
1313: * JDBC transaction isolation levels:
1314: * <ul>
1315: * <li>{@link Connection#TRANSACTION_NONE}</li>
1316: * <li>{@link Connection#TRANSACTION_READ_UNCOMMITTED}</li>
1317: * <li>{@link Connection#TRANSACTION_READ_COMMITTED}</li>
1318: * <li>{@link Connection#TRANSACTION_REPEATABLE_READ}</li>
1319: * <li>{@link Connection#TRANSACTION_SERIALIZABLE}</li>
1320: * </ul>
1321: * @param isolationLevel The isolation level
1322: * @return Query
1323: */
1324: public Query startQuery(int isolationLevel) {
1325: return new Query(this , new Integer(isolationLevel));
1326: }
1327:
1328: /**
1329: * Start a new transaction.
1330: *
1331: * @return Transaction
1332: * @throws BrokerException
1333: */
1334: public Transaction startTransaction() throws BrokerException {
1335: return new Transaction(this , this .transactionIsolation);
1336: }
1337:
1338: /**
1339: * Start a new transaction with a given transaction isolation level.
1340: * JDBC transaction isolation levels:
1341: * <ul>
1342: * <li>{@link Connection#TRANSACTION_NONE}</li>
1343: * <li>{@link Connection#TRANSACTION_READ_UNCOMMITTED}</li>
1344: * <li>{@link Connection#TRANSACTION_READ_COMMITTED}</li>
1345: * <li>{@link Connection#TRANSACTION_REPEATABLE_READ}</li>
1346: * <li>{@link Connection#TRANSACTION_SERIALIZABLE}</li>
1347: * </ul>
1348:
1349: * @param isolationLevel The isolation level
1350: * @return Transaction
1351: * @throws BrokerException
1352: */
1353: public Transaction startTransaction(int isolationLevel)
1354: throws BrokerException {
1355: return new Transaction(this , new Integer(isolationLevel));
1356: }
1357:
1358: /**
1359: * @see java.lang.Object#toString()
1360: */
1361: public String toString() {
1362: return "Broker: " + this.brokerName;
1363: }
1364:
1365: }
|