Commit d305d136 authored by Matija Obreza's avatar Matija Obreza

Introducing accession listeners

- Add hardwired listeners to AccessionLIstenersConfig (e.g. GLIS)
- Implement AccessionModifiedAspect.AccessionModifiedListener interface to receive updates
- Listeners invoked using existing thread pool (without other context)
parent 7902bccc
/*
* Copyright 2018 Global Crop Diversity Trust
*
* 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 org.genesys2.server.service.worker;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.genesys2.server.model.genesys.Accession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import com.google.common.collect.Lists;
/**
* Trigger out-of-bound actions when accessions are added, updated or removed from
* Genesys.
*
* @author Matija Obreza
*/
@Aspect
@Component
public class AccessionModifiedAspect implements InitializingBean {
/**
* A processor that wishes to receive notifications about changes to Accession
* records in Genesys.
*
* The code will be executed as a task in a common thread pool and no security
* context will be available to the implementing class.
*/
public static interface AccessionModifiedListener {
/**
* Execute an action when {@link Accession} records are removed from Genesys.
*
* @param accessions the accessions that were removed. Never null, never blank.
*/
void onRemove(Collection<Accession> accessions);
/**
* Execute an action when {@link Accession} records are updated in Genesys.
*
* @param accessions the accessions that were updated. Never null, never blank.
*/
void onUpdate(Collection<Accession> accessions);
}
@Autowired(required = false)
private List<AccessionModifiedListener> accessionModifiedListeners;
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
@Override
public void afterPropertiesSet() throws Exception {
if (accessionModifiedListeners == null) {
LOG.error("List of Listeners must not be null. Check your configuration.");
}
assert(accessionModifiedListeners != null);
}
/** The Constant LOG. */
public static final Logger LOG = LoggerFactory.getLogger(AccessionModifiedAspect.class);
@After(value = "execution(* org.genesys2.server.persistence.AccessionRepository.save(*)) && args(arg)")
public void afterPersistAccession(JoinPoint joinPoint, Object arg) throws Throwable {
StopWatch stopWatch=StopWatch.createStarted();
LOG.trace("After persist accession(s): {}", arg);
if (arg == null) {
return;
}
if (arg instanceof Collection<?>) {
handlePersist((Collection<Accession>) arg);
LOG.debug("Persist notification for {} accession took {}ms", ((Collection<?>) arg).size(), stopWatch.getTime(TimeUnit.MILLISECONDS));
} else if (arg instanceof Accession) {
handlePersist(Lists.newArrayList((Accession) arg));
LOG.debug("Persist notification for 1 accession took {}ms", stopWatch.getTime(TimeUnit.MILLISECONDS));
}
}
private void handlePersist(Collection<Accession> accessions) {
if (CollectionUtils.isEmpty(accessions)) {
return;
}
LOG.info("{} accessions were updated", accessions.size());
accessionModifiedListeners.forEach(listener -> taskExecutor.submit(() -> listener.onUpdate(accessions)));
}
@Before(value = "execution(* org.genesys2.server.persistence.AccessionRepository.delete(*)) && args(arg)")
public void beforeDeleteAccession(JoinPoint joinPoint, Object arg) throws Throwable {
StopWatch stopWatch=StopWatch.createStarted();
LOG.trace("Before delete accession(s): {}", arg);
if (arg == null) {
return;
}
if (arg instanceof Collection<?>) {
handleDelete((Collection<Accession>) arg);
LOG.debug("Delete notification for {} accession took {}ms", ((Collection<?>) arg).size(), stopWatch.getTime(TimeUnit.MILLISECONDS));
} else if (arg instanceof Accession) {
handleDelete(Lists.newArrayList((Accession) arg));
LOG.debug("Delete notification for 1 accession took {}ms", stopWatch.getTime(TimeUnit.MILLISECONDS));
}
}
private void handleDelete(Collection<Accession> accessions) {
if (CollectionUtils.isEmpty(accessions)) {
return;
}
LOG.info("{} accessions were deleted", accessions.size());
accessionModifiedListeners.forEach(listener -> taskExecutor.submit(() -> listener.onRemove(accessions)));
}
}
/*
* Copyright 2018 Global Crop Diversity Trust
*
* 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 org.genesys2.spring.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.genesys.glis.v1.TermConstants;
import org.genesys.glis.v1.api.GenesysApi;
import org.genesys.glis.v1.model.UpdateTargets;
import org.genesys.glis.v1.model.UpdatedTarget;
import org.genesys2.server.model.genesys.Accession;
import org.genesys2.server.service.worker.AccessionModifiedAspect.AccessionModifiedListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.collect.Lists;
/**
* The AccessionListenersConfig configures hard-wired listeners to accession
* modifications in Genesys.
*
* @author Matija Obreza
*/
@Configuration
public class AccessionListenersConfig {
@Bean
public List<AccessionModifiedListener> accessionModifiedListeners(GenesysApi glisGenesysApi) {
ArrayList<AccessionModifiedListener> listeners = Lists.newArrayList(glisUpdater(glisGenesysApi));
System.err.println("Declared AccessionModifiedListeners " + listeners.size());
List<AccessionModifiedListener> active = listeners.stream().filter(listener -> listener != null).collect(Collectors.toList());
System.err.println("Active AccessionModifiedListeners " + listeners.size());
return active;
}
/**
* An in-line implementation of the Genesys updater to GLIS.
*
* @param glisGenesysApi the glis genesys api
* @return the listener
*/
private AccessionModifiedListener glisUpdater(GenesysApi glisGenesysApi) {
if (glisGenesysApi == null) {
System.err.println("GLIS Client not available. Not initializing GLIS Updater.");
return null;
}
return new AccessionModifiedListener() {
private final Logger LOG = LoggerFactory.getLogger(AccessionListenersConfig.class + ".glisUpdater");
@Override
public void onUpdate(Collection<Accession> accessions) {
UpdateTargets targets = new UpdateTargets();
targets.addKwsItem(TermConstants.PASSPORT_DATA);
accessions.stream().map(a -> a.getDoi()).filter(doi -> doi != null).forEach(doi -> targets.addDoisItem(doi));
if (CollectionUtils.isEmpty(targets.getDois())) {
try {
LOG.info("Updating GLIS for {} accessions with DOIs", targets.getDois().size());
List<UpdatedTarget> glisResponse = glisGenesysApi.registerGenesysAsTarget(targets);
glisResponse.forEach(updatedTarget -> {
if (StringUtils.equals("OK", updatedTarget.getResult())) {
LOG.trace("GLIS for {} result={}", updatedTarget.getDoi(), updatedTarget.getResult());
} else if (StringUtils.equals("KO", updatedTarget.getResult())) {
LOG.info("GLIS for {} result={} msg={}", updatedTarget.getDoi(), updatedTarget.getResult());
}
});
} catch (Throwable e) {
LOG.error("Error updating GLIS targets: {}", e.getMessage(), e);
}
}
}
@Override
public void onRemove(Collection<Accession> accessions) {
// No operation
}
};
}
}
......@@ -45,7 +45,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
@Configuration
@Import({ HazelcastConfig.class, CommonConfig.class, SchedulerConfig.class, DatabaseConfig.class, TemplatingConfig.class, MailConfig.class, OAuth2ServerConfig.class, SecurityConfig.class, CacheConfig.class,
ElasticsearchConfig.class, FileRepositoryConfig.class, WebSecurityConfig.class, WebConfiguration.class, AuditConfig.class, GLISConfig.class, SwaggerConfig.class })
ElasticsearchConfig.class, FileRepositoryConfig.class, WebSecurityConfig.class, WebConfiguration.class, AuditConfig.class, GLISConfig.class, SwaggerConfig.class, AccessionListenersConfig.class })
@EnableAspectJAutoProxy
@EnableWebSecurity
......
......@@ -64,7 +64,7 @@ public final class ApplicationConfig {
*/
@Configuration
@Import({ NoHazelcastConfig.class, SchedulerConfig.class, DatabaseConfig.class, TestElasticsearchConfig.class, TemplatingConfig.class, MailConfig.class, SecurityConfig.class, CacheConfig.class,
FileRepositoryTestConfig.class, AuditConfig.class })
FileRepositoryTestConfig.class, AuditConfig.class, TestAccessionListenersConfig.class })
@ComponentScan(basePackages = { "org.genesys.catalog.service.impl", "org.genesys2.brapi.service", "org.genesys2.server.service.impl", "org.genesys2.server.service.worker" } )
@EnableAspectJAutoProxy
public static class BaseConfig {
......
/*
* Copyright 2018 Global Crop Diversity Trust
*
* 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 org.genesys.test.config;
import java.util.ArrayList;
import java.util.List;
import org.genesys2.server.service.worker.AccessionModifiedAspect.AccessionModifiedListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Accession listeners config for tests (no listeners)
*/
@Configuration
public class TestAccessionListenersConfig {
@Bean
public List<AccessionModifiedListener> accessionModifiedListeners() {
return new ArrayList<>();
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment