# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: marian.edu@acorn.ro-20210423120635-qu022uqwhftsh8ej # target_branch: sftp://medu@xfer.goldencode.com/opt/fwd/3821c # testament_sha1: 46c2975b9115edb4f377880391206b1cd8acfe1d # timestamp: 2021-05-04 14:57:41 +0300 # source_branch: 4335a # base_revision_id: ca@goldencode.com-20210323115539-tuxqia3wn5i7l5rf # # Begin patch === modified file 'rules/include/common-progress.rules' --- rules/include/common-progress.rules 2021-02-16 18:01:41 +0000 +++ rules/include/common-progress.rules 2021-04-23 12:06:35 +0000 @@ -10068,6 +10068,8 @@ ovrdNames.put("net.http.filter.ifilterevents", "IFilterEvents") ovrdNames.put("reflect.constructor", "LegacyConstructor") ovrdNames.put("reflect.method", "Legacy4GLMethod") + ovrdNames.put("logging.isupportlogging", "ISupportLogging") + ovrdNames.put("web.webrequest", "RemoteWebRequest") === modified file 'src/com/goldencode/p2j/oo/core/ByteBucket.java' --- src/com/goldencode/p2j/oo/core/ByteBucket.java 2021-03-10 10:53:37 +0000 +++ src/com/goldencode/p2j/oo/core/ByteBucket.java 2021-03-25 08:48:03 +0000 @@ -16,6 +16,7 @@ ** 007 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** ME 20210310 Use object variables defined out of block for methods that returns objects. +** ME 20210325 Move assert inside block else initInput returns an invalid `object`. */ /* @@ -924,10 +925,11 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) public void putString(final object _data) { - Assert.notNull(_data, new character("String data")); object data = TypeFactory.initInput(_data); internalProcedure(this, "PutString", new Block((Body) () -> { + Assert.notNull(data, new character("String data")); + putString(data.ref().getValue(), new longchar(data.ref().getEncoding())); })); } === modified file 'src/com/goldencode/p2j/oo/core/collections/LegacyCollection.java' --- src/com/goldencode/p2j/oo/core/collections/LegacyCollection.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/core/collections/LegacyCollection.java 2021-03-25 06:52:49 +0000 @@ -10,6 +10,7 @@ ** ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 004 ME 20210325 Decrement object reference when removed from collection. */ /* @@ -342,6 +343,10 @@ return function(this, "Remove", logical.class, new Block((Body) () -> { object match = _find(p1); + if (match._isValid()) + { + ObjectOps.decrement(match.ref()); + } returnNormal(match._isValid() && objects.remove(match)); })); @@ -360,7 +365,11 @@ { LegacyCollection other = ObjectOps.cast(p1, LegacyCollection.class).ref(); - List matches = other.objects.stream().map(o -> _find(o)).filter(o -> o._isValid()).collect(Collectors.toList()); + List matches = other.objects.stream().map(o -> _find(o)).filter(o -> o._isValid()).collect(Collectors.toList()); + + matches.forEach(o -> { + ObjectOps.decrement(o.ref()); + }); returnNormal(objects.removeAll(matches)); } === modified file 'src/com/goldencode/p2j/oo/core/collections/LegacyMap.java' --- src/com/goldencode/p2j/oo/core/collections/LegacyMap.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/core/collections/LegacyMap.java 2021-03-25 07:05:29 +0000 @@ -11,6 +11,7 @@ ** ME 20210205 Removed getMap method from interface definition, this is FWD specific. ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 004 ME 20210325 Increment/decrement object references (key/value) when added/removed. */ /* @@ -339,6 +340,12 @@ if (poKey.ref().equals(this)) undoThrow(AppError.newInstance(new character("A Map cannot have itself as key."))); + ObjectOps.increment(poKey.ref()); + if (poValue._isValid()) + { + ObjectOps.increment(poValue.ref()); + } + object obj = map.put(poKey, poValue); if (obj == null) @@ -381,8 +388,13 @@ if (!poKey.isValid().booleanValue()) returnNormal(new object()); + ObjectOps.decrement(poKey.ref()); + object obj = map.remove(poKey); - + + if (obj != null && obj._isValid()) + ObjectOps.decrement(obj.ref()); + if (obj != null) size--; @@ -407,7 +419,7 @@ while (iterator.hasNext().booleanValue()) { - map.remove(iterator.next_()); + remove(iterator.next_()); } } })); === modified file 'src/com/goldencode/p2j/oo/net/MultipartEntity.java' --- src/com/goldencode/p2j/oo/net/MultipartEntity.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/MultipartEntity.java 2021-03-30 06:59:26 +0000 @@ -12,6 +12,7 @@ ** 20201123 Assert positive index on remove (OE 12.2). ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 006 ME 20210303 Increment/decrement object references for entity parts. */ /* @@ -313,6 +314,7 @@ public void clearParts() { internalProcedure(this, "ClearParts", new Block((Body) () -> { + this.parts.forEach(p -> ObjectOps.decrement(p.entity)); this.parts.clear(); })); } @@ -389,10 +391,15 @@ { Assert.isPositive(n, new character("Part num")); - boolean found = this.parts.removeIf(p -> p.num == n.intValue()); + Optional found = this.parts.stream().filter(p -> p.num == n.intValue()).findFirst(); - if (found) + if (found.isPresent()) { + OrderedPart part = found.get(); + + this.parts.remove(part); + ObjectOps.decrement(part.entity); + // if removed reorder all parts that comes after (the OE way) List past = this.parts.stream().filter(p -> p.num > n.intValue()) .sorted((p1, p2) -> p1.num - p2.num).collect(Collectors.toList()); @@ -404,7 +411,7 @@ } } - returnNormal(new logical(found)); + returnNormal(new logical(found.isPresent())); })); } @@ -431,9 +438,13 @@ Assert.isPositive(partNum, new character("Part num")); Assert.notNull(entity, new character("Entity")); + ObjectOps.increment(entity); + Optional part = parts.stream().filter(p -> p.num == partNum.intValue()).findFirst(); if(part.isPresent()) { + ObjectOps.decrement(part.get().entity); + part.get().entity = entity; } else { parts.add(new OrderedPart(partNum.intValue(), entity)); === modified file 'src/com/goldencode/p2j/oo/net/http/HttpHeaderCollection.java' --- src/com/goldencode/p2j/oo/net/http/HttpHeaderCollection.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/HttpHeaderCollection.java 2021-03-29 13:27:35 +0000 @@ -12,6 +12,7 @@ ** 005 CA 20210113 Renamed clear() to clear_(), to follow NameConverter's rules. ** 006 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 007 ME 20210329 Increment/decrement object references on add/remove. */ /* @@ -140,6 +141,10 @@ { internalProcedure(this, "Clear", new Block((Body) () -> { + headers.values().forEach(h -> { + if (h!=null && h._isValid()) + ObjectOps.decrement(h.ref()); + }); headers.clear(); })); } @@ -240,11 +245,14 @@ internalProcedure(this, "Put", new Block((Body) () -> { Assert.notNull(p1, new character("Http Header")); - if (ObjectOps.typeOf(p1, NullHeader.class).booleanValue()) + if (!ObjectOps.typeOf(p1, NullHeader.class).booleanValue()) { - return; + object old = headers.put(p1.ref().getName(), p1); + ObjectOps.increment(p1.ref()); + + if (old != null && old._isValid()) + ObjectOps.decrement(old.ref()); } - headers.put(p1.ref().getName(), p1); })); } @@ -289,7 +297,7 @@ internalProcedure(this, "Put", new Block((Body) () -> { Assert.notNull(p1, new character("Header collection")); - this.headers.putAll(p1.ref().headers); + p1.ref().headers.values().forEach(this::put); })); } @@ -310,7 +318,9 @@ internalProcedure(this, "Remove", new Block((Body) () -> { Assert.notNullOrEmpty(p1, new character("Header name")); - headers.remove(p1); + object old = headers.remove(p1); + if (old != null && old._isValid()) + ObjectOps.decrement(old.ref()); })); } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/JsonBodyWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/JsonBodyWriter.java 2021-02-23 07:39:32 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/JsonBodyWriter.java 2021-03-25 07:26:35 +0000 @@ -12,6 +12,7 @@ ** 004 ME 20210210 Fix/extend implementation as of OE12.2. ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 006 ME 20210324 Moved legacy error serialization in JsonExport. */ /* @@ -76,14 +77,6 @@ import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; import static com.goldencode.p2j.util.BlockManager.undoThrow; -import static com.goldencode.p2j.util.CompareOps._isLessThanOrEqual; -import static com.goldencode.p2j.util.CompareOps.isEqual; -import static com.goldencode.p2j.util.CompareOps.isUnknown; -import static com.goldencode.p2j.util.logical._and; -import static com.goldencode.p2j.util.logical._not; -import static com.goldencode.p2j.util.logical.and; - -import org.apache.commons.lang3.StringUtils; import com.goldencode.p2j.oo.common.support.IcharacterHolder; import com.goldencode.p2j.oo.common.support.IhandleHolder; @@ -97,7 +90,8 @@ import com.goldencode.p2j.oo.io.*; import com.goldencode.p2j.oo.json.objectmodel.*; import com.goldencode.p2j.oo.lang.*; -import com.goldencode.p2j.persist.TargetData; +import com.goldencode.p2j.persist.TargetData; +import com.goldencode.p2j.persist.serial.JsonExport; import com.goldencode.p2j.persist.serial.Serializator; import com.goldencode.p2j.util.*; import com.goldencode.p2j.util.BlockManager.Action; @@ -289,7 +283,7 @@ } else if (ObjectOps.typeOf(poData, LegacyError.class).booleanValue()) { - returnNormal(write(this.writeError(ObjectOps.cast(poData, LegacyError.class)))); + returnNormal(write(JsonExport.serializeError(ObjectOps.cast(poData, LegacyError.class)))); } else if (ObjectOps.typeOf(poData, IcharacterHolder.class).booleanValue()) { @@ -436,76 +430,5 @@ returnNormal(write(oJson)); } })); - } - - /** - * Write error. - * - * @param error error to be written. - */ - private object writeError(object _poError) - { - - object poError = TypeFactory.initInput(_poError); - object oResponse = TypeFactory.object(JsonObject.class); - object oError = TypeFactory.object(JsonObject.class); - object oErrorList = TypeFactory.object(JsonArray.class); - integer iLoop = TypeFactory.integer(); - - Assert.notNull(poError, new character("Error")); - oResponse.assign(ObjectOps.newInstance(JsonObject.class)); - oErrorList.assign(ObjectOps.newInstance(JsonArray.class)); - - if ((ObjectOps.typeOf(poError, AppError.class)).booleanValue()) - { - oResponse.ref().add(new character("_retVal"), ObjectOps.cast(poError, AppError.class).ref().getReturnValue()); - } - - oResponse.ref().add(new character("_errors"), oErrorList); - - for (int i = 0; i < _poError.ref().getNumMessages().intValue(); i++) - { - iLoop.assign(i + 1); - - oError.assign(ObjectOps.newInstance(JsonObject.class)); - oErrorList.ref().add_2(oError); - oError.ref().add(new character("_errorMsg"), poError.ref().getMessage(iLoop)); - oError.ref().add(new character("_errorNum"), poError.ref().getMessageNum(iLoop)); - } - - if ((SessionUtils.isDebugAlert()).booleanValue()) - { - oResponse.ref().add(new character("_type"), poError.ref().getLegacyClass().ref().getTypeName()); - - if (!poError.ref().getCallStack().isUnknown()) - { - oErrorList.assign(ObjectOps.newInstance(JsonArray.class)); - oResponse.ref().add(new character("_stack"), oErrorList); - - String[] callStack = TextOps.entries(poError.ref().getCallStack().getValue(), "\\n"); - - for (int i = 0; i < callStack.length; i++) - { - oErrorList.ref().add(new character(callStack[i])); - } - } - - } - -// TODO: accessing any method in LegacyClass throws NPE -// ProcedureManager.CalleeInfoImpl.push #5351 -// oProp.assign(poError.ref().getLegacyClass().ref().getProperty(new character("InnerError"))); -// -// if (_and(and(oProp.isValid(), () -> isEqual(oProp.ref().getDataType(), DataType.object_)), () -> LegacyClass.getLegacyClass(oProp.ref().getDataTypeName()).ref().isA(ObjectOps.getLegacyClass(LegacyError.class)))) -// { -// oInner.assign(oProp.ref().get(poError)); -// -// if ((oInner.isValid()).booleanValue()) -// { -// this.writeError(oInner); -// } -// } - return oResponse; - }; - + } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/JsonEntityWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/JsonEntityWriter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/JsonEntityWriter.java 2021-03-25 07:26:35 +0000 @@ -9,6 +9,7 @@ ** 002 MP 20200611 Added missing methods as stubs taken by converting the skeleton using FWD. ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 004 ME 20210324 Add missing methods, complete implementation as of OE12.2. */ /* @@ -73,9 +74,23 @@ import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; +import com.goldencode.p2j.oo.common.support.IcharacterHolder; +import com.goldencode.p2j.oo.common.support.IhandleHolder; +import com.goldencode.p2j.oo.common.support.IlongcharHolder; import com.goldencode.p2j.oo.core.*; +import com.goldencode.p2j.oo.core.collections.Iiterator; +import com.goldencode.p2j.oo.core.collections.Imap; +import com.goldencode.p2j.oo.core.collections.ImapEntry; +import com.goldencode.p2j.oo.core.collections.LegacyMap; +import com.goldencode.p2j.oo.core.collections.LegacyMapEntry; import com.goldencode.p2j.oo.json.objectmodel.*; import com.goldencode.p2j.oo.lang.*; +import com.goldencode.p2j.oo.net.http.Cookie; +import com.goldencode.p2j.oo.net.http.HttpHeader; +import com.goldencode.p2j.persist.BufferImpl; +import com.goldencode.p2j.persist.DataSet; +import com.goldencode.p2j.persist.TempTable; +import com.goldencode.p2j.persist.serial.JsonExport; import com.goldencode.p2j.util.*; import com.goldencode.p2j.util.BlockManager.Action; import com.goldencode.p2j.util.BlockManager.Condition; @@ -125,6 +140,27 @@ } /** + * Constructor + * + * @param _poType entity type + */ + @LegacySignature(type = Type.CONSTRUCTOR, parameters = + { + @LegacyParameter(name = "poType", type = "OBJECT", + qualified = "progress.lang.class", mode = "INPUT"), + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + public void __net_http_filter_payload_JsonEntityWriter_constructor__( + object _poType) + { + object poType = TypeFactory.initInput(_poType); + internalProcedure(this, "__net_http_filter_payload_JsonEntityWriter_constructor__", new Block((Body) () -> + { + __net_http_filter_payload_MessageWriter_constructor__(poType); + })); + } + + /** * Opens the writer for output. */ @LegacySignature(type = Type.METHOD, name = "Open") @@ -148,7 +184,7 @@ { internalProcedure(this, "Close", new Block((Body)() -> { super.close(); - moParser.assign((ObjectModelParser)null); + moParser.setUnknown(); })); } @@ -201,7 +237,7 @@ return function(this, "Write", int64.class, new Block((Body) () -> { int64 len = _poData.length(); - Assert.isZeroOrPositive(len, new character("Data")); + Assert.isZeroOrPositive(len, new character("Data size")); if (len.longValue() == 0) { this.entity.assign(ObjectOps.newInstance(JsonObject.class)); @@ -256,13 +292,25 @@ { @LegacyParameter(name = "phData", type = "HANDLE", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public int64 write(handle _phData) { + handle phData = TypeFactory.initInput(_phData); + return function(this, "Write", int64.class, new Block((Body) () -> { - UnimplementedFeature.missing("JsonEntityWriter:Write h METHOD"); + Assert.notNull(phData, new character("Data")); + + WrappedResource res = phData.getResource(); + + if (res instanceof TempTable || res instanceof DataSet || res instanceof BufferImpl) + { + this.entity.assign(ObjectOps.newInstance(JsonObject.class)); + ObjectOps.cast(this.entity, JsonObject.class).ref().read(phData); + } + + returnNormal(new unknown()); })); } @@ -270,7 +318,7 @@ { @LegacyParameter(name = "phData", type = "HANDLE", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public int64 writeHandle(final handle _phData) { handle phData = TypeFactory.initInput(_phData); @@ -295,38 +343,46 @@ }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override - public int64 write(object poData) + public int64 write(object _poData) { + object poData = TypeFactory.initInput(_poData); + return function(this, "Write", int64.class, new Block((Body) () -> { object mptr = TypeFactory.object(Memptr.class); - Assert.notNull(poData); - if (ObjectOps.typeOf(poData, LegacyError.class).booleanValue()) + + if (!poData._isValid()) + { + this.entity.assign(ObjectOps.newInstance(JsonObject.class)); + returnNormal(0L); + } + else if (ObjectOps.typeOf(poData, LegacyError.class).booleanValue()) { writeError(ObjectOps.cast(poData, LegacyError.class)); returnNormal(0L); } - if (ObjectOps.typeOf(poData, JsonConstruct.class).booleanValue()) + else if (ObjectOps.typeOf(poData, JsonConstruct.class).booleanValue()) { this.entity.assign(poData); returnNormal(0L); } - if (ObjectOps.typeOf(poData, String.class).booleanValue()) - { - returnNormal(write(ObjectOps.cast(poData, LegacyString.class).ref().getValue())); - } - - if (ObjectOps.typeOf(poData, WidgetHandle.class).booleanValue()) - { - returnNormal(write(ObjectOps.cast(poData, WidgetHandle.class).ref().getValue())); - } - - if (ObjectOps.typeOf(poData, ByteBucket.class).booleanValue()) + else if (ObjectOps.typeOf(poData, IlongcharHolder.class).booleanValue()) + { + returnNormal(write(ObjectOps.cast(poData, IlongcharHolder.class).ref().getValue())); + } + else if (ObjectOps.typeOf(poData, IcharacterHolder.class).booleanValue()) + { + returnNormal(write(ObjectOps.cast(poData, IcharacterHolder.class).ref().getValue())); + } + else if (ObjectOps.typeOf(poData, IhandleHolder.class).booleanValue()) + { + returnNormal(write(ObjectOps.cast(poData, IhandleHolder.class).ref().getValue())); + } + else if (ObjectOps.typeOf(poData, ByteBucket.class).booleanValue()) { returnNormal(write(ObjectOps.cast(poData, ByteBucket.class).ref().getString())); } - - if (ObjectOps.typeOf(poData, Memptr.class).booleanValue()) + else if (ObjectOps.typeOf(poData, Memptr.class).booleanValue()) { mptr.assign(poData); int64 len = mptr.ref().getSize(); @@ -341,17 +397,220 @@ } returnNormal(len); } + else if (ObjectOps.typeOf(poData, Cookie.class).booleanValue()) + { + returnNormal(write(write_1(ObjectOps.cast(poData, Cookie.class)))); + } + else if (ObjectOps.typeOf(poData, HttpHeader.class).booleanValue()) + { + returnNormal(write(write_2(ObjectOps.cast(poData, HttpHeader.class)))); + } + else if (ObjectOps.typeOf(poData, Imap.class).booleanValue()) + { + writeMap(ObjectOps.cast(poData, Imap.class)); + returnNormal(0L); + } + else if (ObjectOps.typeOf(poData, ImapEntry.class).booleanValue()) + { + object mEntry = ObjectOps.cast(poData, ImapEntry.class); + + writeTuple(mEntry.ref().getKey(), mEntry.ref().getValue()); + returnNormal(0L); + } unsupported(poData); })); } + + @LegacySignature(type = Type.METHOD, name = "Write", returns = "OBJECT", qualified = "progress.json.objectmodel.jsonobject", parameters = + { + @LegacyParameter(name = "pCookie", type = "OBJECT", qualified = "openedge.net.http.cookie", mode = "INPUT") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + protected object write_1(final object _pCookie) + { + object pCookie = TypeFactory.initInput(_pCookie); + object oRet = TypeFactory.object(JsonObject.class); + + return function(JsonEntityWriter.class, this, "Write", object.class, new Block((Body) () -> { + + oRet.assign(ObjectOps.newInstance(JsonObject.class)); + oRet.ref().add(new character("name"), pCookie.ref().getName()); + oRet.ref().add(new character("value"), pCookie.ref().getValue()); + oRet.ref().add(new character("path"), pCookie.ref().getPath()); + oRet.ref().add(new character("domain"), pCookie.ref().getDomain()); + oRet.ref().add(new character("expires"), pCookie.ref().getExpiresAt()); + oRet.ref().add(new character("httpOnly"), pCookie.ref().getHttpOnly()); + oRet.ref().add(new character("secure"), pCookie.ref().getSecure()); + + returnNormal(oRet); + })); + } + + @LegacySignature(type = Type.METHOD, name = "Write", returns = "OBJECT", qualified = "progress.json.objectmodel.jsonobject", parameters = + { + @LegacyParameter(name = "pHeader", type = "OBJECT", qualified = "openedge.net.http.httpheader", mode = "INPUT") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + protected object write_2(final object _pHeader) + { + object pHeader = TypeFactory.initInput(_pHeader); + object oRet = TypeFactory.object(JsonObject.class); + + return function(JsonEntityWriter.class, this, "Write", object.class, new Block((Body) () -> { + + oRet.assign(ObjectOps.newInstance(JsonObject.class)); + oRet.ref().add(new character("name"), pHeader.ref().getName()); + oRet.ref().add(new character("value"), pHeader.ref().getValue()); + + // 4GL quirk, always invalid object, missing return maybe + returnNormal(oRet); + })); + } /** * Write error. * - * @param error error to be written. + * @param poError error to be written. */ - private void writeError(object error) - { - // TODO: immplement + @LegacySignature(type = Type.METHOD, name = "WriteError", parameters = + { + @LegacyParameter(name = "poError", type = "OBJECT", qualified = "progress.lang.error", mode = "INPUT") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + protected void writeError(object _poError) + { + object poError = TypeFactory.initInput(_poError); + object jsonErr = TypeFactory.object(JsonObject.class); + object jsonArr = TypeFactory.object(JsonArray.class); + + + internalProcedure(JsonEntityWriter.class, this, "WriteError", new Block((Body) () -> { + + jsonErr.assign(JsonExport.serializeError(poError)); + + if (this.entity._isValid()) + { + if (ObjectOps.typeOf(this.entity, JsonArray.class).booleanValue()) + { + ObjectOps.cast(this.entity, JsonArray.class).ref().add_2(jsonErr); + } + else if (ObjectOps.typeOf(this.entity, JsonObject.class).booleanValue()) + { + jsonArr.assign(ObjectOps.newInstance(JsonArray.class)); + jsonArr.ref().add_2(ObjectOps.cast(this.entity, JsonObject.class)); + jsonArr.ref().add_2(jsonErr); + + this.entity.assign(jsonArr); + } + } + else + { + this.entity.assign(jsonErr); + } + })); + } + + @LegacySignature(type = Type.METHOD, name = "WriteMap", parameters = + { + @LegacyParameter(name = "pValue", type = "OBJECT", qualified = "openedge.core.collections.imap", mode = "INPUT") + }) + protected void writeMap(final object _pValue) + { + object pValue = TypeFactory.initInput(_pValue); + object oRet = TypeFactory.object(JsonObject.class); + + internalProcedure(JsonEntityWriter.class, this, "WriteMap", new Block((Body) () -> { + + Assert.notNull(pValue, new character("Map")); + + oRet.assign(ObjectOps.newInstance(JsonObject.class)); + + if (pValue.ref() instanceof LegacyMap) + { + ((LegacyMap) pValue.ref()).getMap().forEach((k, v) -> { + if (!k.isUnknown()) + { + if (!v.isUnknown()) + { + oRet.ref().add(k.ref().toLegacyString(), v.ref().toLegacyString()); + } + else + { + oRet.ref().addNull(k.ref().toLegacyString()); + } + } + }); + } + else + { + Iiterator it = pValue.ref().getEntrySet().ref().iterator().ref(); + + while (it.hasNext().booleanValue()) + { + object entry = ObjectOps.cast(it.next_(), LegacyMapEntry.class); + if (!entry.isUnknown() && !entry.ref().getKey().isUnknown()) + { + if (!entry.ref().getValue().isUnknown()) + { + oRet.ref().add(entry.ref().getKey().ref().toLegacyString(), entry.ref().getValue().ref().toLegacyString()); + } + else + { + oRet.ref().addNull(entry.ref().getKey().ref().toLegacyString()); + } + } + } + } + + this.entity.assign(oRet); + })); + } + + @LegacySignature(type = Type.METHOD, name = "WriteTuple", parameters = + { + @LegacyParameter(name = "pKey", type = "OBJECT", qualified = "progress.lang.object", mode = "INPUT"), + @LegacyParameter(name = "pValue", type = "OBJECT", qualified = "progress.lang.object", mode = "INPUT") + }) + protected void writeTuple(final object _pKey, final object _pValue) + { + object pKey = TypeFactory.initInput(_pKey); + object pValue = TypeFactory.initInput(_pValue); + object oRet = TypeFactory.object(JsonObject.class); + + internalProcedure(JsonEntityWriter.class, this, "WriteTuple", new Block((Body) () -> { + + Assert.notNull(pKey, new character("Tuple key")); + + character key = pKey.ref().toLegacyString(); + character val = pValue.isUnknown() ? new character() : pValue.ref().toLegacyString(); + + if (this.entity.isUnknown() || ObjectOps.typeOf(this.entity, JsonArray.class).booleanValue()) + { + oRet.assign(ObjectOps.newInstance(JsonObject.class)); + + oRet.ref().add(key, val); + + if (this.entity.isUnknown()) + { + this.entity.assign(oRet); + } + else + { + ObjectOps.cast(this.entity, JsonArray.class).ref().add_2(oRet); + } + } + else if (ObjectOps.typeOf(this.entity, JsonObject.class).booleanValue()) + { + oRet.assign(ObjectOps.cast(this.entity, JsonObject.class)); + if (oRet.ref().has(key).booleanValue()) + { + oRet.ref().set(key, val); + } + else + { + oRet.ref().add(key, val); + } + } + })); } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartEntityWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartEntityWriter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartEntityWriter.java 2021-03-30 07:07:06 +0000 @@ -10,6 +10,7 @@ ** 003 CA 20191024 Added method support levels and updated the class support level. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 005 ME 20210330 Complete implementation as per OE12.2. */ /* @@ -71,6 +72,7 @@ import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.function; import static com.goldencode.p2j.util.BlockManager.internalProcedure; +import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; import java.io.*; @@ -80,12 +82,15 @@ import org.apache.james.mime4j.*; import org.apache.james.mime4j.stream.*; +import com.goldencode.p2j.oo.common.support.ImemptrHolder; import com.goldencode.p2j.oo.core.*; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.oo.net.*; import com.goldencode.p2j.oo.net.http.*; import com.goldencode.p2j.oo.net.http.filter.writer.*; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.util.InternalEntry.*; /** @@ -130,9 +135,9 @@ }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override - public void setBoundary(character boundary) + public void setBoundary(character _boundary) { - character pcEncoding = TypeFactory.initInput(boundary); + character boundary = TypeFactory.initInput(_boundary); internalProcedure(this, "Boundary", new Block((Body) () -> { this.boundary.assign(boundary); @@ -146,6 +151,7 @@ { externalProcedure(MultipartEntityWriter.this, new Block((Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); { } })); @@ -164,6 +170,8 @@ __net_http_filter_payload_MessageWriter_constructor__( ObjectOps.getLegacyClass(MultipartEntity.class) ); + + this.boundary.assign("X-OPENEDGE-BOUNDARY"); })); } @@ -181,25 +189,35 @@ }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override - public int64 write(object poData) + public int64 write(object _poData) { + object poData = TypeFactory.initInput(_poData); + return function(this, "Write", int64.class, new Block((Body) () -> { - object mptr = TypeFactory.object(Memptr.class); - Assert.notNull(poData); - if (ObjectOps.typeOf(poData, Memptr.class).booleanValue()) - { - mptr.assign(ObjectOps.cast(poData, Memptr.class)); + if (!poData._isValid()) + { + returnNormal(0); + } + else if (ObjectOps.typeOf(poData, ImemptrHolder.class).booleanValue()) + { + returnNormal(write(ObjectOps.cast(poData, ImemptrHolder.class).ref().getValue())); } else if (ObjectOps.typeOf(poData, ByteBucket.class).booleanValue()) { + object mptr = TypeFactory.object(Memptr.class); + mptr.assign(ObjectOps.cast(poData, ByteBucket.class).ref().getBytes()); + returnNormal(write(mptr.ref().getValue())); + } + else if (ObjectOps.typeOf(poData, MessagePart.class).booleanValue()) + { + returnNormal(write_1(ObjectOps.cast(poData, MessagePart.class))); } else { unsupported(poData); } - returnNormal(write(mptr.ref().getValue())); })); } @@ -216,10 +234,14 @@ }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override - public int64 write(memptr ptr) + public int64 write(memptr _ptr) { + memptr ptr = TypeFactory.initInput(_ptr); + return function(this, "Write", int64.class, new Block((Body) () -> { + Assert.notNull(ptr.length(), new character("Data size")); + try { returnNormal(parseMutipart(ptr.getByteArray())); @@ -314,4 +336,37 @@ } return new int64(bytes.length); } + + @LegacySignature(type = Type.METHOD, name = "Write", returns = "INT64", parameters = + { + @LegacyParameter(name = "pData", type = "OBJECT", qualified = "openedge.net.messagepart", mode = "INPUT") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + public int64 write_1(final object _pData) + { + object pData = TypeFactory.initInput(_pData); + + return function(MultipartEntityWriter.class, this, "Write", int64.class, new Block((Body) () -> + { + if (pData._isValid()) + { + object mEntity = TypeFactory.object(MultipartEntity.class); + + if (!this.entity._isValid()) + { + mEntity.assign(ObjectOps.newInstance(MultipartEntity.class)); + mEntity.ref().setBoundary(boundary); + this.entity.assign(mEntity); + } + else + { + mEntity.assign(ObjectOps.cast(this.entity, MultipartEntity.class)); + } + + mEntity.ref().addPart(pData); + } + + returnNormal(0); + })); + } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartFormBodyWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartFormBodyWriter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartFormBodyWriter.java 2021-03-31 09:51:16 +0000 @@ -9,6 +9,7 @@ ** ** 002 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 003 ME 20210331 Implemented as per OE12.2. */ /* @@ -70,28 +71,47 @@ import com.goldencode.p2j.util.BlockManager.Action; import com.goldencode.p2j.util.BlockManager.Condition; -import static com.goldencode.p2j.report.ReportConstants.CVT_LVL_FULL; -import static com.goldencode.p2j.report.ReportConstants.RT_LVL_STUB; +import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.*; import static com.goldencode.p2j.util.InternalEntry.Type; +import java.io.File; + +import com.goldencode.p2j.oo.common.support.ImemptrHolder; +import com.goldencode.p2j.oo.core.Assert; +import com.goldencode.p2j.oo.core.ByteBucket; +import com.goldencode.p2j.oo.core.LegacyString; +import com.goldencode.p2j.oo.core.collections.Iiterator; +import com.goldencode.p2j.oo.core.collections.Imap; +import com.goldencode.p2j.oo.core.collections.LegacyMap; +import com.goldencode.p2j.oo.core.collections.LegacyMapEntry; +import com.goldencode.p2j.oo.io.FileInputStream; +import com.goldencode.p2j.oo.net.FileTypeRegistry; +import com.goldencode.p2j.oo.net.MessagePart; +import com.goldencode.p2j.oo.net.MultipartEntity; +import com.goldencode.p2j.oo.net.http.HttpHeader; +import com.goldencode.p2j.oo.net.http.HttpHeaderBuilder; +import com.goldencode.p2j.oo.net.http.filter.writer.BodyWriterBuilder; + /** * Business logic (converted to Java from the 4GL source code * in OpenEdge/Net/HTTP/Filter/Payload/MultipartFormBodyWriter.cls). */ @LegacyResource(resource = "OpenEdge.Net.HTTP.Filter.Payload.MultipartFormBodyWriter") -@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) +@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public class MultipartFormBodyWriter extends com.goldencode.p2j.oo.net.http.filter.payload.MessageWriter implements com.goldencode.p2j.oo.net.ISupportMultipartEntity, com.goldencode.p2j.oo.core.ISupportEncoding { + private static final String CRLF = "\r\n"; + @LegacySignature(type = Type.PROPERTY, name = "Boundary") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private character boundary = TypeFactory.character(); @LegacySignature(type = Type.PROPERTY, name = "Encoding") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private character encoding = TypeFactory.character(); public void __net_http_filter_payload_MultipartFormBodyWriter_execute__() @@ -105,7 +125,7 @@ } @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Boundary") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character getBoundary() { return function(MultipartFormBodyWriter.class, this, "Boundary", character.class, new Block((Body) () -> @@ -118,7 +138,7 @@ { @LegacyParameter(name = "var", type = "CHARACTER", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setBoundary(final character _var) { character var = TypeFactory.initInput(_var); @@ -130,7 +150,7 @@ } @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Encoding") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public character getEncoding() { return function(MultipartFormBodyWriter.class, this, "Encoding", character.class, new Block((Body) () -> @@ -143,7 +163,7 @@ { @LegacyParameter(name = "var", type = "CHARACTER", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void setEncoding(final character _var) { character var = TypeFactory.initInput(_var); @@ -155,24 +175,28 @@ } @LegacySignature(type = Type.CONSTRUCTOR) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_http_filter_payload_MultipartFormBodyWriter_constructor__() { internalProcedure(MultipartFormBodyWriter.class, this, "__net_http_filter_payload_MultipartFormBodyWriter_constructor__", new Block((Body) () -> { - UnimplementedFeature.missing("MultipartFormBodyWriter CONSTRUCTOR"); - //__net_http_filter_payload_MessageWriter_constructor__(); + __net_http_filter_payload_MessageWriter_constructor__(ObjectOps.getLegacyClass(ByteBucket.class)); + this.boundary.assign(SecurityOps.convertUniversalUIDToGlobalUID()); })); } @LegacySignature(type = Type.METHOD, name = "Open") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void open() { internalProcedure(MultipartFormBodyWriter.class, this, "Open", new Block((Body) () -> { - UnimplementedFeature.missing("MultipartFormBodyWriter:Open METHOD"); + if (!this.entity._isValid()) + { + this.entity.assign(ByteBucket.instance()); + } + super.open(); })); } @@ -187,15 +211,92 @@ return function(MultipartFormBodyWriter.class, this, "Write", int64.class, new Block((Body) () -> { - UnimplementedFeature.missing("MultipartFormBodyWriter:Write METHOD"); - })); - } - + Assert.notNull(pData, new character("Multipart entity")); + + ByteBucket bucket = ObjectOps.cast(this.entity, ByteBucket.class).ref(); + long init = bucket.getSize().longValue(); + + this.boundary.assign(pData.ref().getBoundary()); + + character chunk = pData.ref().getPrologue(); + + // prologue + if (!TextOps.isEmpty(chunk)) + bucket.putString(new longchar(TextOps.substitute("&1&2", chunk, CRLF))); + + for (int i = 0; i < pData.ref().getSize().intValue(); i++) + { + bucket.putString(new longchar(TextOps.substitute("--&1&2", this.boundary, CRLF))); + writePart(bucket, pData.ref().getPart(new integer(i + 1))); + } + + // end data + bucket.putString(new longchar(TextOps.substitute("--&1--&2", this.boundary, CRLF))); + + // epilogue + chunk.assign(pData.ref().getEpilogue()); + if (!TextOps.isEmpty(chunk)) + { + bucket.putString(new longchar(chunk)); + } + else + { + // end + bucket.putString(new longchar(CRLF)); + } + + returnNormal(new int64(bucket.getSize().longValue() - init)); + })); + } + + private void writePart (ByteBucket bucket, object part) { + object msgPart = ByteBucket.instance(); + object writer = BodyWriterBuilder.build(part) + .ref().writeTo(msgPart) + .ref().getWriter(); + + Assert.notNull(writer, new character("Part writer")); + writer.ref().open(); + writer.ref().write(part.ref().getBody()); + writer.ref().close(); + + object[] oHhext[] = new object[][] + { + TypeFactory.objectExtent(HttpHeader.class) + }; + integer iRsp = TypeFactory.integer(); + + iRsp.assign(part.ref().getHeaders().ref().getAll(new OutputExtentParameter>() + { + public object[] getVariable() + { + return oHhext[0]; + } + + public void setVariable(final object[] newRef) + { + oHhext[0] = newRef; + } + })); + + for (int i = oHhext[0].length; i > 0; i--) + { + bucket.putString(new longchar(TextOps.substitute("&1&2", oHhext[0][i - 1].ref().toLegacyString(), CRLF))); + } + + // end header + bucket.putString(new longchar(CRLF)); + + bucket.putBytes(msgPart); + // end part + bucket.putString(new longchar(CRLF)); + } + @LegacySignature(returns = "INT64", type = Type.METHOD, name = "Write", parameters = { @LegacyParameter(name = "pData", type = "OBJECT", qualified = "progress.lang.object", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public int64 write(final object _pData) { @@ -203,7 +304,22 @@ return function(MultipartFormBodyWriter.class, this, "Write", int64.class, new Block((Body) () -> { - UnimplementedFeature.missing("MultipartFormBodyWriter:Write METHOD"); + if (!pData._isValid()) + { + returnNormal(write_2(ObjectOps.newInstance(LegacyMap.class))); + } + else if (ObjectOps.typeOf(pData, Imap.class).booleanValue()) + { + returnNormal(write_2(ObjectOps.cast(pData, Imap.class))); + } + else if (ObjectOps.typeOf(pData, MultipartEntity.class).booleanValue()) + { + returnNormal(write_1(ObjectOps.cast(pData, MultipartEntity.class))); + } + else + { + unsupported(pData); + } })); } @@ -211,14 +327,38 @@ { @LegacyParameter(name = "pData", type = "OBJECT", qualified = "openedge.core.collections.imap", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) protected int64 write_2(final object _pData) { object pData = TypeFactory.initInput(_pData); return function(MultipartFormBodyWriter.class, this, "Write", int64.class, new Block((Body) () -> { - UnimplementedFeature.missing("MultipartFormBodyWriter:Write METHOD"); + ByteBucket bucket = ObjectOps.cast(this.entity, ByteBucket.class).ref(); + + long init = bucket.getSize().longValue(); + + // encodings + if (!TextOps.isEmpty(this.encoding)) + writeField(ObjectOps.newInstance(LegacyString.class, "I", "_charset_"), ObjectOps.newInstance(LegacyString.class, "I", this.encoding)); + + Iiterator it = pData.ref().getEntrySet().ref().iterator().ref(); + object entry = TypeFactory.object(LegacyMapEntry.class); + + while (it.hasNext().booleanValue()) + { + entry = ObjectOps.cast(it.next_(), LegacyMapEntry.class); + bucket.putString(new longchar(TextOps.substitute("--&1&2", this.boundary, CRLF))); + writeField(entry.ref().getKey(), entry.ref().getValue()); + } + + // end data + bucket.putString(new longchar(TextOps.substitute("--&1--&2", this.boundary, CRLF))); + + // end + bucket.putString(new longchar(CRLF)); + + returnNormal(new int64(bucket.getSize().longValue() - init)); })); } @@ -227,7 +367,7 @@ @LegacyParameter(name = "pKey", type = "OBJECT", qualified = "progress.lang.object", mode = "INPUT"), @LegacyParameter(name = "pValue", type = "OBJECT", qualified = "progress.lang.object", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) protected void writeField( final object _pKey, final object _pValue) @@ -237,7 +377,70 @@ internalProcedure(MultipartFormBodyWriter.class, this, "WriteField", new Block((Body) () -> { - UnimplementedFeature.missing("MultipartFormBodyWriter:WriteField METHOD"); + Assert.notNull(pKey, new character("Form field name")); + + ByteBucket bucket = ObjectOps.cast(this.entity, ByteBucket.class).ref(); + character contentType = TypeFactory.character("text/plain"); + + object hContentDispo = HttpHeaderBuilder.build(new character("Content-Disposition")) + .ref().value(new character("form-data")) + .ref().getHeader(); + + hContentDispo.ref().setParameterValue(new character("name"), TextOps.substitute("\"&1\"", pKey.ref().toLegacyString())); + + if (pValue._isValid()) + { + if (ObjectOps.typeOf(pValue, ImemptrHolder.class).booleanValue()) + { + contentType.assign("application/octet-stream"); + } + else if (ObjectOps.typeOf(pValue, FileInputStream.class).booleanValue()) + { + object fs = ObjectOps.cast(pValue, FileInputStream.class); + + String fileName = new File(fs.ref().getFileName().getValue()).getName(); + String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1); + + hContentDispo.ref().setParameterValue(new character("filename"), TextOps.substitute("\"&1\"", fileName)); + + contentType.assign(FileTypeRegistry.getRegistry().ref().get(new character(fileExt))); + } + + } + + bucket.putString(new longchar(TextOps.substitute("&1&2", hContentDispo.ref().toLegacyString(), CRLF))); + + if (pValue._isValid()) + { + // content-type header + object hContentType = HttpHeaderBuilder.build(new character("Content-Type")) + .ref().value(contentType) + .ref().getHeader(); + bucket.putString(new longchar(TextOps.substitute("&1&2", hContentType.ref().toLegacyString(), CRLF))); + + // end header + bucket.putString(new longchar(CRLF)); + + // part body + object msgPart = ByteBucket.instance(); + object writer = BodyWriterBuilder.build(contentType) + .ref().writeTo(msgPart) + .ref().getWriter(); + + Assert.notNull(writer, new character("Part writer")); + writer.ref().open(); + writer.ref().write(pValue); + writer.ref().close(); + + bucket.putBytes(msgPart); + } + else + { + // end header + bucket.putString(new longchar(CRLF)); + } + // end part + bucket.putString(new longchar(CRLF)); })); } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartFormEntityWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartFormEntityWriter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartFormEntityWriter.java 2021-03-31 11:09:54 +0000 @@ -9,6 +9,7 @@ ** ** 002 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210331 Update support levels, WEB-CONTEXT not available. */ /* @@ -70,8 +71,7 @@ import com.goldencode.p2j.util.BlockManager.Action; import com.goldencode.p2j.util.BlockManager.Condition; -import static com.goldencode.p2j.report.ReportConstants.CVT_LVL_FULL; -import static com.goldencode.p2j.report.ReportConstants.RT_LVL_STUB; +import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.*; import static com.goldencode.p2j.util.InternalEntry.Type; @@ -95,7 +95,7 @@ } @LegacySignature(type = Type.CONSTRUCTOR) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_http_filter_payload_MultipartFormEntityWriter_constructor__() { internalProcedure(MultipartFormEntityWriter.class, this, "__net_http_filter_payload_MultipartFormEntityWriter_constructor__", new Block((Body) () -> @@ -116,7 +116,8 @@ return function(MultipartFormEntityWriter.class, this, "Write", int64.class, new Block((Body) () -> { - UnimplementedFeature.missing("MultipartFormEntityWriter:Write METHOD"); + // TODO: this needs to get data from WEB-CONTEXT handle (webspeed) + returnNormal(super.write(pData)); })); } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartFormSimpleEntityWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartFormSimpleEntityWriter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/MultipartFormSimpleEntityWriter.java 2021-03-31 09:56:13 +0000 @@ -9,6 +9,7 @@ ** ** 002 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210330 Update support levels, WEB-CONTEXT not available. */ /* @@ -70,8 +71,7 @@ import com.goldencode.p2j.util.BlockManager.Action; import com.goldencode.p2j.util.BlockManager.Condition; -import static com.goldencode.p2j.report.ReportConstants.CVT_LVL_FULL; -import static com.goldencode.p2j.report.ReportConstants.RT_LVL_STUB; +import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.*; import static com.goldencode.p2j.util.InternalEntry.Type; @@ -80,7 +80,7 @@ * in OpenEdge/Net/HTTP/Filter/Payload/MultipartFormSimpleEntityWriter.cls). */ @LegacyResource(resource = "OpenEdge.Net.HTTP.Filter.Payload.MultipartFormSimpleEntityWriter") -@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) +@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL) public class MultipartFormSimpleEntityWriter extends com.goldencode.p2j.oo.net.http.filter.payload.MultipartEntityWriter { @@ -95,7 +95,7 @@ } @LegacySignature(type = Type.CONSTRUCTOR) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_http_filter_payload_MultipartFormSimpleEntityWriter_constructor__() { internalProcedure(MultipartFormSimpleEntityWriter.class, this, "__net_http_filter_payload_MultipartFormSimpleEntityWriter_constructor__", new Block((Body) () -> @@ -108,7 +108,7 @@ { @LegacyParameter(name = "pData", type = "MEMPTR", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL) @Override public int64 write(final memptr _pData) { @@ -116,7 +116,11 @@ return function(MultipartFormSimpleEntityWriter.class, this, "Write", int64.class, new Block((Body) () -> { - UnimplementedFeature.missing("MultipartFormSimpleEntityWriter:Write METHOD"); + // TODO: implementation specific to WEBSPEED (session:client-type = 'WEBSPEED') + // SessionUtil is missing client-type support + // WebContext system handle not available + + returnNormal(super.write(pData)); })); } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/StringBodyWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/StringBodyWriter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/StringBodyWriter.java 2021-03-25 12:03:36 +0000 @@ -9,6 +9,7 @@ ** 002 CA 20191024 Added method support levels and updated the class support level. ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 004 ME 20210325 Implement/fix functionality for OE12.2. */ /* @@ -70,12 +71,19 @@ import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.function; import static com.goldencode.p2j.util.BlockManager.internalProcedure; +import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; +import static com.goldencode.p2j.util.BlockManager.undoThrow; +import com.goldencode.p2j.oo.common.support.IcharacterHolder; +import com.goldencode.p2j.oo.common.support.IlongcharHolder; +import com.goldencode.p2j.oo.common.support.ImemptrHolder; import com.goldencode.p2j.oo.core.*; import com.goldencode.p2j.oo.io.*; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.util.InternalEntry.*; /** @@ -96,6 +104,7 @@ { externalProcedure(StringBodyWriter.this, new Block((Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); { } })); @@ -127,10 +136,12 @@ { internalProcedure(this, "Open", new Block((Body) () -> { - if (!entity.isValid().booleanValue()) + if (!entity._isValid()) { - entity.assign(ByteBucket.instance().ref()); + entity.assign(ByteBucket.instance()); } + + super.open(); })); } @@ -175,8 +186,8 @@ { longchar lc = TypeFactory.longchar(); new LobCopy(new SourceLob(_pData), new TargetLob(lc)).run(); - write(lc); - returnNormal(new int64(TextOps.lengthOf(lc))); + + returnNormal(write(lc)); })); } @@ -199,56 +210,56 @@ return function(this, "Write", int64.class, new Block((Body) () -> { longchar lc = TypeFactory.longchar(); - if (!poData.isValid().booleanValue()) + + if (!poData._isValid()) { returnNormal(new int64(0)); - } - - if (ObjectOps.typeOf(poData, LegacyString.class).booleanValue()) - { - object str = ObjectOps.cast(poData, LegacyString.class); - lc.assign(poData); - bucket().putString(lc); - returnNormal(new int64(TextOps.lengthOf(lc))); - - } - - if (ObjectOps.typeOf(poData, FileInputStream.class).booleanValue()) + } + else if (ObjectOps.typeOf(poData, IcharacterHolder.class).booleanValue()) + { + lc.assign(ObjectOps.cast(poData, IcharacterHolder.class).ref().getValue()); + bucket().putString(lc); + } + else if (ObjectOps.typeOf(poData, IlongcharHolder.class).booleanValue()) + { + lc.assign(ObjectOps.cast(poData, IlongcharHolder.class).ref().getValue()); + bucket().putString(lc); + } + else if (ObjectOps.typeOf(poData, FileInputStream.class).booleanValue()) { object fis = ObjectOps.cast(poData, FileInputStream.class); - String fn = fis.ref().getFileName().getValue(); - String ext = ""; - int dp = fn.lastIndexOf('.'); - if (dp >=0 ) - { - ext = fn.substring(dp + 1); - } - if ("txt".equalsIgnoreCase(ext)) - { - returnNormal(writeFileStream(fis)); - } - String msg = String.format("Unsupported file extension .%s for TXT", ext); - ErrorManager.recordOrThrowError(0, msg, false, true); + + String[] result = TextOps.splitInt(fis.ref().getFileName().getValue(), '.'); + + if (result[result.length - 1].equalsIgnoreCase("txt")) + { + returnNormal(this.writeFileStream(fis)); + } + + String msg = String.format("Unsupported file extension .%s for TXT", result[result.length - 1]); + undoThrow(AppError.newInstance(msg, 0)); } - if (ObjectOps.typeOf(poData, ByteBucket.class).booleanValue()) + else if (ObjectOps.typeOf(poData, ByteBucket.class).booleanValue()) { object mb = ObjectOps.cast(poData, ByteBucket.class); lc.assign(mb.ref().getString()); bucket().putString(lc); - returnNormal(new int64(TextOps.lengthOf(lc))); } - if (ObjectOps.typeOf(poData, Memptr.class).booleanValue()) - { - object mptr = ObjectOps.cast(poData, Memptr.class); - lc.assign(mptr.ref().getString(new int64(1))); - bucket().putString(lc); - returnNormal(new int64(TextOps.lengthOf(lc))); - } - lc.assign(poData.toString()); - bucket().putString(lc); - returnNormal(new int64(TextOps.lengthOf(lc))); + else if (ObjectOps.typeOf(poData, ImemptrHolder.class).booleanValue()) + { + object mptr = ObjectOps.cast(poData, ImemptrHolder.class); + lc.assign(mptr.ref().getValue().getString(new int64(1))); + bucket().putString(lc); + } + else + { + lc.assign(poData.ref().toLegacyString()); + bucket().putString(lc); + } + + returnNormal(TextOps.byteLength(lc)); })); } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/StringEntityWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/StringEntityWriter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/StringEntityWriter.java 2021-03-25 12:03:36 +0000 @@ -10,6 +10,7 @@ ** 003 ME 20200413 Use the ISupportEncoding interface from Net package. ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 005 ME 20210325 Implement/fix functionality for OE12.2. */ /* @@ -71,14 +72,20 @@ import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.function; import static com.goldencode.p2j.util.BlockManager.internalProcedure; +import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; import com.goldencode.p2j.oo.net.ISupportEncoding; +import com.goldencode.p2j.oo.common.support.IcharacterHolder; +import com.goldencode.p2j.oo.common.support.IlongcharHolder; +import com.goldencode.p2j.oo.common.support.ImemptrHolder; import com.goldencode.p2j.oo.core.*; import com.goldencode.p2j.oo.json.objectmodel.*; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.util.InternalEntry.*; /** @@ -139,6 +146,7 @@ { externalProcedure(StringEntityWriter.this, new Block((Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); { } })); @@ -175,10 +183,19 @@ @Override public int64 write(longchar _pData) { + longchar pData = TypeFactory.initInput(_pData); + return function(this, "Write", int64.class, new Block((Body) () -> { + integer iLen = TextOps.byteLength(pData); + + // 4GL quirk, why simply not null? + Assert.isZeroOrPositive(iLen, new character("Data")); + // TODO: fix encoding - this.entity.assign(ObjectOps.newInstance(LegacyString.class, "I", _pData)); + this.entity.assign(ObjectOps.newInstance(LegacyString.class, "I", pData)); + + returnNormal(iLen); })); } @@ -197,11 +214,12 @@ @Override public int64 write(character _pData) { + longchar lc = TypeFactory.longchar(); + return function(this, "Write", int64.class, new Block((Body) () -> { - longchar lc = TypeFactory.longchar(); lc.assign(_pData); - write(lc); + returnNormal(write(lc)); })); } @@ -220,18 +238,23 @@ @Override public int64 write(memptr _pData) { + memptr pData = TypeFactory.initInput(_pData); + return function(this, "Write", int64.class, new Block((Body) () -> { // TODO: fix encoding - int64 len = _pData.length(); + int64 len = pData.length(); Assert.isZeroOrPositive(len, new character("Data size")); longchar lc = TypeFactory.longchar(); + if (len.longValue() > 0) { - new LobCopy(new SourceLob(_pData), new TargetLob(lc)).run(); - this.entity.assign(ObjectOps.newInstance(LegacyString.class, "I", lc)); - returnNormal(len); + new LobCopy(new SourceLob(pData), new TargetLob(lc)).run(); } + + this.entity.assign(ObjectOps.newInstance(LegacyString.class, "I", lc)); + + returnNormal(len); })); } @@ -249,48 +272,61 @@ }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override - public int64 write(object poData) + public int64 write(object _poData) { + object poData = TypeFactory.initInput(_poData); + return function(this, "Write", int64.class, new Block((Body) () -> { - if (!poData.isValid().booleanValue()) + if (!poData._isValid()) { returnNormal(new int64(0)); } - - if (ObjectOps.typeOf(poData, JsonObject.class).booleanValue()) + else if (ObjectOps.typeOf(poData, JsonObject.class).booleanValue()) { object json = ObjectOps.cast(poData, JsonObject.class); this.encoding.assign(new character("UTF-8")); returnNormal(write(json.ref().getJsonText())); } - memptr ptr = TypeFactory.memptr(); - object mptr = TypeFactory.object(Memptr.class); - if (ObjectOps.typeOf(poData, Memptr.class).booleanValue()) - { - mptr.assign(ObjectOps.cast(poData, Memptr.class)); + else if (ObjectOps.typeOf(poData, JsonArray.class).booleanValue()) + { + object json = ObjectOps.cast(poData, JsonArray.class); + this.encoding.assign(new character("UTF-8")); + returnNormal(write(json.ref().getJsonText())); + } + else if (ObjectOps.typeOf(poData, IlongcharHolder.class).booleanValue()) + { + object lc = ObjectOps.cast(poData, IlongcharHolder.class); + returnNormal(write(lc.ref().getValue())); + } + else if (ObjectOps.typeOf(poData, IcharacterHolder.class).booleanValue()) + { + object lc = ObjectOps.cast(poData, IcharacterHolder.class); + returnNormal(write(lc.ref().getValue())); + } + else if (ObjectOps.typeOf(poData, ImemptrHolder.class).booleanValue()) + { + memptr ptr = TypeFactory.memptr(); + + try { + ptr.assign(ObjectOps.cast(poData, ImemptrHolder.class).ref().getValue()); + returnNormal(write(ptr)); + } finally { + ptr.setLength(0); + } } else if (ObjectOps.typeOf(poData, ByteBucket.class).booleanValue()) { - object bb = ObjectOps.cast(poData, ByteBucket.class); - mptr.assign(bb.ref().getBytes()); + memptr ptr = TypeFactory.memptr(); + try { + ptr.assign(ObjectOps.cast(poData, ByteBucket.class).ref().getBytes().ref().getValue()); + returnNormal(write(ptr)); + } finally { + ptr.setLength(0); + } } else { - write(poData.ref().toLegacyString()); - } - if (mptr.isValid().booleanValue()) - { - try - { - ptr.setPointerValue(mptr.ref().getPointerValue()); - ptr.setLength(mptr.ref().getSize()); - returnNormal(write(ptr)); - } - finally - { - ptr.setPointerValue(0); - ptr.setLength(0); - } + returnNormal(write(poData.ref().toLegacyString())); } })); } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/payload/XmlEntityWriter.java' --- src/com/goldencode/p2j/oo/net/http/filter/payload/XmlEntityWriter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/payload/XmlEntityWriter.java 2021-03-26 12:13:44 +0000 @@ -9,6 +9,7 @@ ** 002 CA 20191024 Added method support levels and updated the class support level. ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 004 ME 20210326 Implement as per OE12.2. */ /* @@ -70,11 +71,20 @@ import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.function; import static com.goldencode.p2j.util.BlockManager.internalProcedure; +import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; +import static com.goldencode.p2j.util.BlockManager.undoThrow; +import com.goldencode.p2j.oo.common.support.IcharacterHolder; +import com.goldencode.p2j.oo.common.support.IhandleHolder; +import com.goldencode.p2j.oo.common.support.IlongcharHolder; +import com.goldencode.p2j.oo.common.support.ImemptrHolder; import com.goldencode.p2j.oo.core.*; +import com.goldencode.p2j.oo.io.FileInputStream; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.util.InternalEntry.*; import com.goldencode.p2j.xml.*; @@ -95,6 +105,7 @@ { externalProcedure(XmlEntityWriter.this, new Block((Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); { } })); @@ -111,7 +122,7 @@ new Block((Body) () -> { __net_http_filter_payload_MessageWriter_constructor__( - ObjectOps.getLegacyClass(WidgetHandle.class) + ObjectOps.getLegacyClass(IhandleHolder.class) ); })); } @@ -131,11 +142,13 @@ @Override public int64 write(longchar _poData) { + longchar poData = TypeFactory.initInput(_poData); + return function(this, "Write", int64.class, new Block((Body) () -> { - Assert.notNull(_poData, new character("Data")); + Assert.notNull(poData, new character("Data")); int64 len = TypeFactory.int64(); - len.assign(TextOps.byteLength(_poData)); + len.assign(TextOps.byteLength(poData)); handle hdoc = TypeFactory.handle(); XmlFactory.createXDocument(hdoc); if (len.longValue() == 0) @@ -144,7 +157,7 @@ } else { - hdoc.unwrapXDocument().load(new character("longchar"), _poData, new logical(false)); + hdoc.unwrapXDocument().load(new character("longchar"), poData, new logical(false)); } this.entity.assign(ObjectOps.newInstance(WidgetHandle.class, "I", hdoc)); returnNormal(len); @@ -166,11 +179,13 @@ @Override public int64 write(character _poData) { + character poData = TypeFactory.initInput(_poData); + return function(this, "Write", int64.class, new Block((Body) () -> { longchar lc = TypeFactory.longchar(); - lc.assign(_poData); - write(lc); + lc.assign(poData); + returnNormal(write(lc)); })); } @@ -189,10 +204,13 @@ @Override public int64 write(memptr _poData) { + memptr poData = TypeFactory.initInput(_poData); + return function(this, "Write", int64.class, new Block((Body) () -> { int64 len = TypeFactory.int64(); - len.assign(_poData.length()); + len.assign(poData.length()); + handle hdoc = TypeFactory.handle(); XmlFactory.createXDocument(hdoc); if (len.longValue() == 0) @@ -201,7 +219,7 @@ } else { - hdoc.unwrapXDocument().load(new character("memptr"), _poData, new logical(false)); + hdoc.unwrapXDocument().load(new character("memptr"), poData, new logical(false)); } this.entity.assign(ObjectOps.newInstance(WidgetHandle.class, "I", hdoc)); returnNormal(len); @@ -223,14 +241,41 @@ @Override public int64 write(handle _phData) { + handle phData = TypeFactory.initInput(_phData); + return function(this, "Write", int64.class, new Block((Body) () -> { - if (!handle.isHandleTypeOf(_phData, XDocument.class)) - { - String msg = "argument is not if type XmlDocument"; - ErrorManager.recordOrThrowError(0, msg, false, true); - } - this.entity.assign(ObjectOps.newInstance(WidgetHandle.class, "I", _phData)); + if (!phData._isValid()) + { + returnNormal(0); + } + else if (handle.isHandleTypeOf(phData, XDocument.class)) + { + this.entity.assign(ObjectOps.newInstance(WidgetHandle.class, "I", phData)); + } + else if (handle.isHandleTypeOf(phData, XNodeRef.class)) + { + handle xDoc = TypeFactory.handle(); + + if (!this.entity._isValid()) + { + XmlFactory.createXDocument(xDoc); + this.entity.assign(ObjectOps.newInstance(WidgetHandle.class, "I", xDoc)); + } + else + { + xDoc.assign(ObjectOps.cast(this.entity, IhandleHolder.class).ref().getValue()); + } + + xDoc.unwrapXDocument().appendChild(phData); + } + else + { + undoThrow(AppError.newInstance(String.format("Unsupported handle type: %s", + phData.unwrapType().getResourceType().toStringMessage()), 0)); + + } + returnNormal(new int64()); })); } @@ -249,39 +294,57 @@ }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override - public int64 write(object poData) + public int64 write(object _poData) { + object poData = TypeFactory.initInput(_poData); + return function(this, "Write", int64.class, new Block((Body) () -> { - object mptr = TypeFactory.object(Memptr.class); - if (ObjectOps.typeOf(poData, Memptr.class).booleanValue()) - { - mptr.assign(ObjectOps.cast(poData, Memptr.class)); + if (!poData._isValid()) + { + returnNormal(0); + } + if (ObjectOps.typeOf(poData, IlongcharHolder.class).booleanValue()) + { + returnNormal(write(ObjectOps.cast(poData, IlongcharHolder.class).ref().getValue())); + } + if (ObjectOps.typeOf(poData, IcharacterHolder.class).booleanValue()) + { + returnNormal(write(ObjectOps.cast(poData, IcharacterHolder.class).ref().getValue())); + } + if (ObjectOps.typeOf(poData, IhandleHolder.class).booleanValue()) + { + returnNormal(write(ObjectOps.cast(poData, IhandleHolder.class).ref().getValue())); + } + if (ObjectOps.typeOf(poData, ImemptrHolder.class).booleanValue()) + { + returnNormal(write(ObjectOps.cast(poData, ImemptrHolder.class).ref().getValue())); } else if (ObjectOps.typeOf(poData, ByteBucket.class).booleanValue()) { - mptr.assign(ObjectOps.cast(poData, ByteBucket.class).ref().getBytes()); + object mptr = TypeFactory.object(Memptr.class); + + mptr = ObjectOps.cast(poData, ByteBucket.class).ref().getBytes(); + returnNormal(write(mptr.ref().getValue())); + } + else if (ObjectOps.typeOf(poData, FileInputStream.class).booleanValue()) + { + object fis = ObjectOps.cast(poData, FileInputStream.class); + + String[] result = TextOps.splitInt(fis.ref().getFileName().getValue(), '.'); + + if (result[result.length - 1].equalsIgnoreCase("xml") || result[result.length - 1].equalsIgnoreCase("xsd")) + { + returnNormal(this.writeFileStream(fis)); + } + + String msg = String.format("Unsupported file extension .%s for XML", result[result.length - 1]); + undoThrow(AppError.newInstance(msg, 0)); } else { unsupported(poData); } - handle hdoc = TypeFactory.handle(); - XmlFactory.createXDocument(hdoc); - int64 len = TypeFactory.int64(); - len.assign(mptr.ref().getSize()); - if (len.longValue() == 0) - { - logMessage(new character(String.format( - "Zero-length data received in %s", poData.ref().toLegacyString().getValue())), - new integer(5)); - } - else - { - hdoc.unwrapXDocument().load(new character("memptr"), mptr.ref().getValue(), new logical(false)); - } - this.entity.assign(ObjectOps.newInstance(WidgetHandle.class, "I", hdoc)); - returnNormal(len); })); } } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/status/AuthorizationStatusFilter.java' --- src/com/goldencode/p2j/oo/net/http/filter/status/AuthorizationStatusFilter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/status/AuthorizationStatusFilter.java 2021-03-31 13:07:03 +0000 @@ -11,6 +11,7 @@ ** - TypeFactory.initInput will take care of this. ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 004 ME 20210331 Implement as per OE12.2. */ /* @@ -68,6 +69,8 @@ package com.goldencode.p2j.oo.net.http.filter.status; +import com.goldencode.p2j.oo.core.Assert; +import com.goldencode.p2j.oo.core.IAdaptable; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.oo.net.http.*; import com.goldencode.p2j.util.*; @@ -88,7 +91,7 @@ * */ @LegacyResource(resource = "OpenEdge.Net.HTTP.Filter.Status.AuthorizationStatusFilter") -@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) +@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public class AuthorizationStatusFilter extends BaseObject implements IHttpMessageWriter @@ -96,6 +99,9 @@ @LegacySignature(type = Type.PROPERTY, name = "Message") private object message = TypeFactory.object(com.goldencode.p2j.oo.net.http.IhttpMessage.class); + /** Authorization challenge */ + private final character challenge = TypeFactory.character(); + public void __net_http_filter_status_AuthorizationStatusFilter_execute__() { externalProcedure(AuthorizationStatusFilter.class, AuthorizationStatusFilter.this, new Block((Init) () -> @@ -111,7 +117,7 @@ } @LegacySignature(returns = "OBJECT", qualified = "OpenEdge.Net.HTTP.IHttpMessage", type = Type.GETTER, name = "Message") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public object getMessage() { return function(AuthorizationStatusFilter.class, this, "Message", object.class, new Block((Body) () -> @@ -124,7 +130,7 @@ { @LegacyParameter(name = "poMessage", type = "OBJECT", qualified = "openedge.net.http.ihttprequest", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_http_filter_status_AuthorizationStatusFilter_constructor__(final object _poMessage) { object poMessage = TypeFactory.initInput(_poMessage); @@ -132,6 +138,8 @@ internalProcedure(AuthorizationStatusFilter.class, this, "__net_http_filter_status_AuthorizationStatusFilter_constructor__", new Block((Body) () -> { __lang_BaseObject_constructor__(); + Assert.notNull(poMessage, new character("Http request")); + this.message.assign(poMessage); })); } @@ -140,12 +148,12 @@ * times without requiring a new writer each time. */ @LegacySignature(type = Type.METHOD, name = "Open") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void open() { internalProcedure(AuthorizationStatusFilter.class, this, "Open", new Block((Body) () -> { - UnimplementedFeature.missing("AuthorizationStatusFilter:Open METHOD"); + this.challenge.assign(""); })); } @@ -159,15 +167,21 @@ @LegacyParameter(name="poData", type = "OBJECT", qualified = "Progress.Lang.Object", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void write(object _poData) { - object poData = TypeFactory.initInput(_poData); + object poData = TypeFactory.initInput(_poData); - internalProcedure(AuthorizationStatusFilter.class, this, "Write", new Block((Body) () -> - { - UnimplementedFeature.missing("AuthorizationStatusFilter:Write5 METHOD"); - })); + internalProcedure(this, "Write", new Block((Body) () -> { + Assert.isType(poData, ObjectOps.getLegacyClass(IhttpResponse.class)); + + object response = ObjectOps.cast(poData, IhttpResponse.class); + + if (response.ref().hasHeader(new character("WWW-Authenticate")).booleanValue()) + { + this.challenge.assign(response.ref().getHeader(new character("WWW-Authenticate")).ref().getValue()); + } + })); } /** @@ -179,13 +193,16 @@ { @LegacyParameter(name="pcData", type = "LONGCHAR", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - public void write(longchar pcData) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + public void write(longchar _pcData) { + longchar pcData = TypeFactory.initInput(_pcData); + internalProcedure(AuthorizationStatusFilter.class, this, "Write", new Block((Body) () -> { - UnimplementedFeature.missing("AuthorizationStatusFilter:Write4 METHOD"); - })); + Assert.notNullOrEmpty(pcData, new character("Challenge")); + this.challenge.assign(pcData); + })); } /** @@ -197,15 +214,10 @@ { @LegacyParameter(name="phData", type = "HANDLE", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void write(handle _phData) { - handle phData = TypeFactory.initInput(_phData); - - internalProcedure(AuthorizationStatusFilter.class, this, "Write", new Block((Body) () -> - { - UnimplementedFeature.missing("AuthorizationStatusFilter:Write3 METHOD"); - })); + // no-op } /** @@ -217,12 +229,15 @@ { @LegacyParameter(name="pcData", type = "CHARACTER", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - public void write(character pcData) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + public void write(character _pcData) { + character pcData = TypeFactory.initInput(_pcData); + internalProcedure(AuthorizationStatusFilter.class, this, "Write", new Block((Body) () -> { - UnimplementedFeature.missing("AuthorizationStatusFilter:Write2 METHOD"); + Assert.notNullOrEmpty(pcData, new character("Challenge")); + this.challenge.assign(pcData); })); } @@ -235,14 +250,15 @@ { @LegacyParameter(name="pmData", type = "MEMPTR", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void write(memptr _pmData) { memptr pmData = TypeFactory.initInput(_pmData); internalProcedure(AuthorizationStatusFilter.class, this, "Write", new Block((Body) () -> { - UnimplementedFeature.missing("AuthorizationStatusFilter:Write1 METHOD"); + Assert.isPositive(pmData.length(), new character("Data size")); + this.challenge.assign(pmData.getString(1)); })); } @@ -250,25 +266,51 @@ * Flushes data to the output location. What data is flushed depends on the implementation */ @LegacySignature(type = Type.METHOD, name = "Flush") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) public void flush() { - internalProcedure(AuthorizationStatusFilter.class, this, "Flush", new Block((Body) () -> - { - UnimplementedFeature.missing("AuthorizationStatusFilter:Flush METHOD"); - })); + internalProcedure(AuthorizationStatusFilter.class, this, "Flush", new Block((Body) () -> { + object authReq = TypeFactory + .object(IAuthenticatedRequest.class); + + if (challenge.isUnknown()) + challenge.assign(""); + + character authMethod = TextOps.entry(1, challenge, " "); + + if (TextOps.isEmpty(authMethod)) + authMethod.assign(AuthenticationMethodEnum.none); + + if (ObjectOps.typeOf(this.message, IAdaptable.class).booleanValue()) + { + authReq.assign(ObjectOps.cast(this.message, IAdaptable.class).ref() + .getAdapter(ObjectOps.getLegacyClass(IAuthenticatedRequest.class))); + } + + if (!authReq._isValid()) + { + if (!ObjectOps.typeOf(this.message, IAuthenticatedRequest.class).booleanValue()) + { + this.message.assign(RequestBuilder.decorateRequest(ObjectOps.getLegacyClass(IAuthenticatedRequest.class), + ObjectOps.cast(this.message, IhttpRequest.class))); + } + authReq.assign(ObjectOps.cast(this.message, IAuthenticatedRequest.class)); + } + + authReq.ref().setChallenge(authMethod, challenge); + })); } /** * Closes the output. See Open() */ @LegacySignature(type = Type.METHOD, name = "Close") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void close() { internalProcedure(AuthorizationStatusFilter.class, this, "Close", new Block((Body) () -> { - UnimplementedFeature.missing("AuthorizationStatusFilter:Close METHOD"); + flush(); })); } === modified file 'src/com/goldencode/p2j/oo/net/http/filter/status/RedirectStatusFilter.java' --- src/com/goldencode/p2j/oo/net/http/filter/status/RedirectStatusFilter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/status/RedirectStatusFilter.java 2021-03-31 13:07:03 +0000 @@ -11,6 +11,7 @@ ** 004 ME 20200410 Added setter method for logger. ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 006 ME 20210331 Implement as per OE12.2. */ /* @@ -72,21 +73,17 @@ import static com.goldencode.p2j.util.BlockManager.function; import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.internalProcedure; -import static com.goldencode.p2j.report.ReportConstants.*; +import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.util.BlockManager.returnNormal; -import static com.goldencode.p2j.report.ReportConstants.*; - -import java.util.regex.*; - -import org.apache.axis2.dataretrieval.*; import com.goldencode.p2j.oo.core.*; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.oo.logging.*; import com.goldencode.p2j.oo.net.*; import com.goldencode.p2j.oo.net.http.*; -import com.goldencode.p2j.oo.net.http.filter.payload.*; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.util.InternalEntry.*; /** @@ -100,9 +97,6 @@ extends BaseObject implements IHttpMessageWriter, ISupportLogging { - // Absolute URI regexp - private static final Pattern ABSOLUTE_URI = Pattern.compile("(http|https|ftp|file)://.*"); - /** Logger */ private final object logger = TypeFactory.object(IlogWriter.class); @@ -115,9 +109,6 @@ /** Redirect location */ private final character location = TypeFactory.character(); - /** Cookies */ - private final object[] cookies = TypeFactory.objectExtent(Cookie.class); - /** * Execute method */ @@ -125,6 +116,7 @@ { externalProcedure(RedirectStatusFilter.this, new Block((Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); { } })); @@ -142,14 +134,20 @@ }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL) public void __net_http_filter_status_RedirectStatusFilter_constructor__( - object poMessage) + object _poMessage) { + object poMessage = TypeFactory.initInput(_poMessage); + internalProcedure(this, "__net_http_filter_status_RedirectStatusFilter_constructor__", new Block((Body) () -> { __lang_BaseObject_constructor__(); + + Assert.notNull(poMessage, new character("Http request")); this.message.assign(poMessage); // TODO: init logger + logger.assign(ObjectOps.newInstance(VoidLogger.class)); + })); } @@ -158,13 +156,12 @@ * times without requiring a new writer each time. */ @LegacySignature(type = Type.METHOD, name = "Open") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void open() { internalProcedure(this, "Open", new Block((Body)() -> { this.location.assign(""); - // TODO: clean cookies })); } @@ -173,27 +170,22 @@ * * @param poData Data to write */ - @LegacySignature(type = Type.METHOD, name = "Write", parameters = - { - @LegacyParameter(name="poData", type = "OBJECT", - qualified = "Progress.Lang.Object", mode = "INPUT") - }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL) + @LegacySignature(type = Type.METHOD, name = "Write", parameters = { + @LegacyParameter(name = "poData", type = "OBJECT", qualified = "Progress.Lang.Object", mode = "INPUT") }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override - public void write(object poData) + public void write(object _poData) { - internalProcedure(this, "Write", new Block((Body)() -> - { + object poData = TypeFactory.initInput(_poData); + + internalProcedure(this, "Write", new Block((Body) () -> { Assert.isType(poData, ObjectOps.getLegacyClass(IhttpResponse.class)); - if (ObjectOps.cast(poData, IhttpResponse.class).ref(). - hasHeader(new character("Location")).booleanValue()) - { - this.location.assign( - ObjectOps.cast(poData, IhttpResponse.class).ref(). - getHeader(new character("Location")).ref().getValue() - // TODO: extract cookies - ); - } + + object response = ObjectOps.cast(poData, IhttpResponse.class); + + this.location.assign(response.ref().getHeader(new character("Location")).ref().getValue()); + + logger.ref().debug(TextOps.substitute("HTTP status &1", response.ref().getStatusCode())); })); } @@ -213,8 +205,8 @@ longchar pcData = TypeFactory.initInput(_pcData); internalProcedure(this, "Write", new Block((Body)() -> { - Assert.notNullOrEmpty(pcData, new character("Data size")); - this.location.assign(pcData.toString()); + Assert.notNullOrEmpty(pcData, new character("Location")); + this.location.assign(pcData); })); } @@ -251,7 +243,7 @@ character pcData = TypeFactory.initInput(_pcData); internalProcedure(this, "Write", new Block((Body)() -> { - Assert.notNullOrEmpty(pcData, new character("Data size")); + Assert.notNullOrEmpty(pcData, new character("Location")); this.location.assign(pcData); })); } @@ -269,10 +261,12 @@ @Override public void write(memptr _poData) { + memptr poData = TypeFactory.initInput(_poData); + internalProcedure(this, "Write", new Block((Body)() -> { - Assert.isPositive(new integer(_poData.getSize()), new character("Data size")); - this.location.assign(_poData.getString(1)); + Assert.isPositive(poData.length(), new character("Data size")); + this.location.assign(poData.getString(1)); })); } @@ -280,22 +274,29 @@ * Flushes data to the output location. What data is flushed depends on the implementation */ @LegacySignature(type = Type.METHOD, name = "Flush") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void flush() { + object redirectUri = TypeFactory.object(Uri.class); + internalProcedure(this, "Flush", new Block((Body)() -> { - Assert.notNullOrEmpty(this.location, new character("Location")); - String uri = location.getValue(); - if (!ABSOLUTE_URI.matcher(uri).matches()) - { - this.location.assign( - ObjectOps.cast(this.message, IhttpRequest.class).ref(). - getUri().ref().getBaseUri().getValue() + this.location.getValue() - ); - } - ObjectOps.cast(this.message, IhttpRequest.class).ref().setUri(Uri.parse(this.location)); - // TODO: set cookies + + if (TextOps.isEmpty(this.location)) + return; + + redirectUri.assign(ObjectOps.cast(message, IhttpRequest.class).ref().getUri()); + + logger.ref().debug(TextOps.substitute("Redirect from URL \"&1\" to \"&2\"", + redirectUri.ref().toLegacyString(), + location)); + + redirectUri.assign(Uri.resolveRelativeReference(redirectUri, location)); + ObjectOps.cast(message, IhttpRequest.class).ref().setUri(redirectUri); + + logger.ref().debug(TextOps.substitute("Redirect URL: &1", + redirectUri.ref().toLegacyString())); + })); } @@ -346,6 +347,16 @@ })); } + /** + * Set logger. + * + * @param logger. + */ + @LegacySignature(type = Type.SETTER, name = "Logger", parameters = + { + @LegacyParameter(name = "logger", type = "OBJECT", mode = "INPUT", qualified = "OpenEdge.Logging.ILogWriter") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void setLogger(object _logger) { === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/EntityWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/EntityWriterBuilder.java 2021-03-11 08:18:20 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/EntityWriterBuilder.java 2021-03-30 06:41:54 +0000 @@ -11,6 +11,7 @@ ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** ME 20210310 Add block 'referent' for static methods. +** ME 20210330 Add static ctor, undo-throw. */ /* @@ -68,17 +69,18 @@ package com.goldencode.p2j.oo.net.http.filter.writer; +import static com.goldencode.p2j.util.BlockManager.externalProcedure; import static com.goldencode.p2j.util.BlockManager.function; +import static com.goldencode.p2j.util.BlockManager.onBlockLevel; import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.returnNormal; -import static com.goldencode.p2j.report.ReportConstants.*; - -import org.apache.http.protocol.*; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.oo.net.*; import com.goldencode.p2j.oo.net.http.*; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.util.InternalEntry.*; /** @@ -91,6 +93,17 @@ public class EntityWriterBuilder extends BaseObject { + + @LegacySignature(type = Type.CONSTRUCTOR) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + public static void __net_http_filter_writer_EntityWriterBuilder_constructor__static__() + { + externalProcedure(EntityWriterBuilder.class, new Block((Body) () -> { + + onBlockLevel(Condition.ERROR, Action.THROW); + })); + } + /** * Helper method that returns an entity builder for a message part. * === modified file 'src/com/goldencode/p2j/oo/net/http/filter/writer/MessageWriterBuilder.java' --- src/com/goldencode/p2j/oo/net/http/filter/writer/MessageWriterBuilder.java 2021-03-11 08:18:20 +0000 +++ src/com/goldencode/p2j/oo/net/http/filter/writer/MessageWriterBuilder.java 2021-03-30 06:38:40 +0000 @@ -13,6 +13,7 @@ ** 004 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. ** ME 20210310 Add block 'referent' for static methods, use initInput for object params. +** ME 20210330 Fix registry class type. */ /* @@ -193,7 +194,7 @@ if (!registry._isValid()) { registry.assign(ObjectOps.newInstance(BuilderRegistry.class, "I", - ObjectOps.getLegacyClass(MessageWriter.class))); + ObjectOps.getLegacyClass(MessageWriterBuilder.class))); initializeRegistry(registry); } @@ -213,10 +214,11 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) private static void initializeRegistry(final object _poRegistry) { + object poRegistry = TypeFactory.initInput(_poRegistry); internalProcedure(MessageWriterBuilder.class, "InitializeRegistry", new Block((Body) ()-> { - _poRegistry.ref().put(new character(ObjectOps.getLegacyName(MessageWriterBuilder.class)), + poRegistry.ref().put(new character(ObjectOps.getLegacyName(MessageWriterBuilder.class)), ObjectOps.getLegacyClass(DefaultMessageWriterBuilder.class)); })); } @@ -316,6 +318,7 @@ { character pcContentType = TypeFactory.initInput(_pcContentType); object poRegistry = TypeFactory.initInput(_poRegistry); + object builder = TypeFactory.object(MessageWriterBuilder.class); return function(MessageWriterBuilder.class, "Build", object.class, new Block((Body)() -> { @@ -323,7 +326,6 @@ Assert.notNull(poRegistry, new character("Writer registry")); object builderType = TypeFactory.object(LegacyClass.class); - object builder = TypeFactory.object(MessageWriterBuilder.class); builderType.assign(getRegistry().ref().get(new character(ObjectOps.getLegacyName(MessageWriterBuilder.class)))); @@ -521,12 +523,12 @@ protected object getWriter(character _pcContentType) { character pcContentType = TypeFactory.initInput(_pcContentType); + object writer = TypeFactory.object(LegacyClass.class); + return function(this, "GetWriter", object.class, new Block((Body)() -> { Assert.notNull(pcContentType, new character("Content type")); - object writer = TypeFactory.object(LegacyClass.class); - if (TextOps.isEmpty(pcContentType.getValue())) { returnNormal(writer); === modified file 'src/com/goldencode/p2j/oo/net/http/lib/sockets/LegacySocketLibraryBuilder.java' --- src/com/goldencode/p2j/oo/net/http/lib/sockets/LegacySocketLibraryBuilder.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/net/http/lib/sockets/LegacySocketLibraryBuilder.java 2021-04-02 07:38:02 +0000 @@ -11,6 +11,7 @@ ** 004 MP 20200525 Added constructor and execute method. ** 005 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** ME 20210402 Added annotations, update support levels. */ /* @@ -71,7 +72,6 @@ import com.goldencode.p2j.oo.core.Assert; import com.goldencode.p2j.oo.core.ISupportInitialize; import com.goldencode.p2j.oo.lang.LegacyClass; -import com.goldencode.p2j.oo.lang._BaseObject_; import com.goldencode.p2j.oo.logging.ISupportLogging; import com.goldencode.p2j.oo.logging.IlogWriter; import com.goldencode.p2j.oo.net.http.ClientOptions; @@ -97,12 +97,12 @@ * */ @LegacyResource(resource = "OpenEdge.Net.HTTP.Lib.ABLSockets.ABLSocketLibraryBuilder") -@LegacyResourceSupport(supportLvl = CVT_LVL_PARTIAL | RT_LVL_NONE) +@LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) public class LegacySocketLibraryBuilder extends ClientLibraryBuilder { @LegacySignature(returns = "OBJECT", qualified = "OpenEdge.Net.HTTP.IHttpClientLibrary", type = Type.GETTER, name = "Library") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public object getLibrary() { @@ -115,6 +115,8 @@ private object newLibrary() { + object oLib = TypeFactory.object(IHttpClientLibrary.class); + return function(this, "newLibrary", object.class, new Block((Body) () -> { object oLibType = ObjectOps.newInstance(LegacyClass.class); @@ -122,10 +124,9 @@ object oParams = ObjectOps.newInstance(ClientSocketConnectionParameters.class); object oOptions = ObjectOps.newInstance(ClientOptions.class); - character iHttpClLibTypName = new character(ObjectOps.getLegacyName(IHttpClientLibrary.class)); character iLogWrtTypName = new character(ObjectOps.getLegacyName(IlogWriter.class)); - oLibType.assign(ClientLibraryBuilder.getRegistry().ref().get(iHttpClLibTypName)); + oLibType.assign(ClientLibraryBuilder.getRegistry().ref()._get(ObjectOps.getLegacyName(IHttpClientLibrary.class))); Assert.isType(oLibType, ObjectOps.getLegacyClass(LegacySocketLibrary.class)); @@ -133,24 +134,23 @@ oParams.assign(buildSocketConnectionParams()); - if (super.hasOption(new character("requestTimeout")).booleanValue()) + if (_hasOption("requestTimeout")) { - oOptions.ref().setRequestTimeout(super.getOptionNumericValue(new character("requestTimeout"))); + oOptions.ref().setRequestTimeout(getOptionNumericValue(new character("requestTimeout"))); } - object oLib = - ObjectOps.newInstance(oLibType.ref().getType(), + oLib.assign(ObjectOps.newInstance(oLibType.ref().getType(), "IIIII", new character("Lib-ABLSockets"), new character("0.5.0"), oSocket, oParams, - oOptions); + oOptions)); if (oLib._isValid()) { - if (oLib.ref() instanceof ISupportLogging && super.hasOption(iLogWrtTypName).booleanValue()) + if (oLib.ref() instanceof ISupportLogging && hasOption(iLogWrtTypName).booleanValue()) { - object logger = (object) super.getOptionObjectValue(iLogWrtTypName); + object logger = (object) getOptionObjectValue(iLogWrtTypName); ((ISupportLogging) oLib.ref()).setLogger(logger); } @@ -168,24 +168,26 @@ /** * Creates socket wrapper/holder class, based on the config */ + @LegacySignature(type = Type.METHOD, name = "CreateClientSocket", returns = "OBJECT", qualified = "OpenEdge.Net.ServerConnection.ClientSocket") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) protected object createClientSocket() { - return function(this, "createClientSocket", object.class, new Block((Body) () -> + return function(this, "CreateClientSocket", object.class, new Block((Body) () -> { object clSocket = ObjectOps.newInstance(ClientSocket.class); character clSTypName = new character(ObjectOps.getLegacyName(ClientSocket.class)); character iLogWrtTypName = new character(ObjectOps.getLegacyName(IlogWriter.class)); - if (super.hasOption(clSTypName).booleanValue()) + if (hasOption(clSTypName).booleanValue()) { - clSocket.assign((ClientSocket) super.getOptionObjectValue(clSTypName).ref()); + clSocket.assign((ClientSocket) getOptionObjectValue(clSTypName).ref()); } else { - if ((clSocket.ref() instanceof ISupportLogging) && super.hasOption(iLogWrtTypName).booleanValue()) + if ((clSocket.ref() instanceof ISupportLogging) && hasOption(iLogWrtTypName).booleanValue()) { - object logger = (object) super.getOptionObjectValue(iLogWrtTypName); + object logger = (object) getOptionObjectValue(iLogWrtTypName); ((ISupportLogging) clSocket.ref()).setLogger(logger); } @@ -202,44 +204,46 @@ /** * Builds the connection parameter from the config options. */ + @LegacySignature(type = Type.METHOD, name = "BuildSocketConnectionParams", returns = "OBJECT", qualified = "OpenEdge.Net.ServerConnection.ClientSocketConnectionParameters") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) protected object buildSocketConnectionParams() { - return function(this, "buildSocketConnectionParams", object.class, new Block((Body) () -> + object clSocketConnParam = + ObjectOps.newInstance(ClientSocketConnectionParameters.class); + + return function(this, "BuildSocketConnectionParams", object.class, new Block((Body) () -> { - object clSocketConnParam = - ObjectOps.newInstance(ClientSocketConnectionParameters.class); - character clSockConnParamTypName = new character("OpenEdge.Net.ServerConnection.ClientSocketConnectionParameters"); - if (super.hasOption(clSockConnParamTypName).booleanValue()) + if (hasOption(clSockConnParamTypName).booleanValue()) { - clSocketConnParam.assign((ClientSocketConnectionParameters) super.getOptionObjectValue(clSockConnParamTypName).ref()); + clSocketConnParam.assign((ClientSocketConnectionParameters) getOptionObjectValue(clSockConnParamTypName).ref()); } else { - if (super.hasOption(super.getPropSslHostverify()).booleanValue()) - { - clSocketConnParam.ref().setVerifyHost(super.getOptionLogicalValue(super.getPropSslHostverify())); - } - - if (super.hasOption(super.getPropReusesession()).booleanValue()) - { - clSocketConnParam.ref().setReuseSession(super.getOptionLogicalValue(super.getPropReusesession())); - } - - if (super.hasOption(super.getPropSslProtocols()).booleanValue()) - { - clSocketConnParam.ref().setSslProtocols(super.getOptionStringArrayValue(super.getPropSslProtocols())); - } - - if (super.hasOption(super.getPropSslCiphers()).booleanValue()) - { - clSocketConnParam.ref().setSslCiphers(super.getOptionStringArrayValue(super.getPropSslCiphers())); - } - - if (super.hasOption(super.getPropServername()).booleanValue()) - { - clSocketConnParam.ref().setServerNameIndicator(super.getOptionStringValue(super.getPropServername())); + if (hasOption(getPropSslHostverify()).booleanValue()) + { + clSocketConnParam.ref().setVerifyHost(getOptionLogicalValue(getPropSslHostverify())); + } + + if (hasOption(getPropReusesession()).booleanValue()) + { + clSocketConnParam.ref().setReuseSession(getOptionLogicalValue(getPropReusesession())); + } + + if (hasOption(getPropSslProtocols()).booleanValue()) + { + clSocketConnParam.ref().setSslProtocols(getOptionStringArrayValue(getPropSslProtocols())); + } + + if (hasOption(getPropSslCiphers()).booleanValue()) + { + clSocketConnParam.ref().setSslCiphers(getOptionStringArrayValue(getPropSslCiphers())); + } + + if (hasOption(getPropServername()).booleanValue()) + { + clSocketConnParam.ref().setServerNameIndicator(getOptionStringValue(getPropServername())); } if (clSocketConnParam instanceof ISupportInitialize) @@ -256,12 +260,12 @@ * Constructor */ @LegacySignature(type = Type.CONSTRUCTOR) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL_RESTR) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __net_http_lib_sockets_LegacySocketLibraryBuilder_constructor__() { internalProcedure(this, "__net_http_lib_ablsockets_ABLSocketLibraryBuilder_constructor__", new Block((Body) () -> { - super.__net_http_lib_ClientLibraryBuilder_constructor__(); + __net_http_lib_ClientLibraryBuilder_constructor__(); })); } === modified file 'src/com/goldencode/p2j/oo/web/RemoteWebRequest.java' --- src/com/goldencode/p2j/oo/web/RemoteWebRequest.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/web/RemoteWebRequest.java 2021-04-23 12:06:35 +0000 @@ -10,6 +10,7 @@ ** 003 CA 20200427 A more improved runtime. cookie/header and other support is still missing. ** 004 ME 20210128 Add errors for read-only, not implemented method/properties. ** 005 CA 20210221 Fixed parameters at the LegacySignature annotation. +** 006 ME 20210423 Annotate class and finish implementation as per OE12.2. */ /* @@ -67,32 +68,37 @@ package com.goldencode.p2j.oo.web; -import static com.goldencode.p2j.report.ReportConstants.CVT_LVL_FULL; -import static com.goldencode.p2j.report.ReportConstants.RT_LVL_FULL; +import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.BlockManager.*; import java.io.*; import java.util.*; import java.util.function.Function; +import java.util.stream.Collectors; // explicit import import javax.servlet.http.HttpServletRequest; +import com.goldencode.p2j.directory.Base64; import com.goldencode.p2j.main.*; import com.goldencode.p2j.oo.core.*; -import com.goldencode.p2j.oo.json.objectmodel.*; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.oo.net.*; import com.goldencode.p2j.oo.net.http.*; +import com.goldencode.p2j.oo.net.http.filter.payload.MessageWriter; +import com.goldencode.p2j.oo.net.http.filter.writer.EntityWriterRegistry; +import com.goldencode.p2j.oo.web.WebHandler.WebContext; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.util.InternalEntry.Type; /** * A wrapper over a {@link HttpServletRequest} instance, to expose it as a legacy * {@link IwebRequest}. */ -@LegacyResource(resource = "fwd.web.RemoteWebRequest") -// do not annotate this - is not meant to be used from outside FWD runtime +@LegacyResource(resource = "OpenEdge.Web.WebRequest") +@LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL) public class RemoteWebRequest extends BaseObject implements IwebRequest @@ -161,8 +167,11 @@ /** The paths for this request, as configured at the handler. */ private String[] paths; + /** The MD5 hash of body */ + private raw contentMD5 = TypeFactory.raw(); + /** The body entity. */ - private object entity = null; + private object entity = TypeFactory.object(_BaseObject_.class); /** * The execute method to initialize the FWD-related state for an instance. @@ -171,6 +180,7 @@ { externalProcedure(RemoteWebRequest.this, new Block((Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); { } })); @@ -191,21 +201,31 @@ /** * Initialize this instance with the specified details. * - * @param basepath - * The basepath. - * @param target - * The target. - * @param paths - * The paths for this request. - * @param request - * The servlet request. + * Web context is fetch from WebHandler's context for now. + * */ - public void initialize(String basepath, String target, String[] paths, HttpServletRequest request) + private boolean _initialize() { - this.basepath = basepath; - this.target = target; - this.paths = paths; - this.request = request; + if (this.request == null) + { + WebContext context = WebHandler.getWebContext(); + + if (context != null) + { + this.basepath = context.getBasepath(); + this.target = context.getTarget(); + this.paths = context.getPaths(); + this.request = context.getRequest(); + } + else + { + ErrorManager.recordOrShowWarning(4088, + "Progress does not support the GET-CGI-VALUE attribute for the PSEUDO-WIDGET in this display environment", + true, false, false, true); + } + } + + return this.request != null; } /** @@ -213,10 +233,18 @@ * * @return the HTTP method. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Method") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getMethod() { - return new character(request.getMethod()); + return function(RemoteWebRequest.class, this, "Method", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(request.getMethod())); + })); } /** @@ -241,10 +269,15 @@ * * @return the HTTP URI. */ + @LegacySignature(returns = "OBJECT", type = Type.GETTER, name = "URI", qualified = "OpenEdge.Net.URI" ) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public object getUri() { - return Uri.parse(new character(request.getRequestURL().toString())); + return function(RemoteWebRequest.class, this, "URI", object.class, new Block((Body) () -> + { + returnNormal(Uri.parse(_initialize() ? new character(request.getRequestURL().toString()) : new character())); + })); } /** @@ -270,10 +303,18 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "CharacterEncoding") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getCharacterEncoding() { - return new character(emptyIfNull(request.getCharacterEncoding())); + return function(RemoteWebRequest.class, this, "CharacterEncoding", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getCharacterEncoding()))); + })); } /** @@ -298,10 +339,18 @@ * * @return See above. */ + @LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "ContentLength") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public integer getContentLength() { - return new integer(request.getContentLength()); + return function(RemoteWebRequest.class, this, "ContentLength", integer.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new integer()); + + returnNormal(new integer(request.getContentLength())); + })); } /** @@ -326,11 +375,27 @@ * * @return See above. */ + @LegacySignature(returns = "RAW", type = Type.GETTER, name = "ContentMD5") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public raw getContentMd5() { - UnimplementedFeature.missing("RemoteWebRequest.getContentMd5"); - return new raw(); + return function(RemoteWebRequest.class, this, "ContentLength", raw.class, new Block((Body) () -> + { + if (contentMD5.isUnknown() && _initialize()) + { + try + { + // 4GL QUIRK - this will throw in 4GL because of the setter method throwing error + contentMD5.assign(Base64.byteArrayToBase64(LegacyServiceHandler.readBody(request))); + } + catch (IOException e) + { + } + } + + returnNormal(contentMD5); + })); } /** @@ -355,10 +420,18 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ContentType") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getContentType() { - return new character(emptyIfNull(request.getContentType())); + return function(RemoteWebRequest.class, this, "ContentType", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getContentType()))); + })); } /** @@ -383,51 +456,56 @@ * * @return See above. */ + @LegacySignature(returns = "OBJECT", type = Type.GETTER, name = "Entity", qualified = "Progress.Lang.Object") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public object getEntity() { - if (entity != null) - { - return entity; - } - - byte[] body; - try - { - body = LegacyServiceHandler.readBody(request); - } - catch (IOException e) - { - entity = new object(); - return entity; - } - - String contentType = request.getContentType(); - if (contentType == null) - { - entity = ObjectOps.newInstance(Memptr.class, "I", new raw(body)); - return entity; - } - - if (contentType.equals("application/json")) - { - object parser = ObjectOps.newInstance(ObjectModelParser.class); - - entity = parser.ref().parse(new memptr(body)); - return entity; - } - else if (contentType.equals("application/x-www-form-urlencoded")) - { - String qparams = request.getQueryString(); - if (qparams != null) - { - return ObjectOps.newInstance(Memptr.class, "I", new raw(qparams)); - } - } - - // default - entity = ObjectOps.newInstance(Memptr.class, "I", new raw(body)); - return entity; + return function(RemoteWebRequest.class, this, "Entity", object.class, new Block((Body) () -> + { + if (!entity._isValid() && _initialize()) + { + byte[] body; + try + { + body = LegacyServiceHandler.readBody(request); + + if (body.length > 0) + { + // delegate that to entity writer??? + object writerType = EntityWriterRegistry.getRegistry().ref()._get(request.getContentType()); + + if (writerType._isValid()) { + try + { + object writer = ObjectOps.cast(writerType.ref().new_(), MessageWriter.class); + writer.ref().open(); + writer.ref().write(new memptr(body)); + writer.ref().close(); + + entity.assign(writer.ref().getEntity()); + } + catch (Exception e) + { + // fallback to raw body + entity.assign(ObjectOps.newInstance(Memptr.class, "I", new raw(body))); + } + } + } + } + catch (IOException e) + { + } + } + + if (!entity._isValid()) + { + entity.assign(ObjectOps.newInstance(Memptr.class, "I", new integer(0))); + } + + returnNormal(entity); + })); + } /** @@ -453,10 +531,19 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "TransferEncoding") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getTransferEncoding() { - return new character(emptyIfNull(request.getHeader("Transfer-Encoding"))); + return function(RemoteWebRequest.class, this, "TransferEncoding", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getHeader("Transfer-Encoding")))); + })); + } /** @@ -481,10 +568,18 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "Version") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getVersion() { - return new character(emptyIfNull(request.getProtocol())); + return function(RemoteWebRequest.class, this, "Version", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getProtocol()))); + })); } /** @@ -493,6 +588,11 @@ * @param version * The HTTP version. */ + @LegacySignature(type = Type.SETTER, name = "Version", parameters = + { + @LegacyParameter(name = "version", type = "CHARACTER", mode = "INPUT") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void setVersion(character version) { @@ -507,10 +607,7 @@ @Override public void clearHeaders() { - internalProcedure(this, "ClearHeaders", new Block((Body) () -> - { - throwNotImplementedMethod("ClearHeaders"); - })); + throwNotImplementedMethod("ClearHeaders"); } /** @@ -519,12 +616,34 @@ * @param _pcName * The header name. */ + @LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetHeader", qualified = "OpenEdge.Net.HTTP.HttpHeader", parameters = { + @LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") }) @Override public object getHeader(character _pcName) { - String val = request.getHeader(_pcName.toStringMessage()); + character pcName = TypeFactory.initInput(_pcName); - return ObjectOps.newInstance(HttpHeader.class, "II", _pcName, new character(val)); + return function(this, "GetHeader", object.class, new Block((Body) () -> + { + character val = new character(); + + if (!pcName.isUnknown() && _initialize()) + { + String name = pcName.toStringMessage(); + + Enumeration hnames = request.getHeaderNames(); + while (hnames.hasMoreElements()) + { + String header = hnames.nextElement(); + + if (name.equalsIgnoreCase(header)) + val.assign(request.getHeader(header)); + } + } + + returnNormal(HttpHeaderBuilder.build(pcName) + .ref().value(val).ref().getHeader()); + })); } /** @@ -535,55 +654,78 @@ * * @return The number of read headers. */ + @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "GetHeaders", parameters = { + @LegacyParameter(name = "poHeaders", type = "OBJECT", extent = -1, qualified = "OpenEdge.Net.HTTP.HttpHeader", mode = "OUTPUT") }) @Override - public integer getHeaders(OutputExtentParameter> poHeaders) + public integer getHeaders(OutputExtentParameter> _poHeaders) { - object[][] p1 = new object[][] { TypeFactory.initOutput(poHeaders) }; + object[][] poHeaders = new object[][] { TypeFactory.initOutput(_poHeaders) }; return function(this, "GetHeaders", integer.class, new Block((Body) () -> { Map headers = new LinkedHashMap<>(); + + if (_initialize()) + { + Enumeration hnames = request.getHeaderNames(); + while (hnames.hasMoreElements()) + { + String header = hnames.nextElement(); + headers.put(header, request.getHeader(header)); + } + } + + poHeaders[0] = ArrayAssigner.resize(poHeaders[0], headers.size()); + int i = 1; + for (String header : headers.keySet()) + { + String value = headers.get(header); + object oheader = HttpHeaderBuilder.build(new character(header)) + .ref().value(new character(value)).ref().getHeader(); + + ArrayAssigner.assignSingle(poHeaders[0], i++, oheader); + } + + returnNormal(poHeaders[0].length); + })); + } + + /** + * Check if the specified header name exists. + * + * @param _pcName + * The header name. + * + * @return See above. + */ + @LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "HasCookie", parameters = { + @LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") }) + @Override + public logical hasHeader(character _pcName) + { + character pcName = TypeFactory.initInput(_pcName); + + return function(this, "HasHeader", logical.class, new Block((Body) () -> + { + if (pcName.isUnknown() || !_initialize()) + returnNormal(new logical()); + + String name = _pcName.toStringMessage(); + Enumeration hnames = request.getHeaderNames(); while (hnames.hasMoreElements()) { String header = hnames.nextElement(); - headers.put(header, request.getHeader(header)); - } - - if (!headers.isEmpty()) - { - p1[0] = ArrayAssigner.resize(p1[0], headers.size()); - int i = 1; - for (String header : headers.keySet()) - { - String value = headers.get(header); - object oheader = ObjectOps.newInstance(HttpHeader.class, - "II", - new character(header), - new character(value)); - ArrayAssigner.assignSingle(p1[0], i++, oheader); - } - } - - returnNormal(headers.size()); + + if (name.equalsIgnoreCase(header)) + returnNormal(new logical(true)); + } + + returnNormal(new logical(false)); })); } /** - * Check if the specified header name exists. - * - * @param _pcName - * The header name. - * - * @return See above. - */ - @Override - public logical hasHeader(character _pcName) - { - return new logical(request.getHeader(_pcName.toStringMessage()) != null); - } - - /** * Remove the specified header. This is not implemented. * * @param _pcName @@ -597,10 +739,7 @@ @Override public void removeHeader(character _pcName) { - internalProcedure(this, "RemoveHeader", new Block((Body) () -> - { - throwNotImplementedMethod("RemoveHeader"); - })); + throwNotImplementedMethod("RemoveHeader"); } /** @@ -618,10 +757,7 @@ @Override public void setHeader(object _poHeader) { - internalProcedure(this, "SetHeader", new Block((Body) () -> - { - throwNotImplementedMethod("SetHeader"); - })); + throwNotImplementedMethod("SetHeader"); } @@ -652,10 +788,19 @@ * * @return The servlet path. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ResolvedWebAppPath") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getResolvedWebAppPath() { - return new character(emptyIfNull(request.getServletPath())); + return function(RemoteWebRequest.class, this, "ResolvedWebAppPath", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getServletPath()))); + })); + } /** @@ -663,16 +808,24 @@ * * @return A comma-separated list of CGI var names. */ + @LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetContextNames") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getContextNames() { - String res = ""; - for (String key : CGI_VARS.keySet()) + return function(RemoteWebRequest.class, this, "GetContextNames", character.class, new Block((Body) () -> { - res = res + (res.isEmpty() ? "" : ",") + key; - } - - return new character(res); + if (!_initialize()) + returnNormal(new character()); + + String res = ""; + for (String key : CGI_VARS.keySet()) + { + res = res + (res.isEmpty() ? "" : ",") + key; + } + + returnNormal(new character(res)); + })); } /** @@ -680,11 +833,21 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "DefaultCookieDomain") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getDefaultCookieDomain() { UnimplementedFeature.missing("RemoteWebRequest.getDefaultCookiePath"); - return new character(""); + + return function(RemoteWebRequest.class, this, "DefaultCookieDomain", character.class, new Block((Body) () -> + { + // TODO: WEB properties in PASOE + if (_initialize()) + ; + + returnNormal(new character()); + })); } /** @@ -692,11 +855,21 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "DefaultCookiePath") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getDefaultCookiePath() { UnimplementedFeature.missing("RemoteWebRequest.getDefaultCookiePath"); - return new character(""); + + return function(RemoteWebRequest.class, this, "DefaultCookiePath", character.class, new Block((Body) () -> + { + // TODO: WEB properties in PASOE + if (_initialize()) + ; + + returnNormal(new character("")); + })); } /** @@ -704,10 +877,19 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "LocalAddress") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getLocalAddress() { - return new character(emptyIfNull(request.getLocalAddr())); + return function(RemoteWebRequest.class, this, "LocalAddress", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getLocalAddr()))); + })); + } /** @@ -715,10 +897,18 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "LocalHost") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getLocalHost() { - return new character(emptyIfNull(request.getLocalName())); + return function(RemoteWebRequest.class, this, "LocalHost", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getLocalName()))); + })); } /** @@ -726,10 +916,18 @@ * * @return See above. */ + @LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "LocalPort") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public integer getLocalPort() { - return new integer(request.getLocalPort()); + return function(RemoteWebRequest.class, this, "LocalPort", integer.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new integer()); + + returnNormal(new integer(request.getLocalPort())); + })); } /** @@ -737,9 +935,23 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "PathInfo") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getPathInfo() { + return function(RemoteWebRequest.class, this, "PathInfo", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(_getPathInfo())); + })); + + } + + protected String _getPathInfo() + { // the URL after the transport path (i.e. basePath) String res = target; if (res.indexOf("?") > 0) @@ -751,7 +963,7 @@ res = "/" + res; } - return new character(res); + return res; } /** @@ -759,11 +971,26 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetPathParameterNames") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getPathParameterNames() { - UnimplementedFeature.missing("RemoteWebRequest.getPathParameterNames"); - return new character(""); + return function(this, "GetPathParameterNames", character.class, new Block((Body) () -> + { + String pnames = ""; + + if (_initialize()) + { + pnames = Arrays.stream(paths) + .filter(n -> n.startsWith("{") && n.endsWith("}")) + .map(n -> n.substring(1, n.length() - 1)) + .collect(Collectors.joining(",")); + } + + returnNormal(new character(pnames)); + })); + } /** @@ -771,10 +998,18 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "RemoteAddress") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getRemoteAddress() { - return new character(emptyIfNull(request.getRemoteAddr())); + return function(RemoteWebRequest.class, this, "RemoteAddress", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getRemoteAddr()))); + })); } /** @@ -782,10 +1017,18 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "RemoteHost") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getRemoteHost() { - return new character(emptyIfNull(request.getRemoteHost())); + return function(RemoteWebRequest.class, this, "RemoteHost", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getRemoteHost()))); + })); } /** @@ -793,10 +1036,18 @@ * * @return See above. */ + @LegacySignature(returns = "INTEGER", type = Type.GETTER, name = "RemotePort") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public integer getRemotePort() { - return new integer(request.getRemotePort()); + return function(RemoteWebRequest.class, this, "RemotePort", integer.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new integer()); + + returnNormal(new integer(request.getRemotePort())); + })); } /** @@ -804,10 +1055,19 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "RemoteUser") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getRemoteUser() { - return new character(emptyIfNull(request.getRemoteUser())); + return function(RemoteWebRequest.class, this, "RemoteUser", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getRemoteUser()))); + })); + } /** @@ -815,10 +1075,19 @@ * * @return Always empty string. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ResolvedTransportPath") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getResolvedTransportPath() { - return new character(""); // always empty + // always empty + return function(RemoteWebRequest.class, this, "ResolvedTransportPath", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character("")); + })); } /** @@ -826,10 +1095,19 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "ServerSoftware") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getServerSoftware() { - return new character(emptyIfNull(request.getServletContext().getServerInfo())); + return function(RemoteWebRequest.class, this, "ServerSoftware", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getServletContext().getServerInfo()))); + })); + } /** @@ -837,10 +1115,18 @@ * * @return The {@link #basepath}. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "TransportPath") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getTransportPath() { - return new character(emptyIfNull(basepath)); + return function(RemoteWebRequest.class, this, "TransportPath", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(basepath))); + })); } /** @@ -848,11 +1134,21 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "UriTemplate") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getUriTemplate() { - UnimplementedFeature.missing("RemoteWebRequest.getUriTemplate"); - return new character(""); + return function(RemoteWebRequest.class, this, "UriTemplate", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + String pnames = Arrays.stream(paths) + .collect(Collectors.joining("/", "/", "")); + + returnNormal(new character(pnames)); + })); } /** @@ -860,10 +1156,18 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "WebAppPath") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getWebAppPath() { - return new character(emptyIfNull(request.getServletPath())); + return function(RemoteWebRequest.class, this, "WebAppPath", character.class, new Block((Body) () -> + { + if (!_initialize()) + returnNormal(new character()); + + returnNormal(new character(emptyIfNull(request.getServletPath()))); + })); } /** @@ -874,54 +1178,66 @@ * * @return The context value. */ + @LegacySignature(returns = "LONGCHAR", type = Type.METHOD, name = "GetContextValue", parameters = { + @LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public longchar getContextValue(character _pcName) { - String val = _pcName.toStringMessage(); - String key = val.toUpperCase(); - if (CGI_VARS.containsKey(key)) + return function(RemoteWebRequest.class, this, "GetContextValue", longchar.class, new Block((Body) () -> { - Function f = CGI_VARS.get(key); - Object res = f.apply(request); - if (res == UNSUPPORTED_CGI_VAR) + if (!_initialize()) + returnNormal(new longchar()); + + String val = _pcName.toStringMessage(); + String key = val.toUpperCase(); + if (CGI_VARS.containsKey(key)) { - // check if is an explicit one - switch (key) + Function f = CGI_VARS.get(key); + Object res = f.apply(request); + if (res == UNSUPPORTED_CGI_VAR) { - case "BASE_PATH": - res = basepath; - break; - case "SERVLET_APPLICATION_URL": - res = request.getServletPath(); - break; - case "URI_FINAL_MATCH_GROUP": - res = "/"; - break; - case "URI_TEMPLATE": - res = ""; - for (String p : paths) - { - res = res + "/" + p; - } - break; - default: - res = "UNSUPPORTED"; - UnimplementedFeature.missing("RemoteWebRequest.getContextValue(" + key + ")"); - break; + // check if is an explicit one + switch (key) + { + case "BASE_PATH": + res = basepath; + break; + case "SERVLET_APPLICATION_URL": + res = request.getServletPath(); + break; + case "URI_FINAL_MATCH_GROUP": + res = "/"; + break; + case "URI_TEMPLATE": + res = ""; + for (String p : paths) + { + res = res + "/" + p; + } + break; + default: + res = "UNSUPPORTED"; + UnimplementedFeature.missing("RemoteWebRequest.getContextValue(" + key + ")"); + break; + } } - } - - return new longchar(res == null ? "" : res.toString()); - } - - if (val.startsWith("HTTP_")) - { - // this is a header - String headerName = val.substring("HTTP_".length()); - return new longchar(emptyIfNull(request.getHeader(headerName))); - } - - return new longchar(""); // empty string always + + returnNormal(new longchar(res == null ? "" : res.toString())); + } + + if (val.startsWith("HTTP_")) + { + // this is a header + String headerName = val.substring("HTTP_".length()); + returnNormal(new longchar(emptyIfNull(request.getHeader(headerName)))); + } + + returnNormal(new longchar("")); // empty string always + + })); + + } /** @@ -932,36 +1248,37 @@ * * @return See above. */ + @LegacySignature(returns = "CHARACTER", type = Type.METHOD, name = "GetPathParameter", parameters = { + @LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public character getPathParameter(character _pcName) { - if (_pcName.isUnknown()) - { - // TODO: error? - return new character(""); - } + character pcName = TypeFactory.initInput(_pcName); - String param = _pcName.toStringMessage(); - int paramIdx = -1; - for (int i = 0; i < paths.length; i++) + return function(this, "GetPathParameter", character.class, new Block((Body) () -> { - if (paths[i].equals("{" + param + "}")) + if (!pcName.isUnknown() && _initialize()) { - paramIdx = i; - break; + String param = pcName.toStringMessage(); + + for (int i = 0; i < paths.length; i++) + { + if (paths[i].equalsIgnoreCase("{" + param + "}")) + { + // skip leading /, entry is 1-base + returnNormal(TextOps.entry(i + 1, _getPathInfo().substring(1), "/")); + } + } + } - } - - if (paramIdx == -1) - { - return new character(""); - } - - String rpath = request.getPathInfo(); - rpath = rpath.substring(basepath.length()); - String[] rpaths = LegacyServiceHandler.getPaths(rpath); - return new character(rpaths[paramIdx]); + + returnNormal(new character()); + + })); } + + /** * Get the request cookies. @@ -971,11 +1288,32 @@ * * @return The number of found cookies. */ + @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "GetCookies", parameters = { + @LegacyParameter(name = "poCookies", type = "OBJECT", extent = -1, qualified = "OpenEdge.Net.HTTP.Cookie", mode = "OUTPUT") }) + @Override - public integer getCookies(OutputExtentParameter> poCookies) + public integer getCookies(OutputExtentParameter> _poCookies) { - UnimplementedFeature.missing("RemoteWebRequest.getCookies"); - return new integer(); + object[][] poCookies = new object[][] { TypeFactory.initOutput(_poCookies) }; + + return function(this, "GetCookies", integer.class, new Block((Body) () -> + { + if (_initialize()) + { + poCookies[0] = ArrayAssigner.resize(poCookies[0], request.getCookies().length); + + int i = 1; + + for (javax.servlet.http.Cookie cookie : request.getCookies()) + { + object oCookie = _getCookie(cookie); + + ArrayAssigner.assignSingle(poCookies[0], i++, oCookie); + } + } + + returnNormal(poCookies[0].length); + })); } /** @@ -986,23 +1324,59 @@ * * @return The cookie instance. */ + @LegacySignature(returns = "OBJECT", type = Type.METHOD, name = "GetCookie", qualified = "OpenEdge.Net.HTTP.Cookie", parameters = { + @LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) @Override public object getCookie(character _pcName) { - UnimplementedFeature.missing("RemoteWebRequest.getCookie"); - return new object(); - } - + return function(this, "GetCookie", object.class, new Block((Body) () -> { + + String name = _pcName.isUnknown() ? "" : _pcName.toStringMessage().trim(); + + if ( _initialize() && !name.isEmpty()) + { + for (javax.servlet.http.Cookie cookie : request.getCookies()) + { + if (cookie.getName().equalsIgnoreCase(name)) + returnNormal(_getCookie(cookie)); + } + } + + returnNormal(new object()); + })); + } + + private object _getCookie(javax.servlet.http.Cookie cookie) + { + return ObjectOps.newInstance(Cookie.class, "IIIIIIIII", + new character(cookie.getName()), + new character(cookie.getDomain() != null ? cookie.getDomain() : request.getLocalName()), + new character(cookie.getPath() != null ? cookie.getPath() : "/"), + new character(cookie.getValue()), + cookie.getMaxAge() > 0 ? new integer(cookie.getMaxAge()) : new integer(), + new datetimetz(), + new logical(cookie.getSecure()), + new logical(cookie.isHttpOnly()), + cookie.getVersion() > 0 ? new decimal(cookie.getVersion()) : new decimal()); + } + /** * Set or add the specified cookie. This is a no-op. * * @param _poCookie * The cookie to set. */ + @LegacySignature(type = Type.SETTER, name = "SetCookie", parameters = + { + @LegacyParameter(name = "poHeader", type = "OBJECT", + qualified = "OpenEdge.Net.HTTP.Cookie", mode = "INPUT") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void setCookie(object _poCookie) { - // no-op + throwNotImplementedMethod("SetCookie"); } /** @@ -1013,11 +1387,19 @@ * * @return See above. */ + @LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "HasCookie", parameters = { + @LegacyParameter(name = "poCookie", type = "OBJECT", qualified = "OpenEdge.Net.HTTP.Cookie", mode = "INPUT") }) @Override public logical hasCookie(object _poCookie) { - UnimplementedFeature.missing("RemoteWebRequest.getCookies"); - return new logical(); + object poCookie = TypeFactory.initInput(_poCookie); + + return function(this, "HasCookie", logical.class, new Block((Body) () -> + { + Assert.notNull(poCookie, new character("Cookie")); + + returnNormal(hasCookie(poCookie.ref().getName())); + })); } /** @@ -1028,19 +1410,32 @@ * * @return See above. */ + @LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "HasCookie", parameters = { + @LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") }) @Override public logical hasCookie(character _pcName) { - String name = _pcName.toStringMessage(); - for (javax.servlet.http.Cookie cookie : request.getCookies()) + character pcName = TypeFactory.initInput(_pcName); + + return function(this, "HasCookie", logical.class, new Block((Body) () -> { - if (cookie.getName().equals(name)) + Assert.notNullOrEmpty(pcName, new character("Cookie name")); + + if (_initialize()) { - return new logical(true); + String name = _pcName.toStringMessage(); + + for (javax.servlet.http.Cookie cookie : request.getCookies()) + { + if (cookie.getName().equalsIgnoreCase(name)) + { + returnNormal(new logical(true)); + } + } } - } - - return new logical(false); + + returnNormal(new logical(false)); + })); } /** @@ -1049,13 +1444,15 @@ * @param _pcName * The cookie name. */ + @LegacySignature(type = Type.METHOD, name = "RemoveCookie", parameters = + { + @LegacyParameter(name = "pcName", type = "CHARACTER", mode = "INPUT") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void removeCookie(character _pcName) { - internalProcedure(this, "RemoveCookie", new Block((Body) () -> - { - throwNotImplementedMethod("RemoveCookie"); - })); + throwNotImplementedMethod("RemoveCookie"); } /** @@ -1064,19 +1461,27 @@ * @param _poCookie * The cookie instance. */ + @LegacySignature(type = Type.METHOD, name = "RemoveCookie", parameters = + { + @LegacyParameter(name = "poHeader", type = "OBJECT", + qualified = "OpenEdge.Net.HTTP.Cookie", mode = "INPUT") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void removeCookie(object _poCookie) { - // no-op + throwNotImplementedMethod("RemoveCookie"); } /** * Clear all cookies. This is a no-op. */ + @LegacySignature(type = Type.METHOD, name = "ClearCookies") + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void clearCookies() { - // no-op + throwNotImplementedMethod("ClearCookies"); } /** @@ -1092,14 +1497,19 @@ return val == null ? "" : val; } + @LegacySignature(type = Type.METHOD, name = "SetCookies", parameters = + { + @LegacyParameter(name = "poHeader", type = "OBJECT", + qualified = "OpenEdge.Net.HTTP.Cookie", extent = -1, mode = "INPUT") + }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) @Override public void setCookies(object[] _poCookies) { - // TODO Auto-generated method stub - + throwNotImplementedMethod("SetCookies"); } - @LegacySignature(type = Type.METHOD, name = "SetHeader", parameters = + @LegacySignature(type = Type.METHOD, name = "SetHeaders", parameters = { @LegacyParameter(name = "poHeader", type = "OBJECT", qualified = "OpenEdge.Net.HTTP.HttpHeader", extent = -1, mode = "INPUT") === modified file 'src/com/goldencode/p2j/oo/web/WebHandler.java' --- src/com/goldencode/p2j/oo/web/WebHandler.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/web/WebHandler.java 2021-04-23 12:06:35 +0000 @@ -10,6 +10,7 @@ ** 003 ME 20200410 Added setter method for logger. ** 004 ME 20201009 Cast result of dynamic method invoke to integer. ** 005 CA 20210221 Fixed parameters at the LegacySignature annotation. +** 006 ME 20210423 Expose request web context same as for response (WEB-CONTEXT surrogate). */ /* @@ -68,9 +69,15 @@ package com.goldencode.p2j.oo.web; import com.goldencode.p2j.util.*; -import com.goldencode.p2j.util.InternalEntry.Type; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; +import com.goldencode.p2j.oo.core.Assert; import com.goldencode.p2j.oo.lang.*; import com.goldencode.p2j.oo.logging.IlogWriter; +import com.goldencode.p2j.oo.logging.LogLevelEnum; +import com.goldencode.p2j.oo.logging.VoidLogger; +import com.goldencode.p2j.oo.net.http.MethodEnum; +import com.goldencode.p2j.oo.net.http.StatusCodeEnum; import com.goldencode.p2j.security.*; import static com.goldencode.p2j.util.BlockManager.*; @@ -85,7 +92,7 @@ */ @LegacyResource(resource = "OpenEdge.Web.WebHandler") @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL) -public class WebHandler +public abstract class WebHandler extends BaseObject implements com.goldencode.p2j.oo.web.IwebHandler, com.goldencode.p2j.oo.logging.ISupportLogging @@ -99,7 +106,7 @@ } }; - private object logger = UndoableFactory.object(com.goldencode.p2j.oo.logging.IlogWriter.class); + private object logger = TypeFactory.object(com.goldencode.p2j.oo.logging.IlogWriter.class); public static void clear() { @@ -133,6 +140,7 @@ }, (Body) () -> { + onBlockLevel(Condition.ERROR, Action.THROW); { } })); @@ -177,6 +185,9 @@ internalProcedure(this, "__web_WebHandler_constructor__", new Block((Body) () -> { __lang_BaseObject_constructor__(); + + // TODO: use loggerbuilder to get the correct logger + logger.assign(ObjectOps.newInstance(VoidLogger.class)); })); } @@ -189,21 +200,34 @@ { object p1 = TypeFactory.initInput(_p1); - return function(this, "HandleDelete", integer.class, new Block()); + return function(this, "HandleDelete", integer.class, new Block((Body) () -> { + returnNormal(handleNotAllowedMethod(p1)); + })); } @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandleException", parameters = { - @LegacyParameter(name = "p1", type = "OBJECT", qualified = "progress.lang.error", mode = "INPUT"), - @LegacyParameter(name = "p2", type = "OBJECT", qualified = "openedge.web.iwebrequest", mode = "INPUT") + @LegacyParameter(name = "err", type = "OBJECT", qualified = "progress.lang.error", mode = "INPUT"), + @LegacyParameter(name = "req", type = "OBJECT", qualified = "openedge.web.iwebrequest", mode = "INPUT") }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) - protected integer handleException(final object _p1, final object _p2) + protected integer handleException(final object _err, final object _req) { - object p1 = TypeFactory.initInput(_p1); - object p2 = TypeFactory.initInput(_p2); + object err = TypeFactory.initInput(_err); + object req = TypeFactory.initInput(_req); - return function(this, "HandleException", integer.class, new Block()); + return function(this, "HandleException", integer.class, new Block((Body) () -> { + int64 code = StatusCodeEnum.internalServerError.ref().getValue(); + + if (err._isValid()) { + logger.ref().error(TextOps.substitute("Request error for &1 &2: status code &3", + req.ref().getMethod(), + req.ref().getUri().ref().getPath(), code), + err); + } + + returnNormal(code); + })); } @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandleGet", parameters = @@ -215,7 +239,9 @@ { object p1 = TypeFactory.initInput(_p1); - return function(this, "HandleGet", integer.class, new Block()); + return function(this, "HandleGet", integer.class, new Block((Body) () -> { + returnNormal(handleNotAllowedMethod(p1)); + })); } @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandleHead", parameters = @@ -227,7 +253,9 @@ { object p1 = TypeFactory.initInput(_p1); - return function(this, "HandleHead", integer.class, new Block()); + return function(this, "HandleHead", integer.class, new Block((Body) () -> { + returnNormal(handleNotAllowedMethod(p1)); + })); } @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandleNotAllowedMethod", parameters = @@ -235,24 +263,14 @@ @LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.web.iwebrequest", mode = "INPUT") }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) - protected integer handleNotAllowedMethod(final object _p1) - { - object p1 = TypeFactory.initInput(_p1); - - return function(this, "HandleNotAllowedMethod", integer.class, new Block()); - } + protected abstract integer handleNotAllowedMethod(final object _p1); @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandleNotImplemented", parameters = { @LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.web.iwebrequest", mode = "INPUT") }) @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) - protected integer handleNotImplemented(final object _p1) - { - object p1 = TypeFactory.initInput(_p1); - - return function(this, "HandleNotImplemented", integer.class, new Block()); - } + protected abstract integer handleNotImplemented(final object _p1); @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandleOptions", parameters = { @@ -263,7 +281,9 @@ { object p1 = TypeFactory.initInput(_p1); - return function(this, "HandleOptions", integer.class, new Block()); + return function(this, "HandleOptions", integer.class, new Block((Body) () -> { + returnNormal(handleNotAllowedMethod(p1)); + })); } @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandlePatch", parameters = @@ -275,7 +295,9 @@ { object p1 = TypeFactory.initInput(_p1); - return function(this, "HandlePatch", integer.class, new Block()); + return function(this, "HandlePatch", integer.class, new Block((Body) () -> { + returnNormal(handleNotAllowedMethod(p1)); + })); } @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandlePost", parameters = @@ -287,7 +309,9 @@ { object p1 = TypeFactory.initInput(_p1); - return function(this, "HandlePost", integer.class, new Block()); + return function(this, "HandlePost", integer.class, new Block((Body) () -> { + returnNormal(handleNotAllowedMethod(p1)); + })); } @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandlePut", parameters = @@ -299,34 +323,65 @@ { object p1 = TypeFactory.initInput(_p1); - return function(this, "HandlePut", integer.class, new Block()); + return function(this, "HandlePut", integer.class, new Block((Body) () -> { + returnNormal(handleNotAllowedMethod(p1)); + })); } @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandleRequest") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_BASIC) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) public integer handleRequest() { - return function(this, "HandleRequest", integer.class, new Block() - { - @Override - public void body() - { - WorkArea wa = local.get(); - Class handler = WebHandler.this.getClass(); - String mthdName = "handle" + wa.request.getMethod(); - - if (!SourceNameMapper.hasLegacyMethod(handler, mthdName)) - { - mthdName = "handleNotImplemented"; - } - - object oreq = TypeFactory.object(IwebRequest.class); - oreq.assign(ObjectOps.newInstance(RemoteWebRequest.class)); - ((RemoteWebRequest) oreq.ref()).initialize(wa.basepath, wa.target, wa.paths, wa.request); - - returnNormal((integer) ObjectOps.invoke(ObjectOps.thisObject(), mthdName, "I", oreq)); - } - }); + object oreq = TypeFactory.object(IwebRequest.class); + + return function(this, "HandleRequest", integer.class, new Block((Init) () -> { + catchError(com.goldencode.p2j.oo.lang.LegacyError.class, oErr -> { + returnNormal(handleException(oErr, oreq)); + }); + }, (Body) () -> { + WorkArea wa = local.get(); + oreq.assign(ObjectOps.newInstance(RemoteWebRequest.class)); + + integer ret = TypeFactory.integer(); + long method = MethodEnum.getEnum(oreq.ref().getMethod()).ref()._getValue(); + + if (method == MethodEnum.get.ref()._getValue()) + { + ret.assign(handleGet(oreq)); + } + else if (method == MethodEnum.post.ref()._getValue()) + { + ret.assign(handlePost(oreq)); + } + else if (method == MethodEnum.put.ref()._getValue()) + { + ret.assign(handlePut(oreq)); + } + else if (method == MethodEnum.patch.ref()._getValue()) + { + ret.assign(handlePatch(oreq)); + } + else if (method == MethodEnum.delete.ref()._getValue()) + { + ret.assign(handleDelete(oreq)); + } + else if (method == MethodEnum.head.ref()._getValue()) + { + ret.assign(handleHead(oreq)); + } + else if (method == MethodEnum.options.ref()._getValue()) + { + ret.assign(handleOptions(oreq)); + } + else if (method == MethodEnum.trace.ref()._getValue()) + { + ret.assign(handleTrace(oreq)); + } + else + { + ret.assign(handleNotImplemented(oreq)); + } + })); } @LegacySignature(returns = "INTEGER", type = Type.METHOD, name = "HandleTrace", parameters = @@ -338,7 +393,9 @@ { object p1 = TypeFactory.initInput(_p1); - return function(this, "HandleTrace", integer.class, new Block()); + return function(this, "HandleTrace", integer.class, new Block((Body) () -> { + returnNormal(handleNotAllowedMethod(p1)); + })); } @LegacySignature(type = Type.METHOD, name = "LogMessage", parameters = @@ -346,34 +403,129 @@ @LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT"), @LegacyParameter(name = "p2", type = "INTEGER", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) protected void logMessage(final character _p1, final integer _p2) { character p1 = TypeFactory.initInput(_p1); integer p2 = TypeFactory.initInput(_p2); - internalProcedure(this, "LogMessage", new Block()); + internalProcedure(this, "LogMessage", new Block((Body) () -> { + int level = p2.isUnknown() ? 0 : p2.intValue(); + + if (level == LogLevelEnum.fatal.ref()._getValue()) + logger.ref().fatal(p1); + else if (level == LogLevelEnum.error.ref()._getValue()) + logger.ref().error(p1); + else if (level == LogLevelEnum.info.ref()._getValue()) + logger.ref().info(p1); + else if (level == LogLevelEnum.debug.ref()._getValue()) + logger.ref().debug(p1); + else if (level == LogLevelEnum.trace.ref()._getValue()) + logger.ref().trace(p1); + else + logger.ref().warn(p1); + })); } - @LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "CanAcceptContentType", parameters = - { - @LegacyParameter(name = "p1", type = "OBJECT", qualified = "openedge.web.iwebrequest", mode = "INPUT"), - @LegacyParameter(name = "p2", type = "CHARACTER", mode = "INPUT") - }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - protected logical canAcceptContentType(object _p1, - character _p2) + @LegacySignature(returns = "LOGICAL", type = Type.METHOD, name = "CanAcceptContentType", parameters = { + @LegacyParameter(name = "req", type = "OBJECT", qualified = "openedge.web.iwebrequest", mode = "INPUT"), + @LegacyParameter(name = "content", type = "CHARACTER", mode = "INPUT") }) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_STUB) + protected logical canAcceptContentType(object _req, character _content) { + object req = TypeFactory.initInput(_req); + character content = TypeFactory.initInput(_content); + return function(this, "CanAcceptContentType", logical.class, new Block((Body) () -> { - + Assert.notNull(req, new character("Request")); + Assert.notNullOrEmpty(content, new character("Content type")); + + // accept everything??? + if (!req.ref().hasHeader(new character("Accept")).booleanValue()) + returnNormal(true); + + character[] contents = splitMimeType(content.getValue()); + String[] accepts = req.ref().getHeader(new character("Accept")).ref().getValue() + .getValue().split(","); + + for (String accept : accepts) + { + character[] mime = splitMimeType(accept); + + if ("*".equals(mime[0].getValue()) && "*".equals(mime[1].getValue())) + returnNormal(true); + + if (contents[0].getValue().equalsIgnoreCase(mime[0].getValue()) + && ("*".equals(mime[1].getValue()) + || contents[1].getValue().equalsIgnoreCase(mime[1].getValue()))) + returnNormal(true); + } + + returnNormal(false); })); } + private character[] splitMimeType (String mimeType) { + character[] ret = TypeFactory.characterExtent(2); + + // strip quality + mimeType = mimeType.split(";")[0].trim(); + + ret[0].assign(TextOps.entry(1, mimeType, "/")); + ret[1].assign(TextOps.entry(2, mimeType, "/")); + + return ret; + } + protected static HttpServletResponse getHttpResponse() { return local.get().response; } + protected static WebContext getWebContext() + { + return new WebContext(local.get()); + } + + public static class WebContext + { + private HttpServletRequest request; + + private String[] paths; + + private String target; + + private String basepath; + + private WebContext(WorkArea context) + { + this.request = context.request; + this.paths = context.paths; + this.target = context.target; + this.basepath = context.basepath; + } + + public HttpServletRequest getRequest() + { + return request; + } + + public String[] getPaths() + { + return paths; + } + + public String getTarget() + { + return target; + } + + public String getBasepath() + { + return basepath; + } + } + private static class WorkArea { private HttpServletResponse response; === modified file 'src/com/goldencode/p2j/oo/web/WebResponseWriter.java' --- src/com/goldencode/p2j/oo/web/WebResponseWriter.java 2021-02-21 16:25:50 +0000 +++ src/com/goldencode/p2j/oo/web/WebResponseWriter.java 2021-04-16 11:13:00 +0000 @@ -9,6 +9,7 @@ ** 002 CA 20191024 Added method support levels and updated the class support level. ** 003 CA 20210221 Fixed 'qualified', 'extent' and 'returns' annotations at the legacy ** signature. +** 004 ME 20210413 Implementation as per OE12.2 - webStream property not implemented. */ /* @@ -67,15 +68,21 @@ package com.goldencode.p2j.oo.web; import com.goldencode.p2j.util.*; +import com.goldencode.p2j.util.BlockManager.Action; +import com.goldencode.p2j.util.BlockManager.Condition; import com.goldencode.p2j.oo.core.*; +import com.goldencode.p2j.oo.net.http.Cookie; +import com.goldencode.p2j.oo.net.http.HttpHeader; +import com.goldencode.p2j.oo.net.http.filter.payload.MessageWriter; +import com.goldencode.p2j.oo.net.http.filter.writer.BodyWriterBuilder; import static com.goldencode.p2j.util.BlockManager.*; import static com.goldencode.p2j.report.ReportConstants.*; import static com.goldencode.p2j.util.InternalEntry.Type; -import java.io.*; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicReference; -import javax.servlet.*; import javax.servlet.http.*; /** @@ -87,51 +94,36 @@ public class WebResponseWriter extends com.goldencode.p2j.oo.io.OutputStream { - private character crlf = UndoableFactory.character(); - - private object message = UndoableFactory.object(com.goldencode.p2j.oo.net.http.IhttpMessage.class); - - private object response = UndoableFactory.object(com.goldencode.p2j.oo.net.http.IhttpResponse.class); - - private handle webStream = UndoableFactory.handle(); + private object response = TypeFactory.object(com.goldencode.p2j.oo.net.http.IhttpResponse.class); + + private handle webStream = TypeFactory.handle(); + + private boolean preambleWriten; + private boolean bodyWriten; private HttpServletResponse httpResponse; - private ByteArrayOutputStream byteBody = null; - - private StringBuilder textBody = null; - public void __web_WebResponseWriter_execute__() { externalProcedure(WebResponseWriter.this, new Block((Body) () -> { - { - } + onBlockLevel(Condition.ERROR, Action.THROW); })); } - @LegacySignature(returns = "CHARACTER", type = Type.GETTER, name = "CRLF") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) - public character getCrlf() - { - return function(this, "CRLF", character.class, new Block((Body) () -> - { - returnNormal(crlf); - })); - } @LegacySignature(returns = "OBJECT", qualified = "OpenEdge.Net.HTTP.IHttpMessage", type = Type.GETTER, name = "Message") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - public object getMessage_1() + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + public object getMessage() { return function(this, "Message", object.class, new Block((Body) () -> { - returnNormal(message); + returnNormal(response); })); } @LegacySignature(returns = "OBJECT", qualified = "OpenEdge.Net.HTTP.IHttpResponse", type = Type.GETTER, name = "Response") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public object getResponse() { return function(this, "Response", object.class, new Block((Body) () -> @@ -153,7 +145,6 @@ Assert.notNull(var, new character("Http Response")); response.assign(var); - open(); })); } @@ -185,42 +176,57 @@ @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void __web_WebResponseWriter_constructor__(final object _p1) { + object p1 = TypeFactory + .initInput(_p1); + internalProcedure(this, "__web_WebResponseWriter_constructor__", new Block((Body) () -> { __web_WebResponseWriter_constructor__(); - setResponse(_p1); + setResponse(p1); })); } @LegacySignature(type = Type.METHOD, name = "Close") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_BASIC) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void close() { internalProcedure(this, "Close", new Block((Body) () -> { - flushImpl(); - + flush(); + // TODO: close the webStream httpResponse = null; })); } @LegacySignature(type = Type.METHOD, name = "Flush") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void flush() { internalProcedure(this, "Flush", new Block((Body) () -> { - // TODO: the correct behavior is unknown; we are flushing on close + writeHttpPreamble(); + writeBody(); + + try + { + httpResponse.getOutputStream().flush(); + } + catch (Exception e) + { + } })); } @LegacySignature(type = Type.METHOD, name = "Open") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_BASIC) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public void open() { internalProcedure(this, "Open", new Block((Body) () -> { + // TODO: set the webStream handle httpResponse = WebHandler.getHttpResponse(); + preambleWriten = false; + bodyWriten = false; })); } @@ -228,25 +234,24 @@ { @LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_BASIC) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public int64 write(final character _p1) { character p1 = TypeFactory.initInput(_p1); return function(this, "Write", int64.class, new Block((Body) () -> { - // TODO: validate - if (byteBody != null) - { - // TODO: already bytes?? - } - - if (textBody == null) - { - textBody = new StringBuilder(); - } - - textBody.append(p1.toStringMessage()); + bodyWriten = true; + flush(); + + try + { + httpResponse.getOutputStream().print(p1.toStringMessage()); + } + catch (Exception e) + { + // TODO error handling + } returnNormal(TextOps.length(p1)); })); @@ -256,25 +261,24 @@ { @LegacyParameter(name = "p1", type = "LONGCHAR", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_BASIC) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) public int64 write(final longchar _p1) { longchar p1 = TypeFactory.initInput(_p1); return function(this, "Write", int64.class, new Block((Body) () -> { - // TODO: validate - if (byteBody != null) - { - // TODO: already bytes?? - } - - if (textBody == null) - { - textBody = new StringBuilder(); - } - - textBody.append(p1.toStringMessage()); + bodyWriten = true; + flush(); + + try + { + httpResponse.getOutputStream().print(p1.toStringMessage()); + } + catch (Exception e) + { + // TODO error handling + } returnNormal(TextOps.length(p1)); })); @@ -282,232 +286,220 @@ @LegacySignature(returns = "INT64", type = Type.METHOD, name = "Write", parameters = { - @LegacyParameter(name = "p1", type = "MEMPTR", mode = "INPUT"), - @LegacyParameter(name = "p2", type = "INT64", mode = "INPUT"), - @LegacyParameter(name = "p3", type = "INT64", mode = "INPUT") + @LegacyParameter(name = "data", type = "MEMPTR", mode = "INPUT"), + @LegacyParameter(name = "offset", type = "INT64", mode = "INPUT"), + @LegacyParameter(name = "len", type = "INT64", mode = "INPUT") }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_BASIC) - public int64 write(final memptr _p1, final int64 _p2, final int64 _p3) + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + public int64 write(final memptr _data, final int64 _offset, final int64 _len) { - memptr p1 = TypeFactory.initInput(_p1); - int64 p2 = TypeFactory.initInput(_p2); - int64 p3 = TypeFactory.initInput(_p3); + memptr data = TypeFactory.initInput(_data); + int64 offset = TypeFactory.initInput(_offset); + int64 len = TypeFactory.initInput(_len); return function(this, "Write", int64.class, new Block((Body) () -> { - // TODO: validate - if (textBody != null) - { - // TODO: already text body ?? - } - - if (byteBody == null) - { - byteBody = new ByteArrayOutputStream(); - } - - byte[] b = p1.getBytes(p2, p3).getByteArray(); - try - { - byteBody.write(b); - } - catch (IOException e) - { - // ignore, nothing can happen here - } - returnNormal(b.length); + bodyWriten = true; + flush(); + + int dataLen = data.length().intValue(); + int from = !offset.isUnknown() && offset.intValue() > 0 ? offset.intValue() - 1: -1; + int to = from >= 0 && !len.isUnknown() && len.intValue() > 0 ? from + len.intValue() : 0; + + if (dataLen > 0 && from >= 0 + && to > 0 + && to <= dataLen) { + + byte[] bytes = data.getByteArray(); + try + { + if (from > 0 || to != dataLen) + { + httpResponse.getOutputStream().write(Arrays.copyOfRange(bytes, from, to)); + } + else + { + httpResponse.getOutputStream().write(bytes); + } + } catch (Exception ex) + { + // TODO: error handling + returnNormal(0); + } + + returnNormal(len); + } + + returnNormal(0); })); } - @LegacySignature(returns = "INT64", type = Type.METHOD, name = "Write", parameters = - { - @LegacyParameter(name = "phData", type = "HANDLE", mode = "INPUT") - }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - @Override - public int64 write(handle _phData) - { - throw new UnsupportedOperationException(); - } - @LegacySignature(type = Type.METHOD, name = "WriteBody") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - public void writeBody() + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) + protected void writeBody() { - internalProcedure(this, "WriteBody", new Block((Body) () -> - { - // TODO: + object writer = TypeFactory.object(MessageWriter.class); + + internalProcedure(this, "WriteBody", new Block((Body) () -> { + if (!bodyWriten) + { + bodyWriten = true; + + if (response.ref().getEntity()._isValid()) + { + writer.assign(BodyWriterBuilder.build_1(response).ref().getWriter()); + + if (writer._isValid()) + { + writer.ref().open(); + writer.ref().write(response.ref().getEntity()); + writer.ref().close(); + + memptr data = ObjectOps.cast(writer.ref().getEntity(), ByteBucket.class).ref() + .getBytes().ref().getValue(); + + try { + // try to set content-length header before preamble + response.ref().setContentLength(new integer(data.length())); + writeHttpPreamble(); + + write(data, new int64(1), data.length()); + } finally { + data.setLength(0); + } + + } + } + } })); } @LegacySignature(type = Type.METHOD, name = "WriteCookies") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - public void writeCookies() - { - internalProcedure(this, "WriteCookies", new Block((Body) () -> - { - // TODO: - })); - } - - @LegacySignature(type = Type.METHOD, name = "WriteHeader", parameters = - { - @LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT"), - @LegacyParameter(name = "p2", type = "CHARACTER", mode = "INPUT") - }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_PARTIAL) - public void writeHeader(final character _p1, final character _p2) - { - character p1 = TypeFactory.initInput(_p1); - character p2 = TypeFactory.initInput(_p2); - - internalProcedure(this, "WriteHeader", new Block((Body) () -> - { - // TODO: validate - String name = p1.toStringMessage(); - if (name.isEmpty()) - { - // DO NOT OUTPUT EMPTY HEADER NAMES! This will abend clients like Postman or SoapUI - System.err.println("Empty header name!"); - } - else - { - httpResponse.setHeader(name, p2.toStringMessage()); + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL | RT_LVL_FULL) + protected void writeCookies() + { + internalProcedure(this, "WriteCookies", new Block((Body) () -> { + if (response._isValid()) + { + + AtomicReference[]> cookies = new AtomicReference<>( + TypeFactory.objectExtent(Cookie.class)); + int nh = response.ref() + .getCookies(new OutputExtentParameter>() + { + @Override + public object[] getVariable() + { + return cookies.get(); + } + + @Override + public void setVariable(object[] reference) + { + cookies.set(reference); + } + }).intValue(); + + for (int i = 0; i < nh; i++) + { + object cookie = cookies.get()[i]; + javax.servlet.http.Cookie httpCookie = new javax.servlet.http.Cookie( + cookie.ref().getName().getValue(), cookie.ref().getValue().getValue()); + + httpCookie.setDomain(cookie.ref().getDomain().getValue()); + httpCookie.setPath(cookie.ref().getPath().getValue()); + + if (!cookie.ref().getMaxAge().isUnknown()) + httpCookie.setMaxAge(cookie.ref().getMaxAge().intValue()); + + if (cookie.ref().getSecure().booleanValue()) + httpCookie.setSecure(true); + if (cookie.ref().getHttpOnly().booleanValue()) + httpCookie.setHttpOnly(true); + + httpResponse.addCookie(httpCookie); + } } })); } @LegacySignature(type = Type.METHOD, name = "WriteHeaders") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - public void writeHeaders() + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + protected void writeHeaders() { internalProcedure(this, "WriteHeaders", new Block((Body) () -> { - // TODO: + if (response._isValid()) { + + AtomicReference[]> headers = + new AtomicReference<>(TypeFactory.objectExtent(HttpHeader.class)); + int nh = response.ref().getHeaders( + new OutputExtentParameter>() + { + @Override + public object[] getVariable() + { + return headers.get(); + } + + @Override + public void setVariable(object[] reference) + { + headers.set(reference); + } + }).intValue(); + + for (int i = 0; i < nh; i++) + { + object hdr = headers.get()[i]; + httpResponse.setHeader(hdr.ref().getName().getValue(), + String.format("%s%s", hdr.ref().getBaseValue().getValue(), + hdr.ref().getParameterValues().getValue())); + } + } })); } @LegacySignature(type = Type.METHOD, name = "WriteHttpPreamble") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - public void writeHttpPreamble() + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + protected void writeHttpPreamble() { internalProcedure(this, "WriteHttpPreamble", new Block((Body) () -> { - // TODO: + if (preambleWriten) + return; + + preambleWriten = true; + + writeStatusLine(); + + // this should cover content-type, character encoding as well + writeHeaders(); + writeCookies(); + })); } @LegacySignature(type = Type.METHOD, name = "WriteStatusLine") - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - public void writeStatusLine() + @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_FULL) + protected void writeStatusLine() { internalProcedure(this, "WriteStatusLine", new Block((Body) () -> { - // TODO: - })); - } - - @LegacySignature(type = Type.METHOD, name = "WriteToStream", parameters = - { - @LegacyParameter(name = "p1", type = "CHARACTER", mode = "INPUT") - }) - @LegacyResourceSupport(supportLvl = CVT_LVL_FULL|RT_LVL_STUB) - public void writeToStream(final character _p1) - { - character p1 = TypeFactory.initInput(_p1); - - internalProcedure(this, "WriteToStream", new Block((Body) () -> - { - // TODO: - })); - } - - private void flushImpl() - { - // TODO: set cookies - // TODO: write headers - - if (!response.ref().getStatusCode().isUnknown()) - { - if (response.ref().getStatusReason().isUnknown()) - { - httpResponse.setStatus(response.ref().getStatusCode().intValue()); - } - else - { - // sendError can't be used, as it sends a Jetty error page - httpResponse.setStatus(response.ref().getStatusCode().intValue(), - response.ref().getStatusReason().toStringMessage()); - } - } - - httpResponse.setContentType(response.ref().getContentType().toStringMessage()); - - object entity = response.isUnknown() ? null : response.ref().getEntity(); - try - { - // TODO: what happens if at least two of (text, byte, entity) are set? - byte[] bytes = null; - if (textBody != null) - { - // TODO: the encoding? - String encoding = httpResponse.getCharacterEncoding(); - if (encoding == null) + if (response._isValid()) + { + if (response.ref().getStatusReason().isUnknown()) { - encoding = "UTF-8"; - httpResponse.setCharacterEncoding(encoding); + httpResponse.setStatus(response.ref().getStatusCode().intValue()); } - - // write the text - bytes = textBody.toString().getBytes(encoding); - } - else if (byteBody != null) - { - // write the bytes - bytes = byteBody.toByteArray(); - } - else if (entity == null || entity.isUnknown() || !entity.isValid().booleanValue()) - { - // TODO:? - } - else if (entity.ref() instanceof LegacyString) - { - longchar val = ((LegacyString) entity.ref()).getValue(); - if (!val.isUnknown()) + else { - String encoding = httpResponse.getCharacterEncoding(); - if (encoding == null) - { - encoding = "UTF-8"; - httpResponse.setCharacterEncoding(encoding); - } + httpResponse.setStatus(response.ref().getStatusCode().intValue(), + response.ref().getStatusReason().toStringMessage()); - // write the text - bytes = val.toStringMessage().getBytes(encoding); - } - } - else if (entity.ref() instanceof Memptr) - { - memptr val = ((Memptr) entity.ref()).getValue(); - if (!val.isUnknown()) - { - bytes = val.getByteArray(); - } - } - - if (bytes != null) - { - httpResponse.setContentLength(bytes.length); - - ServletOutputStream output = httpResponse.getOutputStream(); - output.write(bytes); - output.flush(); - } - } - catch (Exception e) - { - // TODO: - e.printStackTrace(); - } + } + } + })); } + } === modified file 'src/com/goldencode/p2j/persist/serial/JsonExport.java' --- src/com/goldencode/p2j/persist/serial/JsonExport.java 2021-03-19 12:11:57 +0000 +++ src/com/goldencode/p2j/persist/serial/JsonExport.java 2021-03-25 07:26:35 +0000 @@ -19,6 +19,7 @@ ** 008 OM 20201120 Improved compatibility with P4GL. ** ME 20210130 Added serializeTempTable method into JsonArray. ** 20210319 Refactor to reuse code for JsonArray/JsonObject Read methods. +** ME 20210324 Add method to serialize 4GL error as Json Object. */ /* @@ -81,6 +82,11 @@ import java.util.*; import java.util.logging.*; import com.fasterxml.jackson.core.*; +import com.goldencode.p2j.oo.core.Assert; +import com.goldencode.p2j.oo.json.objectmodel.JsonArray; +import com.goldencode.p2j.oo.json.objectmodel.JsonObject; +import com.goldencode.p2j.oo.lang.AppError; +import com.goldencode.p2j.oo.lang.LegacyError; import com.goldencode.p2j.persist.*; import com.goldencode.p2j.util.*; import com.goldencode.p2j.util.ErrorManager; @@ -1071,6 +1077,79 @@ } /** + * Serialize 4GL legacy error as Json object. + * + * Used by Json entity/body writers in .net package, might be part of some new class in .json package. + * @param _poError The 4GL legacy error. + * + * @return The JSON serialization of the error as a Json object. + */ + public static object serializeError(object _poError) + { + + object poError = TypeFactory.initInput(_poError); + object oResponse = TypeFactory.object(JsonObject.class); + object oError = TypeFactory.object(JsonObject.class); + object oErrorList = TypeFactory.object(JsonArray.class); + integer iLoop = TypeFactory.integer(); + + Assert.notNull(poError, new character("Error")); + oResponse.assign(ObjectOps.newInstance(JsonObject.class)); + oErrorList.assign(ObjectOps.newInstance(JsonArray.class)); + + if ((ObjectOps.typeOf(poError, AppError.class)).booleanValue()) + { + oResponse.ref().add(new character("_retVal"), ObjectOps.cast(poError, AppError.class).ref().getReturnValue()); + } + + oResponse.ref().add(new character("_errors"), oErrorList); + + for (int i = 0; i < _poError.ref().getNumMessages().intValue(); i++) + { + iLoop.assign(i + 1); + + oError.assign(ObjectOps.newInstance(JsonObject.class)); + oErrorList.ref().add_2(oError); + oError.ref().add(new character("_errorMsg"), poError.ref().getMessage(iLoop)); + oError.ref().add(new character("_errorNum"), poError.ref().getMessageNum(iLoop)); + } + + if ((SessionUtils.isDebugAlert()).booleanValue()) + { + oResponse.ref().add(new character("_type"), poError.ref().getLegacyClass().ref().getTypeName()); + + if (!poError.ref().getCallStack().isUnknown()) + { + oErrorList.assign(ObjectOps.newInstance(JsonArray.class)); + oResponse.ref().add(new character("_stack"), oErrorList); + + String[] callStack = TextOps.entries(poError.ref().getCallStack().getValue(), "\\n"); + + for (int i = 0; i < callStack.length; i++) + { + oErrorList.ref().add(new character(callStack[i])); + } + } + + } + +// TODO: accessing any method in LegacyClass throws NPE +// ProcedureManager.CalleeInfoImpl.push #5351 +// oProp.assign(poError.ref().getLegacyClass().ref().getProperty(new character("InnerError"))); +// +// if (_and(and(oProp.isValid(), () -> isEqual(oProp.ref().getDataType(), DataType.object_)), () -> LegacyClass.getLegacyClass(oProp.ref().getDataTypeName()).ref().isA(ObjectOps.getLegacyClass(LegacyError.class)))) +// { +// oInner.assign(oProp.ref().get(poError)); +// +// if ((oInner.isValid()).booleanValue()) +// { +// this.writeError(oInner); +// } +// } + return oResponse; + }; + + /** * A consumer which accepts one value and throws IOException, to avoid having to handle this * exception in every defined lambda expression. */ === modified file 'src/com/goldencode/p2j/util/ErrorManager.java' --- src/com/goldencode/p2j/util/ErrorManager.java 2021-03-23 11:42:56 +0000 +++ src/com/goldencode/p2j/util/ErrorManager.java 2021-03-23 13:06:09 +0000 @@ -3554,15 +3554,6 @@ } } - public static String getInvalidParameterError(String type, _BaseObject_ instance, String method, String errMsg) - { - return String.format("Invalid %s parameter to %s:%s( ). %s", - type, - instance.getLegacyClass().ref().getTypeName().getValue(), - method, - errMsg); - } - /** * Helper to expose error state in a way that avoids context local lookups. */ === modified file 'src/com/goldencode/p2j/util/ObjectOps.java' --- src/com/goldencode/p2j/util/ObjectOps.java 2021-03-23 11:42:56 +0000 +++ src/com/goldencode/p2j/util/ObjectOps.java 2021-03-30 06:56:24 +0000 @@ -49,6 +49,7 @@ ** CA 20210310 Performance improvement in hasDestructor - cache the result of this method. ** CA 20210322 asResource(LegacyEnum) will always return an ObjectResource instance (and a resource ID ** computed), so temp-table fields can refer enums. +** ME 20210330 Added convenient increment/decrement methods for object references. */ /* @@ -1493,6 +1494,18 @@ } /** + * Increment the reference counter for this object instance. + * + * @param obj + * The legacy object instance. + */ + public static void increment(object obj) + { + if (obj != null && obj._isValid()) + increment(obj.ref()); + } + + /** * Decrement the reference counter for this instance. * * @param ref @@ -1515,6 +1528,18 @@ // TODO: throw exception? } } + + /** + * Decrement the reference counter for this object instance. + * + * @param obj + * The legacy object instance. + */ + public static void decrement(object obj) + { + if (obj != null && obj._isValid()) + decrement(obj.ref()); + } /** * When an INPUT object variable is initialized in the top-level block via {@link TypeFactory#object}, === modified file 'src/com/goldencode/p2j/util/StreamWrapper.java' --- src/com/goldencode/p2j/util/StreamWrapper.java 2019-07-22 20:20:22 +0000 +++ src/com/goldencode/p2j/util/StreamWrapper.java 2021-03-25 11:51:54 +0000 @@ -58,6 +58,7 @@ ** 031 OM 20170622 Implemented peekCh() method. ** 032 ECF 20171026 Added write(byte[], int, int) method. ** 033 HC 20190716 Implemented stream-by-name resolution. +** 034 ME 20210325 Add readBlockRemote method as pass-through. */ /* @@ -1026,4 +1027,17 @@ streamByName.remove(name.toLowerCase()); } } + + @Override + protected int readBlockRemote(memptr var) + { + if (stream != null) + { + return stream.readBlockRemote(var); + } + else + { + return super.readBlockRemote(var); + } + } } === modified file 'src/com/goldencode/p2j/xml/XDocumentImpl.java' --- src/com/goldencode/p2j/xml/XDocumentImpl.java 2021-03-10 21:00:35 +0000 +++ src/com/goldencode/p2j/xml/XDocumentImpl.java 2021-03-26 12:08:13 +0000 @@ -36,6 +36,7 @@ ** 015 CA 20180520 Added all-string initializeDocumentType version. ** 016 OM 20201120 Renamed NAMESPACE-PREFIX setter to fit buffer attributes naming convention. ** OM 20210309 Removed deprecation warnings. +** 017 ME 20210326 Remove last trailing null char, if found, when loading data from memptr. */ /* @@ -799,17 +800,16 @@ ByteArrayInputStream bais = null; // prepare the input stream: keep all bytes until '\0' is encountered - int last = -1; + int last = 0; for (byte b : bytes) { - last = last + 1; - if (b == 0) { break; } + last = last + 1; } - if (last != bytes.length - 1 && last > 0) + if (last != bytes.length) { // we have at least 1 non-zero byte byte[] cleaned = new byte[last]; === modified file 'src/com/goldencode/p2j/xml/XEntityImpl.java' --- src/com/goldencode/p2j/xml/XEntityImpl.java 2021-03-10 21:00:35 +0000 +++ src/com/goldencode/p2j/xml/XEntityImpl.java 2021-03-26 07:37:36 +0000 @@ -32,6 +32,7 @@ ** 014 OM 20201120 Renamed attribute accessors to fit buffer attributes naming convention. ** EVL 20210218 Changed integer to int64 to match MathOps changes. ** OM 20210309 Removed deprecation warnings. +** ME 20210325 Throw error instead of show for invalid xml. */ /* @@ -843,7 +844,7 @@ String msg = "X-NODEREF or X-DOCUMENT %s got an error: %s"; String err = String.format(msg, attribute.toUpperCase(), message); - ErrorManager.recordOrShowError(9082, err, false, false, false); + ErrorManager.recordOrThrowError(9082, err, false, false); } /** # Begin bundle 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 //i7kinChIQJ+Vv4