001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb;
032:
033: import java.io.BufferedInputStream;
034: import java.io.BufferedOutputStream;
035: import java.io.DataInputStream;
036: import java.io.IOException;
037: import java.io.OutputStream;
038: import java.net.Socket;
039:
040: import org.hsqldb.lib.ArrayUtil;
041: import org.hsqldb.rowio.RowInputBinary;
042: import org.hsqldb.rowio.RowOutputBinary;
043: import org.hsqldb.store.ValuePool;
044:
045: /**
046: * Base remote session proxy implementation. Uses instances of Result to
047: * transmit and recieve data. This implementation utilises the updated HSQL
048: * protocol.
049: *
050: * @author fredt@users
051: * @version 1.8.0
052: * @since 1.7.2
053: */
054: public class HSQLClientConnection implements SessionInterface {
055:
056: static final int BUFFER_SIZE = 0x1000;
057: final byte[] mainBuffer = new byte[BUFFER_SIZE];
058: private boolean isClosed;
059: private Socket socket;
060: protected OutputStream dataOutput;
061: protected DataInputStream dataInput;
062: protected RowOutputBinary rowOut;
063: protected RowInputBinary rowIn;
064: private Result resultOut;
065: private int sessionID;
066:
067: //
068: private boolean isReadOnly = false;
069: private boolean isAutoCommit = true;
070:
071: //
072: String host;
073: int port;
074: String path;
075: String database;
076: boolean isTLS;
077: int databaseID;
078:
079: public HSQLClientConnection(String host, int port, String path,
080: String database, boolean isTLS, String user, String password)
081: throws HsqlException {
082:
083: this .host = host;
084: this .port = port;
085: this .path = path;
086: this .database = database;
087: this .isTLS = isTLS;
088:
089: initStructures();
090:
091: Result login = new Result(ResultConstants.SQLCONNECT);
092:
093: login.mainString = user;
094: login.subString = password;
095: login.subSubString = database;
096:
097: initConnection(host, port, isTLS);
098:
099: Result resultIn = execute(login);
100:
101: if (resultIn.isError()) {
102:
103: /** @todo fredt - review error message */
104: throw Trace.error(resultIn);
105: }
106:
107: sessionID = resultIn.sessionID;
108: databaseID = resultIn.databaseID;
109: }
110:
111: /**
112: * resultOut is reused to trasmit all remote calls for session management.
113: * Here the structure is preset for sending attributes.
114: */
115: private void initStructures() {
116:
117: rowOut = new RowOutputBinary(mainBuffer);
118: rowIn = new RowInputBinary(rowOut);
119: resultOut = Result.newSessionAttributesResult();
120:
121: resultOut.add(new Object[7]);
122: }
123:
124: protected void initConnection(String host, int port, boolean isTLS)
125: throws HsqlException {
126: openConnection(host, port, isTLS);
127: }
128:
129: protected void openConnection(String host, int port, boolean isTLS)
130: throws HsqlException {
131:
132: try {
133: socket = HsqlSocketFactory.getInstance(isTLS).createSocket(
134: host, port);
135: dataOutput = new BufferedOutputStream(socket
136: .getOutputStream());
137: dataInput = new DataInputStream(new BufferedInputStream(
138: socket.getInputStream()));
139: } catch (Exception e) {
140:
141: /** @todo fredt - change error to no connetion established */
142: throw Trace.error(Trace.SOCKET_ERROR);
143: }
144: }
145:
146: protected void closeConnection() {
147:
148: try {
149: if (socket != null) {
150: socket.close();
151: }
152: } catch (Exception e) {
153: }
154:
155: socket = null;
156: }
157:
158: public synchronized Result execute(Result r) throws HsqlException {
159:
160: try {
161: r.sessionID = sessionID;
162: r.databaseID = databaseID;
163:
164: write(r);
165:
166: return read();
167: } catch (Throwable e) {
168: throw Trace.error(Trace.CONNECTION_IS_BROKEN, e.toString());
169: }
170: }
171:
172: public void close() {
173:
174: if (isClosed) {
175: return;
176: }
177:
178: isClosed = true;
179:
180: try {
181: resultOut.setResultType(ResultConstants.SQLDISCONNECT);
182: execute(resultOut);
183: } catch (Exception e) {
184: }
185:
186: try {
187: closeConnection();
188: } catch (Exception e) {
189: }
190: }
191:
192: private Object getAttribute(int id) throws HsqlException {
193:
194: resultOut.setResultType(ResultConstants.GETSESSIONATTR);
195:
196: Result in = execute(resultOut);
197:
198: if (in.isError()) {
199: throw Trace.error(in);
200: }
201:
202: return in.rRoot.data[id];
203: }
204:
205: private void setAttribute(Object property, int id)
206: throws HsqlException {
207:
208: resultOut.setResultType(ResultConstants.SETSESSIONATTR);
209: ArrayUtil.fillArray(resultOut.rRoot.data, null);
210:
211: resultOut.rRoot.data[id] = property;
212:
213: Result resultIn = execute(resultOut);
214:
215: if (resultIn.isError()) {
216: throw Trace.error(resultIn);
217: }
218: }
219:
220: public boolean isReadOnly() throws HsqlException {
221:
222: Object info = getAttribute(Session.INFO_CONNECTION_READONLY);
223:
224: isReadOnly = ((Boolean) info).booleanValue();
225:
226: return isReadOnly;
227: }
228:
229: public void setReadOnly(boolean mode) throws HsqlException {
230:
231: if (mode != isReadOnly) {
232: setAttribute(mode ? Boolean.TRUE : Boolean.FALSE,
233: Session.INFO_CONNECTION_READONLY);
234:
235: isReadOnly = mode;
236: }
237: }
238:
239: public boolean isAutoCommit() throws HsqlException {
240:
241: Object info = getAttribute(SessionInterface.INFO_AUTOCOMMIT);
242:
243: isAutoCommit = ((Boolean) info).booleanValue();
244:
245: return isAutoCommit;
246: }
247:
248: public void setAutoCommit(boolean mode) throws HsqlException {
249:
250: if (mode != isAutoCommit) {
251: setAttribute(mode ? Boolean.TRUE : Boolean.FALSE,
252: SessionInterface.INFO_AUTOCOMMIT);
253:
254: isAutoCommit = mode;
255: }
256: }
257:
258: public void setIsolation(int level) throws HsqlException {
259: setAttribute(ValuePool.getInt(level),
260: SessionInterface.INFO_ISOLATION);
261: }
262:
263: public int getIsolation() throws HsqlException {
264:
265: Object info = getAttribute(Session.INFO_ISOLATION);
266:
267: return ((Integer) info).intValue();
268: }
269:
270: public boolean isClosed() {
271: return isClosed;
272: }
273:
274: public Session getSession() {
275: return null;
276: }
277:
278: public void startPhasedTransaction() throws HsqlException {
279: }
280:
281: public void prepareCommit() throws HsqlException {
282:
283: resultOut.setResultType(ResultConstants.SQLENDTRAN);
284:
285: resultOut.updateCount = ResultConstants.HSQLPREPARECOMMIT;
286:
287: resultOut.setMainString("");
288: execute(resultOut);
289: }
290:
291: public void commit() throws HsqlException {
292:
293: resultOut.setResultType(ResultConstants.SQLENDTRAN);
294:
295: resultOut.updateCount = ResultConstants.COMMIT;
296:
297: resultOut.setMainString("");
298: execute(resultOut);
299: }
300:
301: public void rollback() throws HsqlException {
302:
303: resultOut.setResultType(ResultConstants.SQLENDTRAN);
304:
305: resultOut.updateCount = ResultConstants.ROLLBACK;
306:
307: resultOut.setMainString("");
308: execute(resultOut);
309: }
310:
311: public int getId() {
312: return sessionID;
313: }
314:
315: /**
316: * Used by pooled connections to reset the server-side session to a new
317: * one. In case of failure, the connection is closed.
318: *
319: * When the Connection.close() method is called, a pooled connection calls
320: * this method instead of HSQLClientConnection.close(). It can then
321: * reuse the HSQLClientConnection object with no further initialisation.
322: *
323: */
324: public void resetSession() throws HsqlException {
325:
326: Result login = new Result(ResultConstants.HSQLRESETSESSION);
327: Result resultIn = execute(login);
328:
329: if (resultIn.isError()) {
330: isClosed = true;
331:
332: closeConnection();
333:
334: throw Trace.error(resultIn);
335: }
336:
337: sessionID = resultIn.sessionID;
338: databaseID = resultIn.databaseID;
339: }
340:
341: protected void write(Result r) throws IOException, HsqlException {
342: Result.write(r, rowOut, dataOutput);
343: }
344:
345: protected Result read() throws IOException, HsqlException {
346:
347: Result r = Result.read(rowIn, dataInput);
348:
349: rowOut.setBuffer(mainBuffer);
350: rowIn.resetRow(mainBuffer.length);
351:
352: return r;
353: }
354: }
|