AdminServiceImpl.java

/*
** Module   : AdminServiceImpl.java
** Abstract : AdminServiceImpl implements the remote server administration interface.
**
** Copyright (c) 2017-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
** 001 HC  20170116 Initial version.
** 002 EVL 20170926 Javadoc fix.
** 003 GBB 20230825 Adding OS users methods.
** 004 RAA 20240424 Added tenant managing methods.
**     RAA 20240513 Made updateTenant capable of receiving an array of fields and values.
**     RAA 20240517 Fixed list display in updateTenant javadoc.
**     RAA 20240604 Changed return type of addTenant and changeTenantStatus. Changed to a Map parameter
**                  in updateTenant function.
**     RAA 20240610 Added listTenants function.
**     RAA 20240627 Added functions for adding/deleting a single database instance of a tenant.
**     RAA 20240702 Refactored parameters of the addTenant function.
**     RAA 20240715 Clarified the type of database name occurrences.
**     RAA 20240726 Simplified the number of parameters from addDatabaseToTenant.
** 005 GBB 20241010 Added appserver related methods.
** 006 RAA 20241011 Added tenant description logic.
** 007 RAA 20241106 Added tenant info parameter to addTenant().
** 008 RAA 20241118 Added listTenant() method for listing a single tenant.
** 009 OM  20250211 Added sink for collecting and reporting errors occurred during REST invocations.
** 010 GBB 20250403 Added terminateSessions.
*/

/*
** 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.admin.server;

import java.awt.image.*;
import java.io.*;
import java.util.*;
import javax.imageio.*;
import org.apache.pdfbox.rendering.*;
import com.goldencode.p2j.*;
import com.goldencode.p2j.admin.*;
import com.google.gwt.user.server.rpc.*;
import com.google.gwt.view.client.*;
import com.goldencode.p2j.admin.server.reports.PdfReportBuilder.*;
import com.goldencode.p2j.admin.server.reports.*;
import com.goldencode.p2j.admin.shared.*;
import com.goldencode.p2j.persist.orm.TenantDatabaseDescriptor;
import com.goldencode.p2j.persist.orm.TenantManager.*;
import com.goldencode.p2j.security.*;
import com.goldencode.p2j.util.*;

/**
 * Implements the remote server administration interface.
 */
