1 5 package org.h2.engine; 6 7 import java.io.IOException ; 8 import java.net.InetAddress ; 9 import java.net.Socket ; 10 import java.sql.SQLException ; 11 12 import org.h2.command.CommandInterface; 13 import org.h2.command.CommandRemote; 14 import org.h2.command.dml.SetTypes; 15 import org.h2.jdbc.JdbcSQLException; 16 import org.h2.message.Message; 17 import org.h2.message.Trace; 18 import org.h2.message.TraceSystem; 19 import org.h2.store.DataHandler; 20 import org.h2.store.FileStore; 21 import org.h2.util.FileUtils; 22 import org.h2.util.MathUtils; 23 import org.h2.util.NetUtils; 24 import org.h2.util.ObjectArray; 25 import org.h2.util.RandomUtils; 26 import org.h2.util.StringUtils; 27 import org.h2.value.Transfer; 28 import org.h2.value.Value; 29 30 public class SessionRemote implements SessionInterface, DataHandler { 31 32 public static final int SESSION_PREPARE = 0; 33 public static final int SESSION_CLOSE = 1; 34 public static final int COMMAND_EXECUTE_QUERY = 2; 35 public static final int COMMAND_EXECUTE_UPDATE = 3; 36 public static final int COMMAND_CLOSE = 4; 37 public static final int RESULT_FETCH_ROW = 5; 38 public static final int RESULT_RESET = 6; 39 public static final int RESULT_CLOSE = 7; 40 public static final int COMMAND_COMMIT = 8; 41 public static final int CHANGE_ID = 9; 42 public static final int STATUS_ERROR = 0; 43 public static final int STATUS_OK = 1; 44 public static final int STATUS_CLOSED = 2; 45 private TraceSystem traceSystem; 46 private Trace trace; 47 private ObjectArray transferList; 48 private int nextId; 49 private boolean autoCommit = true; 50 private CommandInterface switchOffAutoCommit; 51 private ConnectionInfo connectionInfo; 52 private int objectId; 53 private String databaseName; 54 private String cipher; 55 private byte[] fileEncryptionKey; 56 57 private Transfer initTransfer(ConnectionInfo ci, String db, String server) throws IOException , SQLException { 58 int port = Constants.DEFAULT_SERVER_PORT; 59 int startIndex = server.startsWith("[") ? server.indexOf(']') : 0; 62 int idx = server.indexOf(':', startIndex); 63 if (idx >= 0) { 64 port = MathUtils.decodeInt(server.substring(idx + 1)); 65 server = server.substring(0, idx); 66 } 67 InetAddress address = InetAddress.getByName(server); 68 Socket socket = NetUtils.createSocket(address, port, ci.isSSL()); 69 Transfer trans = new Transfer(this); 70 trans.setSocket(socket); 71 trans.init(); 72 trans.writeInt(Constants.TCP_DRIVER_VERSION); 73 trans.writeString(db); 74 trans.writeString(ci.getOriginalURL()); 75 trans.writeString(ci.getUserName()); 76 trans.writeBytes(ci.getUserPasswordHash()); 77 trans.writeBytes(ci.getFilePasswordHash()); 78 String [] keys = ci.getKeys(); 79 trans.writeInt(keys.length); 80 for(int i=0; i<keys.length; i++) { 81 String key = keys[i]; 82 trans.writeString(key).writeString(ci.getProperty(key)); 83 } 84 try { 85 done(trans); 86 } catch(SQLException e) { 87 trans.close(); 88 throw e; 89 } 90 autoCommit = true; 91 return trans; 92 } 93 94 private void switchOffAutocommitIfCluster() throws SQLException { 95 if(autoCommit && transferList.size() > 1) { 96 if(switchOffAutoCommit == null) { 97 switchOffAutoCommit = prepareCommand("SET AUTOCOMMIT FALSE"); 98 } 99 switchOffAutoCommit.executeUpdate(); 101 autoCommit = true; 103 } 104 } 105 106 public void setAutoCommit(boolean autoCommit) { 107 this.autoCommit = autoCommit; 108 } 109 110 public void autoCommitIfCluster() throws SQLException { 111 if(autoCommit && transferList!= null && transferList.size() > 1) { 112 for(int i=0; i<transferList.size(); i++) { 115 Transfer transfer = (Transfer) transferList.get(i); 116 try { 117 traceOperation("COMMAND_COMMIT", 0); 118 transfer.writeInt(SessionRemote.COMMAND_COMMIT); 119 done(transfer); 120 } catch(IOException e) { 121 removeServer(i); 122 } 123 } 124 } 125 } 126 127 private String getTraceFilePrefix(String dbName) throws SQLException { 128 String dir = Constants.CLIENT_TRACE_DIRECTORY; 129 StringBuffer buff = new StringBuffer (); 130 buff.append(dir); 131 for(int i=0; i<dbName.length(); i++) { 132 char ch = dbName.charAt(i); 133 if(Character.isLetterOrDigit(ch)) { 134 buff.append(ch); 135 } else { 136 buff.append('_'); 137 } 138 } 139 return buff.toString(); 140 } 141 142 public SessionRemote() { 143 } 144 145 public int getPowerOffCount() { 146 return 0; 147 } 148 149 public void setPowerOffCount(int count) throws SQLException { 150 throw Message.getUnsupportedException(); 151 } 152 153 public SessionInterface createSession(ConnectionInfo ci) throws SQLException { 154 return new SessionRemote(ci); 155 } 156 157 private SessionRemote(ConnectionInfo ci) throws SQLException { 158 this.connectionInfo = ci; 159 connect(); 160 } 161 162 private void connect() throws SQLException { 163 ConnectionInfo ci = connectionInfo; 164 String name = ci.getName(); 165 if(name.startsWith("//")) { 166 name = name.substring("//".length()); 167 } 168 int idx = name.indexOf('/'); 169 if(idx<0) { 170 throw ci.getFormatException(); 171 } 172 databaseName = name.substring(idx + 1); 173 String server = name.substring(0, idx); 174 traceSystem = new TraceSystem(null); 175 try { 176 String traceLevelFile = ci.getProperty(SetTypes.TRACE_LEVEL_FILE, null); 177 if(traceLevelFile != null) { 178 int level = Integer.parseInt(traceLevelFile); 179 String prefix = getTraceFilePrefix(databaseName); 180 String file = FileUtils.createTempFile(prefix, Constants.SUFFIX_TRACE_FILE, false); 181 traceSystem.setFileName(file); 182 traceSystem.setLevelFile(level); 183 } 184 String traceLevelSystemOut = ci.getProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT, null); 185 if(traceLevelSystemOut != null) { 186 int level = Integer.parseInt(traceLevelSystemOut); 187 traceSystem.setLevelSystemOut(level); 188 } 189 } catch(Exception e) { 190 throw Message.convert(e); 191 } 192 trace = traceSystem.getTrace(Trace.JDBC); 193 transferList = new ObjectArray(); 194 String serverlist = null; 195 if(server.indexOf(',') >= 0) { 196 serverlist = StringUtils.quoteStringSQL(server); 197 ci.setProperty("CLUSTER", serverlist); 198 } 199 cipher = ci.getProperty("CIPHER"); 200 if(cipher != null) { 201 fileEncryptionKey = RandomUtils.getSecureBytes(32); 202 } 203 String [] servers = StringUtils.arraySplit(server, ',', true); 204 int len = servers.length; 205 transferList = new ObjectArray(); 206 boolean switchOffCluster = false; 208 for(int i=0; i<len; i++) { 209 try { 210 Transfer trans = initTransfer(ci, databaseName, servers[i]); 211 transferList.add(trans); 212 } catch(IOException e) { 213 switchOffCluster = true; 214 } 215 } 216 checkClosed(); 217 if(switchOffCluster) { 218 switchOffCluster(); 219 } 220 switchOffAutocommitIfCluster(); 221 } 222 223 private void switchOffCluster() throws SQLException { 224 CommandInterface ci = prepareCommand("SET CLUSTER ''"); 225 ci.executeUpdate(); 226 } 227 228 public void removeServer(int i) throws SQLException { 229 transferList.remove(i); 230 checkClosed(); 231 switchOffCluster(); 232 } 233 234 public CommandInterface prepareCommand(String sql) throws SQLException { 235 synchronized(this) { 236 checkClosed(); 237 return new CommandRemote(this, transferList, sql); 238 } 239 } 240 241 public void checkClosed() throws SQLException { 242 if(isClosed()) { 243 throw Message.getSQLException(Message.CONNECTION_BROKEN); 245 } 246 } 247 248 public void close() { 249 if(transferList != null) { 250 synchronized(this) { 251 for(int i=0; i<transferList.size(); i++) { 252 Transfer transfer = (Transfer) transferList.get(i); 253 try { 254 traceOperation("SESSION_CLOSE", 0); 255 transfer.writeInt(SessionRemote.SESSION_CLOSE); 256 done(transfer); 257 transfer.close(); 258 } catch(Exception e) { 259 trace.error("close", e); 260 } 261 } 262 } 263 transferList = null; 264 } 265 traceSystem.close(); 266 } 267 268 public Trace getTrace() { 269 return traceSystem.getTrace(Trace.JDBC); 270 } 271 272 public int getNextId() { 273 return nextId++; 274 } 275 276 public int getCurrentId() { 277 return nextId; 278 } 279 280 public void done(Transfer transfer) throws SQLException , IOException { 281 transfer.flush(); 282 int status = transfer.readInt(); 283 if (status == STATUS_ERROR) { 284 String sqlstate = transfer.readString(); 285 String message = transfer.readString(); 286 int errorCode = transfer.readInt(); 287 String trace = transfer.readString(); 288 message = message + "\n" + trace; 289 throw new JdbcSQLException(message, sqlstate, errorCode, null); 290 } else if(status == STATUS_CLOSED) { 291 transferList = null; 292 } 293 } 294 295 public boolean isClustered() { 296 return transferList.size() > 1; 297 } 298 299 public boolean isClosed() { 300 return transferList == null || transferList.size() == 0; 301 } 302 303 public void traceOperation(String operation, int id) { 304 if(trace.debug()) { 305 trace.debug(operation + " " + id); 306 } 307 } 308 309 public int allocateObjectId(boolean needFresh, boolean dataFile) { 310 return objectId++; 311 } 312 313 public void checkPowerOff() throws SQLException { 314 } 315 316 public void checkWritingAllowed() throws SQLException { 317 } 318 319 public int compareTypeSave(Value a, Value b) throws SQLException { 320 throw Message.getInternalError(); 321 } 322 323 public String createTempFile() throws SQLException { 324 try { 325 return FileUtils.createTempFile(databaseName, Constants.SUFFIX_TEMP_FILE, true); 326 } catch (IOException e) { 327 throw Message.convert(e); 328 } 329 } 330 331 public void freeUpDiskSpace() throws SQLException { 332 } 333 334 public int getChecksum(byte[] data, int start, int end) { 335 return 0; 336 } 337 338 public String getDatabasePath() { 339 return ""; 340 } 341 342 public String getLobCompressionAlgorithm(int type) { 343 return null; 344 } 345 346 public int getMaxLengthInplaceLob() { 347 return Constants.DEFAULT_MAX_LENGTH_CLIENTSIDE_LOB; 348 } 349 350 public boolean getTextStorage() { 351 return false; 352 } 353 354 public void handleInvalidChecksum() throws SQLException { 355 throw Message.getSQLException(Message.FILE_CORRUPTED_1, "wrong checksum"); 356 } 357 358 public FileStore openFile(String name, boolean mustExist) throws SQLException { 359 if(mustExist && !FileUtils.exists(name)) { 360 throw Message.getSQLException(Message.FILE_CORRUPTED_1, name); 361 } 362 FileStore store; 363 byte[] magic = Constants.MAGIC_FILE_HEADER.getBytes(); 364 if(cipher == null) { 365 store = FileStore.open(this, name, magic); 366 } else { 367 store = FileStore.open(this, name, magic, cipher, fileEncryptionKey, 0); 368 } 369 try { 370 store.init(); 371 } catch(SQLException e) { 372 store.closeSilently(); 373 throw e; 374 } 375 return store; 376 } 377 378 public DataHandler getDataHandler() { 379 return this; 380 } 381 382 } 383 | Popular Tags |