1 /*- 2 * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved. 3 * 4 * See the file EXAMPLES-LICENSE for license information. 5 * 6 */ 7 8 package db; 9 10 import com.sleepycat.db.*; 11 import java.io.File; 12 import java.io.FileNotFoundException; 13 import java.io.OutputStream; 14 import java.util.Arrays; 15 16 /* 17 * An example of a program for using the external files interface, 18 * which is designed for working with very large records 19 * (such as those over 1 megabyte in size). 20 * 21 * For comparison purposes, this example uses a similar structure 22 * as examples/c/ex_external_file.c. 23 */ 24 public class ExternalFileExample { 25 private static final String progname = "ExternalFileExample"; 26 private Database db; 27 private Environment dbenv; 28 main(String[] argv)29 public static void main(String[] argv) { 30 File envHome = new File("EXTERNALFILE_EX"); 31 ExternalFileExample example = null; 32 33 for (int i = 0; i < argv.length; ++i) { 34 if (argv[i].equals("-h")) { 35 if (++i >= argv.length) 36 usage(); 37 envHome = new File(argv[i]); 38 } else 39 usage(); 40 } 41 42 try { 43 // Clear the example directory. 44 clearDirectory(envHome); 45 46 // Set up the Environment and a database with external files 47 // enabled. 48 example = new ExternalFileExample(envHome); 49 50 System.out.println("Insert external file records"); 51 example.insertExternalFiles(); 52 53 System.out.println("Read external files"); 54 example.readExternalFiles(); 55 } catch (DatabaseException dbe) { 56 System.err.println(progname + ": environment open: " + dbe.toString()); 57 System.exit (1); 58 } catch (FileNotFoundException fnfe) { 59 System.err.println(progname + ": unexpected open environment error " + fnfe); 60 System.exit (1); 61 } 62 63 // Close the database and environment. 64 if (example != null) 65 example.close(); 66 } 67 ExternalFileExample(File envHome)68 public ExternalFileExample(File envHome) 69 throws DatabaseException, FileNotFoundException { 70 71 System.out.println("Setup env"); 72 setupEnvironment(envHome, System.err); 73 74 System.out.println("Create the database"); 75 createExternalFileDatabase(); 76 } 77 setupEnvironment(File home, OutputStream errs)78 private void setupEnvironment(File home, OutputStream errs) 79 throws DatabaseException, FileNotFoundException { 80 81 if (!home.exists()) 82 home.mkdirs(); 83 // Create an environment object and initialize it for error reporting. 84 EnvironmentConfig config = new EnvironmentConfig(); 85 config.setErrorStream(errs); 86 config.setErrorPrefix(progname); 87 88 // Increase the cache size to handle the large records. 89 config.setCacheSize(64 * 1024 * 1024); 90 91 // 92 // Open the environment with full transactional support. 93 // External files have full transactional support when it is enabled. 94 // 95 config.setAllowCreate(true); 96 config.setInitializeCache(true); 97 config.setTransactional(true); 98 config.setInitializeLocking(true); 99 100 dbenv = new Environment(home, config); 101 } 102 createExternalFileDatabase()103 private void createExternalFileDatabase() 104 throws DatabaseException, FileNotFoundException { 105 106 // Open a database configured for external files. 107 DatabaseConfig dbconfig = new DatabaseConfig(); 108 109 // The database is DB_BTREE. 110 dbconfig.setAllowCreate(true); 111 dbconfig.setMode(0644); 112 dbconfig.setType(DatabaseType.BTREE); 113 dbconfig.setTransactional(true); 114 115 // 116 // Enable external files by setting a threshold. Any record 1000 117 // bytes or larger will automatically be stored externally from the 118 // database. The external file threshold must be set at database 119 // creation time, and the value will be ignored if set later. 120 // 121 dbconfig.setExternalFileThreshold(1000); 122 123 db = dbenv.openDatabase(null, "ExternalFileExample.db", null, dbconfig); 124 125 } 126 127 // 128 // Once a database is configured to support external files, there are two 129 // ways to create an external file. Insert a record that is larger than the 130 // configured external file threashold, or insert a record of any size that 131 // is configured as an external file and use DatabaseStream to fill in the 132 // data. 133 // insertExternalFiles()134 public void insertExternalFiles() 135 throws DatabaseException { 136 char[] dataBuffer = new char[1001]; 137 Arrays.fill(dataBuffer, 'a'); 138 String dataString = new String(dataBuffer); 139 String keyBuffer = "1"; 140 141 System.out.println("Insert external file key = 1, data = 'aaa...'."); 142 //Insert a record that is larger than the threshold (1000 bytes). 143 DatabaseEntry data = new DatabaseEntry(dataString.getBytes()); 144 // Set the key as an integer value. 145 DatabaseEntry key = new DatabaseEntry(keyBuffer.getBytes()); 146 147 // 148 // Begin a transaction and insert the record into the database. The 149 // record will automatically be stored as a single file outside of the 150 // database, instead of being broken into peices so it will fit on the 151 // database pages. 152 // 153 Transaction txn = dbenv.beginTransaction(null, null); 154 try { 155 db.put(txn, key, data); 156 } catch (DatabaseException e) { 157 txn.abort(); 158 throw e; 159 } 160 txn.commit(); 161 162 // 163 // When streaming large files into the database, it is useful to 164 // configure the record as an external file regardless of size, then 165 // fill in the data using DatabaseStream. 166 // 167 System.out.println("Insert external file key = 2, data = 'bbb...'."); 168 data = new DatabaseEntry(); 169 keyBuffer = "2"; 170 key = new DatabaseEntry(keyBuffer.getBytes()); 171 Arrays.fill(dataBuffer, 'b'); 172 dataString = new String(dataBuffer); 173 174 Cursor cursor = null; 175 txn = dbenv.beginTransaction(null, null); 176 try { 177 // Use the Cursor to create a zero length external file. 178 cursor = db.openCursor(txn, null); 179 // Configure the entry as an external file. 180 data.setExternalFile(true); 181 cursor.put(key, data); 182 183 // Create a DatabaseStream from the new record. 184 DatabaseStreamConfig dsConfig = new DatabaseStreamConfig(); 185 186 // 187 // Each DatabaseStream write is flushed to disk to prevent data 188 // loss in case of a crash. Turning off SyncPerWrite increases 189 // streaming performance but introduces as small risk of data loss 190 // or corruption. 191 // 192 dsConfig.setSyncPerWrite(false); 193 DatabaseStream stream = cursor.openDatabaseStream(dsConfig); 194 data = new DatabaseEntry(dataString.getBytes()); 195 196 // Add data to the file. 197 stream.write(data, 0); 198 // Append even more data to the file. 199 stream.write(data, stream.size()); 200 201 // Close the stream before closing the cursor. 202 stream.close(); 203 cursor.close(); 204 } catch (DatabaseException e) { 205 if (cursor != null) 206 cursor.close(); 207 txn.abort(); 208 throw e; 209 } 210 txn.commit(); 211 } 212 213 // 214 // External files can be read all at once using the Database or Cursor 215 // get functions, or in pieces using DatabaseStream. 216 // readExternalFiles()217 public void readExternalFiles() 218 throws DatabaseException { 219 220 // Get the external file using Database.get. 221 String keyBuffer = "1"; 222 DatabaseEntry key = new DatabaseEntry(keyBuffer.getBytes()); 223 DatabaseEntry data = new DatabaseEntry(); 224 Transaction txn = dbenv.beginTransaction(null, null); 225 try { 226 db.get(txn, key, data, LockMode.DEFAULT); 227 } catch (DatabaseException e) { 228 txn.abort(); 229 throw e; 230 } 231 txn.commit(); 232 keyBuffer = new String(key.getData()); 233 String dataSample = new String(data.getData(), 0, 10); 234 System.out.println("Read external file key = " 235 + keyBuffer + " data = " + dataSample + "..."); 236 237 // Get the external file using DatabaseStream. 238 Cursor cursor = null; 239 keyBuffer = "2"; 240 data = new DatabaseEntry(); 241 key = new DatabaseEntry(keyBuffer.getBytes()); 242 txn = dbenv.beginTransaction(null, null); 243 try { 244 // Open a cursor and position it on the external file. 245 cursor = db.openCursor(txn, null); 246 // 247 // Use the Partial API to prevent the cursor from reading any 248 // data from the file when positioning the cursor. 249 // 250 data.setPartial(0, 0, true); 251 cursor.getSearchKey(key, data, LockMode.DEFAULT); 252 253 // Open the stream and read the first 10 bytes from the file. 254 DatabaseStream stream = cursor.openDatabaseStream(null); 255 data = new DatabaseEntry(); 256 stream.read(data, 0, 10); 257 dataSample = new String(data.getData()); 258 System.out.println("Read external file key = " 259 + keyBuffer + " data = " + dataSample + "..."); 260 261 // Close the stream and cursor. 262 stream.close(); 263 cursor.close(); 264 } catch (DatabaseException e) { 265 txn.abort(); 266 throw e; 267 } 268 txn.commit(); 269 } 270 271 // Close the Database and Environment. close()272 public void close() { 273 if (db != null) { 274 try { 275 db.close(); 276 } catch (Exception e) {} 277 } 278 if (dbenv != null) { 279 try { 280 dbenv.close(); 281 } catch (Exception e) {} 282 } 283 } 284 clearDirectory(File dir)285 private static void clearDirectory(File dir) { 286 try { 287 if (dir.exists()) { 288 final File[] files = dir.listFiles(); 289 for (File file: files) { 290 if (file.isDirectory()) 291 clearDirectory(file); 292 file.delete(); 293 } 294 } 295 } catch (Exception e) {} 296 } 297 usage()298 private static void usage() { 299 System.err.println("usage: java db.ExternalFileExample [-h home]"); 300 System.exit(1); 301 } 302 } 303