# 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();

    }