001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.tools;
022:
023: import java.io.File;
024: import java.lang.reflect.Modifier;
025:
026: import com.db4o.Db4o;
027: import com.db4o.ObjectContainer;
028: import com.db4o.ext.StoredClass;
029: import com.db4o.foundation.Hashtable4;
030: import com.db4o.foundation.io.File4;
031: import com.db4o.internal.ReflectPlatform;
032: import com.db4o.types.SecondClass;
033:
034: /**
035: * old Deframent source code, now replaced by the built-in functionality in com.db4o.defragment.Defragment.
036: * <br><br><b>This class is deprecated. Please use {@link com.db4o.defragment.Defragment}.</b><br>
037: * This class is delivered as sourcecode in the
038: * path ../com/db4o/tools/<br><br> <b>Prerequites:</b><br> - The database file may not be
039: * in use.<br> - All stored classes need to be available.<br> - If you use yor own special
040: * Db4o translators, they need to be installed before starting Defragment.<br><br>
041: * <b>Performed tasks:</b><br> - Free filespace is removed.<br> - Deleted IDs are
042: * removed.<br> - Unavailable classes are removed.<br> - Unavailable class members are
043: * removed.<br> - Class indices are restored.<br> - Previous rename tasks are removed.<br>
044: * <br>
045: * <b>Backup:</b><br>
046: * Defragment creates a backup file with the name [filename].bak. If
047: * a file with this name is already present, Defragment will not run
048: * for safety reasons.<br><br>
049: * <b>Recommendations:</b><br>
050: * - Keep the backup copy of your database file.<br>
051: * - <b>Always</b> back up your class files with your database files also.<br>
052: * You will need them to restore the full data of all objects from old database file versions.<br>
053: * - Scan the output log for "Class not available" messages.<br><br>
054: * You may also run this task programmatically on a scheduled basis.
055: * In this case note that <code>Defragment</code> modifies db4o
056: * configuration parameters. You may have to restore them for your
057: * application. See the private methods Defragment#configureDb4o() and
058: * Db4o#restoreConfiguration() in the sourcecode of
059: * com.db4o.tools.Defragment.java for the exact changed parameters that
060: * may need to be restored.
061: * @deprecated Please use com.db4o.defragment.Defragment
062: */
063: public class Defragment {
064:
065: private static Hashtable4 _secondClassNames;
066:
067: /**
068: * the main method is the only entry point
069: */
070: public Defragment() {
071: }
072:
073: /**
074: * the main method that runs Defragment.
075: * @param args a String array of length 1, with the name of the database
076: * file as element 0.
077: */
078: public static void main(String[] args) {
079: Db4o.configure().messageLevel(-1);
080: if (args != null && args.length > 0) {
081:
082: // This is a hidden feature: For fast debugging reasons, delete
083: // can be forced by supplying an additional "!" parameter.
084: boolean forceBackupDelete = (args.length > 1 && "!"
085: .equals(args[1]));
086:
087: new Defragment().run(args[0], forceBackupDelete);
088:
089: } else {
090: System.out
091: .println("Usage: java com.db4o.tools.Defragment <database filename>");
092: }
093: }
094:
095: /**
096: * allows to specify a class to be treated as "second class".
097: * Second class objects are not migrated to the new database on their own. A second
098: * class objects is only migrated, if it is referenced by another object.
099: * @param className the fully qualified classname, including the package name for Java,
100: * including the namespaces and assembly name for .NET. Format examples:<br>
101: * Java: 'com.db4o.f1.Pilot'<br>
102: * .NET: 'com.db4o.f1.Pilot, MyAssembly'
103: */
104: public static void setSecondClass(String className) {
105: if (_secondClassNames == null) {
106: _secondClassNames = new Hashtable4();
107: }
108: _secondClassNames.put(className, className);
109: }
110:
111: /**
112: * programmatic interface to run Defragment with a forced delete of a possible
113: * old Defragment backup.
114: * <br>This method is supplied for regression tests only. It is not recommended
115: * to be used by application programmers.
116: * @param filename the database file.
117: * @param forceBackupDelete forces deleting an old backup. <b>Not recommended.</b>
118: */
119: public void run(String filename, boolean forceBackupDelete) {
120: File file = new File(filename);
121: if (file.exists()) {
122: boolean canRun = true;
123: File backupTest = new File(file.getAbsolutePath() + ".bak");
124: if (backupTest.exists()) {
125: if (forceBackupDelete) {
126: backupTest.delete();
127: } else {
128: canRun = false;
129: System.out.println("A backup file with the name ");
130: System.out.println("'"
131: + backupTest.getAbsolutePath() + "'");
132: System.out.println("already exists.");
133: System.out
134: .println("Remove this file before calling 'Defragment'.");
135: }
136: }
137: if (canRun) {
138: file.renameTo(backupTest);
139: try {
140: configureDb4o();
141: ObjectContainer readFrom = Db4o.openFile(backupTest
142: .getAbsolutePath());
143: ObjectContainer writeTo = Db4o.openFile(file
144: .getAbsolutePath());
145: writeTo.ext().migrateFrom(readFrom);
146: migrate(readFrom, writeTo);
147: readFrom.close();
148: writeTo.close();
149: System.out
150: .println("Defragment operation completed successfully.");
151: } catch (Exception e) {
152: System.out.println("Defragment operation failed.");
153: e.printStackTrace();
154: try {
155: new File(filename).delete();
156: File4.copy(backupTest.getAbsolutePath(),
157: filename);
158: } catch (Exception ex) {
159: System.out.println("Restore failed.");
160: System.out
161: .println("Please use the backup file:");
162: System.out.println("'"
163: + backupTest.getAbsolutePath() + "'");
164: return;
165: }
166: System.out
167: .println("The original file was restored.");
168: try {
169: new File(backupTest.getAbsolutePath()).delete();
170: } catch (Exception ex) {
171: }
172: } finally {
173: restoreConfiguration();
174: }
175: }
176: } else {
177: System.out.println("File '" + file.getAbsolutePath()
178: + "' does not exist.");
179: }
180: }
181:
182: private void configureDb4o() {
183: Db4o.configure().activationDepth(0);
184: Db4o.configure().callbacks(false);
185: Db4o.configure().classActivationDepthConfigurable(false);
186: Db4o.configure().weakReferences(false);
187: }
188:
189: private void restoreConfiguration() {
190: Db4o.configure().activationDepth(5);
191: Db4o.configure().callbacks(true);
192: Db4o.configure().classActivationDepthConfigurable(true);
193: Db4o.configure().weakReferences(true);
194: }
195:
196: private void migrate(ObjectContainer origin,
197: ObjectContainer destination) throws ClassNotFoundException {
198:
199: // get all stored classes
200: StoredClass[] classes = origin.ext().storedClasses();
201: removeUnavailableSecondAndAbstractClasses(classes);
202: removeSubclasses(classes);
203: migrateClasses(origin, destination, classes);
204: }
205:
206: private void migrateClasses(ObjectContainer origin,
207: ObjectContainer destination, StoredClass[] classes) {
208:
209: for (int i = 0; i < classes.length; i++) {
210: if (migrateClass(origin, destination, classes[i], true)) {
211: classes[i] = null;
212: }
213: }
214: if (_secondClassNames != null) {
215: for (int i = 0; i < classes.length; i++) {
216: migrateClass(origin, destination, classes[i], false);
217: }
218: }
219:
220: }
221:
222: private boolean migrateClass(ObjectContainer origin,
223: ObjectContainer destination, StoredClass clazz,
224: boolean firstClassPass) {
225: if (clazz == null) {
226: return false;
227: }
228: if (firstClassPass) {
229: if (_secondClassNames != null) {
230: if (_secondClassNames.get(clazz.getName()) != null) {
231: return false;
232: }
233: }
234: }
235:
236: long[] ids = clazz.getIDs();
237: origin.ext().purge();
238: destination.commit();
239: destination.ext().purge();
240: for (int j = 0; j < ids.length; j++) {
241: Object obj = origin.ext().getByID(ids[j]);
242:
243: if (firstClassPass || destination.ext().isStored(obj)) {
244:
245: // prevent possible constructor side effects
246: origin.activate(obj, 1);
247: origin.deactivate(obj, 2);
248:
249: origin.activate(obj, 3);
250: destination.set(obj);
251:
252: // Both Containers keep track of state individually,
253: // so we need to make sure, both know, the object is deactivated
254: origin.deactivate(obj, 1);
255: destination.deactivate(obj, 1);
256: }
257: }
258: return true;
259: }
260:
261: private void removeSubclasses(StoredClass[] classes)
262: throws ClassNotFoundException {
263: // rule out inheritance dependancies
264: for (int i = 0; i < classes.length; i++) {
265: if (classes[i] != null) {
266: Class javaClass = Class.forName(classes[i].getName());
267: for (int j = 0; j < classes.length; j++) {
268: if (classes[j] != null && classes[i] != classes[j]) {
269: Class super Class = Class.forName(classes[j]
270: .getName());
271: if (super Class.isAssignableFrom(javaClass)) {
272: classes[i] = null;
273: break;
274: }
275: }
276: }
277: }
278: }
279: }
280:
281: private void removeUnavailableSecondAndAbstractClasses(
282: StoredClass[] classes) {
283: // remove classes that are currently not available,
284: // abstract classes and all second class objects
285: for (int i = 0; i < classes.length; i++) {
286: Class javaClass = ReflectPlatform.forName(classes[i]
287: .getName());
288: if (javaClass == null
289: || SecondClass.class.isAssignableFrom(javaClass)
290: || Modifier.isAbstract(javaClass.getModifiers())) {
291: classes[i] = null;
292: }
293: }
294: }
295:
296: }
|