EMMA Coverage Report (generated Sun Mar 01 22:06:14 CET 2015)
[all classes][org.h2.engine]

COVERAGE SUMMARY FOR SOURCE FILE [SessionRemote.java]

nameclass, %method, %block, %line, %
SessionRemote.java100% (1/1)90%  (45/50)88%  (1280/1456)86%  (334.2/388)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class SessionRemote100% (1/1)90%  (45/50)88%  (1280/1456)86%  (334.2/388)
checkWritingAllowed (): void 0%   (0/1)0%   (0/1)0%   (0/1)
getLobFileListCache (): SmallLRUCache 0%   (0/1)0%   (0/2)0%   (0/1)
getPowerOffCount (): int 0%   (0/1)0%   (0/2)0%   (0/1)
reconnect (boolean): SessionInterface 0%   (0/1)0%   (0/2)0%   (0/1)
setPowerOffCount (int): void 0%   (0/1)0%   (0/3)0%   (0/1)
openFile (String, String, boolean): FileStore 100% (1/1)54%  (22/41)58%  (7/12)
setAutoCommitFromServer (boolean): void 100% (1/1)56%  (9/16)67%  (4/6)
setAutoCommitSend (boolean): void 100% (1/1)63%  (49/78)59%  (10/17)
hasPendingTransaction (): boolean 100% (1/1)69%  (35/51)58%  (6.4/11)
autoReconnect (int): boolean 100% (1/1)70%  (52/74)64%  (16/25)
initJavaObjectSerializer (): void 100% (1/1)77%  (40/52)78%  (12.5/16)
autoCommitIfCluster (): void 100% (1/1)82%  (36/44)80%  (8/10)
readLob (long, byte [], long, byte [], int, int): int 100% (1/1)84%  (63/75)82%  (15.5/19)
readSerializationSettings (): String 100% (1/1)85%  (28/33)93%  (8.4/9)
cancelStatement (int): void 100% (1/1)90%  (52/58)88%  (14/16)
connectServer (ConnectionInfo): void 100% (1/1)92%  (237/259)88%  (60/68)
close (): void 100% (1/1)94%  (73/78)97%  (23.4/24)
initTransfer (ConnectionInfo, String, String): Transfer 100% (1/1)98%  (135/138)97%  (31/32)
SessionRemote (ConnectionInfo): void 100% (1/1)100% (17/17)100% (6/6)
addTemporaryLob (Value): void 100% (1/1)100% (1/1)100% (1/1)
afterWriting (): void 100% (1/1)100% (1/1)100% (1/1)
cancel (): void 100% (1/1)100% (1/1)100% (1/1)
checkClosed (): void 100% (1/1)100% (8/8)100% (3/3)
checkClusterDisableAutoCommit (String): void 100% (1/1)100% (33/33)100% (7/7)
checkPowerOff (): void 100% (1/1)100% (1/1)100% (1/1)
connectEmbeddedOrServer (boolean): SessionInterface 100% (1/1)100% (84/84)100% (25/25)
done (Transfer): void 100% (1/1)100% (77/77)100% (20/20)
getAutoCommit (): boolean 100% (1/1)100% (3/3)100% (1/1)
getClusterServers (): ArrayList 100% (1/1)100% (39/39)100% (5/5)
getCurrentId (): int 100% (1/1)100% (3/3)100% (1/1)
getDataHandler (): DataHandler 100% (1/1)100% (2/2)100% (1/1)
getDatabasePath (): String 100% (1/1)100% (2/2)100% (1/1)
getFilePrefix (String): String 100% (1/1)100% (38/38)100% (8/8)
getJavaObjectSerializer (): JavaObjectSerializer 100% (1/1)100% (5/5)100% (2/2)
getLastReconnect (): int 100% (1/1)100% (3/3)100% (1/1)
getLobCompressionAlgorithm (int): String 100% (1/1)100% (2/2)100% (1/1)
getLobStorage (): LobStorageInterface 100% (1/1)100% (12/12)100% (3/3)
getLobSyncObject (): Object 100% (1/1)100% (3/3)100% (1/1)
getMaxLengthInplaceLob (): int 100% (1/1)100% (2/2)100% (1/1)
getNextId (): int 100% (1/1)100% (8/8)100% (1/1)
getTempFileDeleter (): TempFileDeleter 100% (1/1)100% (9/9)100% (3/3)
getTrace (): Trace 100% (1/1)100% (5/5)100% (1/1)
isClosed (): boolean 100% (1/1)100% (11/11)100% (1/1)
isClustered (): boolean 100% (1/1)100% (3/3)100% (1/1)
isReconnectNeeded (boolean): boolean 100% (1/1)100% (2/2)100% (1/1)
prepareCommand (String, int): CommandInterface 100% (1/1)100% (11/11)100% (2/2)
removeServer (IOException, int, int): void 100% (1/1)100% (24/24)100% (7/7)
setAutoCommit (boolean): void 100% (1/1)100% (10/10)100% (4/4)
switchOffCluster (): void 100% (1/1)100% (9/9)100% (3/3)
traceOperation (String, int): void 100% (1/1)100% (20/20)100% (3/3)

