package org.pac4j.vertx.core; import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.scribejava.core.model.OAuth1RequestToken; import com.github.scribejava.core.model.Token; import com.nimbusds.oauth2.sdk.Scope; import com.nimbusds.oauth2.sdk.Scope.Value.Requirement; import com.nimbusds.oauth2.sdk.token.AccessTokenType; import com.nimbusds.oauth2.sdk.token.BearerAccessToken; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import org.pac4j.core.exception.TechnicalException; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * <p>Default eventbus object converter</p> * <p>The serialization strategy is:</p> * <ul> * <li>For primitive types (String, Number and Boolean), return as is</li> * <li>For arrays, convert to JsonArray</li> * <li>Otherwise, convert to a JsonObject with the class name in the "class" attribute and the serialized form with Jackson in the "value" attribute. * The (de)serialization Jackson process can be customized using the <code>addMixIn(target, mixinSource)</code> method</li> * </ul> * * @author Michael Remond * @since 1.1.0 * */ public class DefaultJsonConverter implements JsonConverter { private final ObjectMapper mapper = new ObjectMapper(); private static final DefaultJsonConverter INSTANCE = new DefaultJsonConverter(); public static JsonConverter getInstance() { return INSTANCE; } public DefaultJsonConverter() { mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY); mapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE); mapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE); mapper.addMixIn(OAuth1RequestToken.class, OAuth1RequestTokenMixin.class) .addMixIn(BearerAccessToken.class, BearerAccessTokenMixin.class) .addMixIn(Scope.Value.class, ValueMixin.class) .addMixIn(Token.class, TokenMixin.class); } @Override public Object encodeObject(Object value) { if (value == null) { return null; } else if (isPrimitiveType(value)) { return value; } else if (value instanceof Object[]) { Object[] src = ((Object[]) value); List<Object> list = new ArrayList<>(src.length); fillEncodedList(src, list); return new JsonArray(list); } else { try { return new JsonObject().put("class", value.getClass().getName()).put("value", new JsonObject(encode(value))); } catch (Exception e) { throw new TechnicalException("Error while encoding object", e); } } } private void fillEncodedList(Object[] src, List<Object> list) { for (Object object : src) { list.add(encodeObject(object)); } } @Override public Object decodeObject(Object value) { if (value == null) { return null; } else if (isPrimitiveType(value)) { return value; } else if (value instanceof JsonArray) { JsonArray src = (JsonArray) value; List<Object> list = new ArrayList<>(src.size()); fillDecodedList(src, list); return list.toArray(); } else if (value instanceof JsonObject) { JsonObject src = (JsonObject) value; return decode(src); } return null; } private Object decode(JsonObject src) { try { return decode(src.getJsonObject("value").encode(), Class.forName(src.getString("class"))); } catch (Exception e) { throw new TechnicalException("Error while decoding object", e); } } private void fillDecodedList(JsonArray src, List<Object> list) { for (Object object : src) { list.add(decodeObject(object)); } } private boolean isPrimitiveType(Object value) { return value instanceof String || value instanceof Number || value instanceof Boolean; } private String encode(Object value) throws JsonGenerationException, JsonMappingException, IOException { return mapper.writeValueAsString(value); } @SuppressWarnings("unchecked") private <T> T decode(String string, Class<?> clazz) throws JsonParseException, JsonMappingException, IOException { return (T) mapper.readValue(string, clazz); } public static class BearerAccessTokenMixin { @JsonIgnore private AccessTokenType type; @JsonCreator public BearerAccessTokenMixin(@JsonProperty("value") String value, @JsonProperty("lifetime") long lifetime, @JsonProperty("scope") Scope scope) { } } public static class ValueMixin { @JsonCreator public ValueMixin(@JsonProperty("value") String value, @JsonProperty("requirement") Requirement requirement) { } } public static class TokenMixin { @JsonCreator public TokenMixin(@JsonProperty("token") String token, @JsonProperty("secret") String secret, @JsonProperty("rawResponse") String rawResponse) { } } public static class OAuth1RequestTokenMixin { @JsonCreator public OAuth1RequestTokenMixin(@JsonProperty("token") String token, @JsonProperty("tokenSecret") String tokenSecret, @JsonProperty("oauthCallbackConfirmed") boolean oauthCallbackConfirmed, @JsonProperty("rawResponse") String RawResponse) { } } }