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