[CST-11899] general refactoring & improvements for LDN

This commit is contained in:
Stefano Maffei
2023-11-20 15:37:47 +01:00
parent 39c33125a3
commit 5466c263ef
10 changed files with 52 additions and 694 deletions

View File

@@ -1,190 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.ldn;
import static java.lang.String.format;
import static java.lang.String.join;
import static org.dspace.app.ldn.RdfMediaType.APPLICATION_JSON_LD;
import static org.dspace.app.ldn.utility.LDNUtils.processContextResolverId;
import java.net.URI;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.dspace.app.ldn.converter.JsonLdHttpMessageConverter;
import org.dspace.app.ldn.model.Actor;
import org.dspace.app.ldn.model.Context;
import org.dspace.app.ldn.model.Notification;
import org.dspace.app.ldn.model.Object;
import org.dspace.app.ldn.model.Service;
import org.dspace.content.Item;
import org.dspace.content.MetadataField;
import org.dspace.content.MetadataValue;
import org.dspace.handle.service.HandleService;
import org.dspace.services.ConfigurationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.web.client.RestTemplate;
/**
* Linked Data Notification business delegate to facilitate sending
* notification.
*/
public class LDNBusinessDelegate {
private final static Logger log = LogManager.getLogger(LDNBusinessDelegate.class);
@Autowired
private ConfigurationService configurationService;
@Autowired
private HandleService handleService;
private final RestTemplate restTemplate;
/**
* Initialize rest template with appropriate message converters.
*/
public LDNBusinessDelegate() {
restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new JsonLdHttpMessageConverter());
}
/**
* Announce item release notification.
*
* @param item item released (deposited or updated)
* @throws SQLException
*/
public void announceRelease(Item item) {
String serviceIds = configurationService.getProperty("service.service-id.ldn");
for (String serviceId : serviceIds.split(",")) {
doAnnounceRelease(item, serviceId.trim());
}
}
/**
* Build and POST announce release notification to configured service LDN
* inboxes.
*
* @param item associated item
* @param serviceId service id for targer inbox
*/
public void doAnnounceRelease(Item item, String serviceId) {
log.info("Announcing release of item {}", item.getID());
String dspaceServerUrl = configurationService.getProperty("dspace.server.url");
String dspaceUIUrl = configurationService.getProperty("dspace.ui.url");
String dspaceName = configurationService.getProperty("dspace.name");
String dspaceLdnInboxUrl = configurationService.getProperty("ldn.notify.inbox");
log.info("DSpace Server URL {}", dspaceServerUrl);
log.info("DSpace UI URL {}", dspaceUIUrl);
log.info("DSpace Name {}", dspaceName);
log.info("DSpace LDN Inbox URL {}", dspaceLdnInboxUrl);
String serviceUrl = configurationService.getProperty(join(".", "service", serviceId, "url"));
String serviceInboxUrl = configurationService.getProperty(join(".", "service", serviceId, "inbox.url"));
String serviceResolverUrl = configurationService.getProperty(join(".", "service", serviceId, "resolver.url"));
log.info("Target URL {}", serviceUrl);
log.info("Target LDN Inbox URL {}", serviceInboxUrl);
Notification notification = new Notification();
notification.setId(format("urn:uuid:%s", UUID.randomUUID()));
notification.addType("Announce");
notification.addType("coar-notify:ReleaseAction");
Actor actor = new Actor();
actor.setId(dspaceUIUrl);
actor.setName(dspaceName);
actor.addType("Service");
Context context = new Context();
List<Context> isSupplementedBy = new ArrayList<>();
List<MetadataValue> metadata = item.getMetadata();
for (MetadataValue metadatum : metadata) {
MetadataField field = metadatum.getMetadataField();
log.info("Metadata field {} with value {}", field, metadatum.getValue());
if (field.getMetadataSchema().getName().equals("dc") &&
field.getElement().equals("data") &&
field.getQualifier().equals("uri")) {
String ietfCiteAs = metadatum.getValue();
String resolverId = processContextResolverId(ietfCiteAs);
String id = serviceResolverUrl != null
? format("%s%s", serviceResolverUrl, resolverId)
: ietfCiteAs;
Context supplement = new Context();
supplement.setId(id);
supplement.setIetfCiteAs(ietfCiteAs);
supplement.addType("sorg:Dataset");
isSupplementedBy.add(supplement);
}
}
context.setIsSupplementedBy(isSupplementedBy);
Object object = new Object();
String itemUrl = handleService.getCanonicalForm(item.getHandle());
log.info("Item Handle URL {}", itemUrl);
log.info("Item URL {}", itemUrl);
object.setId(itemUrl);
object.setIetfCiteAs(itemUrl);
object.setTitle(item.getName());
object.addType("sorg:ScholarlyArticle");
Service origin = new Service();
origin.setId(dspaceUIUrl);
origin.setInbox(dspaceLdnInboxUrl);
origin.addType("Service");
Service target = new Service();
target.setId(serviceUrl);
target.setInbox(serviceInboxUrl);
target.addType("Service");
notification.setActor(actor);
notification.setContext(context);
notification.setObject(object);
notification.setOrigin(origin);
notification.setTarget(target);
String serviceKey = configurationService.getProperty(join(".", "service", serviceId, "key"));
String serviceKeyHeader = configurationService.getProperty(join(".", "service", serviceId, "key.header"));
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", APPLICATION_JSON_LD.toString());
if (serviceKey != null && serviceKeyHeader != null) {
headers.add(serviceKeyHeader, serviceKey);
}
HttpEntity<Notification> request = new HttpEntity<Notification>(notification, headers);
log.info("Announcing notification {}", request);
restTemplate.postForLocation(URI.create(target.getInbox()), request);
}
}

View File

@@ -1,37 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.ldn.factory;
import org.dspace.app.ldn.LDNBusinessDelegate;
import org.dspace.services.factory.DSpaceServicesFactory;
/**
* Abstract business delegate factory to provide ability to get instance from
* dspace services factory.
*/
public abstract class LDNBusinessDelegateFactory {
/**
* Abstract method to return the business delegate bean.
*
* @return LDNBusinessDelegate business delegate bean
*/
public abstract LDNBusinessDelegate getLDNBusinessDelegate();
/**
* Static method to get the business delegate factory instance.
*
* @return LDNBusinessDelegateFactory business delegate factory from dspace
* services factory
*/
public static LDNBusinessDelegateFactory getInstance() {
return DSpaceServicesFactory.getInstance().getServiceManager()
.getServiceByName("ldnBusinessDelegateFactory", LDNBusinessDelegateFactory.class);
}
}

View File

@@ -1,30 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.ldn.factory;
import org.dspace.app.ldn.LDNBusinessDelegate;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Business delegate factory implementation that autowires business delegate for
* static retrieval.
*/
public class LDNBusinessDelegateFactoryImpl extends LDNBusinessDelegateFactory {
@Autowired(required = true)
private LDNBusinessDelegate ldnBusinessDelegate;
/**
* @return LDNBusinessDelegate
*/
@Override
public LDNBusinessDelegate getLDNBusinessDelegate() {
return ldnBusinessDelegate;
}
}

View File

@@ -1,48 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.ldn.processor;
/**
* Instuctions for adding metadata during notification processing.
*/
public class LDNMetadataAdd extends LDNMetadataChange {
private String qualifier;
// velocity template with notification as it contexts
private String valueTemplate;
/**
* @return String
*/
public String getQualifier() {
return qualifier;
}
/**
* @param qualifier
*/
public void setQualifier(String qualifier) {
this.qualifier = qualifier;
}
/**
* @return String
*/
public String getValueTemplate() {
return valueTemplate;
}
/**
* @param valueTemplate
*/
public void setValueTemplate(String valueTemplate) {
this.valueTemplate = valueTemplate;
}
}

View File

@@ -1,95 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.ldn.processor;
import static org.dspace.app.ldn.LDNMetadataFields.ELEMENT;
import static org.dspace.app.ldn.LDNMetadataFields.SCHEMA;
import static org.dspace.content.Item.ANY;
/**
* Base instructions for metadata change during notification processing.
*/
public abstract class LDNMetadataChange {
private String schema;
private String element;
private String language;
// velocity template with notification as its context
private String conditionTemplate;
/**
* Default coar schema, notify element, any language, and true condition to
* apply metadata change.
*/
public LDNMetadataChange() {
schema = SCHEMA;
element = ELEMENT;
language = ANY;
conditionTemplate = "true";
}
/**
* @return String
*/
public String getSchema() {
return schema;
}
/**
* @param schema
*/
public void setSchema(String schema) {
this.schema = schema;
}
/**
* @return String
*/
public String getElement() {
return element;
}
/**
* @param element
*/
public void setElement(String element) {
this.element = element;
}
/**
* @return String
*/
public String getLanguage() {
return language;
}
/**
* @param language
*/
public void setLanguage(String language) {
this.language = language;
}
/**
* @return String
*/
public String getConditionTemplate() {
return conditionTemplate;
}
/**
* @param conditionTemplate
*/
public void setConditionTemplate(String conditionTemplate) {
this.conditionTemplate = conditionTemplate;
}
}

View File

@@ -9,30 +9,20 @@ package org.dspace.app.ldn.processor;
import static java.lang.String.format;
import java.io.StringWriter;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.resource.loader.StringResourceLoader;
import org.apache.velocity.runtime.resource.util.StringResourceRepository;
import org.dspace.app.ldn.action.ActionStatus;
import org.dspace.app.ldn.action.LDNAction;
import org.dspace.app.ldn.model.Notification;
import org.dspace.app.ldn.utility.LDNUtils;
import org.dspace.content.DSpaceObject;
import org.dspace.content.Item;
import org.dspace.content.MetadataValue;
import org.dspace.content.service.ItemService;
import org.dspace.core.Constants;
import org.dspace.core.Context;
@@ -49,10 +39,6 @@ public class LDNMetadataProcessor implements LDNProcessor {
private final static Logger log = LogManager.getLogger(LDNMetadataProcessor.class);
private final static String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private final VelocityEngine velocityEngine;
@Autowired
private ItemService itemService;
@@ -63,16 +49,11 @@ public class LDNMetadataProcessor implements LDNProcessor {
private List<LDNAction> actions = new ArrayList<>();
private List<LDNMetadataChange> changes = new ArrayList<>();
/**
* Initialize velocity engine for templating.
*/
private LDNMetadataProcessor() {
velocityEngine = new VelocityEngine();
velocityEngine.setProperty(Velocity.RESOURCE_LOADERS, "string");
velocityEngine.setProperty("resource.loader.string.class", StringResourceLoader.class.getName());
velocityEngine.init();
}
/**
@@ -84,106 +65,8 @@ public class LDNMetadataProcessor implements LDNProcessor {
*/
@Override
public void process(Context context, Notification notification) throws Exception {
Item item = doProcess(context, notification);
runActions(context, notification, item);
}
/**
* Perform the actual notification processing. Applies all defined metadata
* changes.
*
* @param context the current context
* @param notification current context notification
* @return Item associated item which persist notification details
* @throws Exception failed to process notification
*/
private Item doProcess(Context context, Notification notification) throws Exception {
log.info("Processing notification {} {}", notification.getId(), notification.getType());
boolean updated = false;
VelocityContext velocityContext = prepareTemplateContext(notification);
Item item = lookupItem(context, notification);
List<MetadataValue> metadataValuesToRemove = new ArrayList<>();
for (LDNMetadataChange change : changes) {
String condition = renderTemplate(velocityContext, change.getConditionTemplate());
boolean proceed = Boolean.parseBoolean(condition);
if (!proceed) {
continue;
}
if (change instanceof LDNMetadataAdd) {
LDNMetadataAdd add = ((LDNMetadataAdd) change);
String value = renderTemplate(velocityContext, add.getValueTemplate());
log.info(
"Adding {}.{}.{} {} {}",
add.getSchema(),
add.getElement(),
add.getQualifier(),
add.getLanguage(),
value);
itemService.addMetadata(
context,
item,
add.getSchema(),
add.getElement(),
add.getQualifier(),
add.getLanguage(),
value);
updated = true;
} else if (change instanceof LDNMetadataRemove) {
LDNMetadataRemove remove = (LDNMetadataRemove) change;
for (String qualifier : remove.getQualifiers()) {
List<MetadataValue> itemMetadata = itemService.getMetadata(
item,
change.getSchema(),
change.getElement(),
qualifier,
Item.ANY);
for (MetadataValue metadatum : itemMetadata) {
boolean delete = true;
for (String valueTemplate : remove.getValueTemplates()) {
String value = renderTemplate(velocityContext, valueTemplate);
if (!metadatum.getValue().contains(value)) {
delete = false;
}
}
if (delete) {
log.info("Removing {}.{}.{} {} {}",
remove.getSchema(),
remove.getElement(),
qualifier,
remove.getLanguage(),
metadatum.getValue());
metadataValuesToRemove.add(metadatum);
}
}
}
}
}
if (!metadataValuesToRemove.isEmpty()) {
itemService.removeMetadataValues(context, item, metadataValuesToRemove);
updated = true;
}
if (updated) {
context.turnOffAuthorisationSystem();
try {
itemService.update(context, item);
context.commit();
} finally {
context.restoreAuthSystemState();
}
}
return item;
runActions(context, notification, item);
}
/**
@@ -241,20 +124,6 @@ public class LDNMetadataProcessor implements LDNProcessor {
this.actions = actions;
}
/**
* @return List<LDNMetadataChange>
*/
public List<LDNMetadataChange> getChanges() {
return changes;
}
/**
* @param changes
*/
public void setChanges(List<LDNMetadataChange> changes) {
this.changes = changes;
}
/**
* Lookup associated item to the notification context. If UUID in URL, lookup bu
* UUID, else lookup by handle.
@@ -309,41 +178,4 @@ public class LDNMetadataProcessor implements LDNProcessor {
return item;
}
/**
* Prepare velocity template context with notification, timestamp and some
* static utilities.
*
* @param notification current context notification
* @return VelocityContext prepared velocity context
*/
private VelocityContext prepareTemplateContext(Notification notification) {
VelocityContext velocityContext = new VelocityContext();
String timestamp = new SimpleDateFormat(DATE_PATTERN).format(Calendar.getInstance().getTime());
velocityContext.put("notification", notification);
velocityContext.put("timestamp", timestamp);
velocityContext.put("LDNUtils", LDNUtils.class);
velocityContext.put("Objects", Objects.class);
velocityContext.put("StringUtils", StringUtils.class);
return velocityContext;
}
/**
* Render velocity template with provided context.
*
* @param context velocity context
* @param template template to render
* @return String results of rendering
*/
private String renderTemplate(VelocityContext context, String template) {
StringWriter writer = new StringWriter();
StringResourceRepository repository = StringResourceLoader.getRepository();
repository.putStringResource("template", template);
velocityEngine.getTemplate("template").merge(context, writer);
return writer.toString();
}
}

View File

@@ -1,51 +0,0 @@
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.ldn.processor;
import java.util.ArrayList;
import java.util.List;
/**
* Instuctions for removing metadata during notification processing.
*/
public class LDNMetadataRemove extends LDNMetadataChange {
private List<String> qualifiers = new ArrayList<>();
// velocity templates with notification as it contexts
private List<String> valueTemplates = new ArrayList<>();
/**
* @return List<String>
*/
public List<String> getQualifiers() {
return qualifiers;
}
/**
* @param qualifiers
*/
public void setQualifiers(List<String> qualifiers) {
this.qualifiers = qualifiers;
}
/**
* @return List<String>
*/
public List<String> getValueTemplates() {
return valueTemplates;
}
/**
* @param valueTemplates
*/
public void setValueTemplates(List<String> valueTemplates) {
this.valueTemplates = valueTemplates;
}
}

View File

@@ -26,3 +26,8 @@ ldn.processor.max.attempts = 5
# of the message. LDN Message with a future queue_timeout is not elaborated. This property is used to calculateas:
# a new timeout, such as: new_timeout = now + ldn.processor.queue.msg.timeout (in minutes)
ldn.processor.queue.msg.timeout = 60
# EMAIL CONFIGURATION
ldn.notification.email = ${mail.admin}

View File

@@ -56,9 +56,6 @@
<bean id="orcidServiceFactory" class="org.dspace.orcid.factory.OrcidServiceFactoryImpl"/>
<bean id="supervisionOrderServiceFactory" class="org.dspace.supervision.factory.SupervisionOrderServiceFactoryImpl"/>
<!-- LDN COAR Notify services -->
<bean id="ldnBusinessDelegateFactory" class="org.dspace.app.ldn.factory.LDNBusinessDelegateFactoryImpl"/>
<bean id="notifyServiceFactory" class="org.dspace.app.ldn.factory.NotifyServiceFactoryImpl"/>

View File

@@ -22,7 +22,6 @@
<bean class="org.dspace.app.ldn.service.impl.NotifyServiceOutboundPatternServiceImpl"/>
<bean class="org.dspace.app.ldn.service.impl.LDNMessageServiceImpl"/>
<bean class="org.dspace.app.ldn.service.impl.NotifyPatternToTriggerImpl"/>
<bean class="org.dspace.app.ldn.LDNBusinessDelegate"></bean>
<bean name="ldnRouter" class="org.dspace.app.ldn.LDNRouter">
<property name="processors">
@@ -52,7 +51,7 @@
<value>coar-notify:ReviewAction</value>
</set>
</key>
<ref bean="acceptReviewAction" />
<ref bean="acceptAckAction" />
</entry>
<entry>
<key>
@@ -61,7 +60,43 @@
<value>coar-notify:ReviewAction</value>
</set>
</key>
<ref bean="rejectReviewAction" />
<ref bean="rejectAckAction" />
</entry>
<entry>
<key>
<set>
<value>Accept</value>
<value>coar-notify:EndorsementAction</value>
</set>
</key>
<ref bean="acceptAckAction" />
</entry>
<entry>
<key>
<set>
<value>Reject</value>
<value>coar-notify:EndorsementAction</value>
</set>
</key>
<ref bean="rejectAckAction" />
</entry>
<entry>
<key>
<set>
<value>Accept</value>
<value>coar-notify:IngestAction</value>
</set>
</key>
<ref bean="acceptAckAction" />
</entry>
<entry>
<key>
<set>
<value>Reject</value>
<value>coar-notify:IngestAction</value>
</set>
</key>
<ref bean="rejectAckAction" />
</entry>
<entry>
<key>
@@ -80,7 +115,7 @@
<property name="actions">
<list value-type="org.dspace.app.ldn.action.LDNAction">
<bean class="org.dspace.app.ldn.action.LDNEmailAction">
<property name="actionSendFilter" value="william_welling@harvard.edu" />
<property name="actionSendFilter" value="${ldn.notification.email}" />
<property name="actionSendEmailTextFile" value="coar_notify_reviewed" />
</bean>
<bean class="org.dspace.app.ldn.action.LDNCorrectionAction">
@@ -94,7 +129,7 @@
<property name="actions">
<list value-type="org.dspace.app.ldn.action.LDNAction">
<bean class="org.dspace.app.ldn.action.LDNEmailAction">
<property name="actionSendFilter" value="william_welling@harvard.edu" />
<property name="actionSendFilter" value="${ldn.notification.email}" />
<property name="actionSendEmailTextFile" value="coar_notify_endorsed" />
</bean>
<bean class="org.dspace.app.ldn.action.LDNCorrectionAction">
@@ -104,82 +139,22 @@
</property>
</bean>
<bean name="acceptReviewAction" class="org.dspace.app.ldn.processor.LDNMetadataProcessor">
<property name="changes">
<list value-type="org.dspace.app.ldn.processor.LDNMetadataChange">
<bean class="org.dspace.app.ldn.processor.LDNMetadataRemove">
<property name="qualifiers">
<list value-type="java.lang.String">
<value>requestreview</value>
<value>refused</value>
</list>
</property>
<property name="valueTemplates">
<list value-type="java.lang.String">
<value>$LDNUtils.removedProtocol($notification.origin.id)</value>
</list>
</property>
</bean>
<bean class="org.dspace.app.ldn.processor.LDNMetadataRemove">
<property name="conditionTemplate" value="$StringUtils.isNotEmpty($notification.inReplyTo)" />
<property name="qualifiers">
<list value-type="java.lang.String">
<value>refused</value>
</list>
</property>
<property name="valueTemplates">
<list value-type="java.lang.String">
<value>$LDNUtils.removedProtocol($notification.origin.id)</value>
<value>$notification.inReplyTo</value>
</list>
</property>
</bean>
<bean class="org.dspace.app.ldn.processor.LDNMetadataAdd">
<property name="conditionTemplate" value="$StringUtils.isNotEmpty($notification.inReplyTo)" />
<property name="qualifier" value="examination" />
<property name="valueTemplate" value="$timestamp||$LDNUtils.removedProtocol($notification.origin.id)||$notification.inReplyTo" />
</bean>
</list>
</property>
<bean name="acceptAckAction" class="org.dspace.app.ldn.processor.LDNMetadataProcessor">
<property name="actions">
<list value-type="org.dspace.app.ldn.action.LDNAction">
<bean class="org.dspace.app.ldn.action.LDNEmailAction">
<property name="actionSendFilter" value="william_welling@harvard.edu" />
<property name="actionSendFilter" value="${ldn.notification.email}" />
<property name="actionSendEmailTextFile" value="coar_notify_accepted" />
</bean>
</list>
</property>
</bean>
<bean name="rejectReviewAction" class="org.dspace.app.ldn.processor.LDNMetadataProcessor">
<property name="changes">
<list value-type="org.dspace.app.ldn.processor.LDNMetadataChange">
<bean class="org.dspace.app.ldn.processor.LDNMetadataRemove">
<property name="conditionTemplate" value="$StringUtils.isNotEmpty($notification.inReplyTo)" />
<property name="qualifiers">
<list value-type="java.lang.String">
<value>examination</value>
<value>requestreview</value>
<value>requestendorsement</value>
</list>
</property>
<property name="valueTemplates">
<list value-type="java.lang.String">
<value>$LDNUtils.removedProtocol($notification.origin.id)</value>
<value>$notification.inReplyTo</value>
</list>
</property>
</bean>
<bean class="org.dspace.app.ldn.processor.LDNMetadataAdd">
<property name="qualifier" value="refused" />
<property name="valueTemplate" value="$timestamp||$LDNUtils.removedProtocol($notification.origin.id)||$notification.inReplyTo" />
</bean>
</list>
</property>
<bean name="rejectAckAction" class="org.dspace.app.ldn.processor.LDNMetadataProcessor">
<property name="actions">
<list value-type="org.dspace.app.ldn.action.LDNAction">
<bean class="org.dspace.app.ldn.action.LDNEmailAction">
<property name="actionSendFilter" value="william_welling@harvard.edu" />
<property name="actionSendFilter" value="${ldn.notification.email}" />
<property name="actionSendEmailTextFile" value="coar_notify_rejected" />
</bean>
</list>
@@ -190,7 +165,7 @@
<property name="actions">
<list value-type="org.dspace.app.ldn.action.LDNAction">
<bean class="org.dspace.app.ldn.action.LDNEmailAction">
<property name="actionSendFilter" value="william_welling@harvard.edu" />
<property name="actionSendFilter" value="${ldn.notification.email}" />
<property name="actionSendEmailTextFile" value="coar_notify_relationship" />
</bean>
<bean class="org.dspace.app.ldn.action.LDNCorrectionAction">