001: /*
002: * Copyright 2007 Hippo Webworks.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.slide.store.impl.rdbms;
018:
019: import java.io.File;
020: import java.io.FileInputStream;
021: import java.io.FileOutputStream;
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.OutputStream;
025: import java.lang.reflect.Method;
026: import java.sql.CallableStatement;
027: import java.sql.Connection;
028: import java.sql.PreparedStatement;
029: import java.sql.ResultSet;
030: import java.sql.SQLException;
031:
032: import org.apache.slide.common.Service;
033: import org.apache.slide.common.ServiceAccessException;
034: import org.apache.slide.common.Uri;
035: import org.apache.slide.content.NodeRevisionContent;
036: import org.apache.slide.content.NodeRevisionDescriptor;
037: import org.apache.slide.util.logger.Logger;
038: import org.apache.slide.store.impl.rdbms.CommonRDBMSAdapter;
039: import org.apache.slide.store.impl.rdbms.SequenceAdapter;
040: import org.apache.slide.store.impl.rdbms.StoreContentZip;
041:
042: /**
043: * Adapter for Oracle 9 and 10 combined with JBoss/J2EE
044: */
045: public class J2EEOracleRDBMSAdapter extends CommonRDBMSAdapter
046: implements SequenceAdapter {
047:
048: // Constructor
049:
050: public J2EEOracleRDBMSAdapter(Service service, Logger logger)
051: throws ClassNotFoundException, NoSuchMethodException {
052: super (service, logger);
053: }
054:
055: // Public Methods
056:
057: protected void storeContent(Connection connection, Uri uri,
058: NodeRevisionDescriptor revisionDescriptor,
059: NodeRevisionContent revisionContent) throws IOException,
060: SQLException {
061: assureVersionInfo(connection, uri, revisionDescriptor);
062:
063: InputStream is = revisionContent.streamContent();
064: if (is != null) {
065: long blobLength = 0;
066: File tempFile = null;
067:
068: if (bcompress) {
069: getLogger().log("Compressing the data", LOG_CHANNEL,
070: Logger.DEBUG);
071: StoreContentZip ziputil = new StoreContentZip();
072: ziputil.Zip(is);
073: is = ziputil.getInputStream();
074:
075: // fix RevisionDescriptor Content Length
076: if (revisionDescriptor.getContentLength() == -1) {
077: revisionDescriptor.setContentLength(ziputil
078: .getInitialContentLength());
079: }
080:
081: blobLength = ziputil.getContentLength();
082: } else {
083: // fix RevisionDescriptor Content Length
084: if (revisionDescriptor.getContentLength() == -1) {
085: try {
086: tempFile = File.createTempFile("content", null);
087: FileOutputStream fos = new FileOutputStream(
088: tempFile);
089: try {
090: byte buffer[] = new byte[4096];
091: do {
092: int nChar = is.read(buffer);
093: if (nChar == -1) {
094: break;
095: }
096: fos.write(buffer, 0, nChar);
097: } while (true);
098: } finally {
099: fos.close();
100: }
101: is.close();
102: is = new FileInputStream(tempFile);
103:
104: revisionDescriptor.setContentLength(tempFile
105: .length());
106:
107: } catch (IOException ex) {
108: getLogger()
109: .log(
110: ex.toString()
111: + " during the calculation of the content length.",
112: LOG_CHANNEL, Logger.ERROR);
113: throw ex;
114: }
115: }
116:
117: blobLength = revisionDescriptor.getContentLength();
118: }
119:
120: CallableStatement statement = null;
121: try {
122: long versionID = getVersionID(connection, uri
123: .toString(), revisionDescriptor);
124: statement = connection
125: .prepareCall("BEGIN "
126: + "insert into VERSION_CONTENT (VERSION_ID, CONTENT) "
127: + "values (?, empty_blob()) RETURN CONTENT into ?;"
128: + "END; ");
129: statement.setLong(1, versionID);
130: statement.registerOutParameter(2, java.sql.Types.BLOB);
131:
132: statement.executeUpdate();
133:
134: // do these two lines using reflection
135: // BLOB blob = (BLOB) statement.getBlob(2);
136: // OutputStream os = blob.getBinaryOutputStream();
137:
138: Object bObj = statement.getBlob(2);
139: OutputStream os = null;
140: try {
141: Method blobGetOutputStreamMethod = bObj.getClass()
142: .getMethod("getBinaryOutputStream",
143: new Class[0]);
144: os = (OutputStream) blobGetOutputStreamMethod
145: .invoke(bObj, new Object[0]);
146: } catch (Exception ex) {
147: ex.printStackTrace();
148: throw new IOException("Reflection error");
149: }
150:
151: try {
152: byte buffer[] = new byte[4096];
153: do {
154: int nChar = is.read(buffer);
155: if (nChar == -1) {
156: break;
157: }
158: os.write(buffer, 0, nChar);
159: } while (true);
160: } finally {
161: os.flush();
162: os.close();
163: }
164:
165: } finally {
166: if (tempFile != null) {
167: try {
168: is.close();
169: } catch (Exception ex) {
170: // ignore
171: }
172: is = null;
173: if (!tempFile.delete()) {
174: logger.log("Could not delete file \""
175: + tempFile.getAbsolutePath() + "\"");
176: }
177: }
178:
179: try {
180: close(statement);
181: } finally {
182: if (is != null) {
183: // XXX some JDBC drivers seem to close the stream upon
184: // closing of
185: // the statement; if so this will raise an IOException
186: // silently ignore it...
187: try {
188: is.close();
189: } catch (IOException ioe) {
190: logger.log("Could not close stream", ioe,
191: LOG_CHANNEL, Logger.DEBUG);
192: }
193: }
194: }
195: }
196: }
197: }
198:
199: protected static String normalizeSequenceName(String sequenceName) {
200: return sequenceName.replace('-', '_').toUpperCase() + "_SEQ";
201: }
202:
203: public boolean isSequenceSupported(Connection conn) {
204: return true;
205: }
206:
207: public boolean createSequence(Connection conn, String sequenceName)
208: throws ServiceAccessException {
209:
210: String query = "CREATE SEQUENCE \""
211: + normalizeSequenceName(sequenceName) + "\"";
212:
213: PreparedStatement statement = null;
214:
215: try {
216: statement = conn.prepareStatement(query);
217: statement.executeUpdate();
218: return true;
219: } catch (SQLException e) {
220: throw new ServiceAccessException(service, e);
221: } finally {
222: close(statement);
223: }
224:
225: }
226:
227: public long nextSequenceValue(Connection conn, String sequenceName)
228: throws ServiceAccessException {
229: String selectQuery = "SELECT \""
230: + normalizeSequenceName(sequenceName)
231: + "\".nextval FROM DUAL";
232:
233: PreparedStatement selectStatement = null;
234: ResultSet res = null;
235:
236: try {
237: selectStatement = conn.prepareStatement(selectQuery);
238: res = selectStatement.executeQuery();
239: if (!res.next()) {
240: throw new ServiceAccessException(service,
241: "Could not increment sequence " + sequenceName);
242: }
243: long value = res.getLong(1);
244: return value;
245: } catch (SQLException e) {
246: throw new ServiceAccessException(service, e);
247: } finally {
248: close(selectStatement, res);
249: }
250: }
251:
252: public boolean sequenceExists(Connection conn, String sequenceName)
253: throws ServiceAccessException {
254:
255: PreparedStatement selectStatement = null;
256: ResultSet res = null;
257:
258: try {
259: selectStatement = conn
260: .prepareStatement("ALTER SEQUENCE \""
261: + normalizeSequenceName(sequenceName)
262: + "\" INCREMENT BY 1");
263: res = selectStatement.executeQuery();
264: return true;
265: } catch (SQLException e) {
266: return false;
267: } finally {
268: close(selectStatement, res);
269: }
270: }
271:
272: // Attributes
273:
274: protected Method blobGetOutStreamMethod;
275: }
276:
277: /*
278: protected Method blobGetOutStreamMethod;
279:
280:
281: The constructor for the class is as follows:
282:
283: public OracleRDBMSAdapter(Service service, Logger logger)
284: throws ClassNotFoundException, NoSuchMethodException
285: {
286: super(service, logger);
287:
288: Class bCls = Class.forName("oracle.sql.BLOB");
289: blobGetOutStreamMethod = bCls.getMethod("getBinaryOutputStream", new
290: Class[] {});
291: }
292:
293: Later on, the Adapter attempts to retrieve the java.io.OutputStream for the Blob
294: by making this call:
295:
296: os = (OutputStream) blobGetOutStreamMethod.invoke(bObj, new Object[] {});
297:
298:
299: Though syntactically correct, this code results in the above exception.
300:
301: In the constructor, the method is retrieved from a Class object and NOT an
302: instance of the Class itself.
303:
304: According to the javadoc for the Method.invoke() call, invoke() will throw:
305:
306: IllegalArgumentException - if the method is an instance method and the specified
307: object argument is not an instance of the class or interface declaring the
308: underlying method
309:
310: Since the method was retrieved from a Class object and not an instance, it's not
311: an Instance method. This is discussed on the Sun Java forums.
312:
313: The correct code is as follows:
314:
315: replace this line(line 142)
316:
317: os = (OutputStream) blobGetOutStreamMethod.invoke(bObj, new Object[] {});
318:
319: with the following two lines:
320:
321: Method blobGetOutputStreamMethod =
322: bObj.getClass().getMethod("getBinaryOutputStream",new Class[0]);
323: os = (OutputStream) blobGetOutputStreamMethod.invoke(bObj, new Object[0]);
324:
325: and remove the two lines in the constructor.
326: */
|