From 82d8d3d21bf942a3ff02b3b20fe81debcb9f50c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Wed, 8 Nov 2023 11:02:31 +0100 Subject: [PATCH 1/2] feature/Add JsonBoxSerializer --- .../commons/util/JsonBoxSerializer.scala | 102 ++++++++++++++++++ .../commons/util/JsonSerializers.scala | 4 +- 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 obp-commons/src/main/scala/com/openbankproject/commons/util/JsonBoxSerializer.scala diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/util/JsonBoxSerializer.scala b/obp-commons/src/main/scala/com/openbankproject/commons/util/JsonBoxSerializer.scala new file mode 100644 index 0000000000..56091ed83a --- /dev/null +++ b/obp-commons/src/main/scala/com/openbankproject/commons/util/JsonBoxSerializer.scala @@ -0,0 +1,102 @@ +/* + * Copyright 2006-2011 WorldWide Conferencing, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.openbankproject.commons.util + +import net.liftweb.common.{Box, Empty, Failure, Full, ParamFailure} +import net.liftweb.json.Extraction.{decompose, extract} +import net.liftweb.json.JsonAST.{JField, JNothing, JNull, JObject, JString, JValue} +import net.liftweb.json.{Formats, MappingException, Serializer, TypeInfo} +import org.apache.commons.codec.binary.Base64 + +import java.io._ +import java.lang.reflect.ParameterizedType + +class JsonBoxSerializer extends Serializer[Box[_]] { + private val BoxClass = classOf[Box[_]] + + def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Box[_]] = { + case (TypeInfo(BoxClass, ptype), json) => json match { + case JNull | JNothing => Empty + case JObject(JField("box_failure", JString("Failure")) :: + JField("msg", JString(msg)) :: + JField("exception", exception) :: + JField("chain", chain) :: Nil) => + Failure(msg, deserializeException(exception), + extract(chain, TypeInfo(BoxClass, Some(typeHoldingFailure))).asInstanceOf[Box[Failure]]) + case JObject(JField("box_failure", JString("ParamFailure")) :: + JField("msg", JString(msg)) :: + JField("exception", exception) :: + JField("chain", chain) :: + JField("paramType", JString(paramType)) :: + JField("param", param) :: Nil) => + val clazz = Thread.currentThread.getContextClassLoader.loadClass(paramType) + ParamFailure(msg, deserializeException(exception), + extract(chain, TypeInfo(BoxClass, Some(typeHoldingFailure))).asInstanceOf[Box[Failure]], + extract(param, TypeInfo(clazz, None))) + case x => + val t = ptype.getOrElse(throw new MappingException("parameterized type not known for Box")) + Full(extract(x, TypeInfo(t.getActualTypeArguments()(0).asInstanceOf[Class[_]], None))) + } + } + + def serialize(implicit format: Formats): PartialFunction[Any, JValue] = { + case Full(x) => decompose(x) + case Empty => JNull + case ParamFailure(msg, exception, chain, param) => + JObject(JField("box_failure", JString("ParamFailure")) :: + JField("msg", JString(msg)) :: + JField("exception", serializeException(exception)) :: + JField("chain", decompose(chain)) :: + JField("paramType", JString(param.asInstanceOf[AnyRef].getClass.getName)) :: + JField("param", decompose(param)) :: Nil) + case Failure(msg, exception, chain) => + JObject(JField("box_failure", JString("Failure")) :: + JField("msg", JString(msg)) :: + JField("exception", serializeException(exception)) :: + JField("chain", decompose(chain)) :: Nil) + } + + private val typeHoldingFailure = new ParameterizedType { + def getActualTypeArguments = Array(classOf[Failure]) + def getOwnerType = classOf[Box[Failure]] + def getRawType = classOf[Box[Failure]] + } + + private def serializeException(exception: Box[Throwable]) = exception match { + case Full(x) => JString(javaSerialize(x)) + case _ => JNull + } + + private def deserializeException(json: JValue) = json match { + case JString(s) => Full(javaDeserialize(s).asInstanceOf[Throwable]) + case _ => Empty + } + + private def javaSerialize(obj: AnyRef): String = { + val bytes = new ByteArrayOutputStream + val out = new ObjectOutputStream(bytes) + out.writeObject(obj) + new String((new Base64).encode(bytes.toByteArray)) + } + + private def javaDeserialize(s: String): Any = { + val bytes = new ByteArrayInputStream((new Base64).decode(s.getBytes("UTF-8"))) + val in = new ObjectInputStream(bytes) + in.readObject + } +} + diff --git a/obp-commons/src/main/scala/com/openbankproject/commons/util/JsonSerializers.scala b/obp-commons/src/main/scala/com/openbankproject/commons/util/JsonSerializers.scala index 262c328036..d286b9f65b 100644 --- a/obp-commons/src/main/scala/com/openbankproject/commons/util/JsonSerializers.scala +++ b/obp-commons/src/main/scala/com/openbankproject/commons/util/JsonSerializers.scala @@ -37,8 +37,10 @@ object JsonSerializers { } } + val BoxSerializer: JsonBoxSerializer = new JsonBoxSerializer + val serializers: List[Serializer[_]] = - AbstractTypeDeserializer :: SimpleEnumDeserializer :: ScalaProductDeserializer :: + BoxSerializer :: AbstractTypeDeserializer :: SimpleEnumDeserializer :: ScalaProductDeserializer :: BigDecimalSerializer :: StringDeserializer :: FiledRenameSerializer :: EnumValueSerializer :: JsonAbleSerializer :: ListResultSerializer.asInstanceOf[Serializer[_]] :: // here must do class cast, or it cause compile error, looks like a bug of scala. From 0050a32aa83d6744388ccfa7b28a6e419aee095a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Wed, 8 Nov 2023 11:27:09 +0100 Subject: [PATCH 2/2] bugfix/Remove JSON AST serialization/deserialization regarding Redis --- obp-api/src/main/scala/code/api/cache/Redis.scala | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/obp-api/src/main/scala/code/api/cache/Redis.scala b/obp-api/src/main/scala/code/api/cache/Redis.scala index b3bb000432..84d6051452 100644 --- a/obp-api/src/main/scala/code/api/cache/Redis.scala +++ b/obp-api/src/main/scala/code/api/cache/Redis.scala @@ -1,6 +1,6 @@ package code.api.cache -import code.api.util.{APIUtil, CustomJsonFormats} +import code.api.util.APIUtil import code.util.Helper.MdcLoggable import com.openbankproject.commons.ExecutionContext.Implicits.global import redis.clients.jedis.Jedis @@ -8,8 +8,6 @@ import scalacache.memoization.{cacheKeyExclude, memoize, memoizeSync} import scalacache.{Flags, ScalaCache} import scalacache.redis.RedisCache import scalacache.serialization.Codec -import net.liftweb.json -import net.liftweb.json.{Extraction, Formats, parse} import scala.concurrent.Future import scala.concurrent.duration.Duration @@ -38,16 +36,14 @@ object Redis extends MdcLoggable { implicit val scalaCache = ScalaCache(RedisCache(url, port)) implicit val flags = Flags(readsEnabled = true, writesEnabled = true) - implicit def formats: Formats = CustomJsonFormats.formats implicit def anyToByte[T](implicit m: Manifest[T]) = new Codec[T, Array[Byte]] { import com.twitter.chill.KryoInjection + def serialize(value: T): Array[Byte] = { - val jsonString = json.compactRender(Extraction.decompose(value)) logger.debug("KryoInjection started") - logger.trace(s"redis.anyToByte.serialize value is $jsonString") - val bytes: Array[Byte] = KryoInjection(jsonString) + val bytes: Array[Byte] = KryoInjection(value) logger.debug("KryoInjection finished") bytes } @@ -56,9 +52,9 @@ object Redis extends MdcLoggable { import scala.util.{Failure, Success} val tryDecode: scala.util.Try[Any] = KryoInjection.invert(data) tryDecode match { - case Success(v) => json.parse(v.toString).extract[T] + case Success(v) => v.asInstanceOf[T] case Failure(e) => - logger.error(e) + println(e) "NONE".asInstanceOf[T] } }