Skip to content

Commit

Permalink
SAK-50724 rubric Implement archive/merge
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianfish committed Jan 22, 2025
1 parent 2251b74 commit f7b28d3
Show file tree
Hide file tree
Showing 27 changed files with 883 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.sakaiproject.rubrics.api.RubricsService;
import org.sakaiproject.search.api.SearchIndexBuilder;
import org.sakaiproject.search.api.SearchService;
import org.sakaiproject.serialization.MapperFactory;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.springframework.orm.hibernate.AdditionalHibernateMappings;
import org.sakaiproject.tags.api.TagService;
Expand Down Expand Up @@ -341,4 +342,11 @@ public TagService tagService() {
public LTIService ltiService() {
return mock(LTIService.class);
}

@Bean(name = "mapperFactory")
public MapperFactory mapperFactory() {
MapperFactory factory = new MapperFactory();
factory.init();
return factory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ protected String archiveSite(Site site, Document doc, Stack stack, String fromSy

if ( pattern != null ) {
NodeList nl = siteNode.getElementsByTagName("property");
List<Element> toRemove = new ArrayList<Element>();
List<Element> toRemove = new ArrayList<>();

for(int i = 0; i < nl.getLength(); i++) {
Element proptag = (Element)nl.item(i);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<value>WebService</value>
<value>LessonBuilderEntityProducer</value>
<value>PollListManager</value>
<value>rubrics</value>
</list>
</property>
<property name="mergeFilteredSakaiRoles">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@
# archive.merge.filter.services=false

# List of data service types that can merge in data from an archive
# DEFAULT: AnnouncementService,AssignmentService,ContentHostingService,CalendarService,ChatEntityProducer,DiscussionService,MailArchiveService,SyllabusService,RWikiObjectService,DiscussionForumService,WebService,LessonBuilderEntityProducer
# DEFAULT: AnnouncementService,AssignmentService,ContentHostingService,CalendarService,ChatEntityProducer,DiscussionService,MailArchiveService,SyllabusService,RWikiObjectService,DiscussionForumService,WebService,LessonBuilderEntityProducer,RubricsService
# archive.merge.filtered.services={list of service names}

# Controls if user role filtering is enabled. If enabled, any user roles not in the list cannot archive or merge data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,16 @@
import java.io.IOException;
import java.io.Serializable;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;

import org.apache.commons.lang3.StringUtils;
import org.sakaiproject.hibernate.HibernateCrudRepository;

import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.api.WstxOutputProperties;
import com.ctc.wstx.stax.WstxInputFactory;
import com.ctc.wstx.stax.WstxOutputFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import org.springframework.beans.factory.annotation.Autowired;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
Expand All @@ -45,13 +36,16 @@
@Slf4j
public abstract class BasicSerializableRepository<T, ID extends Serializable> extends HibernateCrudRepository<T, ID> implements SerializableRepository<T, ID> {

@Setter
@Autowired
private MapperFactory mapperFactory;

@Override
public String toJSON(T t) {
String json = "";
if (t != null) {
sessionFactory.getCurrentSession().refresh(t);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModules(new JavaTimeModule());
ObjectMapper mapper = mapperFactory.getJsonMapper();
try {
json = mapper.writeValueAsString(t);
} catch (JsonProcessingException e) {
Expand All @@ -66,8 +60,7 @@ public String toJSON(T t) {
public T fromJSON(String json) {
T obj = null;
if (StringUtils.isNotBlank(json)) {
ObjectMapper mapper = new ObjectMapper();
mapper.registerModules(new JavaTimeModule());
ObjectMapper mapper = mapperFactory.getJsonMapper();
try {
obj = mapper.readValue(json, getDomainClass());
} catch (IOException e) {
Expand All @@ -83,7 +76,7 @@ public String toXML(T t) {
String xml = "";
if (t != null) {
sessionFactory.getCurrentSession().refresh(t);
final XmlMapper mapper = createXMLMapper();
final XmlMapper mapper = mapperFactory.getWrappedXmlMapper();
try {
xml = mapper.writeValueAsString(t);
} catch (JsonProcessingException e) {
Expand All @@ -98,7 +91,7 @@ public String toXML(T t) {
public T fromXML(String xml) {
T obj = null;
if (StringUtils.isNotBlank(xml)) {
final XmlMapper mapper = createXMLMapper();
final XmlMapper mapper = mapperFactory.getWrappedXmlMapper();
try {
obj = mapper.readValue(xml, getDomainClass());
} catch (IOException e) {
Expand All @@ -108,22 +101,4 @@ public T fromXML(String xml) {
}
return obj;
}

private XmlMapper createXMLMapper() {
final XMLInputFactory ifactory = new WstxInputFactory();
ifactory.setProperty(WstxInputProperties.P_MAX_ATTRIBUTE_SIZE, 32000);
ifactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);

final XMLOutputFactory ofactory = new WstxOutputFactory();
ofactory.setProperty(WstxOutputProperties.P_OUTPUT_CDATA_AS_TEXT, true);
ofactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);

final XmlFactory xf = new XmlFactory(ifactory, ofactory);

final XmlMapper mapper = new XmlMapper(xf);
mapper.registerModules(new JavaTimeModule());
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.configure(ToXmlGenerator.Feature.WRITE_XML_1_1, true);
return mapper;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright (c) 2003-2017 The Apereo Foundation
*
* Licensed under the Educational Community 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://opensource.org/licenses/ecl2
*
* 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.sakaiproject.serialization;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;

import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.api.WstxOutputProperties;
import com.ctc.wstx.stax.WstxInputFactory;
import com.ctc.wstx.stax.WstxOutputFactory;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlFactory;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import lombok.Getter;

@Getter
public class MapperFactory {

private ObjectMapper jsonMapper;
private XmlMapper wrappedXmlMapper;
private XmlMapper xmlMapper;

public void init() {

jsonMapper = new ObjectMapper();
jsonMapper.registerModules(new JavaTimeModule());

XMLInputFactory inputFactory = new WstxInputFactory();
inputFactory.setProperty(WstxInputProperties.P_MAX_ATTRIBUTE_SIZE, 32000);
inputFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);

XMLOutputFactory wrappedCdataOutputFactory = new WstxOutputFactory();
wrappedCdataOutputFactory.setProperty(WstxOutputProperties.P_OUTPUT_CDATA_AS_TEXT, true);
wrappedCdataOutputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);

wrappedXmlMapper = createXmlMapper(new XmlFactory(inputFactory, wrappedCdataOutputFactory));

XMLOutputFactory outputFactory = new WstxOutputFactory();
outputFactory.setProperty(WstxOutputProperties.P_OUTPUT_CDATA_AS_TEXT, false);
outputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);

xmlMapper = createXmlMapper(new XmlFactory(inputFactory, outputFactory));
}

private XmlMapper createXmlMapper(XmlFactory factory) {

XmlMapper mapper = new XmlMapper(factory);
mapper.registerModules(new JavaTimeModule());
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.configure(ToXmlGenerator.Feature.WRITE_XML_1_1, true);
return mapper;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,47 @@ public interface SpringCrudRepository<T extends PersistableEntity<ID>, ID extend
* @throws IllegalArgumentException in case the given entity is {@literal null}.
*/
<S extends T> Iterable<S> saveAll(Iterable<S> entities);

/**
* Serialize object to JSON
*
* @param t
* @return String
*/
String toJSON(T t);

/**
* Deserialize object from JSON
*
* @param json
* @return T
*/
T fromJSON(String json);

/**
* Serialize object to XML. Wraps long strings as cdata inside a wrapper text element
*
* @param t
*
* @return String The xml
*/
String toXML(T t);

/**
* Serialize object to XML
*
* @param t
* @param cdataAsText If false, avoids wrapping long strings as cdata inside a text element
*
* @return String The xml
*/
String toXML(T t, boolean cdataasText);

/**
* Deserialize object from XML
*
* @param xml
* @return T
*/
T fromXML(String xml);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,26 @@
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.sakaiproject.serialization.MapperFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.PageImpl;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;


import java.io.Serializable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
Expand All @@ -43,6 +51,10 @@ public abstract class SpringCrudRepositoryImpl<T extends PersistableEntity<ID>,
@Getter
private final Class<T> domainClass;

@Setter
@Autowired
private MapperFactory mapperFactory;

@Setter
protected SessionFactory sessionFactory;

Expand All @@ -66,7 +78,7 @@ public <S extends T> S save(S entity) {

Session session = sessionFactory.getCurrentSession();

final boolean isNew = entity.getId() == null;
boolean isNew = entity.getId() == null;
if (isNew) {
session.persist(entity);
return entity;
Expand Down Expand Up @@ -174,6 +186,81 @@ public void deleteById(ID id) {
}
}

@Override
public String toJSON(T t) {

if (t == null) return "";

String json = "";

sessionFactory.getCurrentSession().refresh(t);
ObjectMapper mapper = mapperFactory.getJsonMapper();
try {
json = mapper.writeValueAsString(t);
} catch (JsonProcessingException e) {
log.warn("Could not serialize to json", e);
json = "";
}
return json;
}

@Override
public T fromJSON(String json) {

if (StringUtils.isBlank(json)) return null;

T obj = null;

ObjectMapper mapper = mapperFactory.getJsonMapper();
try {
obj = mapper.readValue(json, getDomainClass());
} catch (IOException e) {
log.warn("Could not deserialize json", e);
obj = null;
}
return obj;
}

@Override
public String toXML(T t) {
return toXML(t, true);
}

@Override
public String toXML(T t, boolean cdataasText) {

if (t == null) return "";

String xml = "";

sessionFactory.getCurrentSession().refresh(t);
XmlMapper mapper = cdataasText ? mapperFactory.getWrappedXmlMapper() : mapperFactory.getXmlMapper();
try {
xml = mapper.writeValueAsString(t);
} catch (JsonProcessingException e) {
log.warn("Could not serialize to xml", e);
xml = "";
}
return xml;
}

@Override
public T fromXML(String xml) {

if (StringUtils.isBlank(xml)) return null;

T obj = null;

XmlMapper mapper = mapperFactory.getXmlMapper();
try {
obj = mapper.readValue(xml, getDomainClass());
} catch (IOException e) {
log.warn("Could not deserialize xml", e);
obj = null;
}
return obj;
}

/**
* Starts a Hibernate Criteria query for the type T in HibernateCrudRespository&lt;T, I&gt;
* @return a Criteria query
Expand Down
Loading

0 comments on commit f7b28d3

Please sign in to comment.