public class AdminServiceImpl
extends RemoteServiceServlet
implements AdminService
{
   /**
    * Returns the server's name.
    * 
    * @return   This server's name
    */
   @Override
   public String getServerName()
   {
      return AdminServerImpl.getServerName();
   }

   /**
    * Tells whether the server can modify the directory.
    * Only the master server can. The others have read-only access to
    * the directory.
    * 
    * @return   <code>true</code> if it can 
    */
   @Override
   public boolean isMaster()
   {
      return AdminServerImpl.isMaster();
   }

   /**
    * Sets the live server's directory as the target.
    *
    * @param   stayCurrent
    *          if <code>true</code>, all operations affect the most current
    *          generation of the security cache, otherwise they affect
    *          the generation attached to the admin session
    *
    * @return  security cache generation serial number
    */
   @Override
   public int setTargetLive(boolean stayCurrent)
   {
      return AdminServerImpl.setTargetLive(stayCurrent);
   }

   /**
    * Sets the specified directory file as the offline target.
    * The file can't be the live directory file.
    *
    * @param   name
    *          name of a directory file to use as the target
    *
    * @param   create
    *          <code>true</code> means a new directory file should be created,
    *          otherwise the named directory file should exist.
    *
    * @return  <code>true</code> if the new target was set successfully
    */
   @Override
   public boolean setTargetFile(String name, boolean create)
   {
      return AdminServerImpl.setTargetFile(name, create);
   }

   /**
    * Refreshes the current security cache if the target is the live directory
    * and only if there were changes performed since the last call to the
    * targetRefresh().
    *
    * @return  Security cache generation number. The same number as before
    *          means the cache was not refreshed.
    */
   @Override
   public int targetRefresh()
   {
      return AdminServerImpl.targetRefresh();
   }

   /**
    * Check if there is pending security cache refresh.
    * 
    * @return   <code>true</code> if refresh is pending and <code>false</code>
    *           is there is no pending refresh or current user has not enough 
    *           rights to perform it.
    */
   @Override
   public boolean isRefreshPending()
   {
      return AdminServerImpl.isRefreshPending();
   }

   /**
    * Check if current user can perform a refresh.
    * 
    * @return   <code>true</code> user has enough rights to perform refresh.
    */
   @Override
   public boolean canRefresh()
   {
      return AdminServerImpl.canRefresh();
   }

   /**
    * Returns an array of messages accumulated since the previous call or
    * all messages since the last message cleanup.
    * Messages can be retrieved multiple times. New messages are moved to the
    * saved messages category after the first retrieval.
    *
    * @param all
    *           if <code>true</code>, then old and new messages are retrieved,
    *           otherwise new messages only
    *
    * @return   array of messages, that can be empty 
    */
   @Override
   public String[][] getMessages(boolean all)
   {
      return AdminServerImpl.getMessages(all);
   }

   /**
    * Returns the most recent server message without changing its state 
    * on the server.
    *
    * @return   message or <code>null</code> if nothing is generated 
    */
   @Override
   public String peekMessage()
   {
      return AdminServerImpl.peekMessage();
   }

   /**
    * Clears the old messages saved on the server, if any.
    * New messages are not affected and will be returned with the next call
    * to getMessages().
    */
   @Override
   public void clearMessages()
   {
      AdminServerImpl.clearMessages();
   }

   /**
    * Returns the configuration information about the administration interface 
    * itself.
    * This information should be used to get access to the custom admin
    * extensions
    *
    * @return   container with admin related configuration information 
    */
   @Override
   public AdminDef getAdminDef()
   {
      return AdminServerImpl.getAdminDef();
   }

   /**
    * Returns the admin permissions profile for an arbitrary set of resource
    * instance names.
    *
    * @param name
    *        array of admin non-leaf resource instance names to query
    *
    * @return permissions for the given names or <code>null</code>
    */
   @Override
   public AdminProfile[] getProfile(String[] name)
   {
      return AdminServerImpl.getProfile(name);
   }

   /**
    * Returns the admin permissions profile for an arbitrary set of permissions
    * for a resource instance name.
    *
    * @param name
    *        admin leaf resource instance name to query
    *
    * @param perm
    *        array of permissions to query
    *
    * @return permissions for the given name or <code>null</code>
    */
   @Override
   public AdminProfile[] getProfile(String name, int[] perm)
   {
      return AdminServerImpl.getProfile(name, perm);
   }

   /**
    * Backup the directory into a new file.
    * 
    * @param    newfile
    *           name of a backup file to create
    *           
    * @return   <code>true</code> if backup was successful.
    */
   @Override
   public boolean backupDirectory(String newfile)
   {
      return AdminServerImpl.backupDirectory(newfile);
   }

   /**
    * Lock directory for rereading from the file.
    * 
    * @param    seconds
    *           Seconds to wait before lock will expire.
    *           
    * @return   <code>true</code> if lock was successful.
    */
   @Override
   public boolean lockForRereadDir(int seconds)
   {
      return AdminServerImpl.lockForRereadDir(seconds);
   }

   /**
    * Reread the directory file into the backend's cache after obtaining 
    * a refresh lock.
    * 
    * @return   error code. Following error codes are possible:
    *           <ol>
    *           <li>{@link AdminConstants#ERR_NO_LOCK} - is returned when no lock is present</li>
    *           <li>{@link AdminConstants#ERR_LOCK_EXPIRED}- is returned if lock is present 
    *           but it is expired or belongs to other thread.</li>
    *           <li>{@link AdminConstants#ERR_CANT_REFRESH_BACKEND} - is returned when 
    *           backend can't update directory.</li>
    *           <li>{@link AdminConstants#ERR_ILLEGAL} - is returned when no rights etc</li>
    *           </ol>
    */
   @Override
   public int rereadDirectory()
   {
      return AdminServerImpl.rereadDirectory();
   }

   /**
    * Cancel (possibly) planned refreshing operation.
    */
   @Override
   public void cancelRereadDir()
   {
      AdminServerImpl.cancelRereadDir();
   }

   /**
    * Creates a report of all current user and process sessions.
    *
    * @return   Session descriptors suitable for reporting purposes or
    *           <code>null</code> if insufficient rights exist to obtain the
    *           list.
    */
   @Override
   public SessionInfo[] getSessionList()
   {
      return AdminServerImpl.getSessionList();
   }

   /**
    * Forcibly terminate the specified session.
    *
    * @param    sid
    *           Session identifier.
    */
   @Override
   public void terminateSession(int sid)
   {
      AdminServerImpl.terminateSession(sid);
   }

   /**
    * Creates a report of all record locks for the server.
    *
    * @return   Record lock descriptors suitable for reporting purposes or
    *           <code>null</code> if insufficient rights exist to obtain the
    *           list.
    */
   @Override
   public RecordLockInfo[] getRecordLockList()
   {
      return AdminServerImpl.getRecordLockList();
   }

   /**
    * Get the list of databases managed by this P2J server.
    *
    * @return  Names of databases managed by this P2J server.
    */
   @Override
   public String[] getDatabases()
   {
      return AdminServerImpl.getDatabases();
   }

   /**
    * Get the list of tables of the given database.
    *
    * @param   database
    *          Target database.
    *
    * @return  See above.
    *          Names of the tables of the given database.
    */
   @Override
   public String[] getDatabaseTables(String database)
   {
      return AdminServerImpl.getDatabaseTables(database);
   }

   /**
    * Lock the specified records. Locks are obtained in a single batch.<p>
    * You can unlock records using {@link #unlockRecords()}.
    *
    * @param   records
    *          Records to lock.
    *
    * @return  <code>true</code> if records were successfully locked.
    */
   @Override
   public boolean lockRecords(RecordInfo[] records)
   {
      return AdminServerImpl.lockRecords(records);
   }

   /**
    * Gets the records which have been previously locked in the given context
    * using {@link #lockRecords}.
    * 
    * @return   Locked records in the given context.
    */
   @Override
   public RecordInfo[] getLockRecords()
   {
      return AdminServerImpl.getLockRecords();
   }

   /**
    * Unlock the records which has been previously locked in the given context
    * using {@link #lockRecords}.
    *
    * @return <code>true</code> if records were successfully unlocked.
    */
   @Override
   public boolean unlockRecords()
   {
      return AdminServerImpl.unlockRecords();
   }

   /**
    * Shuts the server down.
    *
    * @return  <code>true</code> if successful
    */
   @Override
   public boolean shutdown()
   {
      return AdminServerImpl.shutdown();
   }

   /**
    * Returns an array of resource plugin names.
    * 
    * @return   All defined resource plugin names 
    */
   @Override
   public String[] listResourceNames()
   {
      return AdminServerImpl.listResourceNames();
   }

   /**
    * Returns the registered resource type name for the given resource plugin.
    *
    * @param name
    *        plugin name
    *
    * @return registered resource type name or <code>null</code> if no match
    */
   @Override
   public String getResourceType(String name)
   {
      return AdminServerImpl.getResourceType(name);
   }

   /**
    * Modifies resource name definitions.
    * 
    * @param   additions
    *          array of resource names to add or <code>null</code>
    *          if no additions are specified
    * @param   deletions
    *          array of resource names to delete or <code>null</code>
    *          if no deletions are specified
    * @return  boolean success indicator 
    */
   @Override
   public boolean changeResourceNames(String[] additions, String[] deletions)
   {
      return AdminServerImpl.changeResourceNames(additions, deletions);
   }

   /**
    * Gets authentication configuration, which includes authentication mode,
    * password aging and security tracing level.
    * 
    * @return   an instance with all current authentication values 
    */
   @Override
   public AuthConfigDef getAuthConfig()
   {
      return AdminServerImpl.getAuthConfig();
   }

   /**
    * Sets authentication configuration from the specified instance, which
    * includes authentication mode, password aging and security tracing level.
    * 
    * @param   authConfig
    *          an instance loaded with the required configuration
    * 
    * @return  boolean success indicator 
    */
   @Override
   public boolean setAuthConfig(AuthConfigDef authConfig)
   {
      return AdminServerImpl.setAuthConfig(authConfig);
   }

   /**
    * Converts <code>java.util.Date[]</code>, the array of data transferable objects, into
    * the array of <code>com.goldencode.p2j.util.date</code> objects.
    * 
    * @param    dts
    *           The array of data transferable objects representing by <code>java.util.Date</code>
    * 
    * @return   The array of <code>com.goldencode.p2j.util.date</code> objects
    */
   private com.goldencode.p2j.util.date[] fromDTO(Date[] dts)
   {
      if (dts == null)
      {
         return null;
      }
      int size = dts.length;

      com.goldencode.p2j.util.date[] p2jDates = new com.goldencode.p2j.util.date[size];

      for(int i = size - 1; i >= 0; i--)
      {
         /** @see com.goldencode.p2j.util.date#date(Date d)*/
         p2jDates[i] = new com.goldencode.p2j.util.date(dts[i]);
      }

      return p2jDates;
   }

   /**
    * Converts <code>com.goldencode.p2j.util.date[]</code> to the array of
    * <code>java.util.Date</code> data transferable objects.
    *
    * @param    dts
    *           The array of <code>com.goldencode.p2j.util.date</code> objects
    *
    * @return   dto object.
    */
   private Date[] toDTO(com.goldencode.p2j.util.date[] dts)
   {
      if (dts == null)
      {
         return null;
      }

      int size = dts.length;

      Date[] transferableDates = new Date[size];

      for(int i = size - 1; i >= 0; i--)
      {
         /** @see com.goldencode.p2j.util.date#dateValue() */
         transferableDates[i] = dts[i].dateValue();
      }

      return transferableDates;
   }

   /**
    * Returns an array of defined holidays.
    * 
    * @return   All defined holidays 
    */
   @Override
   public Date[] listHolidays()
   {
      return toDTO(AdminServerImpl.listHolidays());
   }

   /**
    * Modifies defined holidays.
    * 
    * @param   additions
    *          array of dates to add or <code>null</code>
    *          if no additions are specified
    * @param   deletions
    *          array of dates to delete or <code>null</code>
    *          if no deletions are specified
    * 
    * @return  boolean success indicator 
    */
   @Override
   public boolean changeHolidays(Date[] additions, Date[] deletions)
   {
      return AdminServerImpl.changeHolidays(fromDTO(additions), fromDTO(deletions));
   }

   /**
    * Gets the audit.
    *
    * @return   The audit
    */
   @Override
   public AuditDef getAudit()
   {
      return AdminServerImpl.getAudit();
   }

   /**
    * Sets the audit.
    * 
    * @param    audit
    *           The audit
    * 
    * @return   True, iff this action is completed successfully
    */
   @Override
   public boolean setAudit(AuditDef audit)
   {
      return AdminServerImpl.setAudit(audit);
   }

   /**                                              
    * Returns an array of defined group names.
    * 
    * @return   All defined group accounts or <code>null</code>
    */
   @Override
   public TaggedName[] listGroups()
   {
      return AdminServerImpl.listGroups();
   }

   /**                                              
    * Check the group account existence.
    *
    * @param    name
    *           group name
    * 
    * @return   minimum group information (name, description)
    *           or <code>null</code> if the named group does not exist
    */
   @Override
   public TaggedName checkGroup(String name)
   {
      return AdminServerImpl.checkGroup(name);
   }

   /**
    * Returns the group definition for the specified group name.
    *
    * @param    name
    *           group name
    *
    * @return   group definition or <code>null</code> if no such group
    */
   @Override
   public GroupDef getGroup(String name)
   {
      return AdminServerImpl.getGroup(name);
   }

   /**
    * Changes the group definition for the named group.
    *
    * @param    group
    *           group definition
    *
    * @return   <code>true</code> if successfully changed the group
    */
   @Override
   public boolean setGroup(GroupDef group)
   {
      return AdminServerImpl.setGroup(group);
   }

   /**
    * Adds new group account definition.
    *
    * @param    group
    *           group definition for a new account
    *
    * @return   <code>true</code> if successfully added the group
    */
   @Override
   public boolean addGroup(GroupDef group)
   {
      return AdminServerImpl.addGroup(group);
   }

   /**
    * Deletes a group account definition.
    * The account is deleted. All references to this group are deleted from
    * the user accounts and from ACLs.
    *
    * @param    name
    *           group name for the group being deleted
    *
    * @return   <code>true</code> if successfully deleted the group
    */
   @Override
   public boolean deleteGroup(String name)
   {
      synchronized (AdminServerImpl.class)
      {
         return AdminServerImpl.deleteGroup(name);
      }
   }

   /**
    * Clones an existing group account into a new one.
    * Users of the source group become also the users of the clone.
    * ACLs are modified to give the source group's rights to the clone, if
    * this option is requested.
    *
    * @param    nameFrom
    *           group name for the source group
    *
    * @param    nameTo
    *           group name for the clone
    *
    * @param    acls
    *           <code>true</code> tells to clone ACLs as well
    *
    * @return   <code>true</code> if successfully cloned the group
    */
   @Override
   public boolean cloneGroup(String nameFrom, String nameTo, boolean acls)
   {
      return AdminServerImpl.cloneGroup(nameFrom, nameTo, acls);
   }

   /**
    * Enumerates the users of a named group.
    *
    * @param    name
    *           group name
    *
    * @return   array of user names, that may be empty, or <code>null</code>
    *           if there is no such group
    */
   @Override
   public TaggedName[] listGroupUsers(String name)
   {
      return AdminServerImpl.listGroupUsers(name);
   }

   /**
    * Changes the group by adding and/or removing users.
    *
    * @param    name
    *           group name
    *
    * @param    additions
    *           array of user names to be added to the group; may be empty
    *
    * @param    deletions
    *           array of user names to be excluded from the group;
    *           may be empty
    *
    * @return   <code>true</code> if successfully changed the group
    */
   @Override
   public boolean changeGroupUsers(String name, String[] additions, String[] deletions)
   {
      return AdminServerImpl.changeGroupUsers(name, additions, deletions);
   }

   /**
    * Returns an array of defined user account names and their owners.
    * 
    * @return   All defined user accounts or <code>null</code>
    */
   @Override
   public TaggedName[] listUsers()
   {
      return AdminServerImpl.listUsers();
   }

   /**                                              
    * Check the user account existence.
    *
    * @param    name
    *           user account name
    * 
    * @return   minimum user account information (name, person)
    *           or <code>null</code> if the named account does not exist
    */
   @Override
   public TaggedName checkUser(String name)
   {
      return AdminServerImpl.checkUser(name);
   }

   /**
    * Returns the user account definition for the specified account name.
    *
    * @param    name
    *           user account name
    *
    * @return   user account definition or <code>null</code> if no such user
    */
   @Override
   public UserDef getUser(String name)
   {
      return AdminServerImpl.getUser(name);
   }

   /**
    * Changes the user account definition for the named account.
    * Optional fields are synchronized with the directory. Password aging
    * information is set only if a password is set.
    * Any existing custom account extensions remain unchanged.
    *
    * @param    user
    *           user account definition
    *
    * @return   <code>true</code> if successfully changed the account
    */
   @Override
   public boolean setUser(UserDef user)
   {
      return AdminServerImpl.setUser(updateHashedPassword(user));
   }

   /**
    * Changes the user account definition for the named account.
    * Optional fields are synchronized with the directory. Password aging
    * information is set only if a password is set.
    *
    * @param    user
    *           user account definition
    *
    * @param    ext
    *           optional extension container for custom account extensions
    *
    * @return   <code>true</code> if successfully changed the account
    */
   @Override
   public boolean setUser(UserDef user, Serializable ext)
   {
      return AdminServerImpl.setUser(updateHashedPassword(user), ext);
   }

   /**
    * Adds new user account definition.
    *
    * @param    user
    *           user account definition
    *
    * @param    ext
    *           optional extension container for custom account extensions
    *
    * @return   <code>true</code> if successfully added the account
    */
   @Override
   public boolean addUser(UserDef user, Serializable ext)
   {
      return AdminServerImpl.addUser(updateHashedPassword(user), ext);
   }

   /**
    * Deletes a user account definition.
    * The account is deleted. All references to this account are deleted from
    * the ACLs, but the associated user certificate is left untouched.
    *
    * @param    name
    *           user account name being deleted
    *
    * @return   <code>true</code> if successfully deleted the account
    */
   @Override
   public boolean deleteUser(String name)
   {
      synchronized (AdminServerImpl.class)
      {
         return AdminServerImpl.deleteUser(name);
      }
   }

   /**
    * Clones an existing user account into a new one, password included.
    * ACLs are modified to give the source user rights to the clone, if
    * this option is requested.
    *
    * @param    nameFrom
    *           user account name for the source
    *
    * @param    nameTo
    *           user account name for the clone
    *
    * @param    acls
    *           <code>true</code> tells to clone ACLs as well
    *
    * @return   <code>true</code> if successfully cloned the user account
    */
   @Override
   public boolean cloneUser(String nameFrom, String nameTo, boolean acls)
   {
      return AdminServerImpl.cloneUser(nameFrom, nameTo, acls);
   }

   /**
    * Returns the OS user account definition for the specified account name.
    *
    * @param    name
    *           OS user account name
    *
    * @return   OS user account definition or <code>null</code> if no such user
    */
   @Override
   public OsUserDef getOsUser(String name)
   {
      return AdminServerImpl.getOsUser(name);
   }

   /**
    * Deletes an OS user account definition. The account is deleted. All
    * references to this account are deleted from the ACLs, but the associated
    * user certificate is left untouched.
    *
    * @param    name
    *           OS user account name being deleted
    *
    * @return   {@code true} if successfully deleted the account
    */
   @Override
   public boolean deleteOsUser(String name)
   {
      return AdminServerImpl.deleteOsUser(name);
   }
   
   /**
    * Adds the OS user.
    *
    * @param    user
    *           the user
    *
    * @return   <code>true</code> if successfully added the user
    */
   @Override
   public boolean addOsUser(OsUserDef user)
   {
      if (user.plainPassword != null && !user.plainPassword.isEmpty())
      {
         user.password = com.goldencode.p2j.directory.Base64.byteArrayToBase64(user.plainPassword.getBytes())
                                                            .getBytes();
      }
      return AdminServerImpl.addOsUser(user);
   }
   
   /**
    * Sets the OS user.
    *
    * @param    user
    *           the user
    *
    * @return   <code>true</code> if successfully changed the user
    */
   @Override
   public boolean setOsUser(OsUserDef user)
   {
      user.password = user.plainPassword != null && !user.plainPassword.isEmpty() ?
         com.goldencode.p2j.directory.Base64.byteArrayToBase64(user.plainPassword.getBytes()).getBytes() :
         null;
      return AdminServerImpl.setOsUser(user);
   }
   
   /**
    * Returns an array of defined FWD user names, associated with the OS user.
    *
    * @param    osUser
    *           The OS user name
    *
    * @return   The associated FWD users or <code>null</code>
    */
   @Override
   public TaggedName[] listAssociatedFwdUsers(String osUser)
   {
      return AdminServerImpl.listAssociatedFwdUsers(osUser);
   }

   /**
    * Clones an existing user account into a new one and assigns the provided
    * password to the clone.
    * ACLs are modified to give the source user rights to the clone, if
    * this option is requested.
    *
    * @param    nameFrom
    *           user account name for the source
    *
    * @param    nameTo
    *           user account name for the clone
    *
    * @param    acls
    *           <code>true</code> tells to clone ACLs as well
    *
    * @param    newPassword
    *           a replacement password for the clone
    *
    * @return   <code>true</code> if successfully cloned the user account
    */
   @Override
   public boolean cloneUser(String nameFrom, String nameTo, boolean acls, String newPassword)
   {
      return AdminServerImpl.cloneUser(nameFrom, nameTo, acls, HashPassword.hashPassword(newPassword));
   }

   /**
    * Enumerates the groups the named user is part of.
    *
    * @param    name
    *           user account name
    *
    * @return   array of tagged group names, that may be empty, 
    *           or <code>null</code> if there is no such user etc
    */
   @Override
   public TaggedName[] listUserGroups(String name)
   {
      return AdminServerImpl.listUserGroups(name);
   }
   
   /**
    * Returns an array of defined OS user names and their hashed passwords.
    *
    * @return   All defined OS users or <code>null</code>
    */
   @Override
   public TaggedName[] listOsUsers()
   {
      return AdminServerImpl.listOsUsers();
   }
   
   /**
    * Check the user account existence.
    *
    * @param    name
    *           Os user account name
    *
    * @return   Minimum OS user account information  or <code>null</code> if the account doesn't exist
    */
   @Override
   public TaggedName checkOsUser(String name)
   {
      return AdminServerImpl.checkOsUser(name);
   }

   /**                                              
    * Returns an array of defined process names with their descriptions.
    * 
    * @return   All defined processes accounts or <code>null</code>
    */
   @Override
   public TaggedName[] listProcesses()
   {
      return AdminServerImpl.listProcesses();
   }

   /**                                              
    * Check the process account existence.
    *
    * @param    name
    *           process account name
    * 
    * @return   minimum process account information (name, description)
    *           or <code>null</code> if the named account does not exist
    */
   @Override
   public TaggedName checkProcess(String name)
   {
      return AdminServerImpl.checkProcess(name);
   }

   /**
    * Returns the process definition for the specified process name.
    *
    * @param    name
    *           process name
    *
    * @return   process definition or <code>null</code> if no such process
    */
   @Override
   public ProcessDef getProcess(String name)
   {
      return AdminServerImpl.getProcess(name);
   }

   /**
    * Changes the process definition for the named process.
    * Any existing custom account extensions remain unchanged.
    *
    * @param    process
    *           process definition
    *
    * @return   <code>true</code> if successfully changed the process
    */
   @Override
   public boolean setProcess(ProcessDef process)
   {
      return AdminServerImpl.setProcess(process);
   }

   /**
    * Changes the process definition for the named process.
    *
    * @param    process
    *           process definition
    *
    * @param    ext
    *           optional extension container for custom account extensions
    *
    * @return   <code>true</code> if successfully changed the process
    */
   @Override
   public boolean setProcess(ProcessDef process, Serializable ext)
   {
      return AdminServerImpl.setProcess(process, ext);
   }

   /**
    * Adds new process account definition.
    *
    * @param    process
    *           process definition for a new account
    *
    * @param    ext
    *           optional extension container for custom account extensions
    *
    * @return   <code>true</code> if successfully added the process
    */
   @Override
   public boolean addProcess(ProcessDef process, Serializable ext)
   {
      return AdminServerImpl.addProcess(process, ext);
   }

   /**
    * Deletes a process account definition.
    * The account is deleted. All references to this process are deleted from
    * the ACLs, but the associated process certificate is left untouched.
    *
    * @param    name
    *           process account name being deleted
    *
    * @return   <code>true</code> if successfully deleted the process
    */
   @Override
   public boolean deleteProcess(String name)
   {
      return AdminServerImpl.deleteProcess(name);
   }

   /**
    * Clones an existing process account into a new one.
    * Master server flag is always false for the clone.
    * ACLs are modified to give the source process' rights to the clone, if
    * this option is requested.
    *
    * @param    nameFrom
    *           process name for the source
    *
    * @param    nameTo
    *           process name for the clone
    *
    * @param    acls
    *           <code>true</code> tells to clone ACLs as well
    *
    * @return   <code>true</code> if successfully cloned the process
    */
   @Override
   public boolean cloneProcess(String nameFrom, String nameTo, boolean acls)
   {
      return AdminServerImpl.cloneProcess(nameFrom, nameTo, acls);
   }

   /**
    * Returns an array of defined Certificate Authority certificates 
    * with their distinctive names.
    *
    * @param unusedOnly
    *        if <code>true</code>, lists unused certificates only, otherwise
    *        all certificates
    *
    * @return   All defined CA certificates or <code>null</code>
    */
   @Override
   public TaggedName[] listAuthCerts(boolean unusedOnly)
   {
      return AdminServerImpl.listAuthCerts(unusedOnly);
   }

   /**
    * Returns an array of defined peer (non-CA) certificates 
    * with their distinctive names.
    *
    * @param unusedOnly
    *        if <code>true</code>, lists unused certificates only, otherwise
    *        all certificates
    *
    * @return   All defined peer certificates or <code>null</code>
    */
   @Override
   public TaggedName[] listPeerCerts(boolean unusedOnly)
   {
      return AdminServerImpl.listPeerCerts(unusedOnly);
   }

   /**
    * Returns an array of user account names with their description for users
    * sharing this certificate.
    *
    * @param alias certificate alias name
    *
    * @return all user accounts referring to this certificate or 
    *         <code>null</code> 
    */
   @Override
   public TaggedName[] getCertUsers(String alias)
   {
      return AdminServerImpl.getCertUsers(alias);
   }

   /**
    * Returns an array of process account names with their description for 
    * processes sharing this certificate.
    *
    * @param alias certificate alias name
    *
    * @return all process accounts referring to this certificate or 
    *         <code>null</code> 
    */
   @Override
   public TaggedName[] getCertProcesses(String alias)
   {
      return AdminServerImpl.getCertProcesses(alias);
   }

   /**
    * Returns an array of all account names with their description for accounts
    * sharing this certificate.
    *
    * @param alias certificate alias name
    *
    * @return all accounts referring to this certificate or 
    *         <code>null</code> 
    */
   @Override
   public TaggedName[] getCertAccounts(String alias)
   {
      return AdminServerImpl.getCertAccounts(alias);
   }

   /**
    * Returns a DER-encoded certificate like those found in *.PEM files.
    *
    * @param alias  the alias of the certificate to get
    *
    * @return the encoded certificate or <code>null</code>
    */
   @Override
   public CertDef getCert(String alias)
   {
      return AdminServerImpl.getCert(alias);
   }

   /**
    * Replaces an existing certificate with the given one.
    *
    * @param cert encoded certificate
    *
    * @return <code>true</code> if replaced, <code>false</code> if errors
    */
   @Override
   public boolean setCert(CertDef cert)
   {
      return AdminServerImpl.setCert(cert);
   }

   /**
    * Adds a new certificate according to its definition.
    *
    * @param cert encoded certificate
    *
    * @return <code>true</code> if added, <code>false</code> if errors
    */
   @Override
   public boolean addCert(CertDef cert)
   {
      return AdminServerImpl.addCert(cert);
   }

   /**
    * Changes the certificate's alias.
    *
    * @param aliasFrom current certificate's alias
    *
    * @param aliasTo new certificate's alias
    *
    * @return <code>true</code> if changed, <code>false</code> if errors
    */
   @Override
   public boolean moveCert(String aliasFrom, String aliasTo)
   {
      return AdminServerImpl.moveCert(aliasFrom, aliasTo);
   }

   /**
    * Searches the CA directory for an alias of the certificate by the
    * distinctive name of the subject.
    *
    * @param dn distinctive name
    *
    * @return alias of the certificate if exists, or <code>null</code>.
    */
   @Override
   public String searchCert(String dn)
   {
      return AdminServerImpl.searchCert(dn);
   }

   /**
    * Deletes an existing certificate conditionally or unconditionally.
    *
    * @param alias  the alias of the certificate being deleted
    *
    * @param force if <code>true</code>, the certificate is deleted even if
    *              in use
    *
    * @return <code>true</code> if deleted, <code>false</code> if errors
    *
    */
   @Override
   public boolean deleteCert(String alias, boolean force)
   {
      return AdminServerImpl.deleteCert(alias, force);
   }

   /**
    * Locates all instances of ACLs in the directory and returns their names.
    * The shared instance has an empty name.
    * <p>
    * ACL instances are the directory branches that store ACLs. The shared
    * instance is the one found under <code>/security/acl</code> branch.
    * Private instances are found under <code>/security/acls/<i>name</i></code>
    * branches.
    *
    * @return array of ACL instance names; may be empty
    */
   @Override
   public String[] listAclInstances()
   {
      return AdminServerImpl.listAclInstances();
   }

   /**
    * Locates all instances of ACLs in the directory and returns their names
    * for those instances which have ACLs defined for the specified resource
    * type.
    * The shared instance has an empty name.
    * <p>
    * ACL instances are the directory branches that store ACLs. The shared
    * instance is the one found under <code>/security/acl</code> branch.
    * Private instances are found under <code>/security/acls/<i>name</i></code>
    * branches.
    *
    * @param resourceType
    *        name of the resource of interest
    *
    * @return array of ACL instance names; may be empty
    */
   @Override
   public String[] listAclInstances(String resourceType)
   {
      return AdminServerImpl.listAclInstances(resourceType);
   }

   /**
    * Lists resource names for which the specified ACL instance has ACLs
    * defined.
    *
    * @param instanceName
    *        name of the ACL instance; empty string for the shared one
    *
    * @return array of resource type names; may be empty
    */
   @Override
   public String[] listInstanceAcls(String instanceName)
   {
      return AdminServerImpl.listInstanceAcls(instanceName);
   }

   /**
    * Gets the full ACL definition information from the directory for the
    * specified ACL instance and resource type.
    *
    * @param resourceType
    *        name of the resource of interest; can't be <code>null</code>
    *
    * @param instanceName
    *        name of the ACL instance; empty string for the shared one;
    *        can't be <code>null</code>
    *
    * @return ACL definition or <code>null</code>
    */
   @Override
   public AclDef getAcl(String resourceType, String instanceName)
   {
      return AdminServerImpl.getAcl(resourceType, instanceName);
   }

   /**
    * Gets the full ACL definition information from the directory for the
    * specified ACL instance or all of them.
    *
    * @param instanceName
    *        name of the ACL instance; empty string for the shared one;
    *        if <code>null</code>, all ACLs are returned and some may be
    *        combined
    *
    * @return an array of ACL definitions, one per resource type and instance,
    *         or <code>null</code>
    */
   @Override
   public AclDef[] getAllAcls(String instanceName)
   {
      return AdminServerImpl.getAllAcls(instanceName);
   }

   /**
    * Gets the combined ACL definition information from the directory for the
    * specified resource type.
    * The combination is done according to the regular server's rules taking
    * the private instance for the live server and the shared one.
    *
    * @param resourceType
    *        name of the resource of interest; can't be <code>null</code>
    *
    * @return ACL definition or <code>null</code>
    */
   @Override
   public AclDef getEffectiveAcls(String resourceType)
   {
      return AdminServerImpl.getEffectiveAcls(resourceType);
   }

   /**
    * Adds new set of ACLs based on the contents of the given ACL definition.
    * Both the target ACL instance and resource part will be created if they 
    * don't exist.
    *
    * @param resourceType
    *        name of the resource to add the ACL for
    *
    * @param acl
    *        ACL definition, that should come fully defined, including the
    *        instance name.
    *
    * @param subjMap
    *        a map to translate numeric subject IDs back to names
    *
    * @return <code>true</code> if added, <code>false</code> if errors
    */
   @Override
   public boolean addAcl(String resourceType, Acl acl, Map<Integer, String> subjMap)
   {
      return AdminServerImpl.addAcl(resourceType, acl, subjMap);
   }

   /**
    * Conditionally edits the existing ACL based on the contents of the given  
    * ACL and the selected conditions. 
    *
    * @param resourceType
    *        name of the resource to set the ACL for
    *
    * @param acl
    *        ACL definition
    *
    * @param subjMap
    *        a map to translate numeric subject IDs back to names;
    *        needed only when setSubjects is <code>true</code> and can be
    *        <code>null</code> all other times
    *
    * @param setInstance
    *        <code>true</code> if setting the resource instance name
    *
    * @param setInstanceMode
    *        <code>true</code> if setting the resource instance name mode
    *
    * @param setSubjects
    *        <code>true</code> if setting the subjects IDs
    *
    * @param setRights
    *        <code>true</code> if setting the rights for this ACL
    *
    * @return <code>true</code> if set, <code>false</code> if errors
    */
   @Override
   public boolean setAcl(String resourceType, Acl acl, Map<Integer, String> subjMap,
            boolean setInstance, boolean setInstanceMode, boolean setSubjects, boolean setRights)
   {
      return AdminServerImpl.setAcl(resourceType, acl, subjMap, setInstance, setInstanceMode,
               setSubjects, setRights);
   }

   /**
    * Moves the specified ACL to another ACL instance, private or shared.
    * The target instance will be created, if it doesn't exist.
    *
    * @param resourceType
    *        name of the resource of interest
    *
    * @param instanceName
    *        name of the source ACL instance; empty string for the shared one
    *
    * @param oid
    *        ACL object ID that selects the ACL to move
    *
    * @param newInstanceName
    *        name of the target ACL instance; empty string for the shared one
    *
    * @return <code>true</code> if moved, <code>false</code> if errors
    */
   @Override
   public boolean moveAcl(String resourceType, String instanceName, int oid,
            String newInstanceName)
   {
      return AdminServerImpl.moveAcl(resourceType, instanceName, oid, newInstanceName);
   }

   /**
    * Moves the specified ACL to another position within the same ACL instance.
    *
    * @param resourceType
    *        name of the resource of interest
    *
    * @param instanceName
    *        name of the source ACL instance; empty string for the shared one
    *
    * @param oid
    *        ACL object ID that selects the ACL to move
    *
    * @param newOid
    *        new ACL object ID
    *
    * @return <code>true</code> if moved, <code>false</code> if errors
    */
   @Override
   public boolean moveAcl(String resourceType, String instanceName, int oid, int newOid)
   {
      return AdminServerImpl.moveAcl(resourceType, instanceName, oid, newOid);
   }

   /**
    * Deletes the specified ACL from the specified ACL instance.
    *
    * @param resourceType
    *        name of the resource of interest
    *
    * @param instanceName
    *        name of the source ACL instance; empty string for the shared one
    *
    * @param oid
    *        ACL object ID that selects the ACL to delete
    *
    * @return <code>true</code> if deleted, <code>false</code> if errors
    */
   @Override
   public boolean deleteAcl(String resourceType, String instanceName, int oid)
   {
      return AdminServerImpl.deleteAcl(resourceType, instanceName, oid);
   }

   /**
    * Clones the specified ACL to another position within the same ACL instance.
    *
    * @param resourceType
    *        name of the resource of interest
    *
    * @param instanceName
    *        name of the source ACL instance; empty string for the shared one
    *
    * @param oid
    *        ACL object ID that selects the ACL to clone
    *
    * @param cloneOid
    *        ACL object ID for the clone
    *
    * @return <code>true</code> if cloned, <code>false</code> if errors
    */
   @Override
   public boolean cloneAcl(String resourceType, String instanceName, int oid, int cloneOid)
   {
      return AdminServerImpl.cloneAcl(resourceType, instanceName, oid, cloneOid);
   }

   /**
    * Assigns new object IDs sequentially for all ACLs in the given ACL instance
    * and resource type.
    *
    * @param resourceType
    *        name of the resource of interest
    *
    * @param instanceName
    *        name of the source ACL instance; empty string for the shared one
    *
    * @param start
    *        the starting number
    *
    * @param step
    *        the increment
    *
    * @return <code>true</code> if success, <code>false</code> if errors
    */
   @Override
   public boolean resequenceAcls(String resourceType, String instanceName, int start, int step)
   {
      return AdminServerImpl.resequenceAcls(resourceType, instanceName, start, step);
   }

   /**
    * Creates an empty container for the specified ACL instance.
    * The instance should not exist.
    *
    * @param instanceName
    *        name of the ACL instance; empty string for the shared one
    *
    * @return <code>true</code> if created, <code>false</code> if errors
    */
   @Override
   public boolean addAclInstance(String instanceName)
   {
      return AdminServerImpl.addAclInstance(instanceName);
   }

   /**
    * Deletes entire ACL instance from the directory.
    * The instance should exist.
    *
    * @param instanceName
    *        name of the ACL instance; empty string for the shared one
    *
    * @return <code>true</code> if deleted, <code>false</code> if errors
    */
   @Override
   public boolean deleteAclInstance(String instanceName)
   {
      return AdminServerImpl.deleteAclInstance(instanceName);
   }

   /**
    * Deletes the resource specific part of an ACL instance from the directory.
    * The instance and the resource part should exist.
    *
    * @param resourceType
    *        name of the resource of interest
    *
    * @param instanceName
    *        name of the ACL instance; empty string for the shared one
    *
    * @return <code>true</code> if deleted, <code>false</code> if errors
    */
   @Override
   public boolean deleteAclInstance(String resourceType, String instanceName)
   {
      return AdminServerImpl.deleteAclInstance(resourceType, instanceName);
   }

   /**
    * Clones entire ACL instance.
    * The source ACL instance should exist. The target ACL instance should not
    * exist.
    *
    * @param instanceFrom
    *        name of the source ACL instance; empty string for the shared one
    *
    * @param instanceTo
    *        name of the target ACL instance; empty string for the shared one
    *
    * @return <code>true</code> if cloned, <code>false</code> if errors
    */
   @Override
   public boolean cloneAclInstance(String instanceFrom, String instanceTo)
   {
      return AdminServerImpl.cloneAclInstance(instanceFrom, instanceTo);
   }

   /**
    * Clones the resource specific part of an ACL instance.
    * The source ACL instance and the resource part should exist. 
    * The target ACL instance should exist.
    * The resource part in the target ACL instance should NOT exist.
    *
    * @param resourceType
    *        name of the resource of interest
    *
    * @param instanceFrom
    *        name of the source ACL instance; empty string for the shared one
    *
    * @param instanceTo
    *        name of the target ACL instance; empty string for the shared one
    *
    * @return <code>true</code> if cloned, <code>false</code> if errors
    */
   @Override
   public boolean cloneAclInstance(String resourceType, String instanceFrom, String instanceTo)
   {
      return AdminServerImpl.cloneAclInstance(resourceType, instanceFrom, instanceTo);
   }

   /**
    * Moves entire ACL instance.
    * The source ACL instance should exist. The target ACL instance should not
    * exist.
    *
    * @param instanceFrom
    *        name of the source ACL instance; empty string for the shared one
    *
    * @param instanceTo
    *        name of the target ACL instance; empty string for the shared one
    *
    * @return <code>true</code> if moved, <code>false</code> if errors
    */
   @Override
   public boolean moveAclInstance(String instanceFrom, String instanceTo)
   {
      return AdminServerImpl.moveAclInstance(instanceFrom, instanceTo);
   }

   /**
    * Moves the resource specific part of an ACL instance.
    * The source ACL instance and the resource part should exist. 
    * The target ACL instance should exist.
    * The resource part in the target ACL instance should NOT exist.
    *
    * @param resourceType
    *        name of the resource of interest
    *
    * @param instanceFrom
    *        name of the source ACL instance; empty string for the shared one
    *
    * @param instanceTo
    *        name of the target ACL instance; empty string for the shared one
    *
    * @return <code>true</code> if moved, <code>false</code> if errors
    */
   @Override
   public boolean moveAclInstance(String resourceType, String instanceFrom, String instanceTo)
   {
      return AdminServerImpl.moveAclInstance(resourceType, instanceFrom, instanceTo);
   }

   /**
    * Returns the list of all server instances found in the directory
    * under the "/server" node. The "default" instance is returned as
    * an empty string.
    *
    * @return array of server names
    */
   @Override
   public String[] listServers()
   {
      return AdminServerImpl.listServers();
   }

   /**
    * Returns the list of server instances under the "/server" node, that
    * have the specified container (or nested chain of) as a child.
    *
    * @param containers
    *        one or more container names separated by "/"
    *
    * @return array of server names holding the named containers
    */
   @Override
   public String[] listServers(String containers)
   {
      return AdminServerImpl.listServers(containers);
   }

   /**
    * Get the list of available auth plugins.
    *
    * @return  the list of available auth plugins encoded into tagged names or
    *          <code>null</code> if no plugins are available.
    */
   @Override
   public TaggedName[] listAuthPlugins()
   {
      return AdminServerImpl.listAuthPlugins();
   }

   /**
    * Preforms bulk update of authentication settings of the accounts of the
    * specified type.
    *
    * @param  targetAccounts
    *         Type of accounts to update. <code>Account.ACC_USER</code> and
    *         <code>Account.ACC_GROUP</code> are supported.
    * @param  excludedAccounts
    *         The list of IDs of the accounts that should NOT be updated. Can
    *         be <code>null</code>.
    * @param  authMode
    *         Authentication mode to set.
    * @param  authPlugin
    *         Authentication plugin to set.
    *
    * @return the number of updated accounts or <code>-1</code> if an error
    *         has occured.
    */
   @Override
   public int bulkAuthModeUpdate(int targetAccounts, String[] excludedAccounts, int authMode,
            String authPlugin)
   {
      return AdminServerImpl.bulkAuthModeUpdate(targetAccounts, excludedAccounts, authMode,
               authPlugin);
   }

   /**
    * Get the list of registered jars (which represent customer libs).
    *
    * @return the list of registered jar, each returned
    *         <code>TaggedName</code> represents a single jar and contains jar
    *         file name, fully qualified class name of the active hook (it
    *         may be <code>null</code>) and value indicating whether active
    *         hook class matches stored hook class which is
    *         <code>"true"</code> if they match.
    */
   @Override
   public TaggedName[] getRegisteredJars()
   {
      return AdminServerImpl.getRegisteredJars();
   }

   /**
    * Get the list of jars, which reside in the customer libs directory,
    * but haven't been registered yet and do not present in the server
    * classpath.
    *
    * @return the list of the names of unregistered jar files available for
    *         registration.
    */
   @Override
   public String[] getUnregisteredJars()
   {
      return AdminServerImpl.getUnregisteredJars();
   }

   /**
    * Get the list of classes which reside into the specified jar file and
    * implement the {@link com.goldencode.p2j.main.InitTermListener}
    * interface. These classes can be used as "hook" classes which should
    * be called after the jar representing a customer lib was successfully
    * registered, to initialize it.
    *
    * @param  jar
    *         The name of the target jar file.
    *
    * @return the list of fully qualified names of the classes which reside
    *         into the specified jar and implement the
    *         {@link com.goldencode.p2j.main.InitTermListener} interface.
    */
   @Override
   public String[] getAvailableJarHookClasses(String jar)
   {
      return AdminServerImpl.getAvailableJarHookClasses(jar);
   }

   /**
    * Get hook class specified in the directory for the target jar.
    *
    * @param  jar
    *         The name of the target jar file (should be loaded).
    *
    * @return fully qualified class name of the hook specified in the
    *         directory for the target jar or <code>null</code> if the jar has
    *         no hook specified.
    */
   @Override
   public String getStoredJarHookClass(String jar)
   {
      return AdminServerImpl.getStoredJarHookClass(jar);
   }

   /**
    * Register the specified jar file (which represent a customer lib). Also,
    * a "hook" class can be registered for this jar, this class will be called
    * after the jar was successfully registered, to initialize it.
    *
    * @param  jar
    *         The name of the jar file to register.
    * @param  hookClass
    *         Fully qualified name of the "hook" class for this jar, can be
    *         <code>null</code>.
    * @param  storeInDirectory
    *         If <code>true</code> then the jar is registered in the
    *         directory, else only runtime registration is performed.
    *
    * @return operation result.
    */
   @Override
   public JarOpErrorCode registerJar(String jar, String hookClass, boolean storeInDirectory)
   {
      return AdminServerImpl.registerJar(jar, hookClass, storeInDirectory);
   }

   /**
    * Remove registration for the specified jar.
    *
    * @param jar
    *        Jar file which registration should be removed.
    * @param  force
    *         If it is <code>true</code> then any sessions that have executed
    *         API(s) associated to this jar are forcibly terminated. If this
    *         parameter is <code>false</code> then, if the jar has any
    *         sessions that have executed API(s) associated to it, the
    *         function returns
    *         {@link JarOpErrorCode#ACTIVE_SESSIONS_PRESENT}.
    * @param  removeFromDirectory
    *         If <code>true</code> then the jar registration is removed from
    *         the directory, else only runtime deregistration is performed.
    *
    * @return operation result.
    */
   @Override
   public JarOpErrorCode deregisterJar(String jar, boolean force, boolean removeFromDirectory)
   {
      return AdminServerImpl.deregisterJar(jar, force, removeFromDirectory);
   }

   /**
    * Remove jar registration from the directory.
    *
    * @param  jar
    *         The name of the jar to be deregistered.
    * @param  storedHookClass
    *         The name of the hook class which is stored for this jar in the
    *         directory or <code>null</code> if no hook is defined for this
    *         jar.
    *
    * @return operation result code.
    */
   @Override
   public JarOpErrorCode deregisterJarFromDirectory(String jar, String storedHookClass)
   {
      return AdminServerImpl.deregisterJarFromDirectory(jar, storedHookClass);
   }

   /**
    * Change the server-wide or global hook class of the specific jar.
    *
    * @param  jar
    *         The name of the target jar file.
    * @param  hookClass
    *         The new hook class for the target jar. <code>null</code>
    *         indicates that no hook class should be specified.
    *
    * @return operation result.
    */
   @Override
   public JarOpErrorCode changeJarHookClass(String jar, String hookClass)
   {
      return AdminServerImpl.changeJarHookClass(jar, hookClass);
   }

   /**
    * Get the list of APIs registered for the specified jar.
    *
    * @param  jar
    *         The name of the target jar file.
    *
    * @return the list of APIs registered for the specified jar, each returned
    *         TaggedName represents a single API and contains fully qualified
    *         name of the interface and fully qualified name of the
    *         implementation class.
    */
   @Override
   public TaggedName[] getRegisteredJarAPIs(String jar)
   {
      return AdminServerImpl.getRegisteredJarAPIs(jar);
   }

   /**
    * Get the list of interfaces available in the specified jar file.
    *
    * @param  jar
    *         The name of the target jar file.
    * @param  showRegistered
    *         If <code>true</code> then resulting list contains interfaces
    *         that have already have been registered for an API for the given
    *         jar (assuming that each API is defined using an unique
    *         interface).
    *
    * @return the list of fully qualified names of interfaces available in the
    *         specified jar file.
    */
   @Override
   public String[] getAvailableJarAPIInterfaces(String jar, boolean showRegistered)
   {
      return AdminServerImpl.getAvailableJarAPIInterfaces(jar, showRegistered);
   }

   /**
    * Get the list of classes which reside in the specified jar and
    * statically implement all methods defined by the specified interface.
    *
    * @param  jar
    *         The name of the target jar file.
    * @param  interfaceClass
    *         Fully qualified name of the target interface.
    *
    * @return the list of fully qualified names of classes which reside in the
    *         specified jar and statically implement all methods defined by
    *         the specified interface.
    */
   @Override
   public String[] getAvailableJarAPIImplClasses(String jar, String interfaceClass)
   {
      return AdminServerImpl.getAvailableJarAPIImplClasses(jar, interfaceClass);
   }

   /**
    * Register API specified by the given interface and implementation class.
    *
    * @param  interfaceClass
    *         Fully qualified name of the interface which defines the target
    *         API.
    * @param  implementationClass
    *         Fully qualified name of the class which implements the target
    *         API.
    *
    * @return operation result.
    */
   @Override
   public JarOpErrorCode registerJarAPI(String interfaceClass, String implementationClass)
   {
      return AdminServerImpl.registerJarAPI(interfaceClass, implementationClass);
   }

   /**
    * Remove registration of the specified API in the specified jar.
    *
    * @param  jar
    *         The name of the target jar file.
    * @param  interfaceClass
    *         Fully qualified name of the interface which defines the target
    *         API.
    * @param  force
    *         If it is <code>true</code> then any sessions that have executed
    *         the target API are forcibly terminated. If this parameter is
    *         <code>false</code> then, if the jar has any
    *         sessions that have executed API(s) associated to it, the
    *         function returns
    *         {@link JarOpErrorCode#ACTIVE_SESSIONS_PRESENT}.
    *
    * @return operation result.
    */
   @Override
   public JarOpErrorCode deregisterJarAPI(String jar, String interfaceClass, boolean force)
   {
      return AdminServerImpl.deregisterJarAPI(jar, interfaceClass, force);
   }

   /**
    * Make the current server use server-wide or global hooks, optionally
    * copying or moving hooks from one scope to another.
    *
    * @param  serverWide
    *         if <code>true</code> then server-wide hooks should be used,
    *         else global hooks should be used.
    * @param  move
    *         if <code>true</code> then existing hooks are copied/moved from
    *         one scope to another, otherwise they don't.
    *
    * @return operation result.
    */
   @Override
   public JarOpErrorCode changeJarHooksType(boolean serverWide, boolean move)
   {
      return AdminServerImpl.changeJarHooksType(serverWide, move);
   }

   /**
    * Get the list of all active sessions which have executed the specified
    * API or API(s) associated to the specified jar.
    *
    * @param   jar
    *          The name of the target jar file.
    * @param   apiInterfaceClass
    *          Fully qualified name of the interface which defines the target
    *          API. Can be <code>null</code> if you want to get sessions of
    *          all APIs of the target jar.
    *
    * @return  the list of all active sessions which have executed the
    *          specified API or API(s) associated to the specified jar.
    */
   @Override
   public SessionInfo[] getActiveSessions(String jar, String apiInterfaceClass)
   {
      return AdminServerImpl.getActiveSessions(jar, apiInterfaceClass);
   }

   /**
    * Returns information whether server-wide or global hooks are used for
    * the current server.
    *
    * @return <code>true</code> if server-wide hooks are used,
    *         <code>false</code> if global hooks are used, <code>null</code>
    *         if you have insufficient access rights.
    */
   @Override
   public Boolean isUsingServerWideHooks()
   {
      return AdminServerImpl.isUsingServerWideHooks();
   }

   /**
    * Allows to determine whether
    * {@link com.goldencode.p2j.classloader.MultiClassLoader} is used as the
    * system class loader.
    *
    * @return <code>true</code> if
    *         {@link com.goldencode.p2j.classloader.MultiClassLoader} is used
    *         as the system class loader, <code>false</code> if the standard
    *         class loader is used, <code>null</code> if you do not have
    *         enough permissions
    */
   @Override
   public Boolean isUsingMultiClassLoader()
   {
      return AdminServerImpl.isUsingMultiClassLoader();
   }

   /**
    * Get information about managed libraries directory (path to it and
    * information whether it exists).
    *
    * @return TaggedName which contains fully qualified path to the managed
    *         libraries directory and string value which is
    *         <code>"true"</code> if this directory exists and is accessible.
    *         If you do not have admin rights, <code>null</code> is returned.
    */
   @Override
   public TaggedName getManagedLibsDirInformation()
   {
      return AdminServerImpl.getManagedLibsDirInformation();
   }

   @Override
   public String[] listLoggingInstances()
   {
      return AdminServerImpl.listLoggingInstances();
   }

   @Override
   public LoggingDef getLogging(String name)
   {
      return AdminServerImpl.getLogging(name);
   }

   @Override
   public boolean setLogging(LoggingDef logging)
   {
      return AdminServerImpl.setLogging(logging);
   }

   @Override
   public boolean addLogging(String name, LoggingDef logging)
   {
      return AdminServerImpl.addLogging(name, logging);
   }

   @Override
   public boolean cloneLogging(String nameFrom, String nameTo)
   {
      return AdminServerImpl.cloneLogging(nameFrom, nameTo);
   }

   @Override
   public boolean moveLogging(String nameFrom, String nameTo)
   {
      return AdminServerImpl.moveLogging(nameFrom, nameTo);
   }

   @Override
   public boolean deleteLogging(String name)
   {
      return AdminServerImpl.deleteLogging(name);
   }

   @Override
   public String[] listExportsInstances()
   {
      return AdminServerImpl.listExportsInstances();
   }

   @Override
   public ExportsDef getExports(String name)
   {
      return AdminServerImpl.getExports(name);
   }

   @Override
   public boolean setExports(ExportsDef logging)
   {
      return AdminServerImpl.setExports(logging);
   }

   @Override
   public boolean addExports(String name, ExportsDef logging)
   {
      return AdminServerImpl.addExports(name, logging);
   }

   @Override
   public boolean cloneExports(String nameFrom, String nameTo)
   {
      return AdminServerImpl.cloneExports(nameFrom, nameTo);
   }

   @Override
   public boolean moveExports(String nameFrom, String nameTo)
   {
      return AdminServerImpl.moveExports(nameFrom, nameTo);
   }

   @Override
   public boolean deleteExports(String name)
   {
      return AdminServerImpl.deleteExports(name);
   }

   @Override
   public String[] listStartupInstances()
   {
      return AdminServerImpl.listStartupInstances();
   }

   @Override
   public StartupDef getStartup(String name)
   {
      return AdminServerImpl.getStartup(name);
   }

   @Override
   public boolean setStartup(StartupDef startup)
   {
      return AdminServerImpl.setStartup(startup);
   }

   @Override
   public boolean addStartup(String name, StartupDef logging)
   {
      return AdminServerImpl.addStartup(name, logging);
   }

   @Override
   public boolean cloneStartup(String nameFrom, String nameTo)
   {
      return AdminServerImpl.cloneStartup(nameFrom, nameTo);
   }

   @Override
   public boolean moveStartup(String nameFrom, String nameTo)
   {
      return AdminServerImpl.moveStartup(nameFrom, nameTo);
   }

   @Override
   public boolean deleteStartup(String name)
   {
      return AdminServerImpl.deleteStartup(name);
   }

   @Override
   public String[] listPersistenceInstances()
   {
      return AdminServerImpl.listPersistenceInstances();
   }

   @Override
   public PersistenceDef getPersistence(String name)
   {
      return AdminServerImpl.getPersistence(name);
   }

   @Override
   public boolean setPersistence(PersistenceDef persistence)
   {
      return AdminServerImpl.setPersistence(persistence);
   }

   @Override
   public boolean addPersistence(String name, PersistenceDef persistence)
   {
      return AdminServerImpl.addPersistence(name, persistence);
   }

   @Override
   public boolean clonePersistence(String nameFrom, String nameTo)
   {
      return AdminServerImpl.clonePersistence(nameFrom, nameTo);
   }

   @Override
   public boolean movePersistence(String nameFrom, String nameTo)
   {
      return AdminServerImpl.movePersistence(nameFrom, nameTo);
   }

   @Override
   public boolean deletePersistence(String name)
   {
      return AdminServerImpl.deletePersistence(name);
   }

   @Override
   public String[] listDatabaseInstances()
   {
      return AdminServerImpl.listDatabaseInstances();
   }

   @Override
   public DatabaseDef getDatabase(String name)
   {
      return AdminServerImpl.getDatabase(name);
   }

   @Override
   public boolean setDatabase(DatabaseDef database)
   {
      return AdminServerImpl.setDatabase(database);
   }

   @Override
   public boolean addDatabase(String name, DatabaseDef logging)
   {
      return AdminServerImpl.addDatabase(name, logging);
   }

   @Override
   public boolean cloneDatabase(String nameFrom, String nameTo)
   {
      return AdminServerImpl.cloneDatabase(nameFrom, nameTo);
   }

   @Override
   public boolean moveDatabase(String nameFrom, String nameTo)
   {
      return AdminServerImpl.moveDatabase(nameFrom, nameTo);
   }

   @Override
   public boolean deleteDatabase(String name)
   {
      return AdminServerImpl.deleteDatabase(name);
   }

   @Override
   public String[] listRuntimeNames()
   {
      return AdminServerImpl.listRuntimeNames();
   }

   @Override
   public String[] listRuntimeServers()
   {
      return AdminServerImpl.listRuntimeServers();
   }

   @Override
   public String[] listRuntimeAccounts()
   {
      return AdminServerImpl.listRuntimeAccounts();
   }

   @Override
   public String[] searchRuntimeByAccount(String account, String itemName)
   {
      return AdminServerImpl.searchRuntimeByAccount(account, itemName);
   }

   @Override
   public RuntimeDef getRuntime(String serverName, String accountName)
   {
      return AdminServerImpl.getRuntime(serverName, accountName);
   }

   @Override
   public boolean setRuntime(RuntimeDef runtime)
   {
      return AdminServerImpl.setRuntime(runtime);
   }

   @Override
   public boolean addRuntime(String serverName, String accountName, RuntimeDef runtime)
   {
      return AdminServerImpl.addRuntime(serverName, accountName, runtime);
   }

   @Override
   public boolean cloneRuntime(String serverNameFrom, String accountNameFrom,
            String serverNameTo, String accountNameTo)
   {
      return AdminServerImpl.cloneRuntime(serverNameFrom, accountNameFrom, serverNameTo,
               accountNameTo);
   }

   @Override
   public boolean moveRuntime(String serverNameFrom, String accountNameFrom, String serverNameTo,
            String accountNameTo)
   {
      return AdminServerImpl.moveRuntime(serverNameFrom, accountNameFrom, serverNameTo,
               accountNameTo);
   }

   @Override
   public boolean deleteRuntime(String serverName, String accountName)
   {
      return AdminServerImpl.deleteRuntime(serverName, accountName);
   }

   /**
    * Given a certificate alias, it will compute the certificate signing chain.
    * 
    * @param    alias
    *           The certificate from which to start chain building
    * 
    * @return   a list of (alias, DN) entries, where root certificate is on first position
    */
   @Override
   public TaggedName[] getCertificateHierarchy(String alias)
   {
      return AdminServerImpl.getCertificateHierarchy(alias);
   }

   /**
    * Validate the given certificate, by its PEM data.
    * 
    * @param    cert
    *           The PEM representation of the certificate
    * 
    * @return   a value representing certificate validity state.
    */
   @Override
   public int validateCertificate(CertDef cert)
   {
      return AdminServerImpl.validateCertificate(cert);
   }

   /**
    * Get a map of the owner or issuer Distinctive Name properties.
    * 
    * @param    cd
    *           The certificate definition from which the info will be extracted.
    * @param    owner
    *           <code>true</code> if the owner property should be retrieved;
    *           <code>false</code> for the issuer property.
    * 
    * @return   A map with certificate properties or <code>null</code> if the certificate PEM data
    *           could not be loaded.
    */
   @Override
   public Map<String, String> getCertificateDNProperties(CertDef cd, boolean owner)
   {
      return AdminServerImpl.getCertificateDNProperties(cd, owner);
   }

   /**
    * Get the value of the specified property, from the Distinctive Name entry associated
    * with the owner or issuer.
    * 
    * @param    cd
    *           The certificate definition from which the info will be extracted.
    * @param    owner
    *           <code>true</code> if the owner property should be retrieved;
    *           <code>false</code> for the issuer property.
    * 
    * @return   The value for the specified property.
    */
   @Override
   public String getCertificateDNProperty(CertDef cd, String prop, boolean owner)
   {
      return AdminServerImpl.getCertificateDNProperty(cd, prop, owner);
   }

   /**
    * Loads the X509 certificate from the specified definition and returns
    * a map with properties for:
    * <ul>
    *    <li>
    *       {@link CertificateUtils#CERT_OWNER} - this property will keep a Map of Distinctive 
    *       Name properties for the owner
    *    </li>
    *    <li>
    *       {@link CertificateUtils#CERT_ISSUER} - this property will keep a Map of Distinctive 
    *       Name properties for the issuer
    *    </li>
    *    <li>
    *       {@link CertificateUtils#CERT_VALIDITY_NOT_BEFORE} - the date from which this 
    *       certificate starts to be valid. 
    *    </li>
    *    <li>
    *       {@link CertificateUtils#CERT_VALIDITY_NOT_AFTER} - the date from which this 
    *       certificate's validity period ends. 
    *    </li>
    * </ul> 
    * 
    * @param    cd
    *           The certificate definition from which the info will be extracted.
    * 
    * @return   A map with certificate properties or <code>null</code> if the certificate PEM data
    *           could not be loaded.
    */
   @Override
   public Map<String, Object> getCertificateProperties(CertDef cd)
   {
      return AdminServerImpl.getCertificateProperties(cd);
   }

   /**
    * Check if the given certificate is self-signed.
    * 
    * @param    cert
    *           The certificate which should be checked.
    * 
    * @return   <code>true</code> if this certificate is self-signed
    */
   @Override
   public Boolean isSelfSigned(CertDef cert)
   {
      return AdminServerImpl.isSelfSigned(cert);
   }
   
   /**
    * Gets report preview.
    * 
    * @param    reportRequest
    *           Holds all required data to build the target report
    * @param    range
    *           The preview range
    * 
    * @return   The report preview object that holds the requested page range and the number of
    *           all available pages for this preview.
    * 
    * @throws   IOException
    *           The IO exception if there is a failed IO operation
    */
   @Override
   public ReportPreview getPreviewImages(ReportRequest reportRequest, Range range)
   throws IOException
   {
      ReportPreview reportPreview = new ReportPreview();
      
      //the start of range is a zero based index but a page number is started from 1.
      int pageNumber = range.getStart() + 1;
      
      int length = range.getLength();
      
      length = length > 0 ? length : 1;
      
      ArrayList<String> previewPages = new ArrayList<>(length);
      for (int i = 0; i < length; i++)
      {
         try (PdfDocument doc = ReportsManager.buildPDFPagePreview(reportRequest, pageNumber + i))
         {
            PDFRenderer previewRenderer = new PDFRenderer(doc.getDocument());
            BufferedImage buffer = previewRenderer.renderImageWithDPI(0, 300, ImageType.RGB);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageIO.write(buffer, "PNG", baos);
            byte[] imageInByte = baos.toByteArray();
            previewPages.add("data:image/png;base64," + Base64.getEncoder().encodeToString(imageInByte));
            reportPreview.setPagesNumber(doc.getPagesNumber());
         }
      }
      
      reportPreview.setPreviewPages(previewPages);
      
      return reportPreview;
   }

   /**
    * Returns the current application version.
    * 
    * @return   String presentation of the current application version number.
    */
   @Override
   public String getVersionInfo()
   {
      return Version.getFWDVersion();
   }

   /**
    * This method forces GWT to include Float into the list of serializable types. Since GWT
    * doesn't provide a mechanism to include serializable types, this method was introduced
    * as a workaround. Float serialization was introduced by
    * {@link AdminServerImpl#getReportParametersMap()}.
    *
    * @return  a float value.
    */
   public Float forceFloatSerialization()
   {
      return Float.MAX_VALUE;
   }

   /**
    * Returns the list of appserver processes.
    *
    * @return   Array of appserver definitions.
    */
   @Override
   public AppserverInfo[] getAppserverList()
   {
      return AdminServerImpl.getAppserverList();
   }

   /**
    * Returns the list of appserver agents.
    *
    * @return   Array of appserver agent definitions.
    */
   @Override
   public AppserverInfo.AgentInfo[] getAppserverAgents(String appserver)
   {
      return AdminServerImpl.getAppserverAgentInfos(appserver);
   }

   /**
    * Restarts all agents of the appserver and returns the refreshed list of appserver agents.
    *
    * @param    appserver
    *           The appserver name
    *
    * @return   Array of appserver agent definitions.
    */
   @Override
   public AppserverInfo.AgentInfo[] restartAgents(String appserver)
   {
      AppServerLauncher.restart(appserver);
      return getAppserverAgents(appserver);
   }

   /**
    * Trims the agents of the appserver and returns the refreshed list of appserver agents.
    *
    * @param    appserver
    *           The appserver name
    *           
    * @return   Array of appserver agent definitions.
    */
   @Override
   public AppserverInfo.AgentInfo[] trimAgents(String appserver)
   {
      AppServerLauncher.trim(appserver);
      return getAppserverAgents(appserver);
   }

   /**
    * Starts a new agent for the appserver and returns the refreshed list of appserver agents.
    *
    * @param    appserver
    *           The appserver name
    *
    * @return   Array of appserver agent definitions.
    */
   @Override
   public AppserverInfo.AgentInfo[] startAppserver(String appserver)
   {
      AppServerLauncher.launch(appserver);
      return getAppserverAgents(appserver);
   }

   /**
    * Terminates the selected agents of the appserver and returns the refreshed list of appserver agents.
    *
    * @param    appserver
    *           The appserver name
    * @param    agentIds
    *           The list of agent ids
    *
    * @return   Array of appserver agent definitions.
    */
   @Override
   public AppserverInfo.AgentInfo[] terminateAgents(String appserver, int[] agentIds)
   {
      AppServerLauncher.terminateAgents(appserver, agentIds);
      return getAppserverAgents(appserver);
   }

   /**
    * Terminates the selected sessions of the appserver and returns the refreshed list of appserver agents.
    *
    * @param    appserver
    *           The appserver name
    * @param    sessions
    *           The list of sessions to be terminated
    */
   @Override
   public AppserverInfo.AgentInfo[] terminateSessions(String appserver, MultiSessionAgentSessionDef[] sessions)
   {
      AppServerLauncher.terminateSessions(appserver, sessions);
      return getAppserverAgents(appserver);
   }

   /**
    * Update the hashed password if the value of the plain password is given and then the plain
    * password is cleared.
    * 
    * @param    userDef
    *           User account definition
    * 
    * @return   The same user definition object with new value for the "hashedPassword" field.
    */
   private static UserDef updateHashedPassword(UserDef userDef)
   {
      if (userDef.plainPassword != null)
      {
         userDef.password = HashPassword.hashPassword(userDef.plainPassword);
         userDef.plainPassword = null;
      }
      
      return userDef;
   }

   /**
    * Returns the map of report parameters to their values saved in the directory.
    * 
    * @return   The report parameters map
    */
   @Override
   public Map<String, Object> getReportParametersMap()
   {
      synchronized (AdminServerImpl.class)
      {
         return AdminServerImpl.getReportParametersMap();
      }
   }
   
   /**
    * Get a list with all the tenants.
    *
    * @param   errorSink
    *          A sink for errors encountered.
    *
    * @return  A list with the registered tenants.
    */
   public List<Tenant> listTenants(List<String> errorSink)
   {
      return AdminServerImpl.listTenants(errorSink == null ? new ArrayList<>() : errorSink);
   }
   
   /**
    * Given a tenant name, get the tenant information.
    * 
    * @param   tenantName
    *          The tenant name for which to retrieve the information.
    * @param   errorSink
    *          A sink for errors encountered. 
    *
    * @return  The tenant with the given tenant name.
    */
   public Tenant listTenant(String tenantName, List<String> errorSink)
   {
      return AdminServerImpl.listTenant(tenantName, errorSink == null ? new ArrayList<>() : errorSink);
   }
   
   /**
    * Add a tenant to the list.
    * 
    * @param   tenantName
    *          The name of the tenant.
    * @param   tenantDescription
    *          The description of the tenant.
    * @param   tenantInfo
    *          The info of the tenant.
    * @param   databases
    *          A list of the databases that will be used by the tenant.
    * @param   errorSink
    *          A sink for errors encountered. 
    *
    * @return  {@code true} if the operation is performed successfully, 
    *          {@code false} if the operation is not performed successfully,
    *          {@code null} if the caller does not have sufficient rights.
    */
   public Boolean addTenant(String tenantName, 
                            String tenantDescription,
                            String tenantInfo,
                            List<TenantDatabaseDescriptor> databases,
                            List<String> errorSink)
   {
      return AdminServerImpl.addTenant(tenantName,
                                       tenantDescription,
                                       tenantInfo,
                                       databases,
                                       errorSink == null ? new ArrayList<>() : errorSink);
   }
   
   /**
    * Function for adding a database instance to a tenant.
    * 
    * @param   tenantName
    *          The tenant for which to add the database instance.
    * @param   db
    *          The database information to be added.
    * @param   errorSink
    *          A sink for errors encountered. 
    *
    * @return  {@code true} if the operation is performed successfully, 
    *          {@code false} if the operation is not performed successfully,
    *          {@code null} if the caller does not have sufficient rights.
    */
   public Boolean addDatabaseToTenant(String tenantName, TenantDatabaseDescriptor db, List<String> errorSink)
   {
      return AdminServerImpl.addDatabaseToTenant(tenantName,
                                                 db,
                                                 errorSink == null ? new ArrayList<>() : errorSink);
   }
   
   /**
    * Update a tenant from the master table. This method allows one or multiple column values to be edited.
    * Possible {@code column} values/names are:
    * <ul>
    *    <li> tenant_name - the name of the tenant.
    *    <li> db_name - the physical name of a database instance that this tenant uses.
    *    <li> url - the URL of a database instance.
    *    <li> username - the username required in order to access a database instance.
    *    <li> password - the password required in order to access a database instance. 
    * </ul>
    * 
    * @param   tenantName
    *          The name of the tenant that is targeted by the update.
    * @param   pdbName
    *          The physical name of the database that is targeted by the update.
    * @param   values
    *          A Map with pairs (column name - new value) that are intended for the update.
    * @param   databaseUpdate
    *          Is this update targeting a database change?
    * @param   errorSink
    *          A sink for errors encountered. 
    *
    * @return  {@code true} if all the updates are performed successfully, 
    *          {@code false} if there is at least an update that fails,
    *          {@code null} if the caller does not have sufficient rights.
    */
   public Boolean updateTenant(String tenantName, 
                               String pdbName, 
                               Map<String, String> values, 
                               boolean databaseUpdate,
                               List<String> errorSink)
   {
      return AdminServerImpl.updateTenant(tenantName,
                                          pdbName,
                                          values,
                                          databaseUpdate,
                                          errorSink == null ? new ArrayList<>() : errorSink);
   }
   
   /**
    * Function for deleting a tenant from the master table.
    * 
    * @param   tenantName
    *          The name of the tenant that is targeted by the delete.
    * @param   errorSink
    *          A sink for errors encountered.
    *
    * @return  {@code true} if the operation was successful, {@code false} otherwise.
    *          {@code null} if the caller does not have sufficient rights.
    */
   public Boolean deleteTenant(String tenantName, List<String> errorSink)
   {
      return AdminServerImpl.deleteTenant(tenantName, errorSink == null ? new ArrayList<>() : errorSink);
   }
   
   /**
    * Delete a database instance that is assigned to a tenant.
    * 
    * @param   tenantName
    *          The tenant for which to remove the database instance.
    * @param   pdbName
    *          The physical database name for the database to be deleted.
    * @param   errorSink
    *          A sink for errors encountered. 
    *
    * @return  {@code true} if the operation was successful, {@code false} otherwise.
    *          {@code null} if the caller does not have sufficient rights.
    */
   public Boolean deleteDatabaseFromTenant(String tenantName, String pdbName, List<String> errorSink)
   {
      return AdminServerImpl.deleteDatabaseFromTenant(tenantName,
                                                      pdbName,
                                                      errorSink == null ? new ArrayList<>() : errorSink);
   }
   
   /**
    * Enable/Disable a specific tenant.
    * 
    * @param   tenantName
    *          The tenant to work with.
    * @param   enable
    *          {@code true} if the intent is to enable the tenant, {@code false} otherwise.
    * @param   errorSink
    *          A sink for errors encountered. 
    *
    * @return  {@code true} if the operation is performed successfully, 
    *          {@code false} if the operation is not performed successfully,
    *          {@code null} if the caller does not have sufficient rights.
    */
   public Boolean changeTenantStatus(String tenantName, boolean enable, List<String> errorSink)
   {
      return AdminServerImpl.changeTenantStatus(tenantName,
                                                enable,
                                                errorSink == null ? new ArrayList<>() : errorSink);
   }
   
   /**
    * Check if a specific tenant is enabled or not.
    * 
    * @param   tenantName
    *          The tenant to work with.
    * @param   errorSink
    *          A sink for errors encountered. 
    *
    * @return  {@code true} if the tenant is enabled, {@code false} otherwise.
    *          {@code null} if the caller does not have sufficient rights.
    */
   public Boolean isTenantEnabled(String tenantName, List<String> errorSink)
   {
      return AdminServerImpl.isTenantEnabled(tenantName, errorSink == null ? new ArrayList<>() : errorSink);
   }
}