XmlRemapper.java
/*
** Module :XmlRemapper.java
** Abstract :XML back-end implementation.
**
** Copyright (c) 2005-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------Description---------------------
** 001 SIY 20050221 @20052 Created initial version
** 002 SIY 20050301 @20373 Added access serialization, added some
** checks to prevent NullPointerExceptions,
** fixed memory-only mode.
** Organised imports and reordered methods.
** Fixed deleteNode().
** 003 SIY 20050322 @20463 Fixed comments.
** 004 SIY 20050325 @20591 Added methods required for LdapRemapper,
** added new object classes. Fixed comments
** and cleaned up code.
** 005 SIY 20050406 @21037 Moved most of the common code into the
** RamRemapper and XmlRemapperIO.
** 006 SIY 20050505 @21206 Unified constructor with LdapRemapper,
** updated documentation.
** 007 NVS 20090928 @44047 load() throws exception if the file that must
** exist can't be loaded.
** 008 SIY 20091007 @44128 save() now is atomic, file is updated or remain
** unchanged in case of any I/O problem. This
** prevents creation of partially saved directory.
** 009 SIY 20091118 @44424 Added save(String) method.
** 010 MAG 20140131 Added moveTo() method in order to fix Windows
** issue File.renameTo().
** 011 EVL 20160222 Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
** 012 SP 20250416 Code formatting fixes.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.directory;
import java.io.*;
import java.nio.file.*;
import com.goldencode.p2j.cfg.*;
/**
* The implementation of the <code>Remapper</code> interface which uses XML
* file as a backing storage for the directory data.
* <p>
* <code>XmlRemapper</code> is derived from the <code>RamRemapper</code>
* which implements all back-end functionality related to directory services
* and combines it with loading/saving data from/to XML file implemented
* by the <code>XmlRemapperIO</code> class.
*
* @author SIY
* @version 1.0
*/
class XmlRemapper
extends RamRemapper
{
/** XML file which will be used to load and store data */
private File file = null;
/** XML file must exist flag */
private boolean mustExist = false;
/**
* Construct an instance of the XmlRemapper for a given file.
*
* @param config
* Configuration data.
* @throws ConfigurationException
* in case of configuration error.
*/
XmlRemapper(BootstrapConfig config) throws ConfigurationException
{
super();
String name = config.getConfigItem("directory", "xml", "FILENAME");
mustExist = config.getConfigItem("directory", "xml", "must_exist") != null;
if (name == null)
{
throw new ConfigurationException("File name is not defined");
}
file = new File(name);
}
/**
* Load tree from the XML document.
*
* @throws Exception
* Various exceptions are forwarded from <code>XmlHelper</code>
* or thrown in case of errors.
*/
protected void load()
throws Exception
{
// Missing file is not necessarily an error, only when mustExist is set
// Such a situation may happen for a new directory.
if (file == null || !file.exists() || !file.isFile())
{
if (mustExist)
{
throw new RuntimeException("the directory file should exist");
}
return;
}
XmlRemapperIO io = new XmlRemapperIO(this, null);
io.load(new FileInputStream(file));
}
/**
* Save current tree into XML file.
*
* @throws Exception
* If I/O error occurred during operation or
* <code>XmlHelper</code> throws an
* <code>ParserConfigurationException</code>.
*/
protected void save()
throws Exception
{
int[] res = new int[] {DirectoryService.OK};
saveWorker(file, res);
}
/**
* Save current tree into XML specified file.
*
* @param newFile
* Name of file to save the directory.
*
* @return return code (refer to {@link DirectoryService} for possible
* error codes).
*/
public int save(String newFile)
{
int[] res = new int[] {DirectoryService.OK};
try
{
saveWorker(new File(newFile), res);
}
catch (Throwable t)
{
// ignore error here
}
return res[0];
}
/**
* Save current tree into XML specified file.
*
* @throws Exception
* If I/O error occurred during operation or
* <code>XmlHelper</code> throws an
* <code>ParserConfigurationException</code>.
*/
private void saveWorker(File file, int[] res)
throws Exception
{
res[0] = DirectoryService.OK;
if (file == null)
{
return;
}
XmlRemapperIO io = new XmlRemapperIO(this, null);
File tmpFile = new File(file.getAbsolutePath().concat(".tmp"));
try
{
res[0] = DirectoryService.ERR_FATAL;
FileOutputStream fos = new FileOutputStream(tmpFile);
res[0] = DirectoryService.ERR_CANT_WRITE;
io.save(fos);
res[0] = DirectoryService.ERR_FATAL;
fos.close();
res[0] = DirectoryService.ERR_CANT_WRITE;
boolean rc = renameTo(tmpFile, file);
if (!rc)
{
throw new IOException("Unable to replace existing directory with updated one.");
}
res[0] = DirectoryService.OK;
}
finally
{
try
{
tmpFile.delete();
}
catch (Exception e)
{
}
}
}
/**
* File.renameTo(File) implementation is platform dependent.
* In Windows if target file already exists the execution of this method fails.
* In Linux on the other hands succeed.
* A cross platform solution is implemented using JDK 7 Files.move feature.
*
* @param srcFile
* source file.
* @param dstFile
* destination file.
*
* @return <code>true</code> on success <code>false</code> on failure.
*/
private boolean renameTo(File srcFile, File dstFile)
{
boolean rc = false;
try
{
FileSystem fileSystem = FileSystems.getDefault();
// get files path
Path source = fileSystem.getPath(srcFile.getAbsolutePath());
Path target = fileSystem.getPath(dstFile.getAbsolutePath());
// do rename
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
rc = true;
}
catch (IOException e)
{
}
return rc;
}
}