View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.serializer;
18  
19  import java.io.BufferedWriter;
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileWriter;
23  import java.io.FilenameFilter;
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.List;
29  import javax.xml.transform.Result;
30  import javax.xml.transform.Source;
31  import javax.xml.transform.Transformer;
32  import javax.xml.transform.TransformerFactory;
33  import javax.xml.transform.stream.StreamResult;
34  import javax.xml.transform.stream.StreamSource;
35  
36  import javolution.xml.XMLBinding;
37  import javolution.xml.XMLObjectReader;
38  
39  import org.apache.commons.configuration.PropertiesConfiguration;
40  import org.apache.ddlutils.model.Database;
41  import org.apache.ddlutils.model.Table;
42  import org.apache.jetspeed.serializer.objects.JSGroup;
43  import org.apache.log4j.Level;
44  import org.apache.log4j.Logger;
45  /***
46   * Jetspeed Serializer DDL- Application
47   * 
48   * invoke with mandatory either
49   * <p>
50   * -I directory or filename of schema files for input, if directory all xml
51   * files will be processed
52   * </p>
53   * and/or
54   * <p>
55   * -O schema file for output log of current database
56   * </p>
57   * and (if -I denotes a directory:)
58   * <p>
59   * -x if directory list provided for input schemas this pattern is excluded
60   * </p>
61   * <p>
62   * -s name of the order file (if schema directory)
63   * </p>
64   * 
65   * <p>
66   * -m if directory list provided this is the merge file to use. If not set here
67   * or in the properties file, a hardcoded version is used
68   * </p>
69   * 
70   * <p>
71   * note that - if -I and -O are specified, the output file will contain the
72   * UPDATED database
73   * </p>
74   * invoke with (optional) parameters
75   * <p>
76   * -P propertyFileName, for settings
77   * <p>
78   * -R (flag) replace : overwrites default "UPDATE" and clears out the database
79   * before processing (ignored with -O option above)
80   * </p>
81   * 
82   * <p>
83   * -dn databaseName, for example MYSQL or ORACLE10
84   * </p>
85   * <p>
86   * -dc driverClass, for example com.mysql.jdbc.Driver
87   * </p>
88   * <p>
89   * -ds url, ruls according to the driver used, URL needs to point to the correct
90   * database
91   * </p>
92   * <p>
93   * -du user, user with create/drop etc. rights on the database
94   * </p>
95   * <p>
96   * -dp password
97   * </p>
98   * <p>
99   * -l log4j-level, ERROR (default), WARN, INFO
100  * </p>
101  * 
102  * @author <a href="mailto:hajo@bluesunrise.com">Hajo Birthelmer</a>
103  * @version $Id: $
104  */
105 public class JetspeedDDLApplication
106 {
107 	public static final String JNDI_DS_NAME = "jetspeed";
108 
109 	String propertyFileName = null;
110 	String exludeFileName = null;
111 	String orderFileName = null;
112 
113 	String logLevel = null;
114 
115 	PropertiesConfiguration configuration = null;
116 
117 	boolean doImport = false;
118 	boolean doExport = false;
119 	String schemaDirectory = null; // if specified all xml files in that
120 	// directory will be processed
121 	String outputFile = null; // if specified the database schema will be
122 	// exported to that file
123 	boolean overwrite = false; // default, do not overwrite the database
124 	// (ignored if only output)
125 	String driverClass = null; // jdbc driver
126 	String url = null; // jdbc url to database
127 	String user = null; // user
128 	String password = null; // password
129 	String databaseName = null;
130 	
131 	String[] filesToProcess = null;
132 
133 	String mergeFile = null; //name of XSLT merge file
134 	String[] args = null;
135 
136 	public static void main(String[] args) throws Exception
137 	{
138 		JetspeedDDLApplication app = new JetspeedDDLApplication();
139 		app.processArguments(args);
140 	}
141 
142 	public JetspeedDDLApplication()
143 	{
144 	}
145 
146 	/***
147 	 * ensure that we have valid database settings
148 	 * 
149 	 */
150 	private void checkDBSettings()
151 	{
152 		if (databaseName == null)
153 			databaseName = System.getProperty(
154 					"org.apache.jetspeed.database.databaseName",
155 					"mysql");
156 		if (driverClass == null)
157 			driverClass = System.getProperty(
158 					"org.apache.jetspeed.database.driverClass",
159 					"com.mysql.jdbc.Driver");
160 		if (url == null)
161 			url = System.getProperty("org.apache.jetspeed.database.url",
162 					"jdbc:mysql://localhost/j2test");
163 		if (user == null)
164 			user = System.getProperty("org.apache.jetspeed.database.user",
165 					"user");
166 		if (password == null)
167 			password = System.getProperty(
168 					"org.apache.jetspeed.database.password", "password");
169 
170 		if (driverClass == null)
171 			throw new IllegalArgumentException(
172 					"Can't proceed without a valid driver");
173 		if (url == null)
174 			throw new IllegalArgumentException(
175 					"Can't proceed without a valid url to the target database");
176 		if (user == null)
177 			throw new IllegalArgumentException(
178 					"Can't proceed without a valid database user");
179 		return;
180 	}
181 
182 	/***
183 	 * parse arguments for process instructions, order and exclude files as well
184 	 * as optional database arguments
185 	 * 
186 	 */
187 	private void parseArguments()
188 	{
189 		// Parse all the command-Oine arguments
190 		for (int n = 0; n < args.length; n++)
191 		{
192 			if (args[n].equals("-I"))
193 			{
194 				doImport = true;
195 				schemaDirectory = args[++n];
196 			} else if (args[n].equals("-O"))
197 			{
198 				doExport = true;
199 				outputFile = args[++n];
200 			} else if (args[n].equals("-s"))
201 			{
202 				orderFileName = args[++n];
203 			}
204 			else if (args[n].equals("-x"))
205 			{
206 				exludeFileName = args[++n];
207 			} 
208 			else if (args[n].equals("-m"))
209 			{
210 				mergeFile = args[++n];
211 			} 
212 			else if (args[n].equals("-R"))
213 				overwrite = true;
214 			else if (args[n].equals("-dn"))
215             {
216 				databaseName = args[++n];
217             }
218 			else if (args[n].equals("-dc"))
219 				driverClass = args[++n];
220 			else if (args[n].equals("-ds"))
221 				url = args[++n];
222 			else if (args[n].equals("-du"))
223             {
224                 if (((n + 1) >= args.length) || args[n + 1].startsWith("-d"))
225                 {
226                     user = "";
227                 } else
228                 {
229                     user = args[++n];
230                 }
231             } 
232 			else if (args[n].equals("-dp"))
233             {
234                 if (((n + 1) >= args.length) || args[n + 1].startsWith("-d"))
235                 {
236                     password = "";
237                 } else
238                 {
239                     password = args[++n];
240                 }
241             } 
242 			else if (args[n].equals("-P"))
243 				propertyFileName = args[++n];
244 			else if (args[n].equals("-l"))
245 				logLevel = args[++n];
246 
247 			else
248 				throw new IllegalArgumentException("Unknown argument: "
249 						+ args[n]);
250 		}
251 
252 	}
253 
254 	/***
255 	 * process provided filename or directory name
256 	 * 
257 	 * @return one or more files to be processed
258 	 */
259 	private String[] parseFiles()
260 	{
261 		String[] fileList = null;
262 		try
263 		{
264 			File dir = new File(schemaDirectory);
265 			if (!(dir.exists()))
266 				return fileList;
267 			if (!(dir.isDirectory()))
268 			{
269 				fileList = new String[1];
270 				fileList[0] = schemaDirectory;
271 				return fileList;
272 			}
273 			// Handling a directory
274 			LocalFilenameFilter filter = new LocalFilenameFilter(
275 					exludeFileName, orderFileName);
276 			File[] files = dir.listFiles(filter);
277 			if (files == null)
278 				return fileList;
279 
280 			fileList = new String[files.length];
281 			String sortorderFile = filter.getSortFile();
282 			if (sortorderFile == null)
283 			{
284 				for (int i = 0; i < files.length; i++)
285 					fileList[i] = files[i].getAbsolutePath();
286 				return fileList;
287 			}
288 			try
289 			{
290 				ArrayList list = readOrderFile(sortorderFile);
291 				fileList = new String[files.length];
292 				if ((list == null) || (list.size() == 0))
293 				{
294 					for (int i = 0; i < files.length; i++)
295 						fileList[i] = files[i].getAbsolutePath();
296 					return fileList;
297 				}
298 				String[] tempList = new String[files.length];
299 				for (int i = 0; i < files.length; i++)
300 					tempList[i] = files[i].getName();
301 				Iterator _it = list.iterator();
302 				int j = 0;
303 				while (_it.hasNext())
304 				{
305 					String filename = null;
306 					try
307 					{
308 						filename = ((JSGroup) _it.next()).getName();
309 					} catch (Exception eeee)
310 					{
311 					}
312 					if (filename != null)
313 					{
314 						for (int i = 0; i < files.length; i++)
315 						{
316 							if (filename.equalsIgnoreCase(tempList[i]))
317 							{
318 								fileList[j++] = files[i].getAbsolutePath();
319 								tempList[i] = null;
320 							}
321 						}
322 					}
323 				}
324 				for (int i = 0; i < files.length; i++)
325 				{
326 					if (tempList[i] != null)
327 						fileList[j++] = files[i].getAbsolutePath();
328 				}
329 				return fileList;
330 			} catch (Exception eee)
331 			{
332 				eee.printStackTrace();
333 				return null;
334 			}
335 
336 		} catch (Exception e)
337 		{
338 			e.printStackTrace();
339 			throw new IllegalArgumentException(
340 					"Processing the schema-directory " + schemaDirectory
341 							+ " caused exception " + e.getLocalizedMessage());
342 		}
343 
344 	}
345 
346 	/***
347 	 * setup environment by processing all arguments and call requested process
348 	 * routine
349 	 * 
350 	 * @param arguments
351 	 * @throws Exception
352 	 */
353 	private void processArguments(String[] arguments) throws Exception
354 	{
355 		this.args = arguments;
356 		if (args == null)
357 			throw new IllegalArgumentException(
358 					"Either a schema directory, a schema file or an output filename have to be defined (-D followed by a driectory, -I or -O  followed by the filename");
359 
360 		parseArguments();
361 
362 		processPropertyFile();
363 
364 		checkDBSettings();
365 
366 		/***
367 		 * The only required argument is the filename for either export or
368 		 * import
369 		 */
370 		if ((!doImport) && (!doExport))
371 			throw new IllegalArgumentException(
372 					"Either a schema directory, a schema file or an output filename have to be defined (-I or -O  followed by the directory-/filename");
373 
374 		if (doImport)
375 		{
376 			filesToProcess = parseFiles();
377 			if (filesToProcess == null)
378 				return;
379 		}
380 
381 		/*** create the instruction map */
382 		JetspeedDDLUtil ddlUtil = null;
383 
384 		HashMap context = new HashMap();
385 		context.put(JetspeedDDLUtil.DATASOURCE_DATABASENAME, databaseName);
386 		context.put(JetspeedDDLUtil.DATASOURCE_DRIVER, driverClass);
387 		context.put(JetspeedDDLUtil.DATASOURCE_URL, url);
388 		context.put(JetspeedDDLUtil.DATASOURCE_USERNAME, user);
389 		context.put(JetspeedDDLUtil.DATASOURCE_PASSWORD, password);
390 
391 		Logger logger = Logger.getLogger("org.apache.ddlutils");
392 		Level level = logger.getLevel();
393 		if (logLevel == null)
394 			logger.setLevel(Level.ERROR);
395 		else if (logLevel.equalsIgnoreCase("INFO"))
396 			logger.setLevel(Level.INFO);
397 		else if (logLevel.equalsIgnoreCase("WARN"))
398 			logger.setLevel(Level.WARN);
399 		else
400 			logger.setLevel(Level.ERROR);
401 
402 		try
403 		{
404 			ddlUtil = new JetspeedDDLUtil();
405 			ddlUtil.startUp();
406 			ddlUtil.init(context);
407 		} catch (Exception e)
408 		{
409 			System.err.println("Failed to initialize Utility!!!!!");
410 			e.printStackTrace();
411 			System.exit(-1);
412 		}
413 		try
414 		{
415 			if (doImport)
416 				processImport(ddlUtil);
417 			if (doExport)
418 				processExport(ddlUtil);
419 		} catch (Exception e)
420 		{
421 			System.err.println("Failed to process XML "
422 					+ (doExport ? "export" : "import") + ":" + e);
423 			e.printStackTrace();
424 		} finally
425 		{
426 			try
427 			{
428 				logger.setLevel(level);
429 				if (ddlUtil != null)
430 					ddlUtil.tearDown();
431 			} catch (Exception e1)
432 			{
433 				System.out
434 						.println("starter framework teardown caused exception "
435 								+ e1.getLocalizedMessage());
436 				e1.printStackTrace();
437 
438 			}
439 		}
440 
441 	}
442 
443 	/***
444 	 * create/alter database
445 	 * 
446 	 * @param ddlUtil
447 	 */
448 	private void processImport(JetspeedDDLUtil ddlUtil)
449 	{
450 		String file = null;
451 		if ((filesToProcess == null) || (filesToProcess.length == 0))
452 			return;
453 		if (filesToProcess.length > 1)
454 			file = mergeFiles(filesToProcess);
455 
456 		System.out.println("Importing " + file);
457 		Database db = ddlUtil.createDatabaseSchemaFromXML(file);
458 		try
459 		{
460 			if (overwrite)
461 				ddlUtil.createDatabase(db); // overwrite existing
462 			// database
463 			else
464 				ddlUtil.alterDatabase(db);
465 			System.out.println("Importing " + file + " completed");
466 		} catch (Exception ePr)
467 		{
468 			ePr.printStackTrace();
469 			// continue with the process despite that one of the files was
470 			// bad...
471 		}
472 
473 	}
474 
475 	/***
476 	 * Helper routine to create a temporary file
477 	 * 
478 	 * @param suffix
479 	 * @return
480 	 */
481 	private File createTemp(String suffix)
482 	{
483 		try
484 		{
485 			// Create temp file.
486 			File temp = File.createTempFile("tmp", suffix);
487 
488 			// Delete temp file when program exits.
489 			temp.deleteOnExit();
490 			return temp;
491 		} catch (IOException e)
492 		{
493 			System.out.println("Failed to create temproary file with "
494 					+ e.getLocalizedMessage());
495 			e.printStackTrace();
496 			return null;
497 		}
498 
499 	}
500 	/***
501 	 * Open the merge file from disk
502 	 * 
503 	 * @param fileName
504 	 * @return
505 	 */
506 	private File createXSLTFromFile(String fileName)
507 	{
508 		if (fileName == null)
509 			return null;
510 		try
511 		{
512 			File f = new File(fileName);
513 			if (f.exists())
514 				return f;
515 			return null;
516 		}
517 		catch (Exception e)
518 		{
519 			System.out.println("Failed to open merge template " + e.getLocalizedMessage());
520 			e.printStackTrace();
521 			return null;
522 		}
523 	}
524 	/***
525 	 * If everything else fails, use a hardcoded XSLT here
526 	 * 
527 	 * @return
528 	 */
529 	private File createXSLTFromMemory()
530 	{
531 		StringBuffer buffer = new StringBuffer();
532 
533 		buffer.append("<?xml version=\"1.0\"?>");
534 		buffer
535 				.append("<xslt:transform version=\"1.0\" xmlns:xslt=\"http://www.w3.org/1999/XSL/Transform\">");
536 		buffer
537 				.append("<!-- Simple template to merge two database schemas into one  -->");
538 		buffer.append("<xslt:param name=\"fileTwo\" />");
539 		buffer.append("<xslt:template match=\"/\">");
540 
541 		buffer.append("<xslt:message>");
542 		buffer
543 				.append("<xslt:text /> Merging input with '<xslt:value-of select=\"$fileTwo\"/>");
544 		buffer.append("<xslt:text>'</xslt:text>");
545 		buffer.append("</xslt:message>");
546 		buffer.append("<xslt:if test=\"string($fileTwo)=''\">");
547 		buffer.append("<xslt:message terminate=\"yes\">");
548 		buffer
549 				.append("<xslt:text>No input file specified (parameter 'fileTwo')</xslt:text>");
550 		buffer.append("</xslt:message>");
551 		buffer.append("</xslt:if>");
552 		buffer.append("<database name=\"generic\">");
553 		buffer.append("<xslt:apply-templates />");
554 		buffer.append("</database>");
555 		buffer.append("</xslt:template>");
556 		buffer.append("<xslt:template match=\"database\">");
557 		buffer.append("<xslt:apply-templates />");
558 		buffer.append("<xslt:apply-templates select=\"document($fileTwo)/database/table\"/>");
559 		buffer.append("</xslt:template>");
560 
561 		buffer.append("<xslt:template match=\"@*|node()\">");
562 		buffer.append("<xslt:copy>");
563 		buffer.append("<xslt:apply-templates select=\"@*|node()\"/>");
564 		buffer.append("</xslt:copy>");
565 		buffer.append("</xslt:template>");
566 		buffer.append("</xslt:transform>");
567 
568 		File xslt = createTemp(".xslt");
569 		try
570 		{
571 			// Write to temp file
572 
573 			BufferedWriter out = new BufferedWriter(new FileWriter(xslt));
574 			out.write(buffer.toString());
575 			out.close();
576 			return xslt;
577 		} catch (Exception e)
578 		{
579 			e.printStackTrace();
580 			return null;
581 		}
582 	}
583 	/***
584 	 * process of merging two or more schema files into one schema file.
585 	 *  
586 	 * @param fileList The filelist contains a (potentially) ordered list of schemas
587 	 * @return The name of the created temporary schema file
588 	 */
589 	private String mergeFiles(String[] fileList)
590 	{
591 		try
592 		{
593 			File xsltFile = createXSLTFromFile(mergeFile);
594 			if (xsltFile == null)
595 				xsltFile = createXSLTFromMemory();
596 			Source xslt = new StreamSource(xsltFile);
597 			Transformer transformer = TransformerFactory.newInstance()
598 					.newTransformer(xslt);
599 
600 			String sourceName = fileList[0];
601 			File target = null;
602 			for (int i = 1; i < fileList.length; i++)
603 			{
604 				File soureFile = new File(sourceName);
605 				Source source = new StreamSource(soureFile);
606 				// JAXP reads data using the Source interface
607 				target = createTemp(".xml");
608 
609 				Result targetResult = new StreamResult(target);
610 				File f = new File(fileList[i]);
611 				String other = "file:///" + f.getCanonicalPath();  // required on Win-platforms
612 				other = other.replace('//', '/');
613 
614 				transformer.setParameter("fileTwo", other);
615 				transformer.transform(source, targetResult);
616 				sourceName = target.getAbsolutePath();
617 			}
618 			return sourceName;
619 
620 		} catch (Exception e)
621 		{
622 			e.printStackTrace();
623 			return null;
624 		}
625 
626 	}
627 
628 	/***
629 	 * read database schema to file
630 	 * 
631 	 */
632 	private void processExport(JetspeedDDLUtil ddlUtil)
633 	{
634 		// TODO: implement
635 		ddlUtil.writeDatabaseSchematoFile(this.outputFile);
636 
637 	}
638 
639 	/***
640 	 * read the property file and read what has not yet been defined
641 	 */
642 	private void processPropertyFile()
643 	{
644 		/*** get system property definition */
645 		if (propertyFileName == null)
646 			propertyFileName = System.getProperty(
647 					"org.apache.jetspeed.xml.ddlUtil.configuration", null);
648 
649 		if (propertyFileName == null)
650 			return;
651 		try
652 		{
653 			configuration = new PropertiesConfiguration(propertyFileName);
654 		} catch (Exception e)
655 		{
656 			e.printStackTrace();
657 			return;
658 		}
659 		if (configuration != null)
660 		{
661 			/*** only read what was not defined on the command line */
662 
663 			if (driverClass == null)
664 				driverClass = configuration.getString("driverClass");
665 			if (url == null)
666 				url = configuration.getString("url");
667 			if (user == null)
668 				user = configuration.getString("user");
669 			if (password == null)
670 				password = configuration.getString("password");
671 			if (mergeFile == null)
672 				mergeFile = configuration.getString("mergeFile");
673 			if (!(doImport))
674 			{
675 				schemaDirectory = configuration.getString("schema");
676 				if (schemaDirectory != null)
677 					doImport = true;
678 			}
679 			if (!(doExport))
680 			{
681 				outputFile = configuration.getString("outputFile");
682 				if (outputFile != null)
683 					doExport = true;
684 			}
685 			if (logLevel == null)
686 				logLevel = configuration.getString("loglevel");
687 
688 		}
689 
690 	}
691 
692     /*
693 	private static String[] getTokens(String _line)
694 	{
695 		if ((_line == null) || (_line.length() == 0))
696 			return null;
697 
698 		StringTokenizer st = new StringTokenizer(_line, ",");
699 		ArrayList list = new ArrayList();
700 
701 		while (st.hasMoreTokens())
702 			list.add(st.nextToken());
703 		String[] s = new String[list.size()];
704 		for (int i = 0; i < list.size(); i++)
705 			s[i] = (String) list.get(i);
706 		return s;
707 	}
708     */
709 
710 	public List getRows(JetspeedDDLUtil ddlUtil, String tableName)
711 	{
712 		Table table = ddlUtil.getModel().findTable(tableName,
713 				ddlUtil.getPlatform().isDelimitedIdentifierModeOn());
714 
715 		return ddlUtil.getPlatform().fetch(ddlUtil.getModel(),
716 				getSelectQueryForAllString(ddlUtil, table), new Table[]
717 				{table});
718 	}
719 
720 	public String getSelectQueryForAllString(JetspeedDDLUtil ddlUtil,
721 			Table table)
722 	{
723 
724 		StringBuffer query = new StringBuffer();
725 
726 		query.append("SELECT * FROM ");
727 		if (ddlUtil.getPlatform().isDelimitedIdentifierModeOn())
728 		{
729 			query.append(ddlUtil.getPlatform().getPlatformInfo()
730 					.getDelimiterToken());
731 		}
732 		query.append(table.getName());
733 		if (ddlUtil.getPlatform().isDelimitedIdentifierModeOn())
734 		{
735 			query.append(ddlUtil.getPlatform().getPlatformInfo()
736 					.getDelimiterToken());
737 		}
738 		System.out.println(query.toString());
739 		return query.toString();
740 	}
741 
742 	/***
743 	 * read an xml file describing the basic order of the files to be processed
744 	 * 
745 	 * @param importFileName
746 	 * @return
747 	 * @throws SerializerException
748 	 */
749 
750 	private ArrayList readOrderFile(String importFileName)
751 	{
752 		XMLObjectReader reader = null;
753 
754 		XMLBinding binding = new XMLBinding();
755 		binding.setAlias(ArrayList.class, "ProcessOrder");
756 		binding.setAlias(JSGroup.class, "File");
757 
758 		ArrayList snap = null;
759 		try
760 		{
761 			reader = XMLObjectReader.newInstance(new FileInputStream(
762 					importFileName));
763 		} catch (Exception e)
764 		{
765 			e.printStackTrace();
766 			return null;
767 		}
768 		try
769 		{
770 			reader.setBinding(binding);
771 			snap = (ArrayList) reader.read("ProcessOrder", ArrayList.class);
772 
773 		} catch (Exception e)
774 		{
775 			e.printStackTrace();
776 		} finally
777 		{
778 			/*** ensure the reader is closed */
779 			try
780 			{
781 				reader.close();
782 			} catch (Exception e1)
783 			{
784 				/***
785 				 * don't do anything with this exception - never let the bubble
786 				 * out of the finally block
787 				 */
788 			}
789 		}
790 		return snap;
791 	}
792 
793 	class LocalFilenameFilter implements FilenameFilter
794 	{
795 
796 		String exclude = null;
797 		String sortFile = null;
798 		String sort = null;
799 
800 		String getSortFile()
801 		{
802 			return sortFile;
803 		}
804 		LocalFilenameFilter(String exclude, String sort)
805 		{
806 			this.exclude = exclude;
807 			this.sort = sort;
808 
809 		}
810 		public boolean accept(File dir, String name)
811 		{
812 			if (exclude != null)
813 				if (name.equalsIgnoreCase(exclude))
814 					return false;
815 			if (sort != null)
816 				if (name.equalsIgnoreCase(sort))
817 				{
818 					sortFile = dir.getAbsolutePath() + "/" + sort;
819 					return false;
820 				}
821 
822 			return name.endsWith(".xml");
823 		}
824 
825 	}
826 
827 }