001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.servicemix.components.jdbc;
018:
019: import java.sql.Connection;
020: import java.sql.ResultSet;
021: import java.sql.ResultSetMetaData;
022: import java.sql.SQLException;
023: import java.sql.Statement;
024: import java.util.HashMap;
025: import java.util.LinkedList;
026: import java.util.List;
027: import java.util.Map;
028:
029: import javax.jbi.messaging.MessageExchange;
030: import javax.jbi.messaging.MessagingException;
031: import javax.jbi.messaging.NormalizedMessage;
032: import javax.sql.DataSource;
033: import javax.xml.transform.Source;
034:
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037: import org.apache.servicemix.MessageExchangeListener;
038: import org.apache.servicemix.components.util.TransformComponentSupport;
039: import org.apache.servicemix.jbi.jaxp.SourceTransformer;
040: import org.apache.servicemix.jbi.jaxp.StringSource;
041: import org.apache.xpath.CachedXPathAPI;
042: import org.w3c.dom.Node;
043:
044: public class JdbcComponent extends TransformComponentSupport implements
045: MessageExchangeListener {
046: private static final Log log = LogFactory
047: .getLog(JdbcComponent.class);
048:
049: private DataSource dataSource;
050: private boolean responseRequired = false;
051:
052: public boolean transform(MessageExchange exchange,
053: NormalizedMessage in, NormalizedMessage out)
054: throws MessagingException {
055: log.debug("Received a JDBC request. Datasource=" + dataSource
056: + ", ResponseRequired=" + responseRequired);
057: Connection conn = null;
058: Statement stmt = null;
059: ResultSet rs = null;
060:
061: try {
062:
063: SourceTransformer domTransform = new SourceTransformer();
064: Node domNode = domTransform.toDOMNode(in);
065:
066: // Return the exception message
067: // if (isExceptionXml(domNode)) {
068: // log.debug("Found an exception message: " + domNode);
069: // out.setContent(in.getContent());
070: // return true;
071: // }
072:
073: String query = getQuery(domNode);
074: log.debug("Retrieved query: " + query);
075:
076: conn = dataSource.getConnection();
077:
078: stmt = conn.createStatement();
079:
080: Source outMsg = null;
081: if (query != null && query.length() > 0) {
082: if (stmt.execute(query)) {
083: // Result is a ResultSet object
084: rs = stmt.getResultSet();
085:
086: log.debug("Formatting ResultSet: " + rs);
087: outMsg = toXmlSource(rs);
088: } else {
089: int updateCount = stmt.getUpdateCount();
090: if (updateCount > -1) {
091: log.debug("Formatting UpdateCount: "
092: + updateCount);
093: // Result is an update count
094: outMsg = toXmlSource(updateCount);
095: } else {
096: log.debug("Formatting NoResult.");
097: // Result is neither a result set nor an update count
098: outMsg = null;
099: }
100: }
101: }
102:
103: if (outMsg != null) {
104: // There is a valid response
105: log.debug("Response: " + domTransform.toString(outMsg));
106: out.setContent(outMsg);
107: return true;
108:
109: } else if (responseRequired) {
110: // Create an empty <sqlResult> element
111: log.debug("Response: Empty Response");
112: out.setContent(toXmlSource());
113: return true;
114:
115: } else {
116: log.debug("Response: No Response");
117: // There is no valid response
118: return false;
119: }
120: } catch (Exception e) {
121: log.error("JDBC Component Exception: ", e);
122: // out.setContent(createExceptionXml(e));
123: // return true;
124: throw new MessagingException(e);
125: } finally {
126: if (rs != null) {
127: try {
128: rs.close();
129: } catch (SQLException e) {
130: // Ignore
131: }
132: }
133:
134: if (stmt != null) {
135: try {
136: stmt.close();
137: } catch (SQLException e) {
138: // Ignore
139: }
140: }
141:
142: if (conn != null) {
143: try {
144: conn.close();
145: } catch (SQLException e) {
146: // Ignore
147: }
148: }
149: }
150: }
151:
152: public String getQuery(Node node) throws Exception {
153: CachedXPathAPI xpath = new CachedXPathAPI();
154:
155: node = xpath.selectSingleNode(node, "sql/child::text()");
156:
157: // First child should be <sql></sql> element
158: if (node == null) {
159: throw new IllegalStateException(
160: "Expecting <sql></sql> node. Found: " + node);
161: }
162:
163: return node.getNodeValue();
164: }
165:
166: public void setDataSource(DataSource ds) {
167: dataSource = ds;
168: }
169:
170: public DataSource getDataSource() {
171: return dataSource;
172: }
173:
174: /**
175: * If true, an empty <sqlResult> element is created and send as a response if there is no result
176: * @param val
177: */
178: public void setResponseRequired(boolean val) {
179: responseRequired = val;
180: }
181:
182: public boolean getResponseRequired() {
183: return responseRequired;
184: }
185:
186: protected Source toXmlSource(ResultSet rs) throws Exception {
187:
188: ResultSetMetaData meta = rs.getMetaData();
189: int colCount = meta.getColumnCount();
190:
191: String[] colNames = getUniqueColumnNames(meta);
192:
193: StringBuffer buff = new StringBuffer("");
194:
195: while (rs.next()) {
196: buff.append("<row ");
197: for (int i = 0; i < colCount; i++) {
198: buff.append(colNames[i].toLowerCase() + "='"
199: + rs.getString(i + 1) + "' ");
200: }
201: buff.append("/>");
202: }
203:
204: if (buff.length() > 0) {
205: // If non-empty result, insert parent tags
206: buff.insert(0, "<sqlResult><resultSet>");
207: buff.append("</resultSet></sqlResult>");
208: } else {
209: // If empty result, return null source
210: return null;
211: }
212:
213: return new StringSource(buff.toString());
214: }
215:
216: /**
217: * Returns a String[] containing unique ColNames.
218: */
219: protected String[] getUniqueColumnNames(ResultSetMetaData metaData)
220: throws SQLException {
221:
222: List colNames = new LinkedList();
223: Map chanedNames = new HashMap();
224:
225: for (int i = 1; i <= metaData.getColumnCount(); i++) {
226: String name = metaData.getColumnName(i);
227:
228: if (name.equals("")) {
229: name = "__unknown_column__";
230: }
231:
232: if (colNames.contains(name)) {
233:
234: int count;
235: if (chanedNames.containsKey(name)) {
236: Integer integer = (Integer) chanedNames.get(name);
237: Integer newInteger = new Integer(
238: integer.intValue() + 1);
239: chanedNames.put(name, newInteger);
240: count = newInteger.intValue();
241: } else {
242: chanedNames.put(name, new Integer(1));
243: count = 1;
244: }
245:
246: name = name + "_" + count;
247:
248: }
249:
250: colNames.add(name);
251:
252: }
253:
254: return (String[]) colNames.toArray(new String[colNames.size()]);
255: }
256:
257: protected Source toXmlSource(int updateCount) throws Exception {
258: return new StringSource("<sqlResult><updateCount value='"
259: + updateCount + "'/></sqlResult>");
260: }
261:
262: protected Source toXmlSource() throws Exception {
263: return new StringSource("<sqlResult></sqlResult>");
264: }
265:
266: // Custom method to create and handle exception
267: /*
268: public boolean isExceptionXml(Node document) {
269: return document.getFirstChild().getNodeName().equalsIgnoreCase("exception");
270: }
271:
272: public Source createExceptionXml(Throwable e) {
273: return new StringSource("<exception class='" + e.getClass() + "'>'" + e.getMessage() + "'</exception>");
274: } */
275: }
|