= !MapGuide RFC 185 - Use vanilla SWIG to generate API bindings = This page contains a change request (RFC) for the !MapGuide Open Source project. More !MapGuide RFCs can be found on the [wiki:MapGuideRfcs RFCs] page. == Status == ||RFC Template Version||(1.0)|| ||Submission Date||7 Jul 2022|| ||Last Modified||16 Sep 2022|| ||Author||Jackie Ng|| ||RFC Status||adopted|| ||Implementation Status||completed|| ||Proposed Milestone||4.0|| ||Assigned PSC guide(s)||(when determined)|| ||'''Voting History'''||(vote date)|| ||+1||Jackie,Gordon,Crispin|| ||+0|||| ||-0|||| ||-1|||| ||no vote|| || [[TOC]] == Overview == This RFC proposes to adopt using the latest un-modified version of SWIG to generate our API bindings for .net, Java and PHP. == Motivation == For the longest time now, MapGuide has been shackled to PHP 5.6 which has been out of support for a long time. It is not a good look for us security-wise to ship new releases of MapGuide that bundle a long out-of-date version of PHP. The reason we haven't upgraded our bundled copy of PHP is because PHP has changed the internal zend extension APIs for PHP 7+ in such a breaking manner that it is not possible to generate API bindings with our internal (heavily-modified) copy of SWIG that will work with the currently supported version of PHP (8.1.x) The current version of SWIG (4.0) supports generating bindings for PHP 7 and the current git master supports generating bindings for PHP 7/8. To finally be able to bundle the latest version of PHP with our MapGuide releases, we need to be able to generate functional MapGuide API bindings for PHP using the official version of SWIG instead of our ancient and heavily-modified internal copy in `Oem/SwigEx`. In the process, we might as well also be using official SWIG to try and generate functional MapGuide API bindings for .net and Java as well. == Proposed Solution == We will use the current git master of swig at https://github.com/swig/swig to generate MapGuide API bindings for .net, Java and PHP. The rest of this RFC will refer to this version of SWIG as "vanilla SWIG" The PHP binding targets PHP 8.1, and we will be bundling PHP 8.1 with the MapGuide install packages. A new top-level `Bindings` subdirectory and solution will house the various API binding projects and supporting tools/libraries. The following projects/directories will be removed as a result of being obsoleted by this new bindings: * `Oem/SwigEx` (SWIG will now be acquired externally through official channels and is assumed to exist before building) * `Oem/SQLite` (Proof-of-concept SWIG binding to SQLite and legacy Web API test administration tools) * `BuildTools/CC.Net` (unused CI configuration) * `BuildTools/WebTools/IMake` (relocated to new top-level `Bindings` directory) * `Web/src/JavaApi` (legacy "crufty" Java binding) * `Web/src/JavaApiEx` (legacy "non-crufty" Java binding) * `Web/src/PhpApi` (legacy PHP 5.6 binding) * `Web/src/PhpMapGuideApiEnvConfig` (support library for the legacy PHP 5.6 binding) Vanilla SWIG is now a build requirement. On Windows, pre-compiled binaries of the expected version of SWIG will be provided for convenience. On Linux, the build scripts will acquire and build SWIG source from the github repo if the expected version of SWIG is not present. Fusion and all existing web tier applications (eg. AJAX Viewer, Site Administrator) will be migrated across to work against these new API bindings. The existing web tier test suite will also be migrated across to work against these new API bindings. These changes have been implemented in the [https://trac.osgeo.org/mapguide/browser/sandbox/jng/vanilla_swig vanilla_swig sandbox] for MapGuide. A compatible version of Fusion that works against the PHP 8 binding has been implemented in the [https://trac.osgeo.org/fusion/browser/sandbox/php8 php8 sandbox branch] in the Fusion repo. Upon the adoption of this RFC, these changes will be merged into trunk in their respective repos. The implementation of this RFC does not include updates to the Windows installer and does not include the tarball for PHP 8.1 (to avoid svn merge problems). Such items will be addressed after RFC adoption and trunk merges. == Impact summary of API changes == To support generating functional API bindings for .net/Java/PHP with vanilla SWIG, we had to make a few changes to our C++ API surface to facilitate/accommodate the code generation needs of vanilla SWIG. Such changes are detailed below. === General (all languages) === * The exception hierarchy has been flattened to now be just `MgException`. All existing sub-classes of `MgException` have been removed. A new `GetExceptionCode()` method is available to fetch the appropriate error classification. The available classifications are simply the names of all the removed sub-classes and whose names are now contained in a new `MgExceptionCodes` string constant class. * This was done to avoid having to deal with `MgException` polymorphism in a consistent manner across our 3 SWIG language targets. * If you used to catch specific `MgException` subclass types, refer to the example below for how to migrate your exception handling code for your target language Before (PHP): {{{#!php try { ... } catch (MgUnauthorizedAccessException $e) { ... } }}} After (PHP): {{{#!php try { ... } catch (MgException $e) { if ($e->GetExceptionCode() == MgExceptionCodes::MgUnauthorizedAccessException) { ... } } }}} Before (Java): {{{#!java try { ... } catch (MgUnauthorizedAccessException e) { ... } }}} After (Java): {{{#!java try { ... } catch (MgException e) { if (e.getExceptionCode() == MgExceptionCodes.MgUnauthorizedAccessException) { ... } } }}} Before (C#/.net): {{{#!csharp try { ... } catch (MgUnauthorizedAccessException e) { ... } }}} After (C#/.net): {{{#!csharp try { ... } catch (MgException e) { if (e.GetExceptionCode() == MgExceptionCodes.MgUnauthorizedAccessException) { ... } } }}} After (C#/.net alternative method): {{{#!csharp try { ... } catch (MgException e) when (e.GetExceptionCode() == MgExceptionCodes.MgUnauthorizedAccessException) { ... } }}} * The constant members of the `MgResourcePermission` class are prefixed with `Permission`. For example `MgResourcePermission::ReadOnly` is now `MgResourcePermission::PermissionReadOnly` * This was done to prevent reserved name collisions in PHP where `readonly` is now a reserved keyword and cannot be used as a static member name. === PHP specific notes === * `constants.php` is no longer required! All constants of the MapGuide API are now baked into the PHP zend extension, eliminating the need for this file. When migrating, remove all `include`/`require` references to this file. * The `MgPlotSpecification` constructor now requires you to specify all 4 margin parameters . If you don't need a margin, or need to set it later on (with the `SetMargins()` method), you can pass `0.0` for all 4 arguments. * PHP 8 is more strict about data types and attempting to do things like assigning `int` to `double` and vice-versa and will generally error out in such cases. * This strictness will generally arise in calls to APIs like `MgRenderingService`::`RenderMap` where there is a mixture of `int` and `double` parameters in the method signature. * To ensure the correct data types are passed in, wrap expected `int` parameters with `intval()` and expected `double` parameters with `doubleval()` In addition to the above changes, several public APIs are renamed specifically in the PHP binding to avoid incompatible signatures due to how overloaded/inherited methods are handled in PHP 8. `MgCoordinateSystemMeasure` The following method signatures in `MgCoordinateSystemMeasure` are renamed with a `Simple` suffix added. * `double GetDistance(double x1, double y1, double x2, double y2)` (now called `GetDistanceSimple`) * `double GetAzimuth(double lon1, double lat1, double lon2, double lat2)` (now called `GetAzimuthSimple`) * `MgCoordinate GetCoordinate(double lon, double lat, double azimuth, double distance)` (now called `GetCoordinateSimple`) `MgMap` The new overload of `Create` introduced in [wiki:MapGuideRfc157 MapGuide RFC 157] has been renamed to `CreateStateless` === Java specific notes === The new Java binding carries all the enhancements from the previous `MapGuideJavaApiEx` binding, whose changes will be repeated here: All method names now adopt `camelCase` instead of `PascalCase` to match Java naming conventions. Proxies for various collection classes implement the `java.util.Collection` interface allowing for greater interoperability with existing Java code that works with such interfaces: These classes include: * `MgClassDefinitionCollection` (as `Collection`) * `MgFeatureSchemaCollection` (as `Collection`) * `MgPropertyDefinitionCollection` (as `Collection`) * `MgPropertyCollection` (as `Collection`) * `MgStringCollection` (as `Collection`) * `MgLayerCollection` (as `Collection`) * `MgLayerGroupCollection` (as `Collection`) The following collection classes do not implement the `java.util.Collection` interface due to disparity in interface and implementation * `MgIntCollection` (`int` is not an object in Java) * `MgBatchPropertyCollection` (can't implement as `Collection` due to a different `contains()` API) The following proxy classes implement `java.util.Iterable` * `MgReadOnlyLayerCollection` Classes that implement `java.util.Collection` or `java.lang.Iterable` can be iterated with java's enhanced for loop like so: {{{#!java MgClassDefinitionCollection classes = ... for (MgClassDefinition clsDef : classes) { ... } }}} As a result of adopting `camelCase` naming conventions along with some classes implementing special Java interfaces, various methods in the MapGuide API have been renamed specifically for Java: * The converted `MgException.getStackTrace()` conflicts with `java.lang.Throwable.getStackTrace()` * The converted `MgPropertyDefinition.delete()` conflicts with the SWIG-generated `delete()` method for finalization * The converted `MgClassDefinition.delete()` conflicts with the SWIG-generated `delete()` method for finalization * The converted `MgFeatureSchema.delete()` conflicts with the SWIG-generated `delete()` method for finalization * The converted `add()` method of any MapGuide collection class conflicts with the `add()` method as specified in the `java.util.Collection` interface (MapGuide's `add()` returns `void`, `java.util.Collection`'s `add()` returns `boolean`) The conflicting methods are renamed in the wrapper as so: * `MgException::getStackTrace()` will become `MgException.getExceptionStackTrace()` * `MgPropertyDefinition.delete()` will become `MgPropertyDefinition.markAsDeleted()` * `MgClassDefinition.delete()` will become `MgClassDefinition.markAsDeleted()` * `MgFeatureSchema.delete()` will become `MgFeatureSchema.markAsDeleted()` * The `add()` method of any MapGuide collection class implementing `java.util.Collection` will become `addItem()` `MapGuideApiEx.jar` is no longer generated and bundled. Java 8 or higher is required to create/run Java MapGuide applications. === .net specific notes === This wrapper is now provided as a set of NuGet packages targeting `netstandard2.0` * `OSGeo.MapGuide.Foundation` * `OSGeo.MapGuide.Geometry` * `OSGeo.MapGuide.PlatformBase` * `OSGeo.MapGuide.MapGuideCommon` * `OSGeo.MapGuide.Web` Being a `netstandard2.0` package, you can use this in: * Your existing (legacy) ASP.net WebForms/MVC application targeting (legacy) .net Framework 4.8 * A greenfield ASP.net application targeting .net Core/.net 5+ Refer to deployment impact summary below for remarks around .net deployment. As part of this RFC, we will offer experimental support to target [.net Core/.net 5+] on Linux. This is achieved by adding support in our Linux build system for building the "common libs subset". This subset consists of: * Internal Thirdparty Libraries * ACE * GEOS * JsonCpp * Xerces-C * CS-Map * MapGuide Common Libraries * Foundation * Geometry * PlatformBase * MapGuideCommon * Web Tier common libraries * HttpHandler * WebApp * WebSupport Along with the .net SWIG interop glue libraries. Once this subset is built, all the `.so` library files will be copied over to a Windows build environment, where such files will be bundled into the resulting NuGet packages we create for the MapGuide API for .net For maximum portability on Linux, the "common libs subset" should be built against internal Thirdparty libraries on a Linux distro with the oldest supported version of glibc. On adoption of this RFC, is is expected that this subset will be built in a CentOS 7 environment. For a full end-to-end build of MapGuide for both Windows/Linux, the recommended build order will be: 1. Common libs subset on a CentOS 7 environment 2. Copy/transfer the compiled `.so` libraries from the CentOS 7 environment to the Windows environment 3. Build MapGuide on Windows 4. Build MapGuide on supported Linux distros Despite now being a `netstandard2.0` library, Mac OSX is not (and will not be) a supported runtime environment == Impact summary for deployments (Windows) == === PHP === The PHP binding is built for the Non-Thread-Safe (NTS) profile. This means that we no longer configure PHP support in Apache via `mod_php`. Instead PHP support in Apache is now configured via FastCGI through `mod_fcgid`. This allows us to bundle/ship one set of PHP NTS binaries that can correctly work in both IIS and Apache configurations on Windows. Previously, we did the incorrect thing of bundling the Thread-Safe (TS) PHP binaries and used it for both Apache (supported) and IIS (un-supported). Barring the required migrations in your PHP codebase, the deployment process/experience should remain the same. === Java === The bundled version of Tomcat is 9.0.37. Refer to the Tomcat documentation for any migration/configuration/breaking changes from previous Tomcat versions === .net === The MapGuide web tier installation will still assume the default of IIS and .net Framework 4.8 for the .net configuration. If you are intending to deploy an ASP.net core MapGuide application to your MapGuide Web Tier, refer to the [https://docs.microsoft.com/en-us/aspnet/core/tutorials/publish-to-iis?view=aspnetcore-6.0&tabs=visual-studio Microsoft-recommended deployment process] for deploying to IIS == Impact summary for deployments (Linux) == === PHP === Bundled PHP support does not use FastCGI like Windows. It still uses `mod_php` === Java === The bundled version of Tomcat is 9.0.37. Refer to the Tomcat documentation for any migration/configuration/breaking changes from previous Tomcat versions === .net === If you are intending to take advantage of the experimental Linux support of the .net binding, you should follow the [https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/linux-apache?view=aspnetcore-6.0 Microsoft-recommended deployment process] for deploying an ASP.net core application to Linux with Apache as the web server, and make the necessary adjustments so that any part of the documentation that refers to Apache httpd refers to the copy bundled with the MapGuide installation. == Test Plan == Ensure the migrated web tier test suites pass against the new bindings. Ensure AJAX viewer for all 3 languages are functional with these new bindings. Ensure all code samples work under these new bindings. Ensure Fusion is functional with these new bindings. Ensure Site Administrator is functional. Ensure Schema Report is functional. == Funding / Resources == Community