AdminServerImpl.java
/*
** Module : AdminServerImpl.java
** Abstract : Implementation of the Administration Server APIs.
**
** Copyright (c) 2007-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- --------------------------Description--------------------------------
** 001 NVS 20070312 @32375 Created the first version of the file.
** 002 GES 20080318 @37532 Disabled troublesome code that needs to be
** modified to avoid translating chars when
** writing XML.
** 003 NVS 20090304 @41404 Minor change to logging.
** 004 NVS 20090311 @41521 Reworked to implement AdminExports interface,
** initial implementation.
** 005 NVS 20090318 @41635 Added targetRefresh(). This method and the
** existing setTargetLive() return the current
** cache serial number.
** 006 NVS 20090326 @41693 Added getErrors() method and fixed groups
** retrieval.
** 007 NVS 20090428 @42032 Added peekMessage() method.
** 008 NVS 20090429 @42075 Added isMaster() method.
** Implemented listProcesses(), checkProcess(),
** getProcess(), setProcess(), addProcess(),
** deleteProcess(), cloneProcess(),
** 009 NVS 20090505 @42099 Implemented certificate related APIs.
** 010 NVS 20090506 @42136 Implemented the user account related APIs.
** 011 NVS 20090518 @42242 Implemented some more certificate related APIs.
** 012 NVS 20090520 @42417 Added listUserGroups() API implementation.
** 013 NVS 20090525 @42447 Added APIs for custom account extensions support.
** 014 NVS 20090602 @42545 Partially implemented getAdminDef() method.
** 015 CA 20090603 @42555 Set methods addUser and addProcess as static.
** 016 NVS 20090604 @42604 Added a form of cloneUser() that assigns new
** password.
** 017 NVS 20090609 @42621 Started Console group of APIs with
** consReloadDirectory() method.
** 018 CA 20090615 @42691 Modified getAdminDef to call its SecurityAdmin
** counterpart (existing code was moved here).
** 019 GES 20090617 @42753 Added getSessionList().
** 020 GES 20090619 @42779 Added backing support for terminateSession().
** 021 NVS 20090626 @42966 Implemented the first group of ACL related APIs -
** listAclInstances() and the like
** 022 NVS 20090701 @42997 Implemented the ACL getters group of ACL related
** APIs.
** 023 NVS 20090703 @43040 Implemented the addAcl() method.
** 024 NVS 20090703 @43049 Implemented the setAcl() method.
** 025 NVS 20090706 @43066 Implemented APIs moveAcl(), deleteAcl(),
** cloneAcl() and resequenceAcl().
** 026 GES 20090721 @43300 Added getRecordLockList().
** 027 ECF 20090802 @43502 Completed implementation of getRecordLockList().
** 028 NVS 20090807 @43559 Added two forms of listServers() method.
** 029 NVS 20090812 @43608 Fixed a NPE in listServers() method.
** 030 NVS 20090816 @43647 Added shutdown() method implementation.
** 031 NVS 20091007 @44120 Implemented two flavors of getProfile() APIs.
** 032 NVS 20091116 @44343 Cleanup.
** 033 NVS 20091117 @44414 Safe directory reload support APIs added.
** 034 NVS 20091118 @44426 Safe directory backup support API added.
** 035 SVL 20091126 @44447 Implemented APIs for locking database records.
** 036 SIY 20091216 @44515 Added support for pending refresh handling.
** 037 SIY 20100419 @44826 Added DirectoryService accessor with admin rights
** check. Added method which allows refreshing of
** plugins with specified name.
** 038 SIY 20100422 @44832 Fixed Javadocs.
** 039 SVL 20100916 Added listAuthPlugins function.
** 040 SVL 20100920 Added bulkAuthModeUpdate function.
** 041 SVL 20110619 Password is preserved during user cloning if
** newPassword is null into cloneUser(String,
** String, boolean, byte[]).
** 042 SVL 20111128 Implemented customer libs management.
** 043 ECF 20131028 Import change.
** 044 EVL 20160222 Javadoc fixes to make compatible with Oracle Java 8 for Solaris 10.
** 045 SBI 20170225 Moved administration profile type, access control list type and
** record lock type to their own modules respectively.
** SBI 20170920 Fixed getDatabases() to get a list of primary databases, added
** getReportParametersMap.
** 046 CA 20180605 Added program traces in the session and record locks views.
** 047 ECF 20200906 New ORM implementation.
** 048 IAS 20210505 Changed exception thrown on failed shutdown.
** 049 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
** 050 CA 20230803 Invoked DatabaseManager.autoConnect() on setTargetLive(boolean).
** 051 GBB 20230825 Adding new methods to support OS user screens and dialogs.
** 052 TJD 20240123 Java 17 compatibility updates
** 053 ICP 20240705 Fixed method of obtaining the class loader.
** 054 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 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.
** 055 GBB 20241010 Adding getAppserverList & getAppserverAgents methods.
** 056 RAA 20241011 Added tenant description logic.
** RAA 20241014 Expanded setTargetLive() with databaseAutoConnect and restCall parameters.
** 057 RAA 20241106 Added tenant info parameter to addTenant().
** 058 RAA 20241118 Added listTenant() method for listing a single tenant.
** 059 OM 20241128 Multi-tenant runtime support: selected the proper persistence context, eventually
** based on [sharedDb] parameter.
** 060 RNC 20250109 Added convertProgramTrace() and getSessionById() methods.
** 061 OM 20250128 Added admin REST API for sec_auth domains. Added sink for collecting and
** reporting errors occurred during REST invocations.
** 062 GBB 20250403 getAppserverList to support new multi-session appserver.
*/
/*
** 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;
import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.text.*;
import java.util.*;
import java.util.regex.*;
import java.util.stream.Stream;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.*;
import com.goldencode.p2j.main.*;
import com.goldencode.p2j.net.*;
import com.goldencode.p2j.security.*;
import com.goldencode.p2j.persist.*;
import com.goldencode.p2j.persist.lock.*;
import com.goldencode.p2j.persist.orm.*;
import com.goldencode.p2j.persist.orm.TenantManager.*;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.Utils.DirScope;
import com.goldencode.p2j.admin.shared.ReportParameters;
import com.goldencode.p2j.classloader.*;
import com.goldencode.p2j.directory.*;
import com.goldencode.p2j.util.appserver.*;
import com.goldencode.p2j.util.logging.*;
/**
* Implements AdminExports interface.
* Methods of this interface are exported through the NET package and
* are used by administration clients.
*
* @author NVS
* @version 2.0
*/
public class AdminServerImpl
{
/** Context-local global data area for admin session state */
private static final ContextLocal<WorkArea> workArea = new ContextLocal<WorkArea>()
{
/**
* Initializes the work area, the first time it is requested within a
* new context.
*
* @return The newly instantiated work area.
*/
protected synchronized WorkArea initialValue()
{
return new WorkArea();
}
};
/** Logger */
private static CentralLogger LOG = CentralLogger.get(AdminServerImpl.class);
/** instance of the security manager */
private static com.goldencode.p2j.security.SecurityManager sm = null;
/** Maximum timeout (in milliseconds) before give up on acquiring a lock.*/
private static final int LOCK_ACQUIRE_MAXIMUM_WAIT_TIME = 30000;
/** Interval (in milliseconds) between lock acquisition attempts. */
private static final int LOCK_ACQUIRE_INTERVAL = 1000;
/** Defines the empty locks array. */
private static final RecordInfo[] EMPTY_LOCK_LIST = new RecordInfo[0];
/** Global refresh counter. */
private static Integer refreshGlobal = Integer.valueOf(0);
/** Synchronization object for refresh counters. */
private static final Object refreshMutex = new Object();
/** The report settings template to build the node id of the given report parameter. */
private static final MessageFormat REPORT_SETTINGS = new MessageFormat("report-settings/{0}");
/**
* Initializes this component.
*/
public static void initialize()
{
sm = com.goldencode.p2j.security.SecurityManager.getInstance();
}
//***********************************************************************
// Admin Session Control API
//***********************************************************************
/**
* Returns the server's name.
*
* @return This server's name or {@code null}
*/
public static String getServerName()
{
WorkArea wa = workArea.get();
return sm.adminGetServerName(wa.stayCurrent);
}
/**
* 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} if it can
*/
public static boolean isMaster()
{
WorkArea wa = workArea.get();
return wa.sa.isMaster();
}
/**
* Sets the live server's directory as the target.
*
* @param stayCurrent
* if {@code true}, 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
*/
public static int setTargetLive(boolean stayCurrent)
{
return setTargetLive(stayCurrent, true, false);
}
/**
* Sets the live server's directory as the target.
*
* @param stayCurrent
* if {@code true}, all operations affect the most current
* generation of the security cache, otherwise they affect the
* generation attached to the admin session
* @param databaseAutoConnect
* {@code true} if database auto connecting should be included in this function execution,
* {@code false} otherwise.
* @param restCall
* {@code true} if this function was called from a REST environment (e.g. the tenant API),
* {@code false} otherwise.
*
* @return security cache generation serial number
*/
public static int setTargetLive(boolean stayCurrent, boolean databaseAutoConnect, boolean restCall)
{
WorkArea wa = workArea.get();
if (restCall && wa.initialized)
{
return 0;
}
if (databaseAutoConnect)
{
DatabaseManager.autoConnect();
}
// mark session as targeted
wa.targeted = true;
wa.stayCurrent = stayCurrent;
// bind session to the directory service
wa.ds = DirectoryService.getInstance();
if (wa.ds.bind())
{
wa.bound = true;
}
wa.sa = new SecurityAdmin(wa.ds);
wa.initialized = true;
return sm.adminGetCacheSerial(wa.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} means a new directory file should be
* created, otherwise the named directory file should exist.
*
* @return {@code true} if the new target was set successfully
*/
public static boolean setTargetFile(String name, boolean create)
{
// TO-DO: implement later
return false;
}
/**
* 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.
*/
public static int targetRefresh()
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.canRefresh())
{
return -1;
}
// refresh the cache
int newSerial = sm.adminRefresh(wa.freshMessages);
if (newSerial == -1)
{
return -1;
}
wa.resetCounters();
message(new String[] {"Security Cache generation " + newSerial +
" created."});
return newSerial;
}
/**
* 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}, then old and new messages are retrieved,
* otherwise new messages only
*
* @return array of messages, that can be empty
*/
public static String[][] getMessages(boolean all)
{
List<String[]> msgs = new LinkedList<String[]>();
WorkArea wa = workArea.get();
// fetch old messages first, if requested
if (all)
{
msgs.addAll(wa.savedMessages);
}
// fetch new messages unconditionally
msgs.addAll(wa.freshMessages);
// age fresh messages
wa.savedMessages.addAll(wa.freshMessages);
wa.freshMessages.clear();
// return messages
return msgs.toArray(new String[][] {});
}
/**
* Returns the most recent server message without changing its state on the
* server.
*
* @return message or {@code null} if nothing is generated
*/
public static String peekMessage()
{
WorkArea wa = workArea.get();
// check the fresh messages first
if (!wa.freshMessages.isEmpty())
{
String[] last = wa.freshMessages.getLast();
return last[last.length - 1];
}
// check the old messages next
else if (!wa.savedMessages.isEmpty())
{
String[] last = wa.savedMessages.getLast();
return last[last.length - 1];
}
return null;
}
/**
* 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().
*/
public static void clearMessages()
{
WorkArea wa = workArea.get();
wa.savedMessages.clear();
}
/**
* 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
*/
public static AdminDef getAdminDef()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return wa.sa.getAdminDef();
}
/**
* Check if there is pending security cache refresh.
*
* @return {@code true} if refresh is pending and {@code false}
* is there is no pending refresh or current user has not enough
* rights to perform it.
*/
public static boolean isRefreshPending()
{
return workArea.get().isRefreshPending();
}
/**
* Check if current user can perform a refresh.
*
* @return {@code true} user has enough rights to perform refresh.
*/
public static boolean canRefresh()
{
return workArea.get().sa.canRefresh();
}
/**
* Returns the admin permissions profile for an arbitrary set of resource
* instance names.
*
* @param name
* array of admin resource instance names to query
*
* @return permissions for the given names or {@code null}
*/
public static AdminProfile[] getProfile(String[] name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return wa.sa.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}
*/
public static AdminProfile[] getProfile(String name, int[] perm)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return wa.sa.getProfile(name, perm);
}
/**
* Backup the directory into a new file.
*
* @param newfile
* name of a backup file to create
*
* @return {@code true} if backup was successful.
*/
public static boolean backupDirectory(String newfile)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// check the file name
if (newfile.trim().length() == 0)
{
message (new String[] {"file name can't be blank"});
}
File file = new File(newfile);
if (file.exists())
{
if (file.isDirectory())
{
message (new String[] {"file name is an existing directory"});
}
else
{
message (new String[] {"file already exists"});
}
return false;
}
int rc = wa.ds.backupDirectory(newfile);
String err = null;
switch (rc)
{
case 0:
return true;
case DirectoryService.ERR_SAVE_NOT_SUPPORTED:
err = "Directory backup is not supported in this environment";
break;
case DirectoryService.ERR_CANT_WRITE:
err = "File write operation failed";
break;
case DirectoryService.ERR_CANT_RENAME:
err = "Failure during file rename";
break;
case DirectoryService.ERR_EDITING_IS_ACTIVE:
err = "Directory editing batch is open; can't proceed";
break;
case DirectoryService.ERR_CANT_LOCK_DIRECTORY:
err = "Directory lock can't be obtained";
break;
case DirectoryService.ERR_FATAL:
err = "Unrecoverable error in directory backend";
break;
}
message(new String[] {err});
return false;
}
/**
* Lock directory for rereading from the file.
*
* @param seconds
* Seconds to wait before lock will expire.
*
* @return {@code true} if lock was successful.
*/
public static boolean lockForRereadDir(int seconds)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
boolean rc = wa.ds.lockForRefresh(seconds);
if (!rc)
{
message (new String[] {"directory lock failed"});
}
return rc;
}
/**
* Reread the directory file into the backend's cache after obtaining
* a refresh lock.
*
* @return error code. Following error codes are possible:
* <ol>
* <li>ERR_NO_LOCK - is returned when no lock is present</li>
* <li>ERR_LOCK_EXPIRED - is returned if lock is present
* but it is expired or belongs to other thread.</li>
* <li>ERR_CANT_REFRESH_BACKEND - is returned when
* backend can't update directory.</li>
* <li>ERR_ILLEGAL - is returned when no rights etc</li>
* </ol>
*/
public static int rereadDirectory()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return AdminConstants.ERR_ILLEGAL;
}
int rc = wa.ds.refresh();
String err = null;
switch (rc)
{
case 0:
return 0;
case AdminConstants.ERR_NO_LOCK:
err = "Directory was not locked";
break;
case AdminConstants.ERR_LOCK_EXPIRED:
err = "Lock expired. Try again";
break;
case AdminConstants.ERR_CANT_REFRESH_BACKEND:
err = "Problem reading the directory file";
break;
}
message (new String[] {err});
return rc;
}
/**
* Cancel (possibly) planned refreshing operation.
*/
public static void cancelRereadDir()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return;
}
wa.ds.cancelRefresh();
}
/**
* Creates a report of all current user and process sessions.
*
* @return Session descriptors suitable for reporting purposes or
* {@code null} if insufficient rights exist to obtain the
* list.
*/
public static SessionInfo[] getSessionList()
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
SessionInfo[] sessions = wa.sa.getSessionList();
if (sessions != null)
{
setStacktraces(sessions);
}
return sessions;
}
/**
* Creates a report based on the session id provided. In the situation where a single report is
* needed, this function is faster compared to calling {@link #getSessionList()} and then manually
* selecting the report, because internally it uses a cache map.
*
* @param id
* The context's session id of the {@link SecuritySession}.
* It can be retrieved through {@link SessionToken#getSessionID()}.
*
* @return The session descriptor suitable for reporting purposes.
* {@code null} if insufficient rights exist to obtain it
* or the session was not found.
*/
public static SessionInfo getSessionById(int id)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
SessionInfo session = wa.sa.getSessionById(id);
if (session != null)
{
setStacktraces(new SessionInfo[]{session});
}
return session;
}
/**
* Forcibly terminate the specified session.
*
* @param sid
* Session identifier.
*/
public static void terminateSession(int sid)
{
WorkArea wa = workArea.get();
if (!wa.targeted)
{
throw new IllegalStateException("No target instance.");
}
else if (!wa.sa.adminAccess())
{
throw new SecurityException("Administrative access denied.");
}
wa.sa.killSession(sid);
}
/**
* Converts a list of traces into one single JSON node.
*
* @param traces
* The stacktrace which will be serialized.
* @param node
* A JSON array node which will hold the serialized traces.
*/
public static void convertProgramTrace(StacktraceInfo[] traces, ArrayNode node)
{
int length = traces == null ? 0 : traces.length;
if (length == 0 || node == null)
{
return;
}
for (int i = 0; i < traces.length; i++)
{
StacktraceInfo trace = traces[i];
node.add(trace.program + "." + trace.method + "(line:" + trace.line + ")");
}
}
/**
* Creates a report of all record locks for the server.
*
* @return Record lock descriptors suitable for reporting purposes or
* {@code null} if insufficient rights exist to obtain the
* list.
*/
public static RecordLockInfo[] getRecordLockList()
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
RecordLockInfo[] locks = DatabaseManager.getAllRecordLockInfo();
if (locks == null)
{
return null;
}
Set<Thread> threads = getAllConversationThreads();
Map<String, StacktraceInfo[]> programTraces = getProgramTraceByThreadNameMap(threads);
Map<String, String[]> javaTraces= getJavaTraceByThreadNameMap(threads);
for (RecordLockInfo lock : locks)
{
SessionToken[] lockers = lock.getLockers();
StacktraceInfo[][] traces = new StacktraceInfo[lockers.length][];
String[][] jtraces = new String[lockers.length][];
int i = 0;
for (SessionToken locker : lockers)
{
String key = String.format("Conversation [%08X:%s]",
locker.getSessionID(),
locker.getSubject());
traces[i] = programTraces.get(key);
jtraces[i] = javaTraces.get(key);
}
lock.setStacktraces(traces);
lock.setJavaStacktraces(jtraces);
}
return locks;
}
/**
* Get the list of primary databases managed by this P2J server.
*
* @return Names of primary databases managed by this P2J server.
*/
public static String[] getDatabases()
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
List<Database> databases = DatabaseManager.getManagedDatabases();
List<String> list = new ArrayList<String>();
for (Database database : databases)
{
if (database.isPrimary())
{
list.add(database.getName());
}
}
Collections.sort(list);
return list.toArray(new String[list.size()]);
}
/**
* Get the list of tables of the given database.
*
* @param database
* Target database.
*
* @return See above. Names of the tables of the given database.
*/
public static String[] getDatabaseTables(String database)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
List<String> tableNames = DatabaseManager.getDatabaseTables(new Database(database));
if (tableNames == null)
{
return null;
}
Collections.sort(tableNames);
return tableNames.toArray(new String[tableNames.size()]);
}
/**
* Lock the specified records. Locks are obtained in a single batch. Note
* that you can lock non-existing records (in order to create them
* externally).
* <p>
* You can unlock records using {@link #unlockRecords()}.
*
* @param records
* Records to lock.
*
* @return {@code true} if records were successfully locked.
*/
public static boolean lockRecords(RecordInfo[] records)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
for (RecordInfo record : records)
{
Database database = new Database(record.getDatabaseName());
Persistence persistence = PersistenceFactory.getInstance(database);
RecordIdentifier<String> recId = new RecordIdentifier<>(record.getTableName(), record.getRecordID());
// try to obtain lock for periodically until the lock is obtained or
// a reasonable timeout occurs
long startTime = System.currentTimeMillis();
while(true)
{
try
{
persistence.lock(LockType.EXCLUSIVE_NO_WAIT, recId, true, record.isSharedDb());
// store locked record
if (wa.lockedRecords == null)
{
wa.lockedRecords = new HashSet<>();
}
wa.lockedRecords.add(record);
}
catch (LockUnavailableException e)
{
long time = System.currentTimeMillis() - startTime;
if (time > LOCK_ACQUIRE_MAXIMUM_WAIT_TIME)
{
message(new String[]{"Cannot acquire lock on " + recId});
unlockRecords();
return false;
}
try
{
Thread.sleep(LOCK_ACQUIRE_INTERVAL);
}
catch (InterruptedException ie)
{
// suppress
}
// try one more time
continue;
}
break;
}
}
return true;
}
/**
* Gets records that have been previously locked in the given context using {@link #lockRecords}.
* While the records were locked by the admin UI, they could be changed by the external tools,
* so the state of these records into the Hibernate cache is refreshed before records have
* been collected.
*
* @return Records that have been previously locked in the given context.
*/
public static RecordInfo[] getLockRecords()
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return EMPTY_LOCK_LIST;
}
if (wa.lockedRecords == null || wa.lockedRecords.isEmpty())
{
return EMPTY_LOCK_LIST;
}
RecordInfo[] records = new RecordInfo[wa.lockedRecords.size()];
int index = 0;
for (RecordInfo record : wa.lockedRecords)
{
Database database = new Database(record.getDatabaseName());
Persistence persistence = PersistenceFactory.getInstance(database);
String entityName = DatabaseManager.getEntityNameByTableName(database, record.getTableName());
RecordIdentifier<String> recordId = new RecordIdentifier<>(entityName, record.getRecordID());
try
{
persistence.quickLoad(recordId, true, record.isSharedDb());
}
catch (PersistenceException e)
{
LOG.warning("Error while reloading records", e);
}
records[index++] = record;
}
return records;
}
/**
* Unlock the records which has been previously locked in the given context
* using {@link #lockRecords}. While the records were locked by the admin
* UI, they could be changed by the external tools, so the state of these
* records into the Hibernate cache is refreshed before unlocking.
*
* @return {@code true} if records were successfully unlocked.
*/
public static boolean unlockRecords()
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
if (wa.lockedRecords == null || wa.lockedRecords.isEmpty())
{
return true;
}
for (RecordInfo record : wa.lockedRecords)
{
Database database = new Database(record.getDatabaseName());
Persistence persistence = PersistenceFactory.getInstance(database);
String entityName = DatabaseManager.getEntityNameByTableName(database, record.getTableName());
RecordIdentifier<String> recordId = new RecordIdentifier<>(entityName, record.getRecordID());
try
{
// refresh Hibernate cache
persistence.quickLoad(recordId, true, record.isSharedDb());
}
catch (PersistenceException e)
{
LOG.warning("Error while reloading records", e);
}
recordId = new RecordIdentifier<>(record.getTableName(), record.getRecordID());
try
{
persistence.lock(LockType.NONE, recordId, true, record.isSharedDb());
}
catch (LockUnavailableException e)
{
// shouldn't be raised
}
}
wa.lockedRecords.clear();
return true;
}
/**
* Shuts the server down.
*
* @return {@code true} if successful
*/
public static boolean shutdown()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// shut the server down
try
{
SessionManager.get().shutdown();
}
catch (SecurityException e)
{
return false;
}
return true;
}
//
// General Server Configuration API
//
/**
* Returns an array of resource plugin names.
*
* @return All defined resource plugin names
*/
public static String[] listResourceNames()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return wa.sa.listResourceNames();
}
/**
* Returns the registered resource type name for the given resource plugin.
*
* @param name
* plugin name
*
* @return registered resource type name or {@code null} if no match
*/
public static String getResourceType(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return wa.sa.getResourceType(name);
}
/**
* Modifies resource name definitions.
*
* @param additions
* array of resource names to add or {@code null} if no
* additions are specified
* @param deletions
* array of resource names to delete or {@code null} if no
* deletions are specified
* @return boolean success indicator
*/
public static boolean changeResourceNames(String[] additions, String[] deletions)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Gets authentication configuration, which includes authentication mode,
* password aging and security tracing level.
*
* @return an instance with all current authentication values
*/
public static AuthConfigDef getAuthConfig()
{
// TO-DO:
return new AuthConfigDef();
}
/**
* 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
*/
public static boolean setAuthConfig(AuthConfigDef authConfig)
{
// TO-DO:
return false;
}
/**
* Returns an array of defined holidays.
*
* @return All defined holidays
*/
public static date[] listHolidays()
{
// TO-DO:
return new date[] {};
}
/**
* Modifies defined holidays.
*
* @param additions
* array of dates to add or {@code null} if no additions are
* specified
* @param deletions
* array of dates to delete or {@code null} if no deletions
* are specified
* @return boolean success indicator
*/
public static boolean changeHolidays(date[] additions, date[] deletions)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Returns audit info.
*
* @return AuditDef The instance of AuditDef.
*/
public static AuditDef getAudit()
{
// TO-DO:
return new AuditDef();
}
/**
* Sets audit info.
*
* @param audit
* The instance of AuditDef.
*
* @return boolean indicating the success of this operation
*/
public static boolean setAudit(AuditDef audit)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
//
// Accounts Management API
//
/**
* Returns an array of defined group names.
*
* @return All defined group accounts or {@code null}
*/
public static TaggedName[] listGroups()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of group accounts
return wa.sa.listGroups();
}
/**
* Returns the group definition for the specified group name.
*
* @param name
* group name
*
* @return group definition or {@code null} if no such group
*/
public static GroupDef getGroup(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the group definition
return wa.sa.getGroup(name);
}
/**
* Check the group account existence.
*
* @param name
* group name
*
* @return minimum group information (name, description) or
* {@code null} if the named group does not exist
*/
public static TaggedName checkGroup(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the group definition
GroupDef gd = wa.sa.getGroup(name);
if (gd == null)
{
return null;
}
return new TaggedName(gd.subjectId, gd.description);
}
/**
* Changes the group definition for the named group.
*
* @param group
* group definition
*
* @return {@code true} if successfully changed the group
*/
public static boolean setGroup(GroupDef group)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// change the group definition
return wa.incrementCounters(wa.sa.setGroup(group));
}
/**
* Adds new group account definition.
*
* @param group
* group definition for a new account
*
* @return {@code true} if successfully added the group
*/
public static boolean addGroup(GroupDef group)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// add the group definition
return wa.incrementCounters(wa.sa.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} if successfully deleted the group
*/
public static boolean deleteGroup(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// delete the group definition
return wa.incrementCounters(wa.sa.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} tells to clone ACLs as well
*
* @return {@code true} if successfully cloned the group
*/
public static boolean cloneGroup(String nameFrom, String nameTo,
boolean acls)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// get and return the array of group accounts
return wa.incrementCounters(wa.sa.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} if
* there is no such group
*/
public static TaggedName[] listGroupUsers(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of group accounts
return wa.sa.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} if successfully changed the group
*/
public static boolean changeGroupUsers(String name, String[] additions,
String[] deletions)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// get and return the array of group accounts
return wa.incrementCounters(wa.sa.changeGroupUsers(name, additions, deletions));
}
/**
* Returns an array of defined user account names and their owners.
*
* @return All defined user accounts or {@code null}
*/
public static TaggedName[] listUsers()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of user accounts
return wa.sa.listUsers();
}
/**
* Check the user account existence.
*
* @param name
* user account name
*
* @return minimum user account information (name, person) or
* {@code null} if the named account does not exist
*/
public static TaggedName checkUser(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get the user account definition
UserDef ud = wa.sa.getUser(name);
if (ud == null)
{
return null;
}
return new TaggedName(ud.subjectId, ud.person);
}
/**
* Returns the user account definition for the specified account name.
*
* @param name
* user account name
*
* @return user account definition or {@code null} if no such user
*/
public static UserDef getUser(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the user account definition
return wa.sa.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.
*
* @param user
* user account definition
*
* @return {@code true} if successfully changed the account
*/
public static boolean setUser(UserDef user)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// change the user account definition
return wa.incrementCounters(wa.sa.setUser(user, null, false));
}
/**
* 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} if successfully changed the account
*/
public static boolean setUser(UserDef user, Serializable ext)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// change the user account definition
return wa.incrementCounters(wa.sa.setUser(user, ext, true));
}
/**
* Adds new user account definition.
*
* @param user
* user account definition
*
* @param ext
* optional extension container for custom account extensions
*
* @return {@code true} if successfully added the account
*/
public static boolean addUser(UserDef user, Serializable ext)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// add the user account definition
return wa.incrementCounters(wa.sa.addUser(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} if successfully deleted the account
*/
public static boolean deleteUser(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
return wa.incrementCounters(wa.sa.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} tells to clone ACLs as well
*
* @return {@code true} if successfully cloned the user account
*/
public static boolean cloneUser(String nameFrom, String nameTo,
boolean acls)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
return wa.incrementCounters(wa.sa.cloneUser(nameFrom, nameTo, acls, false, null));
}
/**
* 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} tells to clone ACLs as well
*
* @param newPassword
* a replacement password for the clone; {@code null} if the
* password should be preserved
*
* @return {@code true} if successfully cloned the user account
*/
public static boolean cloneUser(String nameFrom, String nameTo,
boolean acls, byte[] newPassword)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
boolean replacePassword = newPassword != null;
return wa.incrementCounters(wa.sa.cloneUser(nameFrom,
nameTo,
acls,
replacePassword,
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} if there is no such user etc
*/
public static TaggedName[] listUserGroups(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return wa.sa.listUserGroups(name);
}
/**
* Returns an array of defined OS user names and their hashed passwords.
*
* @return All defined OS users or <code>null</code>
*/
public static TaggedName[] listOsUsers()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return wa.sa.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
*/
public static TaggedName checkOsUser(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get the user account definition
OsUserDef ud = wa.sa.getOsUser(name);
if (ud == null)
{
return null;
}
return new TaggedName(ud.subjectId, null);
}
/**
* 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
*/
public static OsUserDef getOsUser(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the OS user account definition
return wa.sa.getOsUser(name);
}
/**
* Adds new OS user account definition.
*
* @param user
* OS user account definition
*
* @return {@code true} if successfully added the account
*/
public static boolean addOsUser(OsUserDef user)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// add the user account definition
return wa.incrementCounters(wa.sa.addOsUser(user));
}
/**
* Changes the user account definition for the OS user account. Optional
* fields are synchronized with the directory. Password aging information
* is set only if a password is set.
*
* @param user
* OS user account definition
*
* @return {@code true} if successfully changed the account
*/
public static boolean setOsUser(OsUserDef user)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// change the OS user definition
return wa.incrementCounters(wa.sa.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>
*/
public static TaggedName[] listAssociatedFwdUsers(String osUser)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return wa.sa.listAssociatedFwdUsers(osUser);
}
/**
* 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
*/
public static boolean deleteOsUser(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
return wa.incrementCounters(wa.sa.deleteOsUser(name));
}
/**
* Returns an array of defined process names with their descriptions.
*
* @return All defined processes accounts or {@code null}
*/
public static TaggedName[] listProcesses()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of process accounts
return wa.sa.listProcesses();
}
/**
* Returns the process definition for the specified process name.
*
* @param name
* process name
*
* @return process definition or {@code null} if no such process
*/
public static ProcessDef getProcess(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the process definition
return wa.sa.getProcess(name);
}
/**
* Check the process account existence.
*
* @param name
* process account name
*
* @return minimum process account information (name, description) or
* {@code null} if the named account does not exist
*/
public static TaggedName checkProcess(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the process definition
ProcessDef pd = wa.sa.getProcess(name);
if (pd == null)
{
return null;
}
return new TaggedName(pd.subjectId, pd.description);
}
/**
* Changes the process definition for the named process.
*
* @param process
* process definition
*
* @return {@code true} if successfully changed the process
*/
public static boolean setProcess(ProcessDef process)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// change the process definition
return wa.incrementCounters(wa.sa.setProcess(process, null, false));
}
/**
* Changes the process definition for the named process.
*
* @param process
* process definition
*
* @param ext
* optional extension container for custom account extensions
*
* @return {@code true} if successfully changed the process
*/
public static boolean setProcess(ProcessDef process, Serializable ext)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// change the process definition
return wa.incrementCounters(wa.sa.setProcess(process, ext, true));
}
/**
* 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} if successfully added the process
*/
public static boolean addProcess(ProcessDef process, Serializable ext)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// add the process definition
return wa.incrementCounters(wa.sa.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} if successfully deleted the process
*/
public static boolean deleteProcess(String name)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// delete the process definition
return wa.incrementCounters(wa.sa.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} tells to clone ACLs as well
*
* @return {@code true} if successfully cloned the process
*/
public static boolean cloneProcess(String nameFrom, String nameTo,
boolean acls)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// clone process
return wa.incrementCounters(wa.sa.cloneProcess(nameFrom, nameTo, acls));
}
/**
* Returns an array of defined Certificate Authority certificates with
* their distinctive names.
*
* @param unusedOnly
* if {@code true}, lists unused certificates only,
* otherwise all certificates
*
* @return All defined CA certificates or {@code null}
*/
public static TaggedName[] listAuthCerts(boolean unusedOnly)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of CA certificates
return wa.sa.listCerts(true, unusedOnly);
}
/**
* Returns an array of defined peer (non-CA) certificates with their
* distinctive names.
*
* @param unusedOnly
* if {@code true}, lists unused certificates only,
* otherwise all certificates
*
* @return All defined peer certificates or {@code null}
*/
public static TaggedName[] listPeerCerts(boolean unusedOnly)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of peer certificates
return wa.sa.listCerts(false, 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}
*/
public static TaggedName[] getCertUsers(String alias)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of accounts
return wa.sa.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}
*/
public static TaggedName[] getCertProcesses(String alias)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of accounts
return wa.sa.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}
*/
public static TaggedName[] getCertAccounts(String alias)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of accounts
return wa.sa.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}
*/
public static CertDef getCert(String alias)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the certificate
return wa.sa.getCert(alias);
}
/**
* 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
*/
public static TaggedName[] getCertificateHierarchy(String alias)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
ArrayList<TaggedName> list = new ArrayList<TaggedName>();
do
{
CertDef cdef = wa.sa.getCert(alias);
X509Certificate xcert = CertificateUtils.loadCertificate(cdef.certificate);
// if certificate could not be loaded, add a message
if (xcert == null)
{
list.add(0, new TaggedName(alias, CertificateUtils.CERT_BROKEN));
continue;
}
String DN = xcert.getSubjectX500Principal().getName();
list.add(0, new TaggedName(alias, DN));
// get parent certificate alias, only if this certificate is not
// self-signed
if (CertificateUtils.isSelfSigned(xcert))
{
// found a self-signed certificate, end search
break;
}
else
{
String issuerDN = xcert.getIssuerX500Principal().getName();
// find issuer certificate
alias = wa.sa.searchCert(issuerDN);
if (alias == null)
{
// certificate chain is broken
list.add(0, new TaggedName("*** broken chain ***", ""));
}
}
}
while (alias != null);
return list.toArray(new TaggedName[0]);
}
/**
* Validate the given certificate, by its PEM data.
*
* @param cert
* The PEM representation of the certificate
*
* @return a value representing certificate validity state.
*/
public static int validateCertificate(CertDef cert)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return -1;
}
// check and reassemble the certificate encoding
int len = cert.certificate.length - 2;
if (len < 1 ||
!cert.certificate[0].equals(CertificateUtils.CERT_BEG) ||
!cert.certificate[len + 1].equals(CertificateUtils.CERT_END))
{
return CertificateUtils.CERT_INVALID_BROKEN;
}
for (int i = 1; i < len + 1; i++)
{
if ((i != len && cert.certificate[i].length() != CertificateUtils.SPLIT_CHUNK) ||
(i == len && cert.certificate[i].length() > CertificateUtils.SPLIT_CHUNK))
{
return CertificateUtils.CERT_INVALID_BROKEN;
}
}
X509Certificate x509 = CertificateUtils.loadCertificate(cert.certificate);
// certificate could not be instantiated
if (x509 == null)
{
return CertificateUtils.CERT_INVALID_BROKEN;
}
boolean selfSigned = CertificateUtils.isSelfSigned(x509);
// check if Peer certificate is self-signed
if (!cert.authority && selfSigned)
{
// peer certificates can't be self signed
return CertificateUtils.CERT_INVALID_PEER_SELF_SIGNED;
}
// get the certificate's signer
String signerAlias = selfSigned ? cert.alias
: wa.sa.searchCert(x509.getIssuerX500Principal()
.getName());
if (signerAlias == null)
{
// could not find signer
return CertificateUtils.CERT_INVALID_SIGNER_NOT_FOUND;
}
CertDef signer = selfSigned ? cert : wa.sa.getCert(signerAlias);
if (!signer.authority)
{
// special validation of Peer and CA certificates - must be signed by
// a CA certificate
if (!cert.authority)
{
// Peer certificate is not signed by a CA certificate
return CertificateUtils.CERT_INVALID_PEER_NOT_SIGNED_BY_CA;
}
else
{
// CA certificate must be signed by a CA certificate
return CertificateUtils.CERT_INVALID_CA_NOT_SIGNED_BY_CA;
}
}
X509Certificate s509 = CertificateUtils.loadCertificate(signer.certificate);
// check certificate signature
try
{
x509.verify(s509.getPublicKey());
}
catch (InvalidKeyException xik)
{
return CertificateUtils.CERT_INVALID_CORRUPTED1;
}
catch (NoSuchProviderException xnsp)
{
return CertificateUtils.CERT_INVALID_CORRUPTED2;
}
catch (NoSuchAlgorithmException xnsa)
{
return CertificateUtils.CERT_INVALID_CORRUPTED3;
}
catch (SignatureException xs)
{
return CertificateUtils.CERT_INVALID_CORRUPTED4;
}
catch (CertificateException xc)
{
return CertificateUtils.CERT_INVALID_CORRUPTED5;
}
// check certificate period validity
try
{
x509.checkValidity();
}
catch (CertificateExpiredException e)
{
return CertificateUtils.CERT_INVALID_EXPIRED;
}
catch (CertificateNotYetValidException e)
{
return CertificateUtils.CERT_INVALID_NOT_YET_VALID;
}
return CertificateUtils.CERT_VALID;
}
/**
* 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} if the owner property should be retrieved;
* {@code false} for the issuer property.
*
* @return A map with certificate properties or {@code null} if the certificate PEM data
* could not be loaded.
*/
public static Map<String, String> getCertificateDNProperties(CertDef cd, boolean owner)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return CertificateUtils.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 prop
* The property to query.
* @param owner
* {@code true} if the owner property should be retrieved;
* {@code false} for the issuer property.
*
* @return The value for the specified property.
*/
public static String getCertificateDNProperty(CertDef cd, String prop, boolean owner)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return CertificateUtils.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} if the certificate PEM data
* could not be loaded.
*/
public static Map<String, Object> getCertificateProperties(CertDef cd)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return CertificateUtils.getCertificateProperties(cd);
}
/**
* Check if the given certificate is self-signed.
*
* @param cert
* The certificate which should be checked.
*
* @return {@code true} if this certificate is self-signed
*/
public static Boolean isSelfSigned(CertDef cert)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
X509Certificate x509 = CertificateUtils.loadCertificate(cert.certificate);
return x509 != null && CertificateUtils.isSelfSigned(x509);
}
/**
* Replaces an existing certificate with the given one.
*
* @param cert
* encoded certificate
*
* @return {@code true} if replaced, {@code false} if errors
*/
public static boolean setCert(CertDef cert)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// replace the certificate
return wa.incrementCounters(wa.sa.setCert(cert));
}
/**
* Adds a new certificate according to its definition.
*
* @param cert
* encoded certificate
*
* @return {@code true} if added, {@code false} if errors
*/
public static boolean addCert(CertDef cert)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// add the certificate
return wa.incrementCounters(wa.sa.addCert(cert));
}
/**
* Changes the certificate's alias.
*
* @param aliasFrom
* current certificate's alias
*
* @param aliasTo
* new certificate's alias
*
* @return {@code true} if changed, {@code false} if errors
*/
public static boolean moveCert(String aliasFrom, String aliasTo)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// rename certificate
return wa.incrementCounters(wa.sa.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}.
*/
public static String searchCert(String dn)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// find the certificate
return wa.sa.searchCert(dn);
}
/**
* Deletes an existing certificate conditionally or unconditionally.
*
* @param alias
* the alias of the certificate being deleted
*
* @param force
* if {@code true}, the certificate is deleted even if in
* use
*
* @return {@code true} if deleted, {@code false} if errors
*
*/
public static boolean deleteCert(String alias, boolean force)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// delete the certificate
return wa.incrementCounters(wa.sa.deleteCert(alias, force));
}
//
// Access Control List Management API
//
/**
* 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
*/
public static String[] listAclInstances()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// list ACL instances
return wa.sa.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
*/
public static String[] listAclInstances(String resourceType)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// list ACL instances
return wa.sa.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
*/
public static String[] listInstanceAcls(String instanceName)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// list ACLs for the instance
return wa.sa.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}
*
* @param instanceName
* name of the ACL instance; empty string for the shared one;
* can't be {@code null}
*
* @return ACL definition or {@code null}
*/
public static AclDef getAcl(String resourceType, String instanceName)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// check arguments
if (resourceType == null)
{
message (new String[] {"resource type can't be omitted"});
}
if (instanceName == null)
{
message (new String[] {"instance name can't be omitted"});
}
if (resourceType == null || instanceName == null)
{
return null;
}
// list ACLs for the instance
return wa.sa.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}, all ACLs are returned and some may be
* combined
*
* @return an array of ACL definitions, one per resource type and instance,
* or {@code null}
*/
public static AclDef[] getAllAcls(String instanceName)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// list ACLs for the instance
return wa.sa.getAcls(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}
*
* @return ACL definition or {@code null}
*/
public static AclDef getEffectiveAcls(String resourceType)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// check arguments
if (resourceType == null)
{
message (new String[] {"resource type can't be omitted"});
return null;
}
// list the combined ACLs for the resource
return wa.sa.getAcl(resourceType, null);
}
/**
* 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} if added, {@code false} if errors
*/
public static boolean addAcl(String resourceType, Acl acl,
Map<Integer, String> subjMap)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// check arguments
if (resourceType == null)
{
message (new String[] {"resource type can't be omitted"});
return false;
}
if (acl == null)
{
message (new String[] {"ACL can't be omitted"});
return false;
}
if (acl.origin == null)
{
message (new String[] {"target instance can't be omitted"});
return false;
}
if (subjMap == null)
{
message (new String[] {"subjects map can't be omitted"});
return false;
}
// add the ACL
return wa.incrementCounters(wa.sa.addAcl(resourceType, acl, subjMap));
}
/**
* Get the list of available auth plugins.
*
* @return the list of available auth plugins encoded into tagged names or
* {@code null} if no plugins are available.
*/
public static TaggedName[] listAuthPlugins()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// get and return the array of auth plugins
return wa.sa.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}.
* @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.
*/
public static int bulkAuthModeUpdate(int targetAccounts,
String[] excludedAccounts,
int authMode,
String authPlugin)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return -1;
}
// perform auth mode update
int updateCount = wa.sa.bulkAuthModeUpdate(targetAccounts,
excludedAccounts,
authMode,
authPlugin);
wa.incrementCounters(updateCount > 0);
return updateCount;
}
/**
* Refresh specified plugin.
*
* @param name
* Plugin name.
*/
public static void refreshPlugin(String name)
{
WorkArea wa = workArea.get();
wa.sa.refreshPlugin(name);
}
/**
* 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} and can be
* {@code null} all other times
* @param setInstance
* {@code true} if setting the resource instance name
* @param setInstanceMode
* {@code true} if setting the resource instance name mode
* @param setSubjects
* {@code true} if setting the subjects IDs
* @param setRights
* {@code true} if setting the rights for this ACL
* @return {@code true} if set, {@code false} if errors
*/
public static boolean setAcl(String resourceType, Acl acl,
Map<Integer, String> subjMap,
boolean setInstance, boolean setInstanceMode,
boolean setSubjects, boolean setRights)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// check arguments
if (resourceType == null)
{
message (new String[] {"resource type can't be omitted"});
return false;
}
if (acl == null)
{
message (new String[] {"ACL can't be omitted"});
return false;
}
if (acl.origin == null)
{
message (new String[] {"target instance can't be omitted"});
return false;
}
if (subjMap == null)
{
message (new String[] {"subjects map can't be omitted"});
return false;
}
// set the ACL
return wa.incrementCounters(wa.sa.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} if moved, {@code false} if errors
*/
public static boolean moveAcl(String resourceType, String instanceName,
int oid, String newInstanceName)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// check arguments
if (resourceType == null)
{
message (new String[] {"resource type can't be omitted"});
return false;
}
if (instanceName == null || newInstanceName == null)
{
message (new String[] {"instance names can't be omitted"});
return false;
}
// move the ACL to another instance
return wa.incrementCounters(wa.sa.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} if moved, {@code false} if errors
*/
public static boolean moveAcl(String resourceType, String instanceName,
int oid, int newOid)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// check arguments
if (resourceType == null)
{
message (new String[] {"resource type can't be omitted"});
return false;
}
if (instanceName == null)
{
message (new String[] {"source instance can't be omitted"});
return false;
}
// move the ACL around within the instance
return wa.incrementCounters(wa.sa.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} if deleted, {@code false} if errors
*/
public static boolean deleteAcl(String resourceType, String instanceName,
int oid)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// check arguments
if (resourceType == null)
{
message (new String[] {"resource type can't be omitted"});
return false;
}
if (instanceName == null)
{
message (new String[] {"source instance can't be omitted"});
return false;
}
// delete the ACL
return wa.incrementCounters(wa.sa.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} if cloned, {@code false} if errors
*/
public static boolean cloneAcl(String resourceType, String instanceName,
int oid, int cloneOid)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// check arguments
if (resourceType == null)
{
message (new String[] {"resource type can't be omitted"});
return false;
}
if (instanceName == null)
{
message (new String[] {"source instance can't be omitted"});
return false;
}
// clone the ACL within the instance
return wa.incrementCounters(wa.sa.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} if success, {@code false} if errors
*/
public static boolean resequenceAcls(String resourceType,
String instanceName,
int start, int step)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return false;
}
// check arguments
if (resourceType == null)
{
message (new String[] {"resource type can't be omitted"});
return false;
}
if (instanceName == null)
{
message (new String[] {"source instance can't be omitted"});
return false;
}
// resequence the ACLs within the instance
return wa.incrementCounters(wa.sa.resequenceAcls(resourceType, instanceName,
start, step));
}
//
// Access Control List Management API extension that we may need later
//
/**
* 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} if created, {@code false} if errors
*/
public static boolean addAclInstance(String instanceName)
{
WorkArea wa = workArea.get();
// TO-DO:
return wa.incrementCounters(false);
}
/**
* 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} if deleted, {@code false} if errors
*/
public static boolean deleteAclInstance(String instanceName)
{
WorkArea wa = workArea.get();
// TO-DO:
return wa.incrementCounters(false);
}
/**
* 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} if deleted, {@code false} if errors
*/
public static boolean deleteAclInstance(String resourceType,
String instanceName)
{
WorkArea wa = workArea.get();
// TO-DO:
return wa.incrementCounters(false);
}
/**
* 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} if cloned, {@code false} if errors
*/
public static boolean cloneAclInstance(String instanceFrom,
String instanceTo)
{
WorkArea wa = workArea.get();
// TO-DO:
return wa.incrementCounters(false);
}
/**
* 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} if cloned, {@code false} if errors
*/
public static boolean cloneAclInstance(String resourceType,
String instanceFrom,
String instanceTo)
{
WorkArea wa = workArea.get();
// TO-DO:
return wa.incrementCounters(false);
}
/**
* 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} if moved, {@code false} if errors
*/
public static boolean moveAclInstance(String instanceFrom,
String instanceTo)
{
WorkArea wa = workArea.get();
// TO-DO:
return wa.incrementCounters(false);
}
/**
* 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} if moved, {@code false} if errors
*/
public static boolean moveAclInstance(String resourceType,
String instanceFrom,
String instanceTo)
{
WorkArea wa = workArea.get();
// TO-DO:
return wa.incrementCounters(false);
}
//
// Per Server Runtime configuration: default or named
//
/**
* 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
*/
public static String[] listServers()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// enumerate direct children of "/server"
String[] servers = wa.ds.enumerateNodes("/server");
// edit the list and hide the "default"
for (int i = 0; i < servers.length; i++)
{
if (servers[i].equalsIgnoreCase("default"))
{
servers[i] = "";
}
}
return servers;
}
/**
* 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
*/
public static String[] listServers(String containers)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
// firstly, enumerate direct children of "/server"
String[] servers = wa.ds.enumerateNodes("/server");
// test servers one by one
List<String> found = new LinkedList<String>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < servers.length; i++)
{
sb.setLength(0);
sb.append("/server/");
sb.append(servers[i]);
sb.append("/");
sb.append(containers);
if (wa.ds.getNodeClass(sb.toString()) != null)
{
found.add(servers[i]);
}
}
servers = found.toArray(new String[]{});
// edit the list and hide the "default"
for (int i = 0; i < servers.length; i++)
{
if (servers[i].equalsIgnoreCase("default"))
{
servers[i] = "";
}
}
return servers;
}
/**
* Returns the list of logging instances.
*
* @return The list of logging instances.
*/
public static String[] listLoggingInstances()
{
// TO-DO:
return new String[] {""};
}
/**
* Returns the logging definition by the given name.
*
* @param name
* The given name
*
* @return The target logging definition.
*/
public static LoggingDef getLogging(String name)
{
// TO-DO:
return new LoggingDef();
}
/**
* Sets the logging definition.
*
* @param logging
* The logging definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean setLogging(LoggingDef logging)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Adds the logging definition for the given name
*
* @param name
* The given name
* @param logging
* The logging definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean addLogging(String name, LoggingDef logging)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Clones the logging definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean cloneLogging(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Moves the logging definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean moveLogging(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Deletes the logging definition of the given name.
*
* @param name
* The given name
*
* @return boolean indicating the success of this operation.
*/
public static boolean deleteLogging(String name)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Returns the list of export instances.
*
* @return The string array of export instances.
*/
public static String[] listExportsInstances()
{
// TO-DO:
return new String[] {""};
}
/**
* Returns the export definition by the given export instance name.
*
* @param name
* The given export instance name
*
* @return The export definition
*/
public static ExportsDef getExports(String name)
{
// TO-DO:
return new ExportsDef();
}
/**
* Sets the export definition.
*
* @param logging
* The export definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean setExports(ExportsDef logging)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Adds the export definition for the given name
*
* @param name
* The given name
* @param logging
* The export definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean addExports(String name, ExportsDef logging)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Clones the export definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean cloneExports(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Moves the export definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean moveExports(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Deletes the export definition of the given name.
*
* @param name
* The given name
*
* @return boolean indicating the success of this operation.
*/
public static boolean deleteExports(String name)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Returns the list of startup instances.
*
* @return The string array of startup instances.
*/
public static String[] listStartupInstances()
{
// TO-DO:
return new String[] {""};
}
/**
* Returns the startup definition by the given startup instance name.
*
* @param name
* The given startup instance name
*
* @return The startup definition
*/
public static StartupDef getStartup(String name)
{
// TO-DO:
return new StartupDef();
}
/**
* Sets the startup definition.
*
* @param startup
* The startup definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean setStartup(StartupDef startup)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Adds the startup definition for the given instance name
*
* @param name
* The given name
* @param startup
* The startup definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean addStartup(String name, StartupDef startup)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Clones the startup definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean cloneStartup(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Moves the startup definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean moveStartup(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Deletes the startup definition of the given name.
*
* @param name
* The given name
*
* @return boolean indicating the success of this operation.
*/
public static boolean deleteStartup(String name)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Returns the list of persistence instances.
*
* @return The string array of persistence instances.
*/
public static String[] listPersistenceInstances()
{
// TO-DO:
return new String[] {""};
}
/**
* Returns the persistence definition by the given persistence instance name.
*
* @param name
* The given persistence instance name
*
* @return The persistence definition
*/
public static PersistenceDef getPersistence(String name)
{
// TO-DO:
return new PersistenceDef();
}
/**
* Sets the persistence definition.
*
* @param persistence
* The persistence definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean setPersistence(PersistenceDef persistence)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Adds the persistence definition for the given instance name
*
* @param name
* The given name
* @param persistence
* The persistence definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean addPersistence(String name, PersistenceDef persistence)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Clones the persistence definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean clonePersistence(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Moves the persistence definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean movePersistence(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Deletes the persistence definition of the given name.
*
* @param name
* The given name
*
* @return boolean indicating the success of this operation.
*/
public static boolean deletePersistence(String name)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Returns the list of database instances.
*
* @return The string array of database instances.
*/
public static String[] listDatabaseInstances()
{
// TO-DO:
return new String[] {""};
}
/**
* Returns the database definition by the given database instance name.
*
* @param name
* The given database instance name
*
* @return The database definition
*/
public static DatabaseDef getDatabase(String name)
{
// TO-DO:
return new DatabaseDef();
}
/**
* Sets the database definition.
*
* @param database
* The database definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean setDatabase(DatabaseDef database)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Adds the database definition for the given instance name
*
* @param name
* The given name
* @param database
* The database definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean addDatabase(String name, DatabaseDef database)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Clones the database definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean cloneDatabase(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Moves the database definition from the source to the target.
*
* @param nameFrom
* The source name
* @param nameTo
* The target name
*
* @return boolean indicating the success of this operation.
*/
public static boolean moveDatabase(String nameFrom, String nameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Deletes the database definition of the given database name.
*
* @param name
* The given database name
*
* @return boolean indicating the success of this operation.
*/
public static boolean deleteDatabase(String name)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
//
// Per Account/Server Runtime configuration: default or named
//
/**
* Returns the list of runtime names.
*
* @return The string array of runtime names.
*/
public static String[] listRuntimeNames()
{
// TO-DO:
return new String[] {""};
}
/**
* Returns the list of runtime servers.
*
* @return The string array of runtime servers.
*/
public static String[] listRuntimeServers()
{
// TO-DO:
return new String[] {""};
}
/**
* Returns the list of runtime accounts names.
*
* @return The string array of runtime accounts names.
*/
public static String[] listRuntimeAccounts()
{
// TO-DO:
return new String[] {""};
}
/**
* Returns the list of target runtime names for the given account and the given item name.
*
* @param account
* The given account
* @param itemName
* The given item name
*
* @return The string array of target runtime names
*/
public static String[] searchRuntimeByAccount(String account, String itemName)
{
// TO-DO:
return new String[] {""};
}
/**
* Returns the runtime definition for the given server and account names.
*
* @param serverName
* The given server name
* @param accountName
* The given account name
*
* @return The runtime definition
*/
public static RuntimeDef getRuntime(String serverName, String accountName)
{
// TO-DO:
return new RuntimeDef();
}
/**
* Sets the runtime definition.
*
* @param runtime
* The runtime definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean setRuntime(RuntimeDef runtime)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Adds the runtime definition for the given server and account names.
*
* @param serverName
* The given server name
* @param accountName
* The given account name
* @param runtime
* The runtime definition
*
* @return boolean indicating the success of this operation.
*/
public static boolean addRuntime(String serverName,
String accountName,
RuntimeDef runtime)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Clones the runtime definition from the source to the target.
*
* @param serverNameFrom
* The source server name
* @param accountNameFrom
* The source account name
* @param serverNameTo
* The target server name
* @param accountNameTo
* The target account name
*
* @return boolean indicating the success of this operation.
*/
public static boolean cloneRuntime(String serverNameFrom,
String accountNameFrom,
String serverNameTo,
String accountNameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Moves the runtime definition from the source to the target.
*
* @param serverNameFrom
* The source server name
* @param accountNameFrom
* The source account name
* @param serverNameTo
* The target server name
* @param accountNameTo
* The target account name
*
* @return boolean indicating the success of this operation.
*/
public static boolean moveRuntime(String serverNameFrom,
String accountNameFrom,
String serverNameTo,
String accountNameTo)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Deletes the runtime definition for the given server and account names.
*
* @param serverName
* The given server name
* @param accountName
* The given account name
*
* @return boolean indicating the success of this operation.
*/
public static boolean deleteRuntime(String serverName, String accountName)
{
// TO-DO:
// TO-DO: if there are changed in directory, then return code should
// be wrapped into wa.incrementCounters(rc)
return false;
}
/**
* Return the current {@link SecurityAdmin} instance.
*
* @return the {@link WorkArea#sa} value.
*/
public static SecurityAdmin getSecurityAdmin()
{
WorkArea wa = workArea.get();
return wa.sa;
}
/**
* Return the current {@link DirectoryService} instance.
*
* @return the {@link WorkArea#ds} value.
*/
public static DirectoryService getDirectoryService()
{
WorkArea wa = workArea.get();
return wa.ds;
}
/**
* Utility method to store server messages for later retrieval by clients.
*
* @param msg
* The list of strings
*/
public static void message(String... msg)
{
WorkArea wa = workArea.get();
wa.freshMessages.add(msg);
}
/**
* Get directory service with permission check.
*
* @return The directory service
*/
public static DirectoryService getDirectoryServiceChecked()
{
WorkArea wa = workArea.get();
return (!wa.targeted || !wa.sa.adminAccess()) ? null : wa.ds;
}
/**
* 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}) and value indicating whether active
* hook class matches stored hook class which is
* <code>"true"</code> if they match.
*/
public static TaggedName[] getRegisteredJars()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
String[] jars = MultiClassLoader.listRegisteredJars();
// get active hooks
Map<String, String> activeHooksByJar = new HashMap<String, String>();
for (String jar : jars)
{
String hook = StandardServer.getHookForJar(jar);
activeHooksByJar.put(jar, hook);
}
// get stored hooks
Map<String, String> storedHooksByJar = new HashMap<String, String>();
String path = Utils.findDirectoryNodePath(wa.ds,
"server-hooks",
"strings",
false);
if (path != null)
{
String[] hooks = wa.ds.getNodeStrings(path, "values");
if (hooks != null && hooks.length > 0)
{
for (String hookClass : hooks)
{
for(String jar : jars)
{
if (storedHooksByJar.containsKey(jar))
continue;
if (MultiClassLoader.handledByJar(hookClass, jar))
{
storedHooksByJar.put(jar, hookClass);
break;
}
}
}
}
}
// form TaggedNames
TaggedName[] res = new TaggedName[jars.length];
for (int i = 0; i < jars.length; i++)
{
String activeHook = activeHooksByJar.get(jars[i]);
String storedHook = storedHooksByJar.get(jars[i]);
boolean match;
if (activeHook == null && storedHook == null)
{
match = true;
}
else if (activeHook == null || storedHook == null)
{
match = false;
}
else
{
match = activeHook.equals(storedHook);
}
res[i] = new TaggedName(jars[i], activeHook,
match ? "true" : "false");
}
return res;
}
/**
* 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.
*/
public static String[] getUnregisteredJars()
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return MultiClassLoader.listUnregisteredJars();
}
/**
* 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.
*/
public static String[] getAvailableJarHookClasses(String jar)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
List<String> hooks = new ArrayList<String>();
ClassInfo[] classes = MultiClassLoader.listClasses(jar);
if (classes == null)
return null;
for (ClassInfo cls : classes)
{
if (cls.isPublic() &&
!cls.isAbstract() &&
!cls.isInterface() &&
cls.isImplements(InitTermListener.class))
{
hooks.add(cls.getClassName());
}
}
return hooks.toArray(new String[hooks.size()]);
}
/**
* 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} if the jar has
* no hook specified or you have insufficient rights.
*/
public static String getStoredJarHookClass(String jar)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
String path = Utils.findDirectoryNodePath(wa.ds,
"server-hooks",
"strings",
false);
if (path != null)
{
String[] hooks = wa.ds.getNodeStrings(path, "values");
if (hooks != null && hooks.length > 0)
{
for (String hookClass : hooks)
{
if (MultiClassLoader.handledByJar(hookClass, jar))
{
return hookClass;
}
}
}
}
return null;
}
/**
* 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}.
* @param storeInDirectory
* If {@code true} then the jar is registered in the
* directory, else only runtime registration is performed.
*
* @return operation result.
*/
public static JarOpErrorCode registerJar(String jar,
String hookClass,
boolean storeInDirectory)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
// load jar
ErrorCode res;
try
{
res = MultiClassLoader.addJarLoader(jar);
}
catch (RestrictedUseException e)
{
throw new IllegalStateException("Call of " +
"MultiClassLoader.addJarLoader from this function should" +
"be granted.");
}
if (res != ErrorCode.SUCCESS)
{
return toJarOpErrorCode(res);
}
// load hook
InitTermListener listener = null;
if (hookClass != null)
{
try
{
listener = StandardServer.loadHook(hookClass, jar);
}
catch (Exception e)
{
LOG.severe( "Error while loading hook " + hookClass + " for jar " + jar, e);
//back out
try
{
MultiClassLoader.removeJarLoader(jar);
}
catch (RestrictedUseException ex)
{
throw new IllegalStateException("Call of " +
"MultiClassLoader.removeJarLoader from this function " +
"should be granted.");
}
}
if (listener == null)
{
return JarOpErrorCode.ERROR_LOADING_HOOK;
}
// register with the server
StandardServer.register(listener);
}
if (storeInDirectory)
{
// store jar in the directory
JarOpErrorCode result = addNodeStrings(wa.ds,
"/server/default/jars",
new String[]{jar});
if (result != JarOpErrorCode.SUCCESS)
{
//back out
try
{
MultiClassLoader.removeJarLoader(jar);
}
catch (RestrictedUseException ex)
{
throw new IllegalStateException("Call of " +
"MultiClassLoader.removeJarLoader from this function " +
"should be granted.");
}
if (hookClass != null)
StandardServer.removeHook(hookClass);
return result;
}
}
if (hookClass != null)
{
String path = null;
if (storeInDirectory)
{
// store hook in the directory
path = Utils.findDirectoryNodePath(wa.ds,
"server-hooks",
"strings",
false);
if (path == null)
path = "/server/default/server-hooks";
JarOpErrorCode result = addNodeStrings(wa.ds,
path,
new String[]{hookClass});
if (result != JarOpErrorCode.SUCCESS)
{
//back out
try
{
MultiClassLoader.removeJarLoader(jar);
}
catch (RestrictedUseException ex)
{
throw new IllegalStateException("Call of " +
"MultiClassLoader.removeJarLoader from this function " +
"should be granted.");
}
StandardServer.removeHook(hookClass);
removeNodeStrings(wa.ds,
"/server/default/jars",
new String[]{jar});
return result;
}
}
// initialize hook
try
{
listener.initialize();
}
catch (Throwable t)
{
//back out
try
{
MultiClassLoader.removeJarLoader(jar);
}
catch (RestrictedUseException ex)
{
throw new IllegalStateException("Call of " +
"MultiClassLoader.removeJarLoader from this function " +
"should be granted.");
}
StandardServer.removeHook(hookClass);
if (storeInDirectory)
{
removeNodeStrings(wa.ds,
"/server/default/jars",
new String[]{jar});
removeNodeStrings(wa.ds, path, new String[]{hookClass});
}
return JarOpErrorCode.ERROR_RUN_HOOK;
}
}
return JarOpErrorCode.SUCCESS;
}
/**
* Remove registration for the specified jar.
*
* @param jar
* Jar file which registration should be removed.
* @param force
* If it is {@code true} then any sessions that have executed
* API(s) associated to this jar are forcibly terminated. If this
* parameter is {@code false} 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} then the jar registration is removed from
* the directory, else only runtime deregistration is performed.
*
* @return operation result.
*/
public static JarOpErrorCode deregisterJar(String jar,
boolean force,
boolean removeFromDirectory)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
if (force)
{
// terminate sessions if necessary
if (terminateSessions(jar, null) != JarOpErrorCode.SUCCESS)
{
return JarOpErrorCode.ERROR_TERMINATING_SESSIONS;
}
}
else
{
// check whether there are active sessions
SessionInfo[] info = wa.sa.getActiveSessions(jar, null);
if (info == null)
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
else if (info.length > 0)
{
return JarOpErrorCode.ACTIVE_SESSIONS_PRESENT;
}
}
// get registered APIs
TaggedName[] apis = getRegisteredJarAPIs(jar);
if (apis == null)
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
String[] ifaces = new String[apis.length];
for (int i = 0; i < apis.length; i++)
{
ifaces[i] = apis[i].getName();
}
// deregister APIs
if (!RemoteObject.deregisterStaticNetworkServer(ifaces))
{
return JarOpErrorCode.ERROR_DEREG_NETWORK_SERVER;
}
// terminate jar hook
String activeHookClass = StandardServer.getHookForJar(jar);
String storedHookClass = removeFromDirectory ?
getStoredJarHookClass(jar) :
null;
if (activeHookClass != null)
{
StandardServer.removeHook(activeHookClass);
}
// deregister jar
ErrorCode res;
try
{
res = MultiClassLoader.removeJarLoader(jar);
}
catch (RestrictedUseException e)
{
throw new IllegalStateException("Call of " +
"MultiClassLoader.removeJarLoader from this function should" +
"be granted.");
}
if (res != ErrorCode.SUCCESS)
{
return toJarOpErrorCode(res);
}
if (removeFromDirectory)
{
return deregisterJarFromDirectory(jar, storedHookClass);
}
return JarOpErrorCode.SUCCESS;
}
/**
* 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} if no hook is defined for this
* jar.
*
* @return operation result code.
*/
public static JarOpErrorCode deregisterJarFromDirectory(String jar,
String storedHookClass)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
if (storedHookClass != null)
{
// remove hook information from the directory
String path = Utils.findDirectoryNodePath(wa.ds,
"server-hooks",
"strings",
false);
if (path != null)
{
JarOpErrorCode result = removeNodeStrings(wa.ds,
path,
new String[]{storedHookClass});
if (result != JarOpErrorCode.SUCCESS)
{
return result;
}
}
}
// remove jar information from the directory
return removeNodeStrings(wa.ds,
"/server/default/jars",
new String[]{jar});
}
/**
* 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}
* indicates that no hook class should be specified.
*
* @return operation result.
*/
public static JarOpErrorCode changeJarHookClass(String jar,
String hookClass)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
String path = Utils.findDirectoryNodePath(wa.ds,
"server-hooks",
"strings",
false);
if (path == null)
path = "/server/default/server-hooks";
String oldHookClass = getStoredJarHookClass(jar);
if (oldHookClass != null)
{
JarOpErrorCode result = removeNodeStrings(wa.ds,
path,
new String[]{oldHookClass});
if (result != JarOpErrorCode.SUCCESS)
return result;
}
if (hookClass != null)
{
JarOpErrorCode result = addNodeStrings(wa.ds,
path,
new String[]{hookClass});
if (result != JarOpErrorCode.SUCCESS)
return result;
}
return JarOpErrorCode.SUCCESS;
}
/**
* 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.
*/
public static TaggedName[] getRegisteredJarAPIs(String jar)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
String[] apis = getAvailableJarAPIInterfaces(jar, true);
List<TaggedName> res = new ArrayList<TaggedName>();
for (String api : apis)
{
Class<?> implClass = RemoteObject.getImplClass(api);
if (implClass != null)
{
res.add(new TaggedName(api, implClass.getName()));
}
}
return res.toArray(new TaggedName[res.size()]);
}
/**
* 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.
*/
public static String[] getAvailableJarAPIImplClasses(String jar,
String interfaceClass)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
Class<?> iface;
try
{
iface = MultiClassLoader.getClassLoader().loadClass(interfaceClass);
}
catch (ClassNotFoundException e)
{
LOG.severe("Cannot load class " + interfaceClass + " for jar " + jar, e);
return null;
}
ClassInfo[] implClasses = MultiClassLoader.listClasses(jar);
List<String> selectedClasses = new ArrayList<String>();
// get all possible implementation classes from the jar and see if any
// of them implement all methods of the given interface, as static
// methods
try
{
for (ClassInfo implClass : implClasses)
{
if (!implClass.isInterface() &&
implClass.isPublic() &&
!implClass.isAbstract() &&
implClass.isStaticallyImplements(iface))
selectedClasses.add(implClass.getClassName());
}
}
catch (NoClassDefFoundError e)
{
LOG.severe("Cannot load methods of " + interfaceClass + " for jar " + jar, e);
return null;
}
return selectedClasses.toArray(new String[selectedClasses.size()]);
}
/**
* 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.
*/
public static JarOpErrorCode registerJarAPI(String interfaceClass,
String implementationClass)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
Class<?> iClass;
Class<?> backing;
try
{
iClass = MultiClassLoader.getClassLoader().loadClass(interfaceClass);
}
catch (ClassNotFoundException e)
{
return JarOpErrorCode.ERROR_LOADING_API_IFACE;
}
try
{
backing = MultiClassLoader.getClassLoader().loadClass(implementationClass);
}
catch (ClassNotFoundException e)
{
return JarOpErrorCode.ERROR_LOADING_API_IMPL;
}
Class<?>[] list = new Class<?>[] { iClass };
RemoteObject.registerStaticNetworkServer(list, backing);
return JarOpErrorCode.SUCCESS;
}
/**
* 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} then any sessions that have executed
* the target API are forcibly terminated. If this parameter is
* {@code false} 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.
*/
public static JarOpErrorCode deregisterJarAPI(String jar,
String interfaceClass,
boolean force)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
if (force)
{
// terminate sessions if necessary
if (terminateSessions(jar, interfaceClass) != JarOpErrorCode.SUCCESS)
{
return JarOpErrorCode.ERROR_TERMINATING_SESSIONS;
}
}
else
{
// check whether there are active sessions
SessionInfo[] info = wa.sa.getActiveSessions(jar, interfaceClass);
if (info == null)
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
else if (info.length > 0)
{
return JarOpErrorCode.ACTIVE_SESSIONS_PRESENT;
}
}
String[] list = new String[] { interfaceClass };
if (!RemoteObject.deregisterStaticNetworkServer(list))
{
return JarOpErrorCode.ERROR_DEREG_NETWORK_SERVER;
}
return JarOpErrorCode.SUCCESS;
}
/**
* Returns information whether server-wide or global hooks are used for
* the current server.
*
* @return {@code true} if server-wide hooks are used,
* {@code false} if global hooks are used, {@code null}
* if you have insufficient access rights.
*/
public static Boolean isUsingServerWideHooks()
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
StringBuilder sb = new StringBuilder("/server/");
sb.append(getServerName());
sb.append("/server-hooks");
return wa.ds.getNodeClass(sb.toString()) != null;
}
/**
* 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} 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.
*/
public static SessionInfo[] getActiveSessions(String jar,
String apiInterfaceClass)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
SessionInfo[] sessions = wa.sa.getActiveSessions(jar, apiInterfaceClass);
if (sessions != null)
{
setStacktraces(sessions);
}
return sessions;
}
/**
* Terminate all active sessions which have invoked APIs in that jar or
* the given exported interface, if <code>iface</code> is not
* {@code null}.
*
* @param jar
* The jar name.
* @param iface
* The interface name. May be {@code null}.
*
* @return operation result.
*/
public static JarOpErrorCode terminateSessions(String jar, String iface)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
if (!wa.sa.terminateSessions(jar, iface))
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
return JarOpErrorCode.SUCCESS;
}
/**
* 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} 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.
*/
public static String[] getAvailableJarAPIInterfaces(String jar,
boolean showRegistered)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
List<String> interfaces = new ArrayList<String>();
Set<String> registeredIfaceSet = null;
if (!showRegistered)
{
registeredIfaceSet = new HashSet<String>();
TaggedName[] registeredApisArray = getRegisteredJarAPIs(jar);
for (TaggedName api : registeredApisArray)
{
registeredIfaceSet.add(api.getName());
}
}
ClassInfo[] classes = MultiClassLoader.listClasses(jar);
for (ClassInfo cls : classes)
{
if (!showRegistered && registeredIfaceSet.contains(cls.getClassName()))
continue;
if (cls.isPublic() && cls.isInterface())
{
interfaces.add(cls.getClassName());
}
}
return interfaces.toArray(new String[interfaces.size()]);
}
/**
* 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} then server-wide hooks should be used,
* else global hooks should be used.
* @param move
* if {@code true} then existing hooks are copied/moved from
* one scope to another, otherwise they don't.
*
* @return operation result.
*/
public static JarOpErrorCode changeJarHooksType(boolean serverWide,
boolean move)
{
// access the target
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return JarOpErrorCode.INSUFFICIENT_RIGHTS;
}
StringBuilder sb = new StringBuilder("/server/");
sb.append(getServerName());
sb.append("/server-hooks");
String serverWidePath = sb.toString();
String globalPath = "/server/default/server-hooks";
boolean currentServerWide = wa.ds.getNodeClass(serverWidePath) != null;
if (currentServerWide == serverWide)
return JarOpErrorCode.SUCCESS;
if (serverWide)
{
// from global to server-wide
String[] valuesToCopy = new String[0];
if (move)
{
// copy hooks from global scope to server-wide scope
String[] old = wa.ds.getNodeStrings(globalPath, "values");
if (old != null)
valuesToCopy = old;
}
// create server-wide hooks node
return addNodeStrings(wa.ds, serverWidePath, valuesToCopy);
}
else
{
// from server-wide to global
String[] valuesToMove = new String[0];
if (move)
{
// copy hooks from server-wide scope to global scope
String[] oldServerWideHooks = wa.ds.getNodeStrings(serverWidePath,
"values");
if (oldServerWideHooks != null)
valuesToMove = oldServerWideHooks;
String[] oldGlobalHooks = wa.ds.getNodeStrings(globalPath,
"values");
// remove existing global hooks
if (oldGlobalHooks != null && oldGlobalHooks.length > 0)
{
JarOpErrorCode result = removeNodeStrings(wa.ds,
globalPath,
oldGlobalHooks);
if (result != JarOpErrorCode.SUCCESS)
return result;
}
}
// copy server-wide hooks to global hooks node
if (valuesToMove.length > 0)
{
JarOpErrorCode result = addNodeStrings(wa.ds,
globalPath,
valuesToMove);
if (result != JarOpErrorCode.SUCCESS)
return result;
}
// remove server-wide hooks node
sb = new StringBuilder("/server/");
sb.append(getServerName());
String serverWideBase = sb.toString();
if (!wa.ds.openBatch(serverWideBase))
return JarOpErrorCode.CANNOT_OPEN_DIRECTORY_BRANCH;
JarOpErrorCode result = JarOpErrorCode.SUCCESS;
boolean deleteRes = wa.ds.deleteNode(serverWidePath);
if (!deleteRes)
result = JarOpErrorCode.ERROR_DELETING_NODE;
boolean res = wa.ds.closeBatch(deleteRes, false);
if (!res && deleteRes)
result = JarOpErrorCode.CANNOT_COMMIT_DIRECTORY;
return result;
}
}
/**
* Allows to determine whether
* {@link com.goldencode.p2j.classloader.MultiClassLoader} is used as the
* system class loader.
*
* @return {@code true} if
* {@link com.goldencode.p2j.classloader.MultiClassLoader} is used
* as the system class loader, {@code false} if the standard
* class loader is used, {@code null} if you do not have
* enough permissions
*/
public static Boolean isUsingMultiClassLoader()
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return MultiClassLoader.getClassLoader() instanceof MultiClassLoader;
}
/**
* 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 (can be {@code null} if the directory
* is not specified) and string value which is <code>"true"</code>
* if the directory is specified, exists and is accessible.
* If you do not have admin rights, {@code null} is returned.
*/
public static TaggedName getManagedLibsDirInformation()
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
String path = MultiClassLoader.managedCustomerLibs();
if (path == null)
return new TaggedName(null, "false");
File f = new File(path);
String exists = f.exists() && f.isDirectory() ? "true" : "false";
try
{
path = f.getCanonicalPath();
}
catch (IOException e)
{
exists = "false";
}
catch (SecurityException e)
{
exists = "false";
}
return new TaggedName(path, exists);
}
/**
* Returns the map of report parameters to their values saved in the directory.
*
* @return The report parameters map
*/
public static Map<String, Object> getReportParametersMap()
{
DirectoryService dir = getDirectoryService();
HashMap<String, Object> map = new HashMap<String, Object>();
getIntReportSettings(dir, ReportParameters.MAX_PAGES_NUMBER, 250, map);
// page settings
getStringReportSettings(dir, ReportParameters.PAPER_FORMAT, "A4", map);
getStringReportSettings(dir, ReportParameters.PAPER_ORIENT, "PORTRAIT", map);
// font settings
getStringReportSettings(dir, ReportParameters.FONT_NAME, "Courier", map);
getStringReportSettings(dir, ReportParameters.BOLD_FONT_NAME, "Courier-Bold", map);
getIntReportSettings(dir, ReportParameters.FONT_SIZE, 10, map);
getIntReportSettings(dir, ReportParameters.COLUMN_FONT_SIZE, 12, map);
getIntReportSettings(dir, ReportParameters.HEADER_FONT_SIZE, 14, map);
getIntReportSettings(dir, ReportParameters.FOOTER_FONT_SIZE, 10, map);
// report geometry settings
// Defines page margin in this order: left, top, right and bottom.
getFloatReportSettings(dir, ReportParameters.LEFT_PAGE_MARGIN, 10f, map);
getFloatReportSettings(dir, ReportParameters.TOP_PAGE_MARGIN, 10f, map);
getFloatReportSettings(dir, ReportParameters.RIGHT_PAGE_MARGIN, 10f, map);
getFloatReportSettings(dir, ReportParameters.BOTTOM_PAGE_MARGIN, 10f, map);
getFloatReportSettings(dir, ReportParameters.CELL_MARGIN, 2f, map);
getFloatReportSettings(dir, ReportParameters.CELL_PADDING, 2f, map);
getFloatReportSettings(dir, ReportParameters.HEADER_MARGIN, 10f, map);
getFloatReportSettings(dir, ReportParameters.FOOTER_MARGIN, 5f, map);
return map;
}
/**
* Get a list with all the tenants.
*
* @param errorSink
* A sink for errors encountered.
*
* @return A list with the registered tenants.
*/
public static List<Tenant> listTenants(List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.listTenants(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 static Tenant listTenant(String tenantName, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.getTenant(tenantName, 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 static Boolean addTenant(String tenantName,
String tenantDescription,
String tenantInfo,
List<TenantDatabaseDescriptor> databases,
List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.addTenant(tenantName, tenantDescription, tenantInfo, databases, 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 static Boolean addDatabaseToTenant(String tenantName,
TenantDatabaseDescriptor db,
List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.addDatabaseToTenant(tenantName, db, 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 static Boolean updateTenant(String tenantName,
String pdbName,
Map<String, String> values,
boolean databaseUpdate,
List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.updateTenant(tenantName, pdbName, values, databaseUpdate, errorSink);
}
/**
* Function for deleting a tenant from the master table.
*
* @param tenantName
* The name of the tenant that is targeted by delete operation.
* @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 static Boolean deleteTenant(String tenantName, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.deleteTenant(tenantName, 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 static Boolean deleteDatabaseFromTenant(String tenantName, String pdbName, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.deleteDatabaseFromTenant(tenantName, pdbName, 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 static Boolean changeTenantStatus(String tenantName, boolean enable, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.changeTenantStatus(tenantName, enable, 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 static Boolean isTenantEnabled(String tenantName, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.isTenantEnabled(tenantName, errorSink);
}
/**
* Get a list with all domains.
*
* @param ldbName
* The logical database name whose domains need to be listed.
* @param errorSink
* A sink for errors encountered.
*
* @return A list with the registered domains.
*/
public static List<String> listDomains(String ldbName, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.listDomains(ldbName, errorSink);
}
/**
* Get a list with all domain types.
*
* @param ldbName
* The logical database name whose domain types need to be listed.
* @param errorSink
* A sink for errors encountered.
*
* @return A list with the registered domain types.
*/
public static List<String> listDomainTypes(String ldbName, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.listDomainTypes(ldbName, errorSink);
}
/**
* Get the details for a specific domain of a logical database.
*
* @param ldbName
* The logical database name whose domain are requested.
* @param domain
* The domain whose details are requested.
* @param errorSink
* A sink for errors encountered.
*
* @return A map with details of a registered domain from {@code _sec-authentication-domain} meta table of
* the specified logical database.
*/
public static HashMap<String, BaseDataType> getDomain(String ldbName,
String domain,
List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.getDomain(ldbName, domain, errorSink);
}
/**
* Get the details for a specific domain of a logical database.
*
* @param ldbName
* The logical database name whose domain are requested.
* @param domainTypeName
* The name of the domain type whose details are requested.
* @param errorSink
* A sink for errors encountered.
*
* @return A map with details of a registered domain type from {@code _sec-authentication-system} meta
* table of the specified logical database.
*/
public static HashMap<String, BaseDataType> getDomainType(String ldbName,
String domainTypeName,
List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.getDomainType(ldbName, domainTypeName, errorSink);
}
/**
* Add a domain to the list.
*
* @param ldbName
* The logical database name where the new domains is about to be created.
* @param properties
* The properties of the new domain, including the name of the domain.
* @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 static Boolean addDomain(String ldbName, JsonNode properties, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.addDomain(ldbName, properties, errorSink);
}
/**
* Add a domain to the list.
*
* @param ldbName
* The logical database name where the new domains is about to be created.
* @param properties
* The properties of the new domain, including the name of the domain.
* @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 static Boolean addDomainType(String ldbName, JsonNode properties, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.addDomainType(ldbName, properties, errorSink);
}
/**
* Update a domain from the master table.
*
* @param ldbName
* The logical database name where the new domains is about to be updated.
* @param domainName
* The name of the domain that is targeted by the update.
* @param properties
* A Map with pairs (column name - new value) that are intended for the update.
* @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 static Boolean updateDomain(String ldbName,
String domainName,
JsonNode properties,
List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.updateDomain(ldbName, domainName, properties, errorSink);
}
/**
* Update a domain from the master table.
*
* @param ldbName
* The logical database name where the new domains is about to be updated.
* @param domainType
* The domain type that is targeted by the update.
* @param properties
* A Map with pairs (column name - new value) that are intended for the update.
* @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 static Boolean updateDomainType(String ldbName,
String domainType,
JsonNode properties,
List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.updateDomainType(ldbName, domainType, properties, errorSink);
}
/**
* Function for deleting a domain from the master table.
*
* @param ldbName
* The logical database name whose domain is about to be deleted.
* @param domainName
* The name of the domain that is 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 static Boolean deleteDomain(String ldbName, String domainName, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.deleteDomain(ldbName, domainName, errorSink);
}
/**
* Function for deleting a domain type from the master table.
*
* @param ldbName
* The logical database name whose domain is about to be deleted.
* @param domainType
* The domain type that is 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 static Boolean deleteDomainType(String ldbName, String domainType, List<String> errorSink)
{
WorkArea wa = workArea.get();
if (!wa.targeted || !wa.sa.adminAccess())
{
return null;
}
return TenantManager.deleteDomainType(ldbName, domainType, errorSink);
}
/**
* Compute and set the current stacktrace for each session in the given list.
*
* @param sessions
* The session list.
*/
private static void setStacktraces(SessionInfo[] sessions)
{
Set<Thread> threads = getAllConversationThreads();
Map<String, StacktraceInfo[]> programTraces = getProgramTraceByThreadNameMap(threads);
Map<String, String[]> javaTraces= getJavaTraceByThreadNameMap(threads);
for (SessionInfo session : sessions)
{
String key = String.format("Conversation [%08X:%s]", session.id, session.name);
session.stacktrace = programTraces.get(key);
session.javaStacktrace = javaTraces.get(key);
}
}
/**
* Given a set of Conversation threads, builds a map from full thread names to their computed
* program trace string (programs, line numbers call chain).
*
* @param threads
* All Conversation threads.
*
* @return See above.
*/
private static Map<String, StacktraceInfo[]> getProgramTraceByThreadNameMap(Set<Thread> threads)
{
Map<String, StacktraceInfo[]> programTraceMap = new HashMap<>();
for (Thread t : threads)
{
String threadName = t.getName();
StackTraceElement[] stackTrace = t.getStackTrace();
StacktraceInfo[] thisProgramTrace = buildProgramTrace(stackTrace);
if (thisProgramTrace != null)
{
programTraceMap.put(threadName, thisProgramTrace);
}
}
return programTraceMap;
}
/**
* Given a set of Conversation threads, builds a map from full thread names to their computed
* Java stacktrace.
*
* @param threads
* All Conversation threads.
*
* @return See above.
*/
private static Map<String, String[]> getJavaTraceByThreadNameMap(Set<Thread> threads)
{
Map<String, String[]> javaTraceMap = new HashMap<>();
for (Thread t : threads)
{
String threadName = t.getName();
StackTraceElement[] stackTrace = t.getStackTrace();
if (stackTrace != null)
{
String[] traces = new String[stackTrace.length + 1];
traces[0] = threadName;
for (int i = 0; i < stackTrace.length; i++)
{
traces[i + 1] = stackTrace[i].toString();
}
javaTraceMap.put(threadName, traces);
}
}
return javaTraceMap;
}
/**
* Given an array of stack trace elements, attempts to build a program trace string in the
* following format:
* <p>
* program (line<fromLine<...<fromLine) << fromProgram...
* </p>
* Where program is the name of the Java class currently running (e.g. foo.SomeProgram),
* line is the line where the thread is currently at, and all subsequent fromLine and
* fromProgram lists are from the Java call stack.
* <p>
* Returns {@code null} if no stack trace is provided. Returns empty string if program
* class name wasn't found.
*
* @param stackTrace
* The stacktrace to process.
*
* @return See above.
*/
private static StacktraceInfo[] buildProgramTrace(StackTraceElement[] stackTrace)
{
if (stackTrace == null)
{
return null;
}
String pkgroot = Utils.getDirectoryNodeString(null, "legacy-system/pkgroot", null) + ".";
int programNameFromClassBeginIndex = pkgroot.length();
ArrayList<StacktraceInfo> traces = new ArrayList<>();
for (StackTraceElement stackTraceElement : stackTrace)
{
if (stackTraceElement.getClassName().startsWith(pkgroot) &&
stackTraceElement.getLineNumber() > 0)
{
String thisProgram = stackTraceElement.getClassName();
thisProgram = thisProgram.substring(programNameFromClassBeginIndex);
int javaSubclassIdx = thisProgram.indexOf("$");
if (javaSubclassIdx > 0)
{
thisProgram = thisProgram.substring(0, javaSubclassIdx);
}
String method = stackTraceElement.getMethodName();
if (method.startsWith("lambda$"))
{
method = method.substring("lambda$".length());
}
javaSubclassIdx = method.indexOf("$");
if (javaSubclassIdx > 0)
{
method = method.substring(0, javaSubclassIdx);
}
if (method.equals("null"))
{
method = null;
}
StacktraceInfo trace = new StacktraceInfo();
trace.program = thisProgram;
trace.method = method;
trace.line = stackTraceElement.getLineNumber();
if (method != null)
{
// go back through all traces and set this method - these were lambda expressions
// defined inside this method
int i = traces.size() - 1;
while (i >= 0)
{
StacktraceInfo lambdaTrace = traces.get(i);
if (lambdaTrace.method != null)
{
break;
}
lambdaTrace.method = method;
i--;
}
}
traces.add(trace);
}
}
return traces.toArray(new StacktraceInfo[0]);
}
/**
* Gets all session conversation threads.
*
* @return See above.
*/
private static Set<Thread> getAllConversationThreads()
{
Set<Thread> threads = new HashSet<Thread>();
Pattern threadNameRegexPattern = Pattern.compile("^Conversation \\[");
Set<Thread> allThreads = Thread.getAllStackTraces().keySet();
for (Thread t : allThreads)
{
if (t != null)
{
Matcher matcher = threadNameRegexPattern.matcher(t.getName());
if (matcher.find())
{
threads.add(t);
}
}
}
return threads;
}
/**
* Gets the report parameter value saved at the directory if it has been set, otherwise returns
* the default value.
*
* @param dirService
* The directory service
* @param parameter
* The given report parameter
* @param defValue
* The default value
* @param map
* The report parameters map that is updated with the retrieved value
*
* @return The report parameter value
*/
private static float getFloatReportSettings(DirectoryService dirService,
ReportParameters parameter,
float defValue,
Map<String, Object> map)
{
float dirValue;
try
{
dirValue = Float.parseFloat(Utils.getDirectoryNodeString(
dirService,
REPORT_SETTINGS.format(new String[]
{
parameter.getParameter()
}),
String.valueOf(defValue),
DirScope.BOTH,
null));
}
catch (NumberFormatException ex)
{
dirValue = defValue;
}
map.put(parameter.getParameter(), dirValue);
return dirValue;
}
/**
* Gets the report parameter value saved at the directory if it has been set, otherwise returns
* the default value.
*
* @param dirService
* The directory service
* @param parameter
* The given report parameter
* @param defValue
* The default value
* @param map
* The report parameters map that is updated with the retrieved value
*
* @return The report parameter value
*/
private static String getStringReportSettings(DirectoryService dirService,
ReportParameters parameter,
String defValue,
Map<String, Object> map)
{
String dirValue = Utils.getDirectoryNodeString(dirService,
REPORT_SETTINGS.format(new String[]
{
parameter.getParameter()
}),
defValue,
DirScope.BOTH,
null);
map.put(parameter.getParameter(), dirValue);
return dirValue;
}
/**
* Gets the report parameter value saved at the directory if it has been set, otherwise returns
* the default value.
*
* @param dirService
* The directory service
* @param parameter
* The given report parameter
* @param defValue
* The default value
* @param map
* The report parameters map that is updated with the retrieved value
*
* @return The report parameter value
*/
private static int getIntReportSettings(DirectoryService dirService,
ReportParameters parameter,
int defValue,
Map<String, Object> map)
{
int dirValue = Utils.getDirectoryNodeInt(dirService,
REPORT_SETTINGS.format(new String[]
{
parameter.getParameter()
}),
defValue,
DirScope.BOTH,
null);
map.put(parameter.getParameter(), dirValue);
return dirValue;
}
/**
* Add the strings to the strings node (assuming that the node contains
* unique string values). It creates node if it doesn't exist (even if
* <code>values</code> is an empty array).
*
* @param ds
* Target directory service.
* @param nodeId
* Path of the target node.
* @param values
* Values to be added.
*
* @return operation result.
*/
private static JarOpErrorCode addNodeStrings(DirectoryService ds,
String nodeId,
String[] values)
{
// check existing values
String[] old = ds.getNodeStrings(nodeId, "values");
Set<String> valuesToAdd = new HashSet<String>(Arrays.asList(values));
if (old != null)
{
for (String s : old)
{
valuesToAdd.remove(s);
}
}
// check that node exist
if (ds.getNodeClass(nodeId) == null)
{
// create node
int idx = nodeId.lastIndexOf("/");
String nodeBasePath = nodeId.substring(0, idx);
boolean rc = ds.openBatch(nodeBasePath);
if (!rc)
{
return JarOpErrorCode.CANNOT_OPEN_DIRECTORY_BRANCH;
}
Attribute[] data = new Attribute[]
{
// subjects
new Attribute(ds.getClassNodeAttribute("strings", "values"),
valuesToAdd.toArray()),
};
JarOpErrorCode res = JarOpErrorCode.SUCCESS;
boolean rcModified = ds.addNode(nodeId, "strings", data);
if (!rcModified)
res = JarOpErrorCode.CANNOT_MODIFY_DIRECTORY_NODE;
// close batch with commit or rollback and no refresh
rc = ds.closeBatch(rcModified, false);
if (!rc && rcModified)
res = JarOpErrorCode.CANNOT_COMMIT_DIRECTORY;
return res;
}
else
{
// modify existing node
if (valuesToAdd.size() == 0)
{
// nothing to add
return JarOpErrorCode.SUCCESS;
}
boolean rc = ds.openBatch(nodeId);
if (!rc)
{
return JarOpErrorCode.CANNOT_OPEN_DIRECTORY_BRANCH;
}
JarOpErrorCode res = JarOpErrorCode.SUCCESS;
boolean rcModified = true;
for (String value : valuesToAdd)
{
rcModified = ds.addNodeString(nodeId, "values", value);
if (!rcModified)
{
res = JarOpErrorCode.CANNOT_MODIFY_DIRECTORY_NODE;
break;
}
}
// close batch with commit or rollback and no refresh
rc = ds.closeBatch(rcModified, false);
if (!rc && rcModified)
res = JarOpErrorCode.CANNOT_COMMIT_DIRECTORY;
return res;
}
}
/**
* Remove the strings from the strings node (assuming that the node
* contains unique string values).
*
* @param ds
* Target directory service.
* @param nodeId
* Path of the target node.
* @param values
* Values to be removed.
*
* @return operation result.
*/
private static JarOpErrorCode removeNodeStrings(DirectoryService ds,
String nodeId,
String[] values)
{
// check that node exist
if (ds.getNodeClass(nodeId) == null)
{
// node does not exist
return JarOpErrorCode.SUCCESS;
}
else
{
// modify existing node
boolean rc = ds.openBatch(nodeId);
if (!rc)
{
return JarOpErrorCode.CANNOT_OPEN_DIRECTORY_BRANCH;
}
// get attribute indexes
List<Integer> indexes = new ArrayList<Integer>();
String[] old = ds.getNodeStrings(nodeId, "values");
if (old != null)
{
for (String value : values)
{
for (int i = 0; i < old.length; i++)
{
if (old[i].equals(value))
{
indexes.add(i);
break;
}
}
}
}
JarOpErrorCode res = JarOpErrorCode.SUCCESS;
boolean rcModified = false;
if (indexes.size() > 0)
{
// the list of indexes in reverse order (for deletion of values
// starting from the bottom of the list)
Collections.sort(indexes, Collections.<Object>reverseOrder());
for (Integer index : indexes)
{
rcModified = ds.deleteNodeAttributeValue(nodeId,
"values",
index);
if (!rcModified)
{
res = JarOpErrorCode.CANNOT_MODIFY_DIRECTORY_NODE;
break;
}
}
}
// close batch with commit or rollback and no refresh
rc = ds.closeBatch(rcModified, false);
if (!rc && rcModified)
res = JarOpErrorCode.CANNOT_COMMIT_DIRECTORY;
return res;
}
}
/**
* Converts {@link ErrorCode} to {@link JarOpErrorCode}.
*
* @param code
* source class loader error code.
*
* @return corresponding jar operation result code.
*/
private static JarOpErrorCode toJarOpErrorCode(ErrorCode code)
{
switch (code)
{
case SUCCESS:
return JarOpErrorCode.SUCCESS;
case NOT_FOUND:
return JarOpErrorCode.NOT_FOUND;
case IN_CLASSPATH:
return JarOpErrorCode.IN_CLASSPATH;
case ALREADY_REGISTERED:
return JarOpErrorCode.ALREADY_REGISTERED;
case INIT_ERROR:
return JarOpErrorCode.INIT_ERROR;
case NOT_REGISTERED:
return JarOpErrorCode.NOT_REGISTERED;
case CLOSE_ERROR:
return JarOpErrorCode.CLOSE_ERROR;
case BAD_LOADER:
return JarOpErrorCode.BAD_LOADER;
}
return null;
}
/**
* Returns the list of appserver processes.
*
* @return Array of appserver definitions.
*/
public static AppserverInfo[] getAppserverList()
{
try
{
return Stream.concat(Arrays.stream(ClassicAppserverDefinition.getAll()),
Arrays.stream(MultiSessionAppserverDefinition.getAll()))
.parallel()
.map(def ->
{
AppserverInfo appserverInfo = new AppserverInfo();
appserverInfo.name = def.getAppserverName();
appserverInfo.autoStartTime = def.getSchedulerMode() == null ?
"Disabled" :
def.getSchedulerMode();
appserverInfo.systemUser = def.getSystemUser();
appserverInfo.fwdProcessName = def.getProcessName();
appserverInfo.propath = def.getPropath();
appserverInfo.enableSsl = def.isEnableSsl();
appserverInfo.privateKeyAlias = def.getAlias();
boolean isNewMultiSession = def instanceof MultiSessionAppserverDefinition;
if (isNewMultiSession)
{
MultiSessionAppserverDefinition msAppserver = (MultiSessionAppserverDefinition) def;
appserverInfo.isMultiSession = true;
appserverInfo.isNewMultiSession = true;
appserverInfo.operatingMode = "Mixed";
appserverInfo.initialServers = 1;
appserverInfo.minServers = 1;
appserverInfo.maxServer = 1;
appserverInfo.requestTimeout = msAppserver.getRequestWaitTimeout();
appserverInfo.autoTrimTimeout = -1;
appserverInfo.activateProc = msAppserver.getSessionActivateProc();
appserverInfo.deactivateProc = msAppserver.getSessionDeactivateProc();
appserverInfo.connectProc = msAppserver.getSessionConnectProc();
appserverInfo.disconnectProc = msAppserver.getSessionDisconnProc();
appserverInfo.startupProc = msAppserver.getAgentStartupProc();
appserverInfo.shutdownProc = msAppserver.getAgentShutdownProc();
appserverInfo.startupProcParams = msAppserver.getAgentStartupProcParam();
}
else
{
ClassicAppserverDefinition classicAppserver =
(ClassicAppserverDefinition) def;
appserverInfo.isMultiSession = classicAppserver.isMultiSession();
appserverInfo.operatingMode = classicAppserver.getOperatingMode().toString();
appserverInfo.initialServers = classicAppserver.getInitialAgents();
appserverInfo.minServers = classicAppserver.getMinAgents();
appserverInfo.maxServer = classicAppserver.getMaxAgents();
appserverInfo.requestTimeout = classicAppserver.getRequestTimeout();
appserverInfo.autoTrimTimeout = classicAppserver.getAutoTrimTimeout();
appserverInfo.activateProc = classicAppserver.getActivate();
appserverInfo.deactivateProc = classicAppserver.getDeactivate();
appserverInfo.connectProc = classicAppserver.getConnect();
appserverInfo.disconnectProc = classicAppserver.getDisconnect();
appserverInfo.startupProc = classicAppserver.getStartup();
appserverInfo.shutdownProc = classicAppserver.getShutdown();
appserverInfo.startupProcParams = classicAppserver.getStartupParameter();
}
appserverInfo.agentInfos = getAppserverAgentInfos(def.getAppserverName());
LegacyLogManagerConfigs legacyLogManagerConfigs =
LegacyLogOps.appserverNameLogConfigPairs.get(appserverInfo.name);
appserverInfo.logFilename = legacyLogManagerConfigs.getLogFile();
try
{
appserverInfo.loggingLevel =
Integer.parseInt(legacyLogManagerConfigs.getLevelString());
}
catch (NumberFormatException ignored)
{
}
appserverInfo.logFileThresholdSize = legacyLogManagerConfigs.getFileSize();
appserverInfo.logFilesMaxNumber = legacyLogManagerConfigs.getNumLogFiles();
return appserverInfo;
})
.toArray(AppserverInfo[]::new);
}
catch (Exception e)
{
LOG.warning("getAppserverList", e);
throw e;
}
}
/**
* Returns the list of appserver agents.
*
* @param appserver
* The appserver name.
*
* @return Array of appserver agent definitions.
*/
public static AppserverInfo.AgentInfo[] getAppserverAgentInfos(String appserver)
{
AppserverInfo.AgentInfo[] agentInfos = null;
if (MultiSessionAppserverDefinition.get(appserver) != null)
{
MultiSessionAppserverManager msaManager = MultiSessionAppserverManager.getInstance();
MultiSessionAgentInfo msAgentInfo = msaManager.getAgentInfo(appserver);
agentInfos = new AppserverInfo.AgentInfo[1];
AppserverInfo.AgentInfo agentInfo = new AppserverInfo.AgentInfo();
agentInfo.appserverName = appserver;
if (msAgentInfo != null)
{
agentInfo.agentStartTime = msAgentInfo.getStartupTime();
agentInfo.agentId = msAgentInfo.getAgentId();
agentInfo.sessionInfos = msAgentInfo.getMsaSessionInfos();
}
agentInfos[0] = agentInfo;
return agentInfos;
}
AgentPool agentPool = AppServerManager.getAgentPoolForAppserver(appserver);
if (agentPool != null)
{
Set<Agent> agents = agentPool.getAll();
agentInfos = new AppserverInfo.AgentInfo[agents.size()];
int index = 0;
for (Agent agent : agents)
{
AppserverInfo.AgentInfo agentInfo = new AppserverInfo.AgentInfo();
agentInfo.appserverName = appserver;
agentInfo.pid = agent.getPid();
agentInfo.agentStartTime = agent.getStartupTime();
agentInfo.sessionStartTime = agent.getRunningProcStartTime();
agentInfo.sessionProcedure = agent.getRunningProc();
agentInfo.agentId = agent.getAgentId();
agentInfo.bound = agent.isBound();
agentInfo.state = agent.getState();
agentInfos[index++] = agentInfo;
}
}
return agentInfos;
}
/**
* Stores global data relating to the state of the current context.
*/
private static class WorkArea
{
/** instance of the directory service */
private DirectoryService ds = null;
/** if true, the session is bound to the directory service */
private boolean bound = false;
/** if true, the target is set */
private boolean targeted = false;
/** if true, the live directory is the target */
private boolean liveTarget = false;
/** variations of the live target: latest(true) or session */
private boolean stayCurrent = false;
/**
* If true, the current context is already initialized via setTargetLive().
* This affects only REST calls.
*/
private boolean initialized = false;
/** instance of SecurityAdmin for this session for live target */
private SecurityAdmin sa = null;
/** list of fresh messages: elements are string arrays */
private LinkedList<String[]> freshMessages = new LinkedList<String[]>();
/** list of saved messages: elements are string arrays */
private LinkedList<String[]> savedMessages = new LinkedList<String[]>();
/** list of locked records */
private Set<RecordInfo> lockedRecords = null;
/** Current refresh counter. */
private Integer refresh = Integer.valueOf(0);
/**
* Constructor.
*/
public WorkArea()
{
}
/**
* Increment refresh counters.
*/
public void resetCounters()
{
setRefreshCounters(0);
}
/**
* Increment refresh counters.
*
* @param rc
* directory commit return code.
*
* @return The boolean value inidicating the success of this operation
*/
public boolean incrementCounters(boolean rc)
{
if (rc)
setRefreshCounters(-1);
return rc;
}
/**
* Set refresh counters.
*
* @param i
* New value for the counters. If this parameter is less than 0,
* then both counters are incremented.
*/
private void setRefreshCounters(int i)
{
synchronized(refreshMutex)
{
if (i < 0)
i = refreshGlobal.intValue() + 1;
refresh = Integer.valueOf(i);
refreshGlobal = Integer.valueOf(i);
}
}
/**
* Check if there is pending refresh.
*
* @return {@code true} if refresh is pending and {@code false}
* is there is no pending refresh.
*/
public boolean isRefreshPending()
{
synchronized (refreshMutex)
{
Integer local = refresh;
if (local == null || local.intValue() == 0)
return false;
// local counter is not null and is above 0
if (refreshGlobal.intValue() == 0)
return false;
// global counter is above 0 too, check permissions
return sa.canRefresh();
}
}
}
}