/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you 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 the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>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 org.apereo.portal.groups.pags.dao.jpa; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.ObjectCodec; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; import java.net.URLEncoder; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import org.apereo.portal.groups.pags.dao.IPersonAttributesGroupDefinition; import org.apereo.portal.groups.pags.dao.IPersonAttributesGroupTestDefinition; import org.apereo.portal.groups.pags.dao.IPersonAttributesGroupTestGroupDefinition; import org.apereo.portal.rest.PagsRESTController; import org.apereo.portal.url.IPortalRequestUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * Supports serialization and deserialization of PAGS definitions in various ways. The {@link * JsonSerializer} and {@link JsonDeserializer} classes themselves are nested types on this class, * which is stereotyped as a Spring Component. This approach allows the outer type to access * Spring-managed dependencies and make them (privately, statically) available to the nested types. * */ @Component public final class PagsDefinitionJsonUtils { private static final Map<Class<?>, Object> beans = new HashMap<>(); @Autowired private IPortalRequestUtils portalRequestUtils; /** Makes some beans from the Spring app context available to nested types. */ @PostConstruct public void setUp() { beans.clear(); beans.put(IPortalRequestUtils.class, portalRequestUtils); } /* * Nested Types */ /** * We only want name, description, and a URL that would return the whole object (in JSON) when * we serialize the <code>members</code> attribute of a PAGS definition. */ public static final class DefinitionLinkJsonSerializer extends JsonSerializer<Set<IPersonAttributesGroupDefinition>> { @Override public void serialize( Set<IPersonAttributesGroupDefinition> members, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws JsonGenerationException, IOException { jsonGenerator.writeStartArray(); for (IPersonAttributesGroupDefinition groupDef : members) { jsonGenerator.writeStartObject(); jsonGenerator.writeStringField("name", groupDef.getName()); jsonGenerator.writeStringField("description", groupDef.getDescription()); IPortalRequestUtils portalRequestUtils = (IPortalRequestUtils) beans.get(IPortalRequestUtils.class); HttpServletRequest req = portalRequestUtils.getCurrentPortalRequest(); StringBuilder url = new StringBuilder(); url.append(req.getContextPath()) .append( String.format( PagsRESTController.URL_FORMAT_STRING, URLEncoder.encode(groupDef.getName(), "UTF-8"))); jsonGenerator.writeStringField("url", url.toString()); jsonGenerator.writeEndObject(); } jsonGenerator.writeEndArray(); } } /** * This deserializer will (when it's implemented) translate JSON references to PAGS definitions * into full PAGS definitions. */ public static final class DefinitionLinkJsonDeserializer extends JsonDeserializer<Set<IPersonAttributesGroupDefinition>> { @Override public Set<IPersonAttributesGroupDefinition> deserialize( JsonParser jsonParser, DeserializationContext ctx) throws JsonProcessingException, IOException { ObjectCodec oc = jsonParser.getCodec(); JsonNode node = oc.readTree(jsonParser); // For now, we'll only support deserializing PAGS definitions WITHOUT members... if (node.elements().hasNext()) { throw new UnsupportedOperationException("Members not yet supported"); } return Collections.emptySet(); } } public static final class TestGroupJsonDeserializer extends JsonDeserializer<Set<IPersonAttributesGroupTestGroupDefinition>> { @Override public Set<IPersonAttributesGroupTestGroupDefinition> deserialize( JsonParser jsonParser, DeserializationContext ctx) throws JsonProcessingException, IOException { Set<IPersonAttributesGroupTestGroupDefinition> rslt = new HashSet<>(); ObjectCodec oc = jsonParser.getCodec(); JsonNode json = oc.readTree(jsonParser); for (Iterator<JsonNode> testGroupNodes = json.elements(); testGroupNodes.hasNext(); ) { JsonNode testGroupNode = testGroupNodes.next(); IPersonAttributesGroupTestGroupDefinition testGroupDef = new PersonAttributesGroupTestGroupDefinitionImpl(); Set<IPersonAttributesGroupTestDefinition> testDefs = new HashSet<>(); for (Iterator<JsonNode> testNodes = testGroupNode.get("tests").elements(); testNodes.hasNext(); ) { JsonNode testNode = testNodes.next(); IPersonAttributesGroupTestDefinition testDef = new PersonAttributesGroupTestDefinitionImpl(); testDef.setAttributeName(testNode.get("attributeName").asText()); testDef.setTesterClassName(testNode.get("testerClassName").asText()); testDef.setTestValue(testNode.get("testValue").asText()); testDefs.add(testDef); // NOTE: We are also obligated to establish the backlink // testDef --> testGroupDef; arguably this backlink serves // little purpose and could be removed. testDef.setTestGroup(testGroupDef); } testGroupDef.setTests(testDefs); rslt.add(testGroupDef); } return rslt; } } }