1/*
2 * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
3 * and the EPL 1.0 (http://h2database.com/html/license.html).
4 * Initial Developer: H2 Group
5 */
6package org.h2.engine;
7 
8import java.io.IOException;
9import java.net.Socket;
10import java.util.ArrayList;
11 
12import org.h2.api.DatabaseEventListener;
13import org.h2.api.ErrorCode;
14import org.h2.api.JavaObjectSerializer;
15import org.h2.command.CommandInterface;
16import org.h2.command.CommandRemote;
17import org.h2.command.dml.SetTypes;
18import org.h2.jdbc.JdbcSQLException;
19import org.h2.message.DbException;
20import org.h2.message.Trace;
21import org.h2.message.TraceSystem;
22import org.h2.result.ResultInterface;
23import org.h2.store.DataHandler;
24import org.h2.store.FileStore;
25import org.h2.store.LobStorageFrontend;
26import org.h2.store.LobStorageInterface;
27import org.h2.store.fs.FileUtils;
28import org.h2.util.JdbcUtils;
29import org.h2.util.MathUtils;
30import org.h2.util.NetUtils;
31import org.h2.util.New;
32import org.h2.util.SmallLRUCache;
33import org.h2.util.StringUtils;
34import org.h2.util.TempFileDeleter;
35import org.h2.value.Transfer;
36import org.h2.value.Value;
37 
38/**
39 * The client side part of a session when using the server mode. This object
40 * communicates with a Session on the server side.
41 */
42public class SessionRemote extends SessionWithState implements DataHandler {
43 
44    public static final int SESSION_PREPARE = 0;
45    public static final int SESSION_CLOSE = 1;
46    public static final int COMMAND_EXECUTE_QUERY = 2;
47    public static final int COMMAND_EXECUTE_UPDATE = 3;
48    public static final int COMMAND_CLOSE = 4;
49    public static final int RESULT_FETCH_ROWS = 5;
50    public static final int RESULT_RESET = 6;
51    public static final int RESULT_CLOSE = 7;
52    public static final int COMMAND_COMMIT = 8;
53    public static final int CHANGE_ID = 9;
54    public static final int COMMAND_GET_META_DATA = 10;
55    public static final int SESSION_PREPARE_READ_PARAMS = 11;
56    public static final int SESSION_SET_ID = 12;
57    public static final int SESSION_CANCEL_STATEMENT = 13;
58    public static final int SESSION_CHECK_KEY = 14;
59    public static final int SESSION_SET_AUTOCOMMIT = 15;
60    public static final int SESSION_HAS_PENDING_TRANSACTION = 16;
61    public static final int LOB_READ = 17;
62 
63    public static final int STATUS_ERROR = 0;
64    public static final int STATUS_OK = 1;
65    public static final int STATUS_CLOSED = 2;
66    public static final int STATUS_OK_STATE_CHANGED = 3;
67 
68    private static SessionFactory sessionFactory;
69 
70    private TraceSystem traceSystem;
71    private Trace trace;
72    private ArrayList<Transfer> transferList = New.arrayList();
73    private int nextId;
74    private boolean autoCommit = true;
75    private CommandInterface autoCommitFalse, autoCommitTrue;
76    private ConnectionInfo connectionInfo;
77    private String databaseName;
78    private String cipher;
79    private byte[] fileEncryptionKey;
80    private final Object lobSyncObject = new Object();
81    private String sessionId;
82    private int clientVersion;
83    private boolean autoReconnect;
84    private int lastReconnect;
85    private SessionInterface embedded;
86    private DatabaseEventListener eventListener;
87    private LobStorageFrontend lobStorage;
88    private boolean cluster;
89    private TempFileDeleter tempFileDeleter;
90 
91    private JavaObjectSerializer javaObjectSerializer;
92    private volatile boolean javaObjectSerializerInitialized;
93 
94    public SessionRemote(ConnectionInfo ci) {
95        this.connectionInfo = ci;
96    }
97 
98    @Override
99    public ArrayList<String> getClusterServers() {
100        ArrayList<String> serverList = new ArrayList<String>();
101        for (int i = 0; i < transferList.size(); i++) {
102            Transfer transfer = transferList.get(i);
103            serverList.add(transfer.getSocket().getInetAddress().
104                    getHostAddress() + ":" +
105                    transfer.getSocket().getPort());
106        }
107        return serverList;
108    }
109 
110    private Transfer initTransfer(ConnectionInfo ci, String db, String server)
111            throws IOException {
112        Socket socket = NetUtils.createSocket(server,
113                Constants.DEFAULT_TCP_PORT, ci.isSSL());
114        Transfer trans = new Transfer(this);
115        trans.setSocket(socket);
116        trans.setSSL(ci.isSSL());
117        trans.init();
118        trans.writeInt(Constants.TCP_PROTOCOL_VERSION_6);
119        trans.writeInt(Constants.TCP_PROTOCOL_VERSION_15);
120        trans.writeString(db);
121        trans.writeString(ci.getOriginalURL());
122        trans.writeString(ci.getUserName());
123        trans.writeBytes(ci.getUserPasswordHash());
124        trans.writeBytes(ci.getFilePasswordHash());
125        String[] keys = ci.getKeys();
126        trans.writeInt(keys.length);
127        for (String key : keys) {
128            trans.writeString(key).writeString(ci.getProperty(key));
129        }
130        try {
131            done(trans);
132            clientVersion = trans.readInt();
133            trans.setVersion(clientVersion);
134            if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_14) {
135                if (ci.getFileEncryptionKey() != null) {
136                    trans.writeBytes(ci.getFileEncryptionKey());
137                }
138            }
139            trans.writeInt(SessionRemote.SESSION_SET_ID);
140            trans.writeString(sessionId);
141            done(trans);
142            if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_15) {
143                autoCommit = trans.readBoolean();
144            } else {
145                autoCommit = true;
146            }
147            return trans;
148        } catch (DbException e) {
149            trans.close();
150            throw e;
151        }
152    }
153 
154    @Override
155    public boolean hasPendingTransaction() {
156        if (clientVersion < Constants.TCP_PROTOCOL_VERSION_10) {
157            return true;
158        }
159        for (int i = 0, count = 0; i < transferList.size(); i++) {
160            Transfer transfer = transferList.get(i);
161            try {
162                traceOperation("SESSION_HAS_PENDING_TRANSACTION", 0);
163                transfer.writeInt(
164                        SessionRemote.SESSION_HAS_PENDING_TRANSACTION);
165                done(transfer);
166                return transfer.readInt() != 0;
167            } catch (IOException e) {
168                removeServer(e, i--, ++count);
169            }
170        }
171        return true;
172    }
173 
174    @Override
175    public void cancel() {
176        // this method is called when closing the connection
177        // the statement that is currently running is not canceled in this case
178        // however Statement.cancel is supported
179    }
180 
181    /**
182     * Cancel the statement with the given id.
183     *
184     * @param id the statement id
185     */
186    public void cancelStatement(int id) {
187        for (Transfer transfer : transferList) {
188            try {
189                Transfer trans = transfer.openNewConnection();
190                trans.init();
191                trans.writeInt(clientVersion);
192                trans.writeInt(clientVersion);
193                trans.writeString(null);
194                trans.writeString(null);
195                trans.writeString(sessionId);
196                trans.writeInt(SessionRemote.SESSION_CANCEL_STATEMENT);
197                trans.writeInt(id);
198                trans.close();
199            } catch (IOException e) {
200                trace.debug(e, "could not cancel statement");
201            }
202        }
203    }
204 
205    private void checkClusterDisableAutoCommit(String serverList) {
206        if (autoCommit && transferList.size() > 1) {
207            setAutoCommitSend(false);
208            CommandInterface c = prepareCommand(
209                    "SET CLUSTER " + serverList, Integer.MAX_VALUE);
210            // this will set autoCommit to false
211            c.executeUpdate();
212            // so we need to switch it on
213            autoCommit = true;
214            cluster = true;
215        }
216    }
217 
218    @Override
219    public boolean getAutoCommit() {
220        return autoCommit;
221    }
222 
223    @Override
224    public void setAutoCommit(boolean autoCommit) {
225        if (!cluster) {
226            setAutoCommitSend(autoCommit);
227        }
228        this.autoCommit = autoCommit;
229    }
230 
231    public void setAutoCommitFromServer(boolean autoCommit) {
232        if (cluster) {
233            if (autoCommit) {
234                // the user executed SET AUTOCOMMIT TRUE
235                setAutoCommitSend(false);
236                this.autoCommit = true;
237            }
238        } else {
239            this.autoCommit = autoCommit;
240        }
241    }
242 
243    private void setAutoCommitSend(boolean autoCommit) {
244        if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_8) {
245            for (int i = 0, count = 0; i < transferList.size(); i++) {
246                Transfer transfer = transferList.get(i);
247                try {
248                    traceOperation("SESSION_SET_AUTOCOMMIT", autoCommit ? 1 : 0);
249                    transfer.writeInt(SessionRemote.SESSION_SET_AUTOCOMMIT).
250                            writeBoolean(autoCommit);
251                    done(transfer);
252                } catch (IOException e) {
253                    removeServer(e, i--, ++count);
254                }
255            }
256        } else {
257            if (autoCommit) {
258                if (autoCommitTrue == null) {
259                    autoCommitTrue = prepareCommand(
260                            "SET AUTOCOMMIT TRUE", Integer.MAX_VALUE);
261                }
262                autoCommitTrue.executeUpdate();
263            } else {
264                if (autoCommitFalse == null) {
265                    autoCommitFalse = prepareCommand(
266                            "SET AUTOCOMMIT FALSE", Integer.MAX_VALUE);
267                }
268                autoCommitFalse.executeUpdate();
269            }
270        }
271    }
272 
273    /**
274     * Calls COMMIT if the session is in cluster mode.
275     */
276    public void autoCommitIfCluster() {
277        if (autoCommit && cluster) {
278            // server side auto commit is off because of race conditions
279            // (update set id=1 where id=0, but update set id=2 where id=0 is
280            // faster)
281            for (int i = 0, count = 0; i < transferList.size(); i++) {
282                Transfer transfer = transferList.get(i);
283                try {
284                    traceOperation("COMMAND_COMMIT", 0);
285                    transfer.writeInt(SessionRemote.COMMAND_COMMIT);
286                    done(transfer);
287                } catch (IOException e) {
288                    removeServer(e, i--, ++count);
289                }
290            }
291        }
292    }
293 
294    private String getFilePrefix(String dir) {
295        StringBuilder buff = new StringBuilder(dir);
296        buff.append('/');
297        for (int i = 0; i < databaseName.length(); i++) {
298            char ch = databaseName.charAt(i);
299            if (Character.isLetterOrDigit(ch)) {
300                buff.append(ch);
301            } else {
302                buff.append('_');
303            }
304        }
305        return buff.toString();
306    }
307 
308    @Override
309    public int getPowerOffCount() {
310        return 0;
311    }
312 
313    @Override
314    public void setPowerOffCount(int count) {
315        throw DbException.getUnsupportedException("remote");
316    }
317 
318    /**
319     * Open a new (remote or embedded) session.
320     *
321     * @param openNew whether to open a new session in any case
322     * @return the session
323     */
324    public SessionInterface connectEmbeddedOrServer(boolean openNew) {
325        ConnectionInfo ci = connectionInfo;
326        if (ci.isRemote()) {
327            connectServer(ci);
328            return this;
329        }
330        // create the session using reflection,
331        // so that the JDBC layer can be compiled without it
332        boolean autoServerMode = Boolean.parseBoolean(
333                ci.getProperty("AUTO_SERVER", "false"));
334        ConnectionInfo backup = null;
335        try {
336            if (autoServerMode) {
337                backup = ci.clone();
338                connectionInfo = ci.clone();
339            }
340            if (openNew) {
341                ci.setProperty("OPEN_NEW", "true");
342            }
343            if (sessionFactory == null) {
344                sessionFactory = (SessionFactory) Class.forName(
345                        "org.h2.engine.Engine").getMethod("getInstance").invoke(null);
346            }
347            return sessionFactory.createSession(ci);
348        } catch (Exception re) {
349            DbException e = DbException.convert(re);
350            if (e.getErrorCode() == ErrorCode.DATABASE_ALREADY_OPEN_1) {
351                if (autoServerMode) {
352                    String serverKey = ((JdbcSQLException) e.getSQLException()).
353                            getSQL();
354                    if (serverKey != null) {
355                        backup.setServerKey(serverKey);
356                        // OPEN_NEW must be removed now, otherwise
357                        // opening a session with AUTO_SERVER fails
358                        // if another connection is already open
359                        backup.removeProperty("OPEN_NEW", null);
360                        connectServer(backup);
361                        return this;
362                    }
363                }
364            }
365            throw e;
366        }
367    }
368 
369    private void connectServer(ConnectionInfo ci) {
370        String name = ci.getName();
371        if (name.startsWith("//")) {
372            name = name.substring("//".length());
373        }
374        int idx = name.indexOf('/');
375        if (idx < 0) {
376            throw ci.getFormatException();
377        }
378        databaseName = name.substring(idx + 1);
379        String server = name.substring(0, idx);
380        traceSystem = new TraceSystem(null);
381        String traceLevelFile = ci.getProperty(
382                SetTypes.TRACE_LEVEL_FILE, null);
383        if (traceLevelFile != null) {
384            int level = Integer.parseInt(traceLevelFile);
385            String prefix = getFilePrefix(
386                    SysProperties.CLIENT_TRACE_DIRECTORY);
387            try {
388                traceSystem.setLevelFile(level);
389                if (level > 0 && level < 4) {
390                    String file = FileUtils.createTempFile(prefix,
391                            Constants.SUFFIX_TRACE_FILE, false, false);
392                    traceSystem.setFileName(file);
393                }
394            } catch (IOException e) {
395                throw DbException.convertIOException(e, prefix);
396            }
397        }
398        String traceLevelSystemOut = ci.getProperty(
399                SetTypes.TRACE_LEVEL_SYSTEM_OUT, null);
400        if (traceLevelSystemOut != null) {
401            int level = Integer.parseInt(traceLevelSystemOut);
402            traceSystem.setLevelSystemOut(level);
403        }
404        trace = traceSystem.getTrace(Trace.JDBC);
405        String serverList = null;
406        if (server.indexOf(',') >= 0) {
407            serverList = StringUtils.quoteStringSQL(server);
408            ci.setProperty("CLUSTER", Constants.CLUSTERING_ENABLED);
409        }
410        autoReconnect = Boolean.parseBoolean(ci.getProperty(
411                "AUTO_RECONNECT", "false"));
412        // AUTO_SERVER implies AUTO_RECONNECT
413        boolean autoServer = Boolean.parseBoolean(ci.getProperty(
414                "AUTO_SERVER", "false"));
415        if (autoServer && serverList != null) {
416            throw DbException
417                    .getUnsupportedException("autoServer && serverList != null");
418        }
419        autoReconnect |= autoServer;
420        if (autoReconnect) {
421            String className = ci.getProperty("DATABASE_EVENT_LISTENER");
422            if (className != null) {
423                className = StringUtils.trim(className, true, true, "'");
424                try {
425                    eventListener = (DatabaseEventListener) JdbcUtils
426                            .loadUserClass(className).newInstance();
427                } catch (Throwable e) {
428                    throw DbException.convert(e);
429                }
430            }
431        }
432        cipher = ci.getProperty("CIPHER");
433        if (cipher != null) {
434            fileEncryptionKey = MathUtils.secureRandomBytes(32);
435        }
436        String[] servers = StringUtils.arraySplit(server, ',', true);
437        int len = servers.length;
438        transferList.clear();
439        sessionId = StringUtils.convertBytesToHex(MathUtils.secureRandomBytes(32));
440        // TODO cluster: support more than 2 connections
441        boolean switchOffCluster = false;
442        try {
443            for (int i = 0; i < len; i++) {
444                String s = servers[i];
445                try {
446                    Transfer trans = initTransfer(ci, databaseName, s);
447                    transferList.add(trans);
448                } catch (IOException e) {
449                    if (len == 1) {
450                        throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, e, e + ": " + s);
451                    }
452                    switchOffCluster = true;
453                }
454            }
455            checkClosed();
456            if (switchOffCluster) {
457                switchOffCluster();
458            }
459            checkClusterDisableAutoCommit(serverList);
460        } catch (DbException e) {
461            traceSystem.close();
462            throw e;
463        }
464    }
465 
466    private void switchOffCluster() {
467        CommandInterface ci = prepareCommand("SET CLUSTER ''", Integer.MAX_VALUE);
468        ci.executeUpdate();
469    }
470 
471    /**
472     * Remove a server from the list of cluster nodes and disables the cluster
473     * mode.
474     *
475     * @param e the exception (used for debugging)
476     * @param i the index of the server to remove
477     * @param count the retry count index
478     */
479    public void removeServer(IOException e, int i, int count) {
480        trace.debug(e, "removing server because of exception");
481        transferList.remove(i);
482        if (transferList.size() == 0 && autoReconnect(count)) {
483            return;
484        }
485        checkClosed();
486        switchOffCluster();
487    }
488 
489    @Override
490    public synchronized CommandInterface prepareCommand(String sql, int fetchSize) {
491        checkClosed();
492        return new CommandRemote(this, transferList, sql, fetchSize);
493    }
494 
495    /**
496     * Automatically re-connect if necessary and if configured to do so.
497     *
498     * @param count the retry count index
499     * @return true if reconnected
500     */
501    private boolean autoReconnect(int count) {
502        if (!isClosed()) {
503            return false;
504        }
505        if (!autoReconnect) {
506            return false;
507        }
508        if (!cluster && !autoCommit) {
509            return false;
510        }
511        if (count > SysProperties.MAX_RECONNECT) {
512            return false;
513        }
514        lastReconnect++;
515        while (true) {
516            try {
517                embedded = connectEmbeddedOrServer(false);
518                break;
519            } catch (DbException e) {
520                if (e.getErrorCode() != ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE) {
521                    throw e;
522                }
523                // exclusive mode: re-try endlessly
524                try {
525                    Thread.sleep(500);
526                } catch (Exception e2) {
527                    // ignore
528                }
529            }
530        }
531        if (embedded == this) {
532            // connected to a server somewhere else
533            embedded = null;
534        } else {
535            // opened an embedded connection now -
536            // must connect to this database in server mode
537            // unfortunately
538            connectEmbeddedOrServer(true);
539        }
540        recreateSessionState();
541        if (eventListener != null) {
542            eventListener.setProgress(DatabaseEventListener.STATE_RECONNECTED,
543                    databaseName, count, SysProperties.MAX_RECONNECT);
544        }
545        return true;
546    }
547 
548    /**
549     * Check if this session is closed and throws an exception if so.
550     *
551     * @throws DbException if the session is closed
552     */
553    public void checkClosed() {
554        if (isClosed()) {
555            throw DbException.get(ErrorCode.CONNECTION_BROKEN_1, "session closed");
556        }
557    }
558 
559    @Override
560    public void close() {
561        RuntimeException closeError = null;
562        if (transferList != null) {
563            synchronized (this) {
564                for (Transfer transfer : transferList) {
565                    try {
566                        traceOperation("SESSION_CLOSE", 0);
567                        transfer.writeInt(SessionRemote.SESSION_CLOSE);
568                        done(transfer);
569                        transfer.close();
570                    } catch (RuntimeException e) {
571                        trace.error(e, "close");
572                        closeError = e;
573                    } catch (Exception e) {
574                        trace.error(e, "close");
575                    }
576                }
577            }
578            transferList = null;
579        }
580        traceSystem.close();
581        if (embedded != null) {
582            embedded.close();
583            embedded = null;
584        }
585        if (closeError != null) {
586            throw closeError;
587        }
588    }
589 
590    @Override
591    public Trace getTrace() {
592        return traceSystem.getTrace(Trace.JDBC);
593    }
594 
595    public int getNextId() {
596        return nextId++;
597    }
598 
599    public int getCurrentId() {
600        return nextId;
601    }
602 
603    /**
604     * Called to flush the output after data has been sent to the server and
605     * just before receiving data. This method also reads the status code from
606     * the server and throws any exception the server sent.
607     *
608     * @param transfer the transfer object
609     * @throws DbException if the server sent an exception
610     * @throws IOException if there is a communication problem between client
611     *             and server
612     */
613    public void done(Transfer transfer) throws IOException {
614        transfer.flush();
615        int status = transfer.readInt();
616        if (status == STATUS_ERROR) {
617            String sqlstate = transfer.readString();
618            String message = transfer.readString();
619            String sql = transfer.readString();
620            int errorCode = transfer.readInt();
621            String stackTrace = transfer.readString();
622            JdbcSQLException s = new JdbcSQLException(message, sql, sqlstate,
623                    errorCode, null, stackTrace);
624            if (errorCode == ErrorCode.CONNECTION_BROKEN_1) {
625                // allow re-connect
626                IOException e = new IOException(s.toString(), s);
627                throw e;
628            }
629            throw DbException.convert(s);
630        } else if (status == STATUS_CLOSED) {
631            transferList = null;
632        } else if (status == STATUS_OK_STATE_CHANGED) {
633            sessionStateChanged = true;
634        } else if (status == STATUS_OK) {
635            // ok
636        } else {
637            throw DbException.get(ErrorCode.CONNECTION_BROKEN_1,
638                    "unexpected status " + status);
639        }
640    }
641 
642    /**
643     * Returns true if the connection was opened in cluster mode.
644     *
645     * @return true if it is
646     */
647    public boolean isClustered() {
648        return cluster;
649    }
650 
651    @Override
652    public boolean isClosed() {
653        return transferList == null || transferList.size() == 0;
654    }
655 
656    /**
657     * Write the operation to the trace system if debug trace is enabled.
658     *
659     * @param operation the operation performed
660     * @param id the id of the operation
661     */
662    public void traceOperation(String operation, int id) {
663        if (trace.isDebugEnabled()) {
664            trace.debug("{0} {1}", operation, id);
665        }
666    }
667 
668    @Override
669    public void checkPowerOff() {
670        // ok
671    }
672 
673    @Override
674    public void checkWritingAllowed() {
675        // ok
676    }
677 
678    @Override
679    public String getDatabasePath() {
680        return "";
681    }
682 
683    @Override
684    public String getLobCompressionAlgorithm(int type) {
685        return null;
686    }
687 
688    @Override
689    public int getMaxLengthInplaceLob() {
690        return SysProperties.LOB_CLIENT_MAX_SIZE_MEMORY;
691    }
692 
693    @Override
694    public FileStore openFile(String name, String mode, boolean mustExist) {
695        if (mustExist && !FileUtils.exists(name)) {
696            throw DbException.get(ErrorCode.FILE_NOT_FOUND_1, name);
697        }
698        FileStore store;
699        if (cipher == null) {
700            store = FileStore.open(this, name, mode);
701        } else {
702            store = FileStore.open(this, name, mode, cipher, fileEncryptionKey, 0);
703        }
704        store.setCheckedWriting(false);
705        try {
706            store.init();
707        } catch (DbException e) {
708            store.closeSilently();
709            throw e;
710        }
711        return store;
712    }
713 
714    @Override
715    public DataHandler getDataHandler() {
716        return this;
717    }
718 
719    @Override
720    public Object getLobSyncObject() {
721        return lobSyncObject;
722    }
723 
724    @Override
725    public SmallLRUCache<String, String[]> getLobFileListCache() {
726        return null;
727    }
728 
729    public int getLastReconnect() {
730        return lastReconnect;
731    }
732 
733    @Override
734    public TempFileDeleter getTempFileDeleter() {
735        if (tempFileDeleter == null) {
736            tempFileDeleter = TempFileDeleter.getInstance();
737        }
738        return tempFileDeleter;
739    }
740 
741    @Override
742    public boolean isReconnectNeeded(boolean write) {
743        return false;
744    }
745 
746    @Override
747    public SessionInterface reconnect(boolean write) {
748        return this;
749    }
750 
751    @Override
752    public void afterWriting() {
753        // nothing to do
754    }
755 
756    @Override
757    public LobStorageInterface getLobStorage() {
758        if (lobStorage == null) {
759            lobStorage = new LobStorageFrontend(this);
760        }
761        return lobStorage;
762    }
763 
764    @Override
765    public synchronized int readLob(long lobId, byte[] hmac, long offset,
766            byte[] buff, int off, int length) {
767        checkClosed();
768        for (int i = 0, count = 0; i < transferList.size(); i++) {
769            Transfer transfer = transferList.get(i);
770            try {
771                traceOperation("LOB_READ", (int) lobId);
772                transfer.writeInt(SessionRemote.LOB_READ);
773                transfer.writeLong(lobId);
774                if (clientVersion >= Constants.TCP_PROTOCOL_VERSION_12) {
775                    transfer.writeBytes(hmac);
776                }
777                transfer.writeLong(offset);
778                transfer.writeInt(length);
779                done(transfer);
780                length = transfer.readInt();
781                if (length <= 0) {
782                    return length;
783                }
784                transfer.readBytes(buff, off, length);
785                return length;
786            } catch (IOException e) {
787                removeServer(e, i--, ++count);
788            }
789        }
790        return 1;
791    }
792 
793    @Override
794    public JavaObjectSerializer getJavaObjectSerializer() {
795        initJavaObjectSerializer();
796        return javaObjectSerializer;
797    }
798 
799    private void initJavaObjectSerializer() {
800        if (javaObjectSerializerInitialized) {
801            return;
802        }
803        synchronized (this) {
804            if (javaObjectSerializerInitialized) {
805                return;
806            }
807            String serializerFQN = readSerializationSettings();
808            if (serializerFQN != null) {
809                serializerFQN = serializerFQN.trim();
810                if (!serializerFQN.isEmpty() && !serializerFQN.equals("null")) {
811                    try {
812                        javaObjectSerializer = (JavaObjectSerializer) JdbcUtils
813                                .loadUserClass(serializerFQN).newInstance();
814                    } catch (Exception e) {
815                        throw DbException.convert(e);
816                    }
817                }
818            }
819            javaObjectSerializerInitialized = true;
820        }
821    }
822 
823    /**
824     * Read the serializer name from the persistent database settings.
825     *
826     * @return the serializer
827     */
828    private String readSerializationSettings() {
829        String javaObjectSerializerFQN = null;
830        CommandInterface ci = prepareCommand(
831                "SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS "+
832                " WHERE NAME='JAVA_OBJECT_SERIALIZER'", Integer.MAX_VALUE);
833        try {
834            ResultInterface result = ci.executeQuery(0, false);
835            if (result.next()) {
836                Value[] row = result.currentRow();
837                javaObjectSerializerFQN = row[0].getString();
838            }
839        } finally {
840            ci.close();
841        }
842        return javaObjectSerializerFQN;
843    }
844 
845    @Override
846    public void addTemporaryLob(Value v) {
847        // do nothing
848    }
849}

[all classes][org.h2.engine]
EMMA 2.0.5312 (C) Vladimir Roubtsov