1 |
| |
2 |
| |
3 |
| |
4 |
| |
5 |
| |
6 |
| |
7 |
| |
8 |
| |
9 |
| |
10 |
| |
11 |
| |
12 |
| |
13 |
| |
14 |
| |
15 |
| |
16 |
| |
17 |
| |
18 |
| |
19 |
| |
20 |
| package org.apache.xindice.core; |
21 |
| |
22 |
| import org.apache.commons.logging.Log; |
23 |
| import org.apache.commons.logging.LogFactory; |
24 |
| import org.apache.xindice.core.cache.DocumentCache; |
25 |
| import org.apache.xindice.core.cache.DocumentCacheImpl; |
26 |
| import org.apache.xindice.core.query.QueryEngine; |
27 |
| import org.apache.xindice.server.Xindice; |
28 |
| import org.apache.xindice.util.Configuration; |
29 |
| import org.apache.xindice.util.Named; |
30 |
| import org.apache.xindice.util.XindiceException; |
31 |
| |
32 |
| import org.w3c.dom.Document; |
33 |
| import org.w3c.dom.Element; |
34 |
| |
35 |
| import javax.xml.parsers.DocumentBuilder; |
36 |
| import javax.xml.parsers.DocumentBuilderFactory; |
37 |
| |
38 |
| import java.io.File; |
39 |
| import java.io.FileOutputStream; |
40 |
| import java.io.IOException; |
41 |
| import java.util.Date; |
42 |
| import java.util.HashMap; |
43 |
| import java.util.Map; |
44 |
| import java.util.Set; |
45 |
| import java.util.Timer; |
46 |
| |
47 |
| |
48 |
| |
49 |
| |
50 |
| |
51 |
| |
52 |
| public final class Database extends Collection |
53 |
| implements Named { |
54 |
| |
55 |
| private static final Log log = LogFactory.getLog(Database.class); |
56 |
| |
57 |
| |
58 |
| public static final String DBROOT = "dbroot"; |
59 |
| public static final String NAME = "name"; |
60 |
| |
61 |
| private static final String QUERYENGINE = "queryengine"; |
62 |
| private static final String DATABASE = "database"; |
63 |
| private static final String METADATA = "use-metadata"; |
64 |
| |
65 |
| public static final String DBROOT_DEFAULT = "./db/"; |
66 |
| |
67 |
| |
68 |
| private static final String COLKEY = "database.xml"; |
69 |
| private static final String METAKEY = "meta.xml"; |
70 |
| |
71 |
| private static final Map databases = new HashMap(); |
72 |
| private static final DatabaseShutdownHandler shutdownHandler = new DatabaseShutdownHandler(); |
73 |
| |
74 |
| static { |
75 |
| |
76 |
| |
77 |
19
| DBObserver.setInstance(new DatabaseChangeObserver());
|
78 |
| } |
79 |
| |
80 |
| |
81 |
| |
82 |
| |
83 |
| |
84 |
| |
85 |
| |
86 |
| |
87 |
| |
88 |
| |
89 |
| |
90 |
44
| public static Database getDatabase(Configuration config) throws DBException {
|
91 |
44
| String name = config.getAttribute(Database.NAME);
|
92 |
| |
93 |
| |
94 |
44
| if (name == null) {
|
95 |
0
| throw new DBException(FaultCodes.DBE_CANNOT_READ,
|
96 |
| "Database configuration didn't contain a database name"); |
97 |
| } |
98 |
| |
99 |
44
| Database database = (Database) databases.get(name);
|
100 |
44
| if (database == null) {
|
101 |
| |
102 |
44
| synchronized (databases) {
|
103 |
| |
104 |
44
| database = (Database) databases.get(name);
|
105 |
44
| if (database == null) {
|
106 |
44
| database = new Database();
|
107 |
44
| try {
|
108 |
44
| database.setConfig(config);
|
109 |
| } catch (XindiceException x) { |
110 |
0
| throw new DBException(FaultCodes.DBE_CANNOT_READ,
|
111 |
| "XindiceException: " + x.getMessage(), x); |
112 |
| } |
113 |
| |
114 |
44
| databases.put(database.getName(), database);
|
115 |
| } |
116 |
| } |
117 |
| } |
118 |
| |
119 |
44
| return database;
|
120 |
| } |
121 |
| |
122 |
| |
123 |
| |
124 |
| |
125 |
| |
126 |
| |
127 |
| |
128 |
| |
129 |
4458
| public static Database getDatabase(String name) {
|
130 |
4458
| Database database = (Database) databases.get(name);
|
131 |
4458
| if (database == null) {
|
132 |
| |
133 |
11
| synchronized (databases) {
|
134 |
11
| database = (Database) databases.get(name);
|
135 |
| } |
136 |
| } |
137 |
| |
138 |
4458
| return database;
|
139 |
| } |
140 |
| |
141 |
0
| public static String[] listDatabases() {
|
142 |
0
| synchronized (databases) {
|
143 |
0
| Set names = databases.keySet();
|
144 |
0
| return (String[]) names.toArray(new String[names.size()]);
|
145 |
| } |
146 |
| } |
147 |
| |
148 |
| |
149 |
| |
150 |
| |
151 |
| |
152 |
| |
153 |
| private DocumentCache docCache; |
154 |
| private QueryEngine engine; |
155 |
| private boolean metaEnabled; |
156 |
| private boolean metaInit; |
157 |
| private MetaSystemCollection metaSystemCollection; |
158 |
| private boolean sysInit; |
159 |
| private SystemCollection systemCollection; |
160 |
| private FileOutputStream lock; |
161 |
| private boolean closed; |
162 |
| |
163 |
| |
164 |
| private Timer timer; |
165 |
| |
166 |
| |
167 |
123
| public Database() {
|
168 |
123
| super();
|
169 |
123
| docCache = new DocumentCacheImpl();
|
170 |
123
| engine = new QueryEngine(this);
|
171 |
123
| closed = true;
|
172 |
| } |
173 |
| |
174 |
| |
175 |
| |
176 |
| |
177 |
| |
178 |
| |
179 |
| |
180 |
| |
181 |
| |
182 |
123
| protected boolean close(boolean removeFromShutdown) throws DBException {
|
183 |
123
| if (removeFromShutdown) {
|
184 |
| |
185 |
119
| shutdownHandler.removeDatabase(this);
|
186 |
| } |
187 |
| |
188 |
123
| synchronized (databases) {
|
189 |
123
| databases.remove(getName());
|
190 |
| } |
191 |
| |
192 |
123
| synchronized (this) {
|
193 |
| |
194 |
123
| if (!closed) {
|
195 |
123
| if (log.isDebugEnabled()) {
|
196 |
0
| log.debug("Shutting down database: '" + getName() + "'");
|
197 |
| } |
198 |
| |
199 |
123
| flushConfig();
|
200 |
123
| super.close();
|
201 |
| |
202 |
| |
203 |
123
| try {
|
204 |
123
| this.lock.close();
|
205 |
123
| new File(getCollectionRoot(), "db.lock").delete();
|
206 |
| } catch (Exception e) { |
207 |
| |
208 |
| } |
209 |
123
| this.lock = null;
|
210 |
| |
211 |
| |
212 |
123
| timer.cancel();
|
213 |
| |
214 |
123
| closed = true;
|
215 |
| } |
216 |
| } |
217 |
| |
218 |
123
| return true;
|
219 |
| } |
220 |
| |
221 |
| |
222 |
| |
223 |
| |
224 |
| |
225 |
119
| public boolean close() throws DBException {
|
226 |
119
| return close(true);
|
227 |
| } |
228 |
| |
229 |
| |
230 |
| |
231 |
| |
232 |
| |
233 |
3932
| public void flushConfig() {
|
234 |
3932
| if (getConfig().isDirty()) {
|
235 |
1321
| try {
|
236 |
1321
| Document d = getConfig().getElement().getOwnerDocument();
|
237 |
1321
| systemCollection.getCollection(SystemCollection.CONFIGS).setDocument(COLKEY, d);
|
238 |
1321
| getConfig().resetDirty();
|
239 |
| } catch (Exception e) { |
240 |
0
| log.error("Error Writing Configuration '" + COLKEY + "', for database " + getName(), e);
|
241 |
| } |
242 |
| |
243 |
| |
244 |
1321
| DBObserver.getInstance().flushDatabaseConfig(this, getConfig());
|
245 |
| } |
246 |
| |
247 |
3932
| if (isMetaEnabled()) {
|
248 |
3928
| Configuration config = metaSystemCollection.getConfig();
|
249 |
3928
| if (config.isDirty()) {
|
250 |
1034
| try {
|
251 |
1034
| Document d = config.getElement().getOwnerDocument();
|
252 |
1034
| systemCollection.getCollection(SystemCollection.CONFIGS).setDocument(METAKEY, d);
|
253 |
1034
| config.resetDirty();
|
254 |
| } catch (Exception e) { |
255 |
0
| log.error("Error writing configuration '" + METAKEY + "', for database " + getName(), e);
|
256 |
| } |
257 |
| } |
258 |
| } |
259 |
| } |
260 |
| |
261 |
| |
262 |
| |
263 |
| |
264 |
46072
| public Database getDatabase() {
|
265 |
46072
| return this;
|
266 |
| } |
267 |
| |
268 |
| |
269 |
| |
270 |
| |
271 |
| |
272 |
| |
273 |
1894
| public DocumentCache getDocumentCache() {
|
274 |
1894
| return docCache;
|
275 |
| } |
276 |
| |
277 |
| |
278 |
| |
279 |
| |
280 |
| |
281 |
| |
282 |
| |
283 |
17768
| public MetaSystemCollection getMetaSystemCollection() {
|
284 |
17768
| return metaSystemCollection;
|
285 |
| } |
286 |
| |
287 |
| |
288 |
| |
289 |
| |
290 |
| |
291 |
| |
292 |
| |
293 |
372
| public QueryEngine getQueryEngine() {
|
294 |
372
| return engine;
|
295 |
| } |
296 |
| |
297 |
| |
298 |
| |
299 |
| |
300 |
3257
| public SystemCollection getSystemCollection() {
|
301 |
3257
| return systemCollection;
|
302 |
| } |
303 |
| |
304 |
| |
305 |
| |
306 |
| |
307 |
| |
308 |
1281
| protected Timer getTimer() {
|
309 |
1281
| return timer;
|
310 |
| } |
311 |
| |
312 |
| |
313 |
| |
314 |
| |
315 |
| |
316 |
21745
| public boolean isMetaEnabled() {
|
317 |
21745
| return metaEnabled;
|
318 |
| } |
319 |
| |
320 |
| |
321 |
| |
322 |
| |
323 |
123
| public void setConfig(Configuration config) throws XindiceException {
|
324 |
| |
325 |
123
| super.setConfig(config);
|
326 |
123
| setCanonicalName('/' + getName());
|
327 |
| |
328 |
| |
329 |
123
| String dbroot = config.getAttribute(DBROOT);
|
330 |
123
| File dbrootDir = new File(dbroot);
|
331 |
123
| if (!dbrootDir.isAbsolute()) {
|
332 |
| |
333 |
120
| log.warn("The specified database root directory '" + dbroot + "' is relative. " +
|
334 |
| "Using property " + Xindice.PROP_XINDICE_DB_HOME + " to resolve."); |
335 |
| |
336 |
120
| String home = System.getProperty(Xindice.PROP_XINDICE_DB_HOME);
|
337 |
120
| if (home == null) {
|
338 |
0
| log.warn("The specified database root directory '" + dbroot + "' is relative " +
|
339 |
| "and there was no " + Xindice.PROP_XINDICE_DB_HOME + " property set, " + |
340 |
| "so Xindice was unable to determine a database location. " + |
341 |
| "Database will be created relative to the current working directory."); |
342 |
| |
343 |
0
| home = ".";
|
344 |
| } |
345 |
120
| try {
|
346 |
| |
347 |
120
| dbrootDir = new File(home, dbroot).getCanonicalFile();
|
348 |
| } catch (IOException e) { |
349 |
0
| throw new XindiceException("Can't get canonical path", e);
|
350 |
| } |
351 |
| } |
352 |
123
| setCollectionRoot(dbrootDir);
|
353 |
123
| if (log.isInfoEnabled()) {
|
354 |
1
| log.info("Database points to " + dbrootDir.getAbsolutePath());
|
355 |
| } |
356 |
| |
357 |
| |
358 |
123
| File lock = new File(getCollectionRoot(), "db.lock");
|
359 |
123
| try {
|
360 |
123
| this.lock = new FileOutputStream(lock);
|
361 |
| |
362 |
123
| if (this.lock.getChannel().tryLock() != null) {
|
363 |
123
| this.lock.write(new Date().toString().getBytes());
|
364 |
| } else { |
365 |
0
| throw new IOException("Unable to acquire file lock.");
|
366 |
| } |
367 |
| } catch (IOException e) { |
368 |
0
| throw new XindiceException("Unable to open lock file " + lock + ". " +
|
369 |
| "Make sure database is not open by another process.", |
370 |
| e); |
371 |
| } |
372 |
| |
373 |
| |
374 |
123
| shutdownHandler.registerDatabase(this);
|
375 |
123
| timer = new Timer(false);
|
376 |
123
| closed = false;
|
377 |
| |
378 |
| |
379 |
123
| try {
|
380 |
123
| Configuration queryCfg = config.getChild(QUERYENGINE);
|
381 |
123
| if (queryCfg != null) {
|
382 |
123
| engine.setConfig(queryCfg);
|
383 |
| } |
384 |
| } catch (Exception e) { |
385 |
0
| if (log.isWarnEnabled()) {
|
386 |
0
| log.warn("ignored exception", e);
|
387 |
| } |
388 |
| } |
389 |
| |
390 |
| |
391 |
123
| if (!sysInit) {
|
392 |
123
| systemCollection = new SystemCollection(this);
|
393 |
123
| systemCollection.init();
|
394 |
123
| super.addCollection(systemCollection);
|
395 |
123
| this.sysInit = true;
|
396 |
| } |
397 |
| |
398 |
123
| Collection sysConfigCollection = systemCollection.getCollection(SystemCollection.CONFIGS);
|
399 |
123
| try {
|
400 |
| |
401 |
| |
402 |
| |
403 |
123
| Document colDoc = sysConfigCollection.getDocument(COLKEY);
|
404 |
123
| if (colDoc == null) {
|
405 |
4
| DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
406 |
4
| colDoc = db.newDocument();
|
407 |
4
| Element root = colDoc.createElement(DATABASE);
|
408 |
4
| root.setAttribute(NAME, getName());
|
409 |
4
| colDoc.appendChild(root);
|
410 |
4
| sysConfigCollection.setDocument(COLKEY, colDoc);
|
411 |
| } |
412 |
| |
413 |
123
| super.setConfig(new Configuration(colDoc.getDocumentElement(), false));
|
414 |
| } catch (Exception e) { |
415 |
0
| if (log.isWarnEnabled()) {
|
416 |
0
| log.warn("ignored exception", e);
|
417 |
| } |
418 |
| } |
419 |
| |
420 |
| |
421 |
| |
422 |
| |
423 |
| |
424 |
| |
425 |
123
| String metaCfg = config.getAttribute(METADATA);
|
426 |
123
| if (metaCfg.equalsIgnoreCase("on")) {
|
427 |
122
| metaEnabled = true;
|
428 |
| } |
429 |
| |
430 |
123
| if (metaEnabled && !metaInit) {
|
431 |
122
| try {
|
432 |
122
| metaSystemCollection = new MetaSystemCollection(this);
|
433 |
| |
434 |
122
| Document colDoc = sysConfigCollection.getDocument(METAKEY);
|
435 |
122
| if (colDoc == null) {
|
436 |
4
| metaSystemCollection.init();
|
437 |
4
| Document metaConfig = metaSystemCollection.getConfig().getElement().getOwnerDocument();
|
438 |
4
| sysConfigCollection.setDocument(METAKEY, metaConfig);
|
439 |
| } else { |
440 |
118
| metaSystemCollection.setConfig(new Configuration(colDoc, false));
|
441 |
| } |
442 |
| |
443 |
122
| super.addCollection(metaSystemCollection);
|
444 |
122
| metaInit = true;
|
445 |
122
| if (log.isDebugEnabled()) {
|
446 |
0
| log.debug("Meta collection is initialized");
|
447 |
| } |
448 |
| } catch (Exception e) { |
449 |
0
| log.error("Meta collection was not initialized", e);
|
450 |
| } |
451 |
| } |
452 |
| |
453 |
| |
454 |
123
| DBObserver.getInstance().setDatabaseConfig(this, getCollections(), config);
|
455 |
| } |
456 |
| |
457 |
| |
458 |
| |
459 |
| |
460 |
| |
461 |
| |
462 |
| |
463 |
| |
464 |
0
| public boolean drop() throws DBException {
|
465 |
0
| throw new DBException(FaultCodes.DBE_CANNOT_DROP,
|
466 |
| "You cannot drop the database"); |
467 |
| } |
468 |
| } |