diff --git a/README.md b/README.md index a6188c173..42ff396be 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -| Branch | Status -|---|--- -|master |[![](https://github.com/genexuslabs/JavaClasses/workflows/Build/badge.svg)](https://github.com/genexuslabs/JavaClasses/actions?query=workflow%3ABuild+branch%3Amaster) -|beta |[![](https://github.com/genexuslabs/JavaClasses/workflows/Build/badge.svg?branch=beta)](https://github.com/genexuslabs/JavaClasses/actions?query=workflow%3ABuild+branch%3Abeta) - # GeneXus Standard Classes for Java - These are the source of the GeneXus Standard Classes for Java, valid since GeneXus 16 Upgrade 4. +## Repo Status +| Branch | Build | Security +|---|---|--- +|master |[![](https://github.com/genexuslabs/JavaClasses/workflows/Build/badge.svg)](https://github.com/genexuslabs/JavaClasses/actions?query=workflow%3ABuild+branch%3Amaster)|[![CodeQL](https://github.com/genexuslabs/JavaClasses/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/genexuslabs/JavaClasses/actions/workflows/codeql-analysis.yml) +|beta |[![](https://github.com/genexuslabs/JavaClasses/workflows/Build/badge.svg?branch=beta)](https://github.com/genexuslabs/JavaClasses/actions?query=workflow%3ABuild+branch%3Abeta)|[![CodeQL](https://github.com/genexuslabs/JavaClasses/actions/workflows/codeql-analysis.yml/badge.svg?branch=beta)](https://github.com/genexuslabs/JavaClasses/actions/workflows/codeql-analysis.yml) + ## Modules | Name | Description diff --git a/common/src/main/java/com/genexus/CommonUtil.java b/common/src/main/java/com/genexus/CommonUtil.java index f0383fd3a..c3bec6a30 100644 --- a/common/src/main/java/com/genexus/CommonUtil.java +++ b/common/src/main/java/com/genexus/CommonUtil.java @@ -179,7 +179,7 @@ public Object initialValue() public static String removeAllQuotes(String fileName) { - StringBuffer out = new StringBuffer(); + StringBuilder out = new StringBuilder(); int len = fileName.length(); for (int i = 0; i < len; i++) if (fileName.charAt(i) != '"') @@ -1023,7 +1023,7 @@ public static String strReplace(String s, String subString, String replacement) return s; int index, start, subLength; - StringBuffer buf = new StringBuffer(); + StringBuilder buf = new StringBuilder(); subLength = subString.length(); for (start = 0, index = s.indexOf(subString, start); index >= 0; start = index + subLength, index = s.indexOf(subString, start)) @@ -1080,7 +1080,7 @@ public static String replicate (char character, int size) if (size <= 0) return ""; - StringBuffer ret = new StringBuffer(size); + StringBuilder ret = new StringBuilder(size); for (int i = 0; i < size; i++) { @@ -1095,7 +1095,7 @@ public static String replicate (String character, int size, int a) if (size <= 0) return ""; - StringBuffer ret = new StringBuffer(size); + StringBuilder ret = new StringBuilder(size); for (int i = 0; i < size; i++) { @@ -1218,7 +1218,7 @@ public static long lval(String text) } catch (Exception e) { - StringBuffer out = new StringBuffer(); + StringBuilder out = new StringBuilder(); boolean first = true; int len = text.length(); @@ -1282,8 +1282,8 @@ public static BigDecimal decimalVal(String text, String sDSep) return BigDecimal.ZERO; } - private static StringBuffer extractNumericStringValue(String text, String sDSep) { - StringBuffer out = new StringBuffer(); + private static StringBuilder extractNumericStringValue(String text, String sDSep) { + StringBuilder out = new StringBuilder(); char dSep = (sDSep.length() > 0) ? sDSep.charAt(0) : '.'; boolean point = false; @@ -1861,7 +1861,7 @@ protected static boolean in(String text , char c) public static String getTimeFormat(String time) { - StringBuffer ret = new StringBuffer(time); + StringBuilder ret = new StringBuilder(time); char hora; boolean app = false; char append = ' '; @@ -2337,7 +2337,7 @@ public static boolean contains(Object []arr, Object item) public static String format(String value, String v1, String v2, String v3, String v4, String v5, String v6, String v7, String v8, String v9) { String[] vs = {v1, v2, v3, v4, v5, v6, v7, v8, v9}; - StringBuffer stringBuilder = new StringBuffer(); + StringBuilder stringBuilder = new StringBuilder(); int valueLength = value.length(); if (value != null && !value.equals("")) { @@ -2560,7 +2560,7 @@ public static String strUnexponentString(String num) int point = num.indexOf('.'); int scale = num.length() - (point == -1 ? num.length () : point + 1) - scaleAdj; - StringBuffer val = new StringBuffer(point == -1 ? num : num.substring(0, point) + num.substring (point + 1)); + StringBuilder val = new StringBuilder(point == -1 ? num : num.substring(0, point) + num.substring (point + 1)); // correct for negative scale as per BigDecimal javadoc for(; scale<0; scale++) @@ -2907,7 +2907,7 @@ public static String strori(double val, int digits, int decimals) if (decimals < 0) decimals = 0; if (digits < 0) digits = 0; - StringBuffer b = new StringBuffer(); + StringBuilder b = new StringBuilder(); boolean hasSign = (val < 0); if (hasSign) @@ -3116,7 +3116,7 @@ public static String addLastPathSeparator(String dir) { } public static String quoteString(String in, boolean entities8bit, boolean encodeQuotes) { - StringBuffer out = new StringBuffer(); + StringBuilder out = new StringBuilder(); for (int i = 0; i < in.length(); i++) { char currentChar = in.charAt(i); @@ -3383,7 +3383,7 @@ public final static String hashtable2query(Hashtable hashtable) if (hashtable == null) return null; - StringBuffer qbuf = new StringBuffer(); + StringBuilder qbuf = new StringBuilder(); for (Enumeration en = hashtable.keys(); en.hasMoreElements();) { Object key = en.nextElement(); qbuf.append((key == null ? null : URLEncode((String)key,"UTF-8")) + "=" + diff --git a/common/src/main/java/com/genexus/GXBaseList.java b/common/src/main/java/com/genexus/GXBaseList.java index 775b84ca4..02ec6de35 100644 --- a/common/src/main/java/com/genexus/GXBaseList.java +++ b/common/src/main/java/com/genexus/GXBaseList.java @@ -5,49 +5,39 @@ import com.genexus.internet.IGxJSONAble; import com.genexus.internet.IGxJSONSerializable; -public abstract class GXBaseList extends Vector implements Serializable, IGxJSONAble, IGxJSONSerializable, IGXAssigned -{ +public abstract class GXBaseList extends Vector implements Serializable, IGxJSONAble, IGxJSONSerializable, IGXAssigned { private boolean IsAssigned; - public GXBaseList() - { + public GXBaseList() { IsAssigned = true; } - public boolean getIsAssigned() - { + public boolean getIsAssigned() { return this.IsAssigned; } - public void setIsAssigned(boolean bAssigned) - { + public void setIsAssigned(boolean bAssigned) { this.IsAssigned = bAssigned; } - public void removeAllItems() - { + public void removeAllItems() { super.clear(); IsAssigned = true; } - public byte removeItem(int index) - { + public byte removeItem(int index) { T item = null; - if(index > 0 && index <= size()) - { + if(index > 0 && index <= size()) { item = super.remove((int)index - 1);//Vector.remove(int) IsAssigned = true; return (byte)1; } return (byte)0; } - public byte removeElement(double index) - { - if(index > 0 && index <= size()) - { + public byte removeElement(double index) { + if(index > 0 && index <= size()) { super.remove((int)index - 1);//Vector.remove(int) IsAssigned = true; return (byte)1; } - else - { + else { return (byte)0; } } @@ -58,24 +48,62 @@ public void addObject(Object obj){ IsAssigned = true; } @SuppressWarnings("unchecked") - public void add(Object item, int index) - { - if(index < 1 || index > size()) - { + public void add(Object item, int index) { + if(index < 1 || index > size()) { add((T)item); //this.add, GXBCLevelCollection.add for example } - else - { + else { super.add(index - 1, (T)item); //Vector insert element IsAssigned = true; } } @SuppressWarnings("unchecked") - public void addBase( Object item) - { + public void addBase( Object item) { super.add((T)item); IsAssigned = true; } + + public boolean addRange( GXBaseList baseList, Number index) { + if (baseList.size() == 0) + return true; + + boolean result; + if (index == null) { + result = addAll(baseList); + } + else { + int nindex = index.intValue(); + if(nindex != 1 && (nindex < 0 || nindex > size() +1)) + return false; + if (nindex == 0) + nindex = 1; + result = addAll(nindex -1, baseList); + } + IsAssigned = true; + return result; + } + + public boolean removeRange( int index, Number count) { + int colSize = size(); + if(index <= 0 || index > colSize || (count != null && index + count.intValue() > colSize)) + return false; + int toIndex; + if (count == null) + toIndex = colSize; + else + toIndex = count.intValue(); + super.removeRange(index -1, toIndex); + IsAssigned = true; + return true; + } + + public boolean setElement( int index, T element) { + if(index < 1 || index > size()) + return false; + super.set(index -1, element); + IsAssigned = true; + return true; + } } diff --git a/common/src/main/java/com/genexus/GXSimpleCollection.java b/common/src/main/java/com/genexus/GXSimpleCollection.java index 8b3b661b3..9a6b34883 100644 --- a/common/src/main/java/com/genexus/GXSimpleCollection.java +++ b/common/src/main/java/com/genexus/GXSimpleCollection.java @@ -1352,5 +1352,17 @@ public boolean update(){ return false; } + public boolean addRange( GXSimpleCollection baseList, Number index) { + return super.addRange(baseList, index); + } + + public boolean removeRange( int index, Number count) { + return super.removeRange(index, count); + } + + public boolean setElement( int index, T element) { + return super.setElement(index, element); + } + } diff --git a/common/src/main/java/com/genexus/util/DelimitedFilesSafe.java b/common/src/main/java/com/genexus/util/DelimitedFilesSafe.java index aee198177..770c01177 100644 --- a/common/src/main/java/com/genexus/util/DelimitedFilesSafe.java +++ b/common/src/main/java/com/genexus/util/DelimitedFilesSafe.java @@ -498,6 +498,7 @@ public byte dfrgdate(java.util.Date[] date, String fmt, String sep) String retstr; Date retdate = CommonUtil.nullDate(); int year = 0, month = 0, day = 0; + boolean getnexttoken = true; if (dfropen_in_use) { @@ -506,6 +507,8 @@ public byte dfrgdate(java.util.Date[] date, String fmt, String sep) String stringDelimitedField = actline.nextToken(fdel); if(fdel.equals(stringDelimitedField) || stringDelimitedField.equals("")) { // Si el token debe estar vac�o... + if (fdel.equals(stringDelimitedField)) + getnexttoken = false; stringDelimitedField = ""; } @@ -563,7 +566,8 @@ else if (month < 1 || month > 12 || day < 1 || day > 31) date[0] = retdate; try { - String stringDelimitedField = actline.nextToken(fdel); + if (getnexttoken) + actline.nextToken(fdel); } catch(Exception e) {//Se sabe que se puede leer un token que no existe al final de la linea diff --git a/common/src/main/java/com/genexus/util/GXProperties.java b/common/src/main/java/com/genexus/util/GXProperties.java index a04ee091f..09f8585c0 100644 --- a/common/src/main/java/com/genexus/util/GXProperties.java +++ b/common/src/main/java/com/genexus/util/GXProperties.java @@ -1,6 +1,6 @@ package com.genexus.util; -import java.util.Vector; +import java.util.LinkedHashMap; import com.genexus.internet.IGxJSONSerializable; @@ -10,188 +10,131 @@ import com.genexus.SdtMessages_Message; import com.genexus.GXBaseCollection; import java.util.Iterator; +import java.util.Map; -public class GXProperties implements IGxJSONSerializable{ - private Vector vector = new Vector(); - private boolean eof; - private int lastElement; - - public GXProperties() { - } - - public void set(String name, String value) - { - put(name, value); - } - - public void add(String name, String value) - { - addToTheEnd(name, value); - } - - public void put(String name, String value) - { - int index = findElement(name); - if ( index >= 0) - { - vector.elementAt(index).setValue(value); - } - else - { - addToTheEnd(name, value); - } - } - public String toString() { - StringBuilder builder = new StringBuilder(); - for (GXProperty property : vector) { - builder.append(property.getValue()); - } - return builder.toString(); - } - private void addToTheEnd(String name, String value){ - - GXProperty prop = new GXProperty(); - prop.setKey(name); - prop.setValue(value); - vector.addElement(prop); - } - public String get(String name) - { - int index = findElement(name); - if (index >= 0) - return vector.elementAt(index).getValue(); - else - return ""; - } - - public void remove(String name) - { - int index = findElement(name); - if (index >= 0) - vector.removeElementAt(index); - } - - public boolean containsKey(String name) - { - if (findElement(name) == -1) - return false; - return true; - } - - private int findElement(String name) - { - int i = 0; - while (count() > i) - { - if (item(i).getKey().equalsIgnoreCase(name)) - return i; - i++; - } - return -1; - } - - public GXProperty item(int i) - { - return vector.elementAt(i); - } - - public int getCount() - { +public class GXProperties implements IGxJSONSerializable { + private LinkedHashMap < String, GXProperty > properties = new LinkedHashMap < > (); + private boolean eof; + private int lastElement; + + public GXProperties() {} + + public void set(String name, String value) { + this.put(name, value); + } + + public void add(String name, String value) { + properties.put(name, new GXProperty(name, value)); + } + + public void put(String name, String value) { + properties.put(name, new GXProperty(name, value)); + } + public String toString() { + StringBuilder builder = new StringBuilder(); + for (GXProperty property: properties.values()) { + builder.append(property.getValue()); + } + return builder.toString(); + } + + public String get(String name) { + return containsKey(name) ? properties.get(name).getValue() : ""; + } + + public void remove(String name) { + properties.remove(name); + } + + public boolean containsKey(String name) { + return properties.containsKey(name); + } + + public GXProperty item(int i) { + int counter = 0; + for (Map.Entry < String, GXProperty > entry: properties.entrySet()) { + if (counter++ == i) { + return entry.getValue(); + } + } + throw new IndexOutOfBoundsException("The provided index is larger than the amount of items stored"); + } + + public int getCount() { return count(); } - public int count() - { - return vector.size(); - } - - public void clear() - { - vector.removeAllElements(); - } - - public GXProperty first() - { - eof = false; - if (count() > 0) - { - lastElement = 0; - return vector.elementAt(0); - } - else - { - eof = true; - return null; - } - } - - public boolean eof() - { - return eof; - } - - public GXProperty next() - { - lastElement ++; - if (count() > lastElement) - { - return vector.elementAt(lastElement); - } - else - { - eof = true; - return null; - } - } - - public Object GetJSONObject() - { + public int count() { + return properties.size(); + } + + public void clear() { + properties.clear(); + } + + public GXProperty first() { + eof = false; + if (count() > 0) { + lastElement = 0; + return properties.entrySet().iterator().next().getValue(); + } else { + eof = true; + return null; + } + } + + public boolean eof() { + return eof; + } + + public GXProperty next() { + lastElement++; + if (count() > lastElement) { + return item(lastElement); + } else { + eof = true; + return null; + } + } + + public Object GetJSONObject() { JSONObject jObj = new JSONObject(); int i = 0; - while (count() > i) - { + while (count() > i) { GXProperty prop = item(i); try { - jObj.put(prop.getKey(), prop.getValue()); - } catch (JSONException e) { - } + jObj.put(prop.getKey(), prop.getValue()); + } catch (JSONException e) {} i++; } - return jObj; + return jObj; } - - public String toJSonString() - { - JSONObject jObj = (JSONObject)GetJSONObject(); + + public String toJSonString() { + JSONObject jObj = (JSONObject) GetJSONObject(); return jObj.toString(); } - public boolean fromJSonString(String s) - { + public boolean fromJSonString(String s) { return fromJSonString(s, null); - } - public boolean fromJSonString(String s, GXBaseCollection messages) - { - this.clear(); + } + public boolean fromJSonString(String s, GXBaseCollection < SdtMessages_Message > messages) { + this.clear(); if (!s.equals("")) { try { JSONObject jObj = new JSONObject(s); - Iterator keys = jObj.keys(); - while( keys.hasNext() ) { + Iterator < String > keys = jObj.keys(); + while (keys.hasNext()) { String key = keys.next(); this.put(key, jObj.get(key).toString()); } return true; - } - catch (JSONException ex) - { + } catch (JSONException ex) { CommonUtil.ErrorToMessages("fromjson error", ex.getMessage(), messages); return false; } - } - else - { + } else { CommonUtil.ErrorToMessages("fromjson error", "empty string", messages); return false; } } -} +} \ No newline at end of file diff --git a/common/src/main/java/com/genexus/util/GXProperty.java b/common/src/main/java/com/genexus/util/GXProperty.java index a55b155ae..b747c91c4 100644 --- a/common/src/main/java/com/genexus/util/GXProperty.java +++ b/common/src/main/java/com/genexus/util/GXProperty.java @@ -1,9 +1,15 @@ package com.genexus.util; -public class GXProperty -{ +public class GXProperty { public String name; public String value; + + public GXProperty() {} + + public GXProperty(String name, String value){ + this.name = name; + this.value = value; + } public String getKey() { @@ -23,6 +29,5 @@ public void setKey(String name) public void setValue(String value) { this.value = value; - } - -} + } +} \ No newline at end of file diff --git a/common/src/main/java/json/org/json/JSONArray.java b/common/src/main/java/json/org/json/JSONArray.java index 7efc7d7a2..61a16dfe0 100644 --- a/common/src/main/java/json/org/json/JSONArray.java +++ b/common/src/main/java/json/org/json/JSONArray.java @@ -324,7 +324,7 @@ public boolean isNull(int index) { */ public String join(String separator) throws JSONException { int len = length(); - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (int i = 0; i < len; i += 1) { if (i > 0) { @@ -820,7 +820,7 @@ String toString(int indentFactor, int indent) throws JSONException { return "[]"; } int i; - StringBuffer sb = new StringBuffer("["); + StringBuilder sb = new StringBuilder("["); if (len == 1) { sb.append(JSONObject.valueToString(this.myArrayList.get(0), indentFactor, indent)); diff --git a/common/src/main/java/json/org/json/JSONObject.java b/common/src/main/java/json/org/json/JSONObject.java index e8cc843ce..7f1eb9d0e 100644 --- a/common/src/main/java/json/org/json/JSONObject.java +++ b/common/src/main/java/json/org/json/JSONObject.java @@ -130,9 +130,7 @@ public String toString() { * The hash map where the JSONObject's properties are kept. */ private HashMap myHashMap; - private ArrayList nameIndexList; - - + private ArrayList nameIndexList; /** * It is sometimes more convenient and less ambiguous to have a @@ -148,7 +146,7 @@ public String toString() { */ public JSONObject() { this.myHashMap = new HashMap<>(); - this.nameIndexList = new ArrayList<>(); + this.nameIndexList = new ArrayList<>(); } @@ -238,9 +236,9 @@ public JSONObject(Map map) { this.myHashMap = (map == null) ? new HashMap<>() : new HashMap<>(map); - this.nameIndexList = (map == null) ? - new ArrayList<>(): - new ArrayList<>(map.keySet()); + this.nameIndexList = (map == null) ? + new ArrayList<>(): + new ArrayList<>(map.keySet()); } @@ -533,7 +531,7 @@ public boolean isNull(String key) { * @return An iterator of the keys. */ public Iterator keys() { - return this.nameIndexList.iterator(); + return this.nameIndexList.iterator(); } @@ -552,7 +550,7 @@ public int length() { */ public void clear() { this.myHashMap.clear(); - this.nameIndexList.clear(); + this.nameIndexList.clear(); } @@ -945,7 +943,7 @@ public static String quote(String string) { char c = 0; int i; int len = string.length(); - StringBuffer sb = new StringBuffer(len + 4); + StringBuilder sb = new StringBuilder(len + 4); String t; sb.append('"'); @@ -993,8 +991,8 @@ public static String quote(String string) { * or null if there was no value. */ public Object remove(String key) { - if (this.nameIndexList.contains(key)) - this.nameIndexList.remove(key); + if (this.nameIndexList.contains(key)) + this.nameIndexList.remove(key); return this.myHashMap.remove(key); } @@ -1055,7 +1053,7 @@ public JSONArray toJSONArray(JSONArray names) throws JSONException { public String toString() { try { Iterator keys = keys(); - StringBuffer sb = new StringBuffer("{"); + StringBuilder sb = new StringBuilder("{"); while (keys.hasNext()) { if (sb.length() > 1) { @@ -1111,7 +1109,7 @@ String toString(int indentFactor, int indent) throws JSONException { return "{}"; } Iterator keys = keys(); - StringBuffer sb = new StringBuffer("{"); + StringBuilder sb = new StringBuilder("{"); int newindent = indent + indentFactor; Object o; if (n == 1) { diff --git a/common/src/main/java/json/org/json/JSONTokener.java b/common/src/main/java/json/org/json/JSONTokener.java index 849ac7967..23070a0c2 100644 --- a/common/src/main/java/json/org/json/JSONTokener.java +++ b/common/src/main/java/json/org/json/JSONTokener.java @@ -209,7 +209,7 @@ public char nextClean() throws JSONException { */ public String nextString(char quote) throws JSONException { char c; - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (;;) { c = next(); switch (c) { @@ -284,7 +284,7 @@ public String nextTo(char d) { */ public String nextTo(String delimiters) { char c; - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); for (;;) { c = next(); if (delimiters.indexOf(c) >= 0 || c == 0 || @@ -331,7 +331,7 @@ public Object nextValue() throws JSONException { * formatting character. */ - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); char b = c; while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { sb.append(c); diff --git a/gxcloudstorage-awss3-v1/src/main/java/com/genexus/db/driver/ExternalProviderS3V1.java b/gxcloudstorage-awss3-v1/src/main/java/com/genexus/db/driver/ExternalProviderS3V1.java index 6666a8888..830e92e11 100644 --- a/gxcloudstorage-awss3-v1/src/main/java/com/genexus/db/driver/ExternalProviderS3V1.java +++ b/gxcloudstorage-awss3-v1/src/main/java/com/genexus/db/driver/ExternalProviderS3V1.java @@ -29,7 +29,6 @@ import java.util.Date; import java.util.List; - public class ExternalProviderS3V1 extends ExternalProviderBase implements ExternalProvider { private static Logger logger = LogManager.getLogger(ExternalProviderS3V1.class); @@ -173,21 +172,31 @@ private String ensureFolder(String... pathPart) { } public void download(String externalFileName, String localFile, ResourceAccessControlList acl) { + S3ObjectInputStream objectData = null; try { S3Object object = client.getObject(new GetObjectRequest(bucket, externalFileName)); - try (InputStream objectData = object.getObjectContent()) { - try (OutputStream outputStream = new FileOutputStream(new File(localFile))){ - int read; - byte[] bytes = new byte[1024]; - while ((read = objectData.read(bytes)) != -1) { - outputStream.write(bytes, 0, read); - } + objectData = object.getObjectContent(); + File file = new File(localFile); + File parentDir = file.getParentFile(); + if (parentDir != null && !parentDir.exists()) + parentDir.mkdirs(); + + try (OutputStream outputStream = new FileOutputStream(file)) { + int read; + byte[] bytes = new byte[1024]; + while ((read = objectData.read(bytes)) != -1) { + outputStream.write(bytes, 0, read); } } } catch (FileNotFoundException ex) { logger.error("Error while downloading file to the external provider", ex); } catch (IOException ex) { logger.error("Error while downloading file to the external provider", ex); + } finally { + try { + if (objectData != null) + objectData.close(); + } catch (IOException ioe) {logger.error("Error while draining the S3ObjectInputStream", ioe);} } } @@ -337,12 +346,20 @@ public String getDirectory(String directoryName) { } public boolean existsDirectory(String directoryName) { - ListObjectsV2Request listObjectsRequest = new ListObjectsV2Request() + if (directoryName == null || directoryName.isEmpty() || directoryName.equals(".") || directoryName.equals("/")) + directoryName = ""; + else + directoryName = StorageUtils.normalizeDirectoryName(directoryName); + + ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request() .withBucketName(bucket) - .withDelimiter(StorageUtils.DELIMITER) - .withPrefix(StorageUtils.normalizeDirectoryName(directoryName)) + .withPrefix(directoryName) .withMaxKeys(1); - return client.listObjectsV2(listObjectsRequest).getKeyCount() > 0; + + if (!directoryName.isEmpty()) + listObjectsV2Request = listObjectsV2Request.withDelimiter(StorageUtils.DELIMITER); + + return client.listObjectsV2(listObjectsV2Request).getKeyCount() > 0; } public void createDirectory(String directoryName) { diff --git a/gxcloudstorage-awss3-v2/src/main/java/com/genexus/db/driver/ExternalProviderS3V2.java b/gxcloudstorage-awss3-v2/src/main/java/com/genexus/db/driver/ExternalProviderS3V2.java index 310be4501..a250db53b 100644 --- a/gxcloudstorage-awss3-v2/src/main/java/com/genexus/db/driver/ExternalProviderS3V2.java +++ b/gxcloudstorage-awss3-v2/src/main/java/com/genexus/db/driver/ExternalProviderS3V2.java @@ -515,12 +515,27 @@ public String getDirectory(String directoryName) { } public boolean existsDirectory(String directoryName) { - ListObjectsV2Request listObjectsRequest = ListObjectsV2Request.builder() - .bucket(bucket) - .delimiter(StorageUtils.DELIMITER) - .prefix(StorageUtils.normalizeDirectoryName(directoryName)) - .maxKeys(1) - .build(); + if (directoryName == null || directoryName.isEmpty() || directoryName.equals(".") || directoryName.equals("/")) + directoryName = ""; + else + directoryName = StorageUtils.normalizeDirectoryName(directoryName); + + + ListObjectsV2Request listObjectsRequest; + if (!directoryName.isEmpty()) + listObjectsRequest = ListObjectsV2Request.builder() + .bucket(bucket) + .prefix(directoryName) + .maxKeys(1) + .delimiter(StorageUtils.DELIMITER) + .build(); + else + listObjectsRequest = ListObjectsV2Request.builder() + .bucket(bucket) + .prefix(directoryName) + .maxKeys(1) + .build(); + return client.listObjectsV2(listObjectsRequest).keyCount() > 0; } diff --git a/gxobservability/pom.xml b/gxobservability/pom.xml new file mode 100644 index 000000000..9b48bd2b8 --- /dev/null +++ b/gxobservability/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + + com.genexus + parent + ${revision}${changelist} + + + gxobservability + GeneXus Observability + + + + io.opentelemetry + opentelemetry-api + + + io.opentelemetry + opentelemetry-sdk-trace + + + io.opentelemetry + opentelemetry-exporter-otlp + + + io.opentelemetry + opentelemetry-sdk + + + io.opentelemetry + opentelemetry-semconv + 1.30.1-alpha + + + io.opentelemetry + opentelemetry-extension-annotations + + + io.opentelemetry + opentelemetry-sdk-extension-autoconfigure + 1.36.0 + + + + + + io.opentelemetry + opentelemetry-bom + 1.23.0 + pom + import + + + + + + gxobservability + + \ No newline at end of file diff --git a/gxobservability/src/main/java/com/genexus/opentelemetry/GXSpanContext.java b/gxobservability/src/main/java/com/genexus/opentelemetry/GXSpanContext.java new file mode 100644 index 000000000..07c68f93b --- /dev/null +++ b/gxobservability/src/main/java/com/genexus/opentelemetry/GXSpanContext.java @@ -0,0 +1,33 @@ +package com.genexus.opentelemetry; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Context; +public class GXSpanContext +{ + private io.opentelemetry.api.trace.SpanContext _spanContext; + + public io.opentelemetry.api.trace.SpanContext getSpanContext() + { + return _spanContext; + } + public GXSpanContext(io.opentelemetry.api.trace.SpanContext spanContext) + { + this._spanContext = spanContext; + } + public GXSpanContext() + { + _spanContext = Span.current().getSpanContext(); + } + public String traceId() + { + return _spanContext.getTraceId(); + } + public String spanId() + { + return _spanContext.getSpanId(); + } +} \ No newline at end of file diff --git a/gxobservability/src/main/java/com/genexus/opentelemetry/GXTraceContext.java b/gxobservability/src/main/java/com/genexus/opentelemetry/GXTraceContext.java new file mode 100644 index 000000000..6367b2938 --- /dev/null +++ b/gxobservability/src/main/java/com/genexus/opentelemetry/GXTraceContext.java @@ -0,0 +1,21 @@ +package com.genexus.opentelemetry; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Context; + +public class GXTraceContext +{ + private Context context; + public GXTraceContext(io.opentelemetry.context.Context context) + { + this.context = context; + } + public Context getTraceContext() + { + return this.context; + } +} \ No newline at end of file diff --git a/gxobservability/src/main/java/com/genexus/opentelemetry/OtelSpan.java b/gxobservability/src/main/java/com/genexus/opentelemetry/OtelSpan.java new file mode 100644 index 000000000..9e1c2d928 --- /dev/null +++ b/gxobservability/src/main/java/com/genexus/opentelemetry/OtelSpan.java @@ -0,0 +1,186 @@ +package com.genexus.opentelemetry; + +import java.util.concurrent.atomic.AtomicReference; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.extension.annotations.SpanAttribute; +import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.baggage.BaggageBuilder; +public class OtelSpan { + private Span span; + public enum SpanStatusCode + { + UNSET, + OK, + ERROR + } + public OtelSpan(Span span) + { + this.span=span; + } + public OtelSpan() + {} + //region EO Properties + public String getTraceId() + { + if (span != null) + return span.getSpanContext().getTraceId(); + return ""; + } + public String getSpanId() + { + if (span != null) + return span.getSpanContext().getSpanId(); + return ""; + } + public Boolean isRecording() + { + if (span != null) + return span.isRecording(); + return false; + } + public GXSpanContext getSpanContext() { + return new GXSpanContext(getSpanContext(span)); + } + //endregion + //region EO Methods + public void endSpan() + { + if (span!=null) + span.end(); + } + public GXTraceContext addBaggage(String key, String value) + { + return new GXTraceContext(addBaggageReturnContext(key, value)); + } + public String getBaggaeItem(String key,GXTraceContext gxTraceContext) + { + return getBaggaeItemInContext(gxTraceContext.getTraceContext(),key); + } + public GXTraceContext getGXTraceContext() + { + return new GXTraceContext(getContext()); + } + public void recordException(String message) + { + recordException(span,new Throwable(message)); + } + public void setStringAttribute(String key, String value) + { + if (span != null) + span.setAttribute(key,value); + } + public void setBooleanAttribute(String key, boolean value) + { + if (span != null) + span.setAttribute(key,value); + } + public void setDoubleAttribute(String key, double value) + { + if (span != null) + span.setAttribute(key,value); + } + public void setLongAttribute(String key, long value) + { + if (span != null) + span.setAttribute(key,value); + } + public void setStatus(Byte spanStatusCodeByte) + { + StatusCode statusCode = toStatusCode(spanStatusCodeByte); + if (span != null) + span.setStatus(statusCode); + } + public void setStatus(Byte spanStatusCodeByte, String message) + { + StatusCode statusCode = toStatusCode(spanStatusCodeByte); + if (span != null) + span.setStatus(statusCode, message); + } + //endregion + + //region Private methods + + private String getBaggaeItemInContext(Context context, String key) + { + AtomicReference value = new AtomicReference<>(""); + Baggage.fromContext(context).asMap().forEach((k, v) -> { + if (k.equals(key)) { + value.set(v.getValue()); + } + }); + if (value != null) + return value.get(); + return ""; + } + private Context addBaggageReturnContext(String key, String value) + { + Baggage baggage = Baggage.current().toBuilder().put(key,value).build(); + return baggage.storeInContext(getContext()); + } + private Context getContext() + { + if (span != null) + return Context.current().with(span); + return null; + } + private Context getContextCurrentSpan() + { + return Context.current(); + } + + private static void recordException(Span span, Throwable exc) { + if (span != null && exc != null) { + span.recordException(exc); + } + } + private io.opentelemetry.api.trace.SpanContext getSpanContext(Span span) + { + if (span != null) + return span.getSpanContext(); + return null; + } + private boolean isRecording(Span span) + { + if (span != null) + return span.isRecording(); + return false; + } + private Span current() + { + return Span.current(); + + } + private static StatusCode toStatusCode (Byte spanStatusCode){ + switch (spanStatusCode) { + case 0: + return StatusCode.UNSET; + case 1: + return StatusCode.OK; + case 2: + return StatusCode.ERROR; + } + return null; + } + private static SpanStatusCode fromStatusCode (StatusCode statusCode){ + switch (statusCode) { + case UNSET: + return SpanStatusCode.UNSET; + case OK: + return SpanStatusCode.OK; + case ERROR: + return SpanStatusCode.ERROR; + } + return null; + } + //endregion + +} \ No newline at end of file diff --git a/gxobservability/src/main/java/com/genexus/opentelemetry/OtelTracer.java b/gxobservability/src/main/java/com/genexus/opentelemetry/OtelTracer.java new file mode 100644 index 000000000..cd7442a96 --- /dev/null +++ b/gxobservability/src/main/java/com/genexus/opentelemetry/OtelTracer.java @@ -0,0 +1,206 @@ +package com.genexus.opentelemetry; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.extension.annotations.SpanAttribute; +import io.opentelemetry.extension.annotations.WithSpan; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; +import java.util.Iterator; +import java.util.regex.*; +public class OtelTracer { + private static final String OTEL_SERVICE_NAME = "OTEL_SERVICE_NAME"; + private static final String OTEL_SERVICE_VERSION = "OTEL_SERVICE_VERSION"; + private static final String OTEL_RESOURCE_ATTRIBUTES = "OTEL_RESOURCE_ATTRIBUTES"; + private static final String JAVA_INSTRUMENTATION_SCOPE_NAME = "JAVA_INSTRUMENTATION_SCOPE_NAME"; + private static final String JAVA_INSTRUMENTATION_SCOPE_VERSION = "JAVA_INSTRUMENTATION_SCOPE_VERSION"; + private static StringPair instrumentationScope=getInstrumentationScope(); + private static Tracer tracer=getTracer(instrumentationScope); + static class StringPair { + final String name; + final String version; + + StringPair(String name, String version) { + this.name = name; + this.version = version; + } + } + public enum SpanType + { + INTERNAL, + SERVER, + CLIENT, + PRODUCER, + CONSUMER + } + public OtelSpan createSpan(String displayName) + { + Span otelspan= createAndStartSpan(displayName); + return new OtelSpan(otelspan); + } + public OtelSpan createSpan(String displayName, Byte spanTypeByte) + { + io.opentelemetry.api.trace.SpanKind spanKind = toSpanKind(spanTypeByte); + Span otelspan = createAndStartSpan(displayName, spanKind); + return new OtelSpan(otelspan); + } + public OtelSpan createSpan(String displayName, GXTraceContext gxTraceContext, Byte spanTypeByte) + { + io.opentelemetry.api.trace.SpanKind spanKind = toSpanKind(spanTypeByte); + Span otelspan = createAndStartSpan(displayName,spanKind,gxTraceContext.getTraceContext()); + return new OtelSpan(otelspan); + } + public OtelSpan createSpan(String displayName, GXTraceContext gxTraceContext, Byte spanTypeByte, Iterator gxSpanContextIterator) + { + io.opentelemetry.api.trace.SpanKind spanKind = toSpanKind(spanTypeByte); + Span otelspan = createAndStartSpan(displayName,spanKind,gxTraceContext.getTraceContext(),gxSpanContextIterator); + return new OtelSpan(otelspan); + } + public static OtelSpan getCurrentSpan() + { + return new OtelSpan(Span.current()); + } + //region Private methods + private static StringPair getInstrumentationScope() + { + String name="GeneXus.Tracing"; + String version=""; + + String javaInstrumentationScopeName = System.getenv(JAVA_INSTRUMENTATION_SCOPE_NAME); + String javaInstrumentationScopeVersion = System.getenv(JAVA_INSTRUMENTATION_SCOPE_VERSION); + + if (javaInstrumentationScopeName!=null && !javaInstrumentationScopeName.trim().isEmpty()) + { + name = javaInstrumentationScopeName; + if (javaInstrumentationScopeVersion!=null && !javaInstrumentationScopeVersion.trim().isEmpty()) { + version = javaInstrumentationScopeVersion; + } + } + else + { + String serviceName = System.getenv(OTEL_SERVICE_NAME); + + if (serviceName==null || serviceName.trim().isEmpty()) { + String pattern = "(?:\\b\\w+\\b=\\w+)(?:,(?:\\b\\w+\\b=\\w+))*"; + Pattern regex = Pattern.compile(pattern); + Matcher matcher = regex.matcher(OTEL_RESOURCE_ATTRIBUTES); + + while (matcher.find()) { + String[] keyValue = matcher.group().split("="); + if (keyValue[0].equals("service.name")) { + serviceName = keyValue[1]; + break; + } + } + } + + String serviceVersion = System.getenv(OTEL_SERVICE_VERSION); + if (serviceVersion==null || serviceVersion.trim().isEmpty()) { + String pattern = "(?:\\b\\w+\\b=\\w+)(?:,(?:\\b\\w+\\b=\\w+))*"; + Pattern regex = Pattern.compile(pattern); + Matcher matcher = regex.matcher(OTEL_RESOURCE_ATTRIBUTES); + + while (matcher.find()) { + + String[] keyValue = matcher.group().split("="); + + if (keyValue[0].equals("service.version")) { + serviceVersion = keyValue[1]; + break; + } + } + } + + if (serviceName!=null && !serviceName.trim().isEmpty()) + name = serviceName; + if (serviceVersion!=null && !serviceVersion.trim().isEmpty()) + version = serviceVersion; + + } + return new StringPair(name,version); + } + + private static Tracer getTracer(StringPair instrumentationScope) + { + OpenTelemetry openTelemetry = GlobalOpenTelemetry.get(); + if (openTelemetry != null) + return openTelemetry.getTracer(instrumentationScope.name,instrumentationScope.version); + return null; + } + + private static Span createAndStartSpan(String displayName) + { + if (tracer != null) { + if (!displayName.isEmpty()) + return tracer.spanBuilder(displayName).startSpan(); + } + return null; + } + private static Span createAndStartSpan(String displayName, io.opentelemetry.api.trace.SpanKind spanKind) + { + if (tracer != null) { + if (!displayName.isEmpty() && spanKind != null) + return tracer.spanBuilder(displayName).setSpanKind(spanKind).startSpan(); + else if (spanKind == null) { + return tracer.spanBuilder(displayName).startSpan(); + } + } + return null; + } + private static Span createAndStartSpan(String displayName, io.opentelemetry.api.trace.SpanKind spanKind, Context context) + { + if (tracer != null) { + if (!displayName.isEmpty() && spanKind != null && context != null) + return tracer.spanBuilder(displayName).setSpanKind(spanKind).setParent(context).startSpan(); + else { + if (!displayName.isEmpty() && spanKind != null && context == null) + return tracer.spanBuilder(displayName).setSpanKind(spanKind).setNoParent().startSpan(); + } + } + return null; + } + private static Span createAndStartSpan(String displayName, io.opentelemetry.api.trace.SpanKind spanKind, Context context, Iterator gxSpanContexts) + { + if (tracer != null) { + SpanBuilder spanBuilder; + if (!displayName.isEmpty() && spanKind != null && context != null) { + spanBuilder = tracer.spanBuilder(displayName).setSpanKind(spanKind).setParent(context); + } else { + if (!displayName.isEmpty() && spanKind != null && context == null) + spanBuilder = tracer.spanBuilder(displayName).setSpanKind(spanKind).setNoParent(); + else + return null; + } + while (gxSpanContexts.hasNext()) { + if (spanBuilder != null) + spanBuilder.addLink(gxSpanContexts.next().getSpanContext()); + } + return spanBuilder.startSpan(); + } + return null; + } + + private static io.opentelemetry.api.trace.SpanKind toSpanKind (Byte spanTypeByte){ + switch (spanTypeByte) { + case 0: + return io.opentelemetry.api.trace.SpanKind.INTERNAL; + case 1: + return io.opentelemetry.api.trace.SpanKind.SERVER; + case 2: + return io.opentelemetry.api.trace.SpanKind.CLIENT; + case 3: + return io.opentelemetry.api.trace.SpanKind.PRODUCER; + case 4: + return io.opentelemetry.api.trace.SpanKind.CONSUMER; + } + return null; + } + //endregion + +} \ No newline at end of file diff --git a/gxoffice/pom.xml b/gxoffice/pom.xml index 0358eddad..e0759788a 100644 --- a/gxoffice/pom.xml +++ b/gxoffice/pom.xml @@ -42,7 +42,7 @@ org.libreoffice libreoffice - 7.5.1 + 7.5.9 diff --git a/gxweb/src/main/java/com/genexus/internet/HttpAjaxContext.java b/gxweb/src/main/java/com/genexus/internet/HttpAjaxContext.java index 232d84c81..4ce3ff9e4 100644 --- a/gxweb/src/main/java/com/genexus/internet/HttpAjaxContext.java +++ b/gxweb/src/main/java/com/genexus/internet/HttpAjaxContext.java @@ -868,7 +868,7 @@ public void ajax_rsp_assign_sdt_attri( String CmpContext, boolean IsMasterPage, { try { JSONObject obj = getGxObject(AttValues, CmpContext, IsMasterPage); - if (obj != null && (dynAjaxEventContext.isParmModified(AttName, SdtObj) || !isUndefinedOutParam( AttName, SdtObj))) + if (obj != null && (!isUndefinedOutParam( AttName, SdtObj) || dynAjaxEventContext.isParmModified(AttName, SdtObj))) { if (SdtObj instanceof IGxJSONAble) obj.put(AttName, ((IGxJSONAble)SdtObj).GetJSONObject()); diff --git a/java/src/main/java/com/genexus/internet/HttpContext.java b/java/src/main/java/com/genexus/internet/HttpContext.java index 3d2c5ab66..f7bf07879 100644 --- a/java/src/main/java/com/genexus/internet/HttpContext.java +++ b/java/src/main/java/com/genexus/internet/HttpContext.java @@ -920,7 +920,7 @@ public boolean isRestService() {"rtx" , "text/richtext"}, {"htm" , "text/html"}, {"html" , "text/html"}, - {"xml" , "text/xml"}, + {"xml" , "application/xml"}, {"aif" , "audio/x-aiff"}, {"au" , "audio/basic"}, {"wav" , "audio/wav"}, diff --git a/java/src/main/java/com/genexus/internet/HttpRequestWeb.java b/java/src/main/java/com/genexus/internet/HttpRequestWeb.java index 76519c7c9..ac2ddc532 100644 --- a/java/src/main/java/com/genexus/internet/HttpRequestWeb.java +++ b/java/src/main/java/com/genexus/internet/HttpRequestWeb.java @@ -8,6 +8,7 @@ import com.genexus.PrivateUtilities; import com.genexus.WrapperUtils; import com.genexus.webpanels.HttpContextWeb; +import org.apache.commons.io.IOUtils; public class HttpRequestWeb extends HttpRequest { @@ -63,7 +64,7 @@ public String getString() if (httpContext.getRequest().getCharacterEncoding() != null && httpContext.getRequest().getCharacterEncoding().length() > 0) requestEncoding = httpContext.getRequest().getCharacterEncoding(); - return new String(PrivateUtilities.readToByteArray(is), requestEncoding); + return new String(IOUtils.toByteArray(is), requestEncoding); } catch (IOException e) { diff --git a/java/src/main/java/com/genexus/sap/SessionManager.java b/java/src/main/java/com/genexus/sap/SessionManager.java index a6415aa6f..2eb5be035 100644 --- a/java/src/main/java/com/genexus/sap/SessionManager.java +++ b/java/src/main/java/com/genexus/sap/SessionManager.java @@ -153,10 +153,10 @@ public void TransactionBegin() try { destinationName = (String)destinationObj; - scopeName = (String)scopeObj; + scopeName = String.valueOf( scopeObj); Log.info("GX SAP - Begin Transaction " + destinationName); JCoDestination destination = null; - if (scopeName.length() == 0) + if (scopeName.isEmpty()) destination = JCoDestinationManager.getDestination(destinationName); else destination = JCoDestinationManager.getDestination(destinationName, scopeName); @@ -180,12 +180,12 @@ public void TransactionCommit() try { JCoDestination destination = null; - destinationName = (String)destinationObj; - if ( scopeObject == null || ((String)scopeObject).length() == 0 ) { + destinationName = (String)destinationObj; + scopeName = String.valueOf(scopeObject); + if ( scopeName.isEmpty() ) { destination = JCoDestinationManager.getDestination(destinationName); } else { - scopeName = (String)scopeObject; destination = JCoDestinationManager.getDestination(destinationName, scopeName); } Log.info("GX SAP - Commit Transaction " + destinationName); @@ -456,4 +456,4 @@ public void setRepositoryName(String value) { repositoryName = value; } -} \ No newline at end of file +} diff --git a/pom.xml b/pom.xml index ca9bf9d3b..d206cd120 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,7 @@ gxcloudstorage-azureblob gxcloudstorage-ibmcos gxcloudstorage-tests + gxobservability gxcloudstorage-awss3-v2