diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml
index 228124cb0..e4d609055 100644
--- a/.github/workflows/Build.yml
+++ b/.github/workflows/Build.yml
@@ -38,7 +38,7 @@ jobs:
- name: Setup Java JDK
uses: actions/setup-java@v1.4.3
with:
- java-version: 1.9
+ java-version: 17
gpg-private-key: ${{ secrets.MAVEN_GPG_BUILDER_PRIVATE_KEY }}
gpg-passphrase: MAVEN_GPG_PASSPHRASE
diff --git a/.github/workflows/cache-redis-tests.yml b/.github/workflows/cache-redis-tests.yml
index fe7eab519..ea10dae99 100644
--- a/.github/workflows/cache-redis-tests.yml
+++ b/.github/workflows/cache-redis-tests.yml
@@ -34,7 +34,7 @@ jobs:
- name: Setup Java JDK
uses: actions/setup-java@v1.4.3
with:
- java-version: 1.9
+ java-version: 17
- name: Setup Maven settings
uses: whelk-io/maven-settings-xml-action@v14
diff --git a/.github/workflows/cloud-storage-tests.yml b/.github/workflows/cloud-storage-tests.yml
index acce56a5e..77b25c8e3 100644
--- a/.github/workflows/cloud-storage-tests.yml
+++ b/.github/workflows/cloud-storage-tests.yml
@@ -34,7 +34,7 @@ jobs:
- name: Setup Java JDK
uses: actions/setup-java@v1.4.3
with:
- java-version: 1.9
+ java-version: 17
- name: Setup Maven settings
uses: whelk-io/maven-settings-xml-action@v14
diff --git a/common/pom.xml b/common/pom.xml
index 97c49d47c..0e24c7544 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -19,6 +19,12 @@
xercesImpl
2.12.2
+
+ org.springframework
+ spring-core
+ 6.0.11
+ provided
+
commons-codec
commons-codec
diff --git a/common/src/main/java/com/genexus/ApplicationContext.java b/common/src/main/java/com/genexus/ApplicationContext.java
index 5248852b8..59717b83a 100644
--- a/common/src/main/java/com/genexus/ApplicationContext.java
+++ b/common/src/main/java/com/genexus/ApplicationContext.java
@@ -1,6 +1,10 @@
package com.genexus;
+import org.springframework.core.io.ClassPathResource;
+
+import java.io.File;
+
public class ApplicationContext
{
private static ApplicationContext instance;
@@ -14,7 +18,8 @@ public class ApplicationContext
private boolean isGXUtility = false;
private boolean isMsgsToUI = true ;
private boolean isServletEngine = false;
- private boolean isEJBEngine = false;
+ private boolean isSpringBootApp = false;
+ private boolean isEJBEngine = false;
private boolean isDeveloperMenu = false;
private static Object syncObject = new Object();
@@ -112,6 +117,24 @@ public boolean isServletEngine()
return isServletEngine;
}
+ public void setSpringBootApp(boolean isSpringBootApp)
+ {
+ this.isSpringBootApp = isSpringBootApp;
+ }
+
+ public boolean isSpringBootApp()
+ {
+ return isSpringBootApp;
+ }
+
+ public boolean checkIfResourceExist(String path)
+ {
+ if (isSpringBootApp())
+ return new ClassPathResource(path).exists();
+ else
+ return new File(path).exists();
+ }
+
public void setEJBEngine(boolean isEJBEngine)
{
this.isEJBEngine = isEJBEngine;
@@ -126,7 +149,7 @@ public boolean isEJBEngine()
private String servletEngineDefaultPath = "";
public void setServletEngineDefaultPath(String servletEngineDefaultPath)
{
- if (servletEngineDefaultPath != null) {
+ if (servletEngineDefaultPath != null && !isSpringBootApp()) {
this.servletEngineDefaultPath = servletEngineDefaultPath.trim();
if(this.servletEngineDefaultPath.endsWith(java.io.File.separator))
{
diff --git a/common/src/main/java/com/genexus/BaseProvider.java b/common/src/main/java/com/genexus/BaseProvider.java
index 5fa4efb50..d3730906b 100644
--- a/common/src/main/java/com/genexus/BaseProvider.java
+++ b/common/src/main/java/com/genexus/BaseProvider.java
@@ -1,12 +1,16 @@
package com.genexus;
import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
+import com.genexus.diagnostics.core.ILogger;
+import com.genexus.diagnostics.core.LogManager;
import com.genexus.xml.XMLReader;
import org.apache.commons.lang.StringUtils;
@@ -16,9 +20,13 @@
import com.genexus.common.interfaces.SpecificImplementation;
import com.genexus.util.GXDirectory;
import com.genexus.util.GXFileCollection;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
public abstract class BaseProvider implements IGXSmartCacheProvider
{
+ private static final ILogger logger = LogManager.getLogger(BaseProvider.class);
+
ConcurrentHashMap> queryTables;
protected Date startupDate;
Object syncLock = new Object();
@@ -45,30 +53,70 @@ private void loadQueryTables()
{
String path = SpecificImplementation.Application.getModelContext().getHttpContext().getDefaultPath();
String configurationDirectoryPath = path + File.separatorChar + "Metadata" + File.separatorChar + "TableAccess";
- ConcurrentHashMap> qTables = new ConcurrentHashMap>();
- GXDirectory configurationDirectory = new GXDirectory(configurationDirectoryPath);
- GXFileCollection files = configurationDirectory.getFiles();
+
+ ConcurrentHashMap> qTables = new ConcurrentHashMap();
+ loadQueryTablesPlatform(configurationDirectoryPath, qTables);
+ startupDate = CommonUtil.now(false,false);
+ queryTables = qTables;
+ }
+ }
+
+ public void loadQueryTablesPlatform(String configurationDirectoryPath, ConcurrentHashMap> qTables) {
+ if (ApplicationContext.getInstance().isSpringBootApp())
+ loadQueryTablesSpringBoot(configurationDirectoryPath, qTables);
+ else
+ loadQueryTablesNone(configurationDirectoryPath, qTables);
+
+ }
+
+ public void loadQueryTablesNone(String configurationDirectoryPath, ConcurrentHashMap> qTables) {
+ GXDirectory configurationDirectory = new GXDirectory(configurationDirectoryPath);
+ GXFileCollection files = configurationDirectory.getFiles();
+ XMLReader reader = new XMLReader();
+ short ok;
+ for(int i=1; i <= files.getItemCount(); i++) {
+ Vector lst = new Vector();
+ lst.add(FORCED_INVALIDATE); // Caso en que se invalido el cache manualmente
+ AbstractGXFile xmlFile = files.item(i);
+ reader.open(xmlFile.getAbsoluteName());
+ ok = reader.readType(1, "Table");
+ while (ok == 1) {
+ lst.add(normalizeKey(reader.getAttributeByName("name")));
+ ok = reader.readType(1, "Table");
+ }
+ reader.close();
+ qTables.put(normalizeKey(xmlFile.getNameNoExt()), lst);
+ }
+ }
+
+ public void loadQueryTablesSpringBoot(String configurationDirectoryPath, ConcurrentHashMap> qTables) {
+ try {
+ Resource[] resources = new PathMatchingResourcePatternResolver().getResources(configurationDirectoryPath + "/*.xml");
XMLReader reader = new XMLReader();
+ reader.setDocEncoding("UTF8");
short ok;
- for(int i=1; i <= files.getItemCount(); i++)
- {
+ String xmlContent;
+ for (int i = 0; i < resources.length; i++) {
Vector lst = new Vector();
- lst.add(FORCED_INVALIDATE); // Caso en que se invalido el cache manualmente
- AbstractGXFile xmlFile = files.item(i);
- reader.open(xmlFile.getAbsoluteName());
+ lst.add(FORCED_INVALIDATE);
+ xmlContent = resources[i].getContentAsString(StandardCharsets.UTF_8);
+ if (!xmlContent.startsWith("<"))
+ xmlContent = xmlContent.substring(1); //Avoid BOM
+ reader.openFromString(xmlContent);
ok = reader.readType(1, "Table");
- while (ok == 1)
- {
- lst.add(normalizeKey((String) reader.getAttributeByName("name")));
+ while (ok == 1) {
+ lst.add(normalizeKey(reader.getAttributeByName("name")));
ok = reader.readType(1, "Table");
}
reader.close();
- qTables.put(normalizeKey((String)xmlFile.getNameNoExt()), lst);
+ qTables.put(normalizeKey(resources[i].getFilename().substring(0, resources[i].getFilename().lastIndexOf("."))), lst);
}
- startupDate = CommonUtil.now(false,false);
- queryTables = qTables;
+ }
+ catch (IOException e) {
+ logger.error("Error reading Table Access metadata", e);
}
}
+
public ConcurrentHashMap> queryTables() {
if (queryTables == null)
{
diff --git a/common/src/main/java/com/genexus/util/ThemeHelper.java b/common/src/main/java/com/genexus/util/ThemeHelper.java
index fc765dd5b..bdd7c272f 100644
--- a/common/src/main/java/com/genexus/util/ThemeHelper.java
+++ b/common/src/main/java/com/genexus/util/ThemeHelper.java
@@ -1,6 +1,7 @@
package com.genexus.util;
import java.io.File;
+import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Collections;
@@ -11,6 +12,7 @@
import com.genexus.CommonUtil;
import com.genexus.common.interfaces.SpecificImplementation;
+import org.springframework.core.io.ClassPathResource;
public class ThemeHelper
{
@@ -34,7 +36,11 @@ private static ThemeData getThemeData(String themeName)
ThemeData themeData;
try
{
- String json = SpecificImplementation.GXutil.readFileToString(themesJsonFile, CommonUtil.normalizeEncodingName("UTF-8"));
+ String json;
+ if (com.genexus.ApplicationContext.getInstance().isSpringBootApp())
+ json = new ClassPathResource("themes/" + themeName + ".json").getContentAsString(StandardCharsets.UTF_8);
+ else
+ json = SpecificImplementation.GXutil.readFileToString(themesJsonFile, CommonUtil.normalizeEncodingName("UTF-8"));
themeData = ThemeData.fromJson(json);
}
catch (Exception ex)
diff --git a/common/src/main/java/com/genexus/xml/XMLReader.java b/common/src/main/java/com/genexus/xml/XMLReader.java
index b9e03a903..78ecaec37 100644
--- a/common/src/main/java/com/genexus/xml/XMLReader.java
+++ b/common/src/main/java/com/genexus/xml/XMLReader.java
@@ -5,6 +5,7 @@
import java.io.*;
import java.util.*;
+import com.genexus.ApplicationContext;
import org.apache.xerces.xni.QName;
import org.apache.xerces.xni.XMLAttributes;
import org.apache.xerces.xni.XMLDocumentHandler;
@@ -30,6 +31,7 @@
import com.genexus.ResourceReader;
import com.genexus.internet.IHttpRequest;
import com.genexus.CommonUtil;
+import org.springframework.core.io.ClassPathResource;
public class XMLReader implements XMLDocumentHandler, XMLErrorHandler, XMLDTDHandler
{
@@ -773,7 +775,19 @@ public void open(String url)
reset();
try
{
- inputSource = new XMLInputSource(null, url, null, new FileInputStream(new File(url)), null);
+ InputStream fileInputStream = null;
+ if (ApplicationContext.getInstance().isSpringBootApp())
+ {
+ ClassPathResource resource = new ClassPathResource(url.replace(".\\", ""));
+ if (resource.exists())
+ fileInputStream = resource.getInputStream();
+ }
+ else
+ {
+ File xmlFile = new File(url);
+ fileInputStream = new FileInputStream(xmlFile);
+ }
+ inputSource = new XMLInputSource(null, url, null, fileInputStream, null);
if (documentEncoding.length() > 0)
inputSource.setEncoding(CommonUtil.normalizeEncodingName(documentEncoding));
parserConfiguration.setInputSource(inputSource);
diff --git a/gxawsserverless/src/test/java/com/gx/serverless/test/test_services_rest.java b/gxawsserverless/src/test/java/com/gx/serverless/test/test_services_rest.java
index c4f40dcbe..daab02fcc 100644
--- a/gxawsserverless/src/test/java/com/gx/serverless/test/test_services_rest.java
+++ b/gxawsserverless/src/test/java/com/gx/serverless/test/test_services_rest.java
@@ -148,8 +148,11 @@ public javax.ws.rs.core.Response Upload( ) throws Exception
super.init( "POST" );
try
{
- builder = new com.genexus.webpanels.GXObjectUploadServices().doInternalRestExecute(restHttpContext);
+ com.genexus.webpanels.GXObjectUploadServices gxObjectUpload = new com.genexus.webpanels.GXObjectUploadServices();
+ String jsonResponse = gxObjectUpload.doInternalRestExecute(restHttpContext);
cleanup();
+ builder = com.genexus.ws.rs.core.Response.statusWrapped(201).entityWrapped(jsonResponse);
+ builder.header("GeneXus-Object-Id", gxObjectUpload.getKeyId());
return (javax.ws.rs.core.Response) builder.build() ;
}
catch ( Exception e )
diff --git a/gxspringboot/pom.xml b/gxspringboot/pom.xml
new file mode 100644
index 000000000..10c756fd6
--- /dev/null
+++ b/gxspringboot/pom.xml
@@ -0,0 +1,39 @@
+
+
+ 4.0.0
+
+
+ com.genexus
+ parent
+ ${revision}${changelist}
+
+
+ gxspringboot
+ GeneXus Spring Boot
+
+
+
+ ${project.groupId}
+ gxclassR
+ ${project.version}
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ 5.0.0
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 3.1.2
+ provided
+
+
+
+
+ gxspringboot
+
+
diff --git a/gxspringboot/src/main/java/com/genexus/springboot/GAMServletsDefinition.java b/gxspringboot/src/main/java/com/genexus/springboot/GAMServletsDefinition.java
new file mode 100644
index 000000000..a79627db9
--- /dev/null
+++ b/gxspringboot/src/main/java/com/genexus/springboot/GAMServletsDefinition.java
@@ -0,0 +1,93 @@
+package com.genexus.springboot;
+import java.lang.reflect.Constructor;
+import jakarta.servlet.Servlet;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+
+public class GAMServletsDefinition {
+ @Bean
+ public ServletRegistrationBean gXOAuthAccessToken() {
+ return registerServletBean("com.genexus.webpanels.GXOAuthAccessToken", "/oauth/access_token");
+ }
+
+ @Bean
+ public ServletRegistrationBean gXOAuthLogout() {
+ return registerServletBean("com.genexus.webpanels.GXOAuthLogout", "/oauth/logout");
+ }
+
+ @Bean
+ public ServletRegistrationBean gXOAuthUserInfo() {
+ return registerServletBean("com.genexus.webpanels.GXOAuthUserInfo", "/oauth/userinfo");
+ }
+
+ @Bean
+ public ServletRegistrationBean gamOAuthSignIn() {
+ return registerServletBean("genexus.security.api.agamextauthinput", "/oauth/gam/signin");
+ }
+
+ @Bean
+ public ServletRegistrationBean gamOAuthCallback() {
+ return registerServletBean("genexus.security.api.agamextauthinput", "/oauth/gam/callback");
+ }
+
+ @Bean
+ public ServletRegistrationBean gamOAuthAccessToken() {
+ return registerServletBean("genexus.security.api.agamoauth20getaccesstoken", "/oauth/gam/access_token");
+ }
+
+ @Bean
+ public ServletRegistrationBean gamAccessTokenV2() {
+ return registerServletBean("genexus.security.api.agamoauth20getaccesstoken_v20", "/oauth/gam/v2.0/access_token");
+ }
+
+ @Bean
+ public ServletRegistrationBean gamOAuthUserInfo() {
+ return registerServletBean("genexus.security.api.agamoauth20getuserinfo", "/oauth/gam/userinfo");
+ }
+
+ @Bean
+ public ServletRegistrationBean oAuthUserInfoV2() {
+ return registerServletBean("genexus.security.api.agamoauth20getuserinfo_v20", "/oauth/gam/v2.0/userinfo");
+ }
+
+ @Bean
+ public ServletRegistrationBean oAuthSSORestV2() {
+ return registerServletBean("genexus.security.api.agamssorestrequesttokenanduserinfo_v20", "/oauth/gam/v2.0/requesttokenanduserinfo");
+ }
+
+ @Bean
+ public ServletRegistrationBean gamOAuthSignOut() {
+ return registerServletBean("genexus.security.api.agamextauthinput", "/oauth/gam/signout");
+ }
+
+ @Bean
+ public ServletRegistrationBean gamOAuthRequestTokenService() {
+ return registerServletBean("genexus.security.api.agamstsauthappgetaccesstoken", "/oauth/RequestTokenService");
+ }
+
+ @Bean
+ public ServletRegistrationBean gamOAuthQueryAccessToken() {
+ return registerServletBean("genexus.security.api.agamstsauthappvalidaccesstoken", "/oauth/QueryAccessToken");
+ }
+
+ /*@Bean
+ public ServletRegistrationBean gamSaml20SignOut() {
+ return registerServletBean("artech.security.saml.servlet.LOGOUT", "/saml/gam/signout");
+ }
+
+ @Bean
+ public ServletRegistrationBean gamSaml20SignIn() {
+ return registerServletBean("artech.security.saml.servlet.SSO", "/saml/gam/signin");
+ }*/
+
+ private ServletRegistrationBean registerServletBean(String className, String path) {
+ try {
+ Constructor> constructor = Class.forName(className).getConstructor();
+ ServletRegistrationBean bean = new ServletRegistrationBean((Servlet) constructor.newInstance(), path);
+ return bean;
+ }
+ catch(Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/gxspringboot/src/main/java/com/genexus/springboot/GXConfig.java b/gxspringboot/src/main/java/com/genexus/springboot/GXConfig.java
new file mode 100644
index 000000000..02fc89f74
--- /dev/null
+++ b/gxspringboot/src/main/java/com/genexus/springboot/GXConfig.java
@@ -0,0 +1,19 @@
+package com.genexus.springboot;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+@EnableWebMvc
+public class GXConfig implements WebMvcConfigurer {
+
+ @Override
+ public void configurePathMatch(PathMatchConfigurer configurer) {
+ AntPathMatcher matcher = new AntPathMatcher();
+ matcher.setCaseSensitive(false);
+ configurer.setPathMatcher(matcher);
+ }
+}
diff --git a/gxspringboot/src/main/java/com/genexus/springboot/GXMultiCall.java b/gxspringboot/src/main/java/com/genexus/springboot/GXMultiCall.java
new file mode 100644
index 000000000..1349c4ae7
--- /dev/null
+++ b/gxspringboot/src/main/java/com/genexus/springboot/GXMultiCall.java
@@ -0,0 +1,42 @@
+package com.genexus.springboot;
+
+import org.springframework.http.*;
+import org.springframework.web.bind.annotation.*;
+
+@RestController("GXMultiCall")
+@RequestMapping(value = {"/gxmulticall", "/rest/gxmulticall"})
+public class GXMultiCall extends GxSpringBootRestService {
+ private final static String METHOD_EXECUTE = "execute";
+
+ @PostMapping(value = "", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
+ public Object gxMultiCall(@RequestBody String jsonStr) throws Exception {
+ super.init( "POST" );
+ ResponseEntity.BodyBuilder builder = null;
+
+ try {
+ com.genexus.webpanels.GXMultiCall.callProcRest(context, jsonStr);
+ }
+ catch(ClassNotFoundException cnf) {
+ builder = ResponseEntity.status(HttpStatus.NOT_FOUND);
+ cleanup();
+ return builder.body(null) ;
+ }
+
+ builder = ResponseEntity.ok();
+ builder.contentType(MediaType.APPLICATION_JSON);
+ cleanup();
+ return builder.body("") ;
+ }
+
+ protected boolean IntegratedSecurityEnabled( ) {
+ return false;
+ }
+
+ protected int IntegratedSecurityLevel( ) {
+ return 0;
+ }
+
+ protected String EncryptURLParameters() {
+ return "NO";
+ }
+}
diff --git a/gxspringboot/src/main/java/com/genexus/springboot/GxSpringBootRestService.java b/gxspringboot/src/main/java/com/genexus/springboot/GxSpringBootRestService.java
new file mode 100644
index 000000000..ecb9a82b9
--- /dev/null
+++ b/gxspringboot/src/main/java/com/genexus/springboot/GxSpringBootRestService.java
@@ -0,0 +1,44 @@
+package com.genexus.springboot;
+
+import com.genexus.GxRestService;
+import json.org.json.JSONException;
+import json.org.json.JSONObject;
+import org.apache.logging.log4j.Logger;
+import org.springframework.http.*;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.http.converter.HttpMessageNotReadableException;
+
+abstract public class GxSpringBootRestService extends GxRestService {
+ private static Logger log = org.apache.logging.log4j.LogManager.getLogger(GxSpringBootRestService.class);
+
+ @ExceptionHandler(Exception.class)
+ protected ResponseEntity