OSResourceManager.java

/*
** Module   : OSResourceManager.java
** Abstract : Management of OS resources, which can be allocated on client or server side.
**
** Copyright (c) 2022-2025, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description----------------------------------------
** 001 CA  20220515 Created initial version.
**      CA 20220918 Allow legacy client sockets to be ran on FWD server instead of FWD client.
** 002 HC  20230427 Implemented server-side file-system resource. File operations performed on
**                  client can be optionally performed directly on server without the expensive
**                  remote call.
** 003 GBB 20230512 Logging methods replaced by CentralLogger/ConversionStatus.
** 004 HC  20230517 Implemented server-side web service calling.
** 005 GBB 20240826 Moving to osresource package. Replacing type string constraints with enum.
**                  Removing the once per JVM init mechanism (moved to concrete classes).
**                  Adding static getters for resources.
** 006 GBB 20240912 Adding support for types mail, launcher and streams. Implicitly enabling related types.
**                  Using enum instead of listing each type, allows for easily extending with new types.
** 007 GBB 20250403 Adding isAllServerSide for when 'all' is specified in server-side-resources.
*/

/*
** 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.util.osresource;

import java.util.*;
import java.util.logging.*;

import com.goldencode.p2j.directory.*;
import com.goldencode.p2j.library.*;
import com.goldencode.p2j.net.SessionManager;
import com.goldencode.p2j.security.*;
import com.goldencode.p2j.util.*;
import com.goldencode.p2j.util.logging.*;

/**
 * Allows configuration of OS resources like {@link memptr} or {@link NativeInvoker library}, to be remoted
 * either from the server or client-side.
 * <p>
 * Server-side resources can be enabled via a directory configuration, per account, group, server or global.
 * The directory node is:
 * <pre>
 * &lt;node class="string" name="server-side-resources"&gt;
 *    &lt;node-attribute name="value" value="memptr,library,streams"&gt;
 * &lt;/node&gt;
 * </pre>
 * <p>
 * The special value <code>all</code> can be used to enable all OS resources at server-side, to not specify
 * them by name.
 */
public abstract class OSResourceManager
{
   /** Logger. */
   protected static final CentralLogger LOG = CentralLogger.get(OSResourceManager.class.getName());

   /** The OS resource manager singleton (client or server side). */
   private static ContextLocal<OSResourceManager> mgr = new ContextLocal<OSResourceManager>()
   {
      protected OSResourceManager initialValue() 
      {
         boolean client = SessionManager.get().isLeaf();

         return client ? new ClientResourceManager() : new ServerResourceManager();
      };
   };
   
   /** Map with flags for each server-side enabled OS resource. */
   private final Map<OsResourceType, Boolean> serverSideResources = new HashMap<>();

   /** All OS resources are enabled server-side. */
   private final boolean IS_ALL_SERVER_SIDE;

   /** The remote objects, either network or local, depending on how the OS resource is configured. */
   final Map<OsResourceType, Object> resources = new HashMap<>();

   /**
    * Initialize this instance with the current context's server-side resource configuration.
    */
   public OSResourceManager()
   {
      Directory dir = DirectoryManager.getInstance();
      String res = dir.getString(Directory.ID_RELATIVE_BOTH, "server-side-resources", "");
      // lowercase the string, to match the case of the resource name constants
      res = res.toLowerCase();

      Collection<OsResourceType> osResources = new HashSet<>();

      IS_ALL_SERVER_SIDE =
         osResources.contains(OsResourceType.ALL) || OsResourceType.ALL.getName().equals(res);
      
      Arrays.stream(OsResourceType.values())
            .filter(type -> type.getName() != null)
            .forEach(type -> serverSideResources.put(type, IS_ALL_SERVER_SIDE));
      
      if (IS_ALL_SERVER_SIDE)
      {
         return;
      }

      osResources.forEach(type -> serverSideResources.put(type, Boolean.TRUE));
      
      for (String resName : res.split(","))
      {
         resName = resName.trim();
         if (resName.isEmpty())
         {
            continue;
         }
         OsResourceType resType = OsResourceType.find(resName);
         if (resType == null)
         {
            continue;
         }

         if (!serverSideResources.containsKey(resType))
         {
            if (LOG.isLoggable(Level.WARNING))
            {
               LOG.log(Level.WARNING, "Failed configuring server-side resource: '" + resName +
                  "' is not a valid name.");
            }

            continue;
         }

         serverSideResources.put(resType, Boolean.TRUE);
      }
      
      if (isServerSide(OsResourceType.ENVIRONMENTS) && !isServerSide(OsResourceType.FILESYSTEM))
      {
         LOG.warning("!!! Implicitly enabling server-side filesystem !!! Server-side 'environments' enabled.");
         serverSideResources.put(OsResourceType.FILESYSTEM, Boolean.TRUE);
      }
      
      if (isServerSide(OsResourceType.FILESYSTEM) && !isServerSide(OsResourceType.STREAMS))
      {
         LOG.warning("!!! Implicitly enabling server-side streams !!! Server-side 'filesystem' enabled.");
         serverSideResources.put(OsResourceType.STREAMS, Boolean.TRUE);
      }
      else if (!isServerSide(OsResourceType.FILESYSTEM) && isServerSide(OsResourceType.STREAMS))
      {
         LOG.warning("!!! Implicitly enabling server-side filesystem !!! Server-side 'streams' enabled.");
         serverSideResources.put(OsResourceType.FILESYSTEM, Boolean.TRUE);
      }
   }
   
   /**
    * Get the OS resource manager instance.
    * 
    * @return   See above.
    */
   public static OSResourceManager getInstance()
   {
      return mgr.get();
   }

   /**
    * Initialize the OS {@link OsResourceType#MEMPTR} resource.
    *
    * @return   The {@link LowLevelBuffer} network or local object.
    */
   public static LowLevelBuffer getMemptr()
   {
      return mgr.get().initializeMemptr();
   }

   /**
    * Initialize the OS {@link OsResourceType#LIBRARY} resource.
    *
    * @return   The {@link NativeAPICaller} network or local object.
    */
   public static NativeAPICaller getLibrary()
   {
      return mgr.get().initializeLibrary();
   }

   /**
    * Initialize the OS {@link OsResourceType#SOCKET} resource.
    *
    * @return   The {@link LowLevelSocket} network or local object.
    */
   public static LowLevelSocket getSocket()
   {
      return mgr.get().initializeSocket();
   }

   /**
    * Initialize the OS {@link OsResourceType#FILESYSTEM} resource.
    *
    * @return   The {@link FileSystem} network or local object.
    */
   public static FileSystem getFileSystem()
   {
      return mgr.get().initializeFileSystem();
   }

   /**
    * Initialize the OS {@link OsResourceType#WEBSERVICE} resource.
    *
    * @return   The {@link WebService} network or local object.
    */
   public static WebService getWebService()
   {
      return mgr.get().initializeWebService();
   }

   /**
    * Get the OS {@link OsResourceType#ENVIRONMENTS} resource.
    *
    * @return   The {@link EnvironmentAccessor} network or local object.
    */
   public static EnvironmentAccessor getEnvironments()
   {
      return mgr.get().initializeEnvironments();
   }
   
   /**
    * Get the OS {@link OsResourceType#MAIL} resource.
    *
    * @return   The {@link RemoteEmailSender} network or local object.
    */
   public static RemoteEmailSender getEmailSender()
   {
      return mgr.get().initializeEmailSender();
   }

   /**
    * Get the OS {@link OsResourceType#LAUNCHER} resource.
    *
    * @return   The {@link Launcher} network or local object.
    */
   public static Launcher getProcessLauncher()
   {
      return mgr.get().initializeProcessLauncher();
   }

   /**
    * Get the OS {@link OsResourceType#STREAMS} resource.
    *
    * @return   The {@link StreamDaemon} network or local object.
    */
   public static StreamHelper getStreamHelper()
   {
      return mgr.get().initializeStreamHelper();
   }

   /**
    * Returns <code>true</code> if all OS resources are enabled server-side.
    *
    * @return   See above.
    */
   public boolean isAllServerSide()
   {
      return IS_ALL_SERVER_SIDE;
   }

   /**
    * Check if the resource type is enabled on the server side.
    *
    * @return   See above.
    */
   public boolean isServerSide(OsResourceType type)
   {
      return serverSideResources.get(type);
   }

   /**
    * Initialize the OS {@link OsResourceType#MEMPTR} resource.
    * 
    * @return   The {@link LowLevelBuffer} network or local object.
    */
   abstract LowLevelBuffer initializeMemptr();

   /**
    * Initialize the OS {@link OsResourceType#LIBRARY} resource.
    * 
    * @return   The {@link NativeAPICaller} network or local object.
    */
   abstract NativeAPICaller initializeLibrary();

   /**
    * Initialize the OS {@link OsResourceType#SOCKET} resource.
    * 
    * @return   The {@link LowLevelSocket} network or local object.
    */
   abstract LowLevelSocket initializeSocket();

   /**
    * Initialize the OS {@link OsResourceType#FILESYSTEM} resource.
    *
    * @return   The {@link FileSystem} network or local object.
    */
   abstract FileSystem initializeFileSystem();

   /**
    * Initialize the OS {@link OsResourceType#WEBSERVICE} resource.
    *
    * @return   The {@link WebService} network or local object.
    */
   abstract WebService initializeWebService();

   /**
    * Initialize the OS {@link OsResourceType#ENVIRONMENTS} resource.
    *
    * @return   The {@link EnvironmentAccessor} network or local object.
    */
   abstract EnvironmentAccessor initializeEnvironments();

   /**
    * Initialize the OS {@link OsResourceType#MAIL} resource.
    *
    * @return   The {@link RemoteEmailSender} network or local object.
    */
   abstract RemoteEmailSender initializeEmailSender();

   /**
    * Initialize the OS {@link OsResourceType#LAUNCHER} resource.
    *
    * @return   The {@link Launcher} network or local object.
    */
   abstract Launcher initializeProcessLauncher();
   
   /**
    * Initialize the OS {@link OsResourceType#STREAMS} resource.
    *
    * @return   The {@link StreamDaemon} network or local object.
    */
   abstract StreamHelper initializeStreamHelper();

}