001package org.kuali.common.util.spring.event; 002 003import static com.google.common.base.Preconditions.checkArgument; 004import static com.google.common.base.Preconditions.checkNotNull; 005 006import java.util.List; 007 008import org.kuali.common.util.execute.Executable; 009import org.kuali.common.util.log.LoggerUtils; 010import org.slf4j.Logger; 011import org.springframework.context.ApplicationEvent; 012import org.springframework.context.event.SmartApplicationListener; 013 014import com.google.common.collect.ImmutableList; 015 016/** 017 * <p> 018 * Associate an executable with one or more Spring framework application events. 019 * </p> 020 * 021 * <p> 022 * If an application event gets fired where both {@code supportsEventType} and {@code supportsSourceType} return {@code true}, {@code onApplicationEvent} invokes the executable. 023 * </p> 024 * 025 * <p> 026 * The default behavior of {@code supportsEventType} and {@code supportsSourceType} is to always return true irrespective of what application event was fired. 027 * </p> 028 * 029 * <p> 030 * To be more discriminatory, provide values for {@code supportedSourceTypes} and {@code supportedEventTypes}. 031 * </p> 032 * 033 * <p> 034 * To limit execution to a specific event type, eg {@code ContextRefreshedEvent}: 035 * </p> 036 * 037 * <pre> 038 * ExecutableApplicationListener.builder(executable).supportedEventType(ContextRefreshedEvent.class).build() 039 * </pre> 040 */ 041public final class GenericExecutableApplicationListener implements SmartApplicationListener { 042 043 private static final Logger logger = LoggerUtils.make(); 044 045 private final Executable executable; 046 private final int order; 047 private final List<Class<?>> supportedSourceTypes; 048 private final List<Class<? extends ApplicationEvent>> supportedEventTypes; 049 050 @Override 051 public void onApplicationEvent(ApplicationEvent event) { 052 logger.info("Received event: [{}]", event.getClass().getCanonicalName()); 053 executable.execute(); 054 } 055 056 @Override 057 public int getOrder() { 058 return order; 059 } 060 061 @Override 062 public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) { 063 return supportedEventTypes.isEmpty() || supportedEventTypes.contains(eventType); 064 } 065 066 @Override 067 public boolean supportsSourceType(Class<?> sourceType) { 068 return supportedSourceTypes.isEmpty() || supportedSourceTypes.contains(sourceType); 069 } 070 071 private GenericExecutableApplicationListener(Builder builder) { 072 this.executable = builder.executable; 073 this.order = builder.order; 074 this.supportedSourceTypes = builder.supportedSourceTypes; 075 this.supportedEventTypes = builder.supportedEventTypes; 076 } 077 078 public static Builder builder(Executable executable) { 079 return new Builder(executable); 080 } 081 082 public static class Builder { 083 084 // Required 085 private final Executable executable; 086 087 // Optional 088 private int order = 0; // Lower values mean higher priority, higher values mean lower priority 089 private List<Class<?>> supportedSourceTypes = ImmutableList.of(); 090 private List<Class<? extends ApplicationEvent>> supportedEventTypes = ImmutableList.of(); 091 092 public Builder(Executable executable) { 093 this.executable = executable; 094 } 095 096 public GenericExecutableApplicationListener build() { 097 this.supportedEventTypes = ImmutableList.copyOf(supportedEventTypes); 098 this.supportedSourceTypes = ImmutableList.copyOf(supportedSourceTypes); 099 GenericExecutableApplicationListener instance = new GenericExecutableApplicationListener(this); 100 validate(instance); 101 return instance; 102 } 103 104 private void validate(GenericExecutableApplicationListener instance) { 105 checkNotNull(instance.getExecutable(), "executable cannot be null"); 106 checkNotNull(instance.getSupportedEventTypes(), "supportedEventTypes cannot be null"); 107 checkNotNull(instance.getSupportedSourceTypes(), "supportedSourceTypes cannot be null"); 108 checkArgument(ImmutableList.class.isAssignableFrom(instance.getSupportedEventTypes().getClass()), "supportedEventTypes must be immutable"); 109 checkArgument(ImmutableList.class.isAssignableFrom(instance.getSupportedSourceTypes().getClass()), "supportedSourceTypes must be immutable"); 110 } 111 112 public Builder order(int order) { 113 this.order = order; 114 return this; 115 } 116 117 public Builder supportedSourceTypes(List<Class<?>> supportedSourceTypes) { 118 this.supportedSourceTypes = supportedSourceTypes; 119 return this; 120 } 121 122 public Builder supportedSourceType(Class<?> supportedSourceType) { 123 return supportedSourceTypes(ImmutableList.<Class<?>> of(supportedSourceType)); 124 } 125 126 public Builder supportedEventTypes(List<Class<? extends ApplicationEvent>> supportedEventTypes) { 127 this.supportedEventTypes = supportedEventTypes; 128 return this; 129 } 130 131 public Builder supportedEventType(Class<? extends ApplicationEvent> supportedEventType) { 132 return supportedEventTypes(ImmutableList.<Class<? extends ApplicationEvent>> of(supportedEventType)); 133 } 134 135 public int getOrder() { 136 return order; 137 } 138 139 public void setOrder(int order) { 140 this.order = order; 141 } 142 143 public List<Class<?>> getSupportedSourceTypes() { 144 return supportedSourceTypes; 145 } 146 147 public void setSupportedSourceTypes(List<Class<?>> supportedSourceTypes) { 148 this.supportedSourceTypes = supportedSourceTypes; 149 } 150 151 public List<Class<? extends ApplicationEvent>> getSupportedEventTypes() { 152 return supportedEventTypes; 153 } 154 155 public void setSupportedEventTypes(List<Class<? extends ApplicationEvent>> supportedEventTypes) { 156 this.supportedEventTypes = supportedEventTypes; 157 } 158 159 public Executable getExecutable() { 160 return executable; 161 } 162 } 163 164 public Executable getExecutable() { 165 return executable; 166 } 167 168 public List<Class<?>> getSupportedSourceTypes() { 169 return supportedSourceTypes; 170 } 171 172 public List<Class<? extends ApplicationEvent>> getSupportedEventTypes() { 173 return supportedEventTypes; 174 } 175 176}