Skip to content

Commit 399e330

Browse files
committed
Ref #34306: support spel in usage stats configuration
1 parent a46c44b commit 399e330

1 file changed

Lines changed: 83 additions & 60 deletions

File tree

src/main/java/eu/openanalytics/containerproxy/stat/StatCollectorFactory.java

Lines changed: 83 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -21,122 +21,139 @@
2121
package eu.openanalytics.containerproxy.stat;
2222

2323
import eu.openanalytics.containerproxy.backend.dispatcher.proxysharing.ProxySharingMicrometer;
24+
import eu.openanalytics.containerproxy.spec.expression.SpecExpressionContext;
25+
import eu.openanalytics.containerproxy.spec.expression.SpecExpressionResolver;
26+
import eu.openanalytics.containerproxy.spec.expression.SpelField;
2427
import eu.openanalytics.containerproxy.stat.impl.InfluxDBCollector;
2528
import eu.openanalytics.containerproxy.stat.impl.JDBCCollector;
2629
import eu.openanalytics.containerproxy.stat.impl.Micrometer;
30+
import jakarta.inject.Inject;
31+
import lombok.AccessLevel;
32+
import lombok.AllArgsConstructor;
33+
import lombok.Builder;
2734
import lombok.Data;
2835
import lombok.Getter;
36+
import lombok.NoArgsConstructor;
2937
import lombok.Setter;
3038
import org.apache.logging.log4j.LogManager;
3139
import org.apache.logging.log4j.Logger;
32-
import org.springframework.beans.BeansException;
33-
import org.springframework.beans.factory.config.BeanDefinition;
34-
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
3540
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
36-
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
37-
import org.springframework.beans.factory.support.GenericBeanDefinition;
38-
import org.springframework.boot.context.properties.bind.BindResult;
39-
import org.springframework.boot.context.properties.bind.Binder;
40-
import org.springframework.context.EnvironmentAware;
41-
import org.springframework.core.env.Environment;
42-
import org.springframework.stereotype.Component;
43-
44-
import javax.annotation.Nonnull;
41+
import org.springframework.boot.context.properties.ConfigurationProperties;
42+
import org.springframework.context.annotation.Configuration;
43+
44+
import javax.annotation.PostConstruct;
4545
import java.util.List;
4646

4747

48-
@Component
49-
public class StatCollectorFactory implements BeanFactoryPostProcessor, EnvironmentAware {
48+
@Configuration
49+
@ConfigurationProperties(prefix = "proxy")
50+
public class StatCollectorFactory {
5051

5152
@Setter
5253
@Getter
5354
private List<UsageStats> usageStats;
5455

5556
@Setter
5657
@Getter
57-
private String usageStatsUrl;
58+
private SpelField.String usageStatsUrl = new SpelField.String();
5859

5960
@Setter
6061
@Getter
61-
private String usageStatsUsername;
62+
private SpelField.String usageStatsUsername = new SpelField.String();
6263

6364
@Setter
6465
@Getter
65-
private String usageStatsPassword;
66+
private SpelField.String usageStatsPassword = new SpelField.String();
6667

6768
@Setter
6869
@Getter
69-
private String usageStatsTableName;
70+
private SpelField.String usageStatsTableName = new SpelField.String();
7071

7172
@Setter
7273
@Getter
7374
private List<UsageStatsAttribute> usageStatsAttributes;
7475

7576
private final Logger log = LogManager.getLogger(StatCollectorFactory.class);
7677

77-
private Environment environment;
78-
79-
@Override
80-
public void postProcessBeanFactory(@Nonnull ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
81-
BindResult<StatCollectorFactory> bindResult = Binder.get(environment).bind("proxy", StatCollectorFactory.class);
82-
if (!bindResult.isBound()) {
83-
return;
78+
@Inject
79+
private SpecExpressionResolver specExpressionResolver;
80+
81+
@Inject
82+
private ConfigurableListableBeanFactory beanFactory;
83+
84+
@PostConstruct
85+
public void init() {
86+
SpecExpressionContext specExpressionContext = SpecExpressionContext.create();
87+
String resolvedUsageStatsUrl = usageStatsUrl.resolve(specExpressionResolver, specExpressionContext).getValueOrNull();
88+
if (resolvedUsageStatsUrl != null && !resolvedUsageStatsUrl.isEmpty()) {
89+
createBean(createCollector(
90+
resolvedUsageStatsUrl,
91+
usageStatsUsername.resolve(specExpressionResolver, specExpressionContext).getValueOrNull(),
92+
usageStatsPassword.resolve(specExpressionResolver, specExpressionContext).getValueOrNull(),
93+
usageStatsTableName.resolve(specExpressionResolver, specExpressionContext).getValueOrNull(),
94+
usageStatsAttributes), "IStatsCollector");
8495
}
85-
StatCollectorFactory result = bindResult.get();
86-
DefaultListableBeanFactory registry = (DefaultListableBeanFactory) configurableListableBeanFactory;
8796

88-
if (result.getUsageStats() != null) {
89-
for (UsageStats usageStats : result.getUsageStats()) {
90-
createCollector(registry, usageStats.url, usageStats.username, usageStats.password, usageStats.tableName, usageStats.attributes);
97+
if (usageStats != null) {
98+
int i = 0;
99+
for (UsageStats usageStats : getUsageStats()) {
100+
UsageStats resolved = usageStats.resolve(specExpressionResolver, specExpressionContext);
101+
createBean(createCollector(
102+
resolved.url.getValueOrNull(),
103+
resolved.username.getValueOrNull(),
104+
resolved.password.getValueOrNull(),
105+
resolved.tableName.getValueOrNull(),
106+
usageStats.attributes), "IStatsCollector-" + i);
107+
i++;
91108
}
92109
}
93110

94-
if (result.usageStatsUrl != null && !result.usageStatsUrl.isEmpty()) {
95-
createCollector(registry, result.usageStatsUrl, result.usageStatsUsername, result.usageStatsPassword, result.usageStatsTableName, result.usageStatsAttributes);
96-
}
97111
}
98112

99-
private void createCollector(DefaultListableBeanFactory registry, String url, String username, String password, String tableName, List<UsageStatsAttribute> usageStatsAttributes) {
100-
log.info(String.format("Enabled. Sending usage statistics to %s.", url));
113+
private IStatCollector createCollector(String url, String username, String password, String tableName, List<UsageStatsAttribute> usageStatsAttributes) {
114+
log.info("Enabled. Sending usage statistics to {}.", url);
101115

102-
BeanDefinition bd = new GenericBeanDefinition();
103116
if (url.toLowerCase().contains("/write?db=")) {
104-
bd.setBeanClassName(InfluxDBCollector.class.getName());
105-
bd.getConstructorArgumentValues().addGenericArgumentValue(url);
117+
return new InfluxDBCollector(url);
106118
} else if (url.toLowerCase().startsWith("jdbc")) {
107-
bd.setBeanClassName(JDBCCollector.class.getName());
108-
bd.getConstructorArgumentValues().addGenericArgumentValue(url);
109-
bd.getConstructorArgumentValues().addGenericArgumentValue(username);
110-
bd.getConstructorArgumentValues().addGenericArgumentValue(password);
111-
bd.getConstructorArgumentValues().addGenericArgumentValue(tableName == null ? "event" : tableName);
112-
bd.getConstructorArgumentValues().addGenericArgumentValue(usageStatsAttributes);
119+
return new JDBCCollector(url, username, password, tableName == null ? "event" : tableName, usageStatsAttributes);
113120
} else if (url.equalsIgnoreCase("micrometer")) {
114-
bd.setBeanClassName(Micrometer.class.getName());
115-
116-
BeanDefinition bd2 = new GenericBeanDefinition();
117-
bd2.setBeanClassName(ProxySharingMicrometer.class.getName());
118-
registry.registerBeanDefinition("ProxySharingMicrometer", bd2);
121+
createBean(new ProxySharingMicrometer(), "ProxySharingMicrometer");
122+
return new Micrometer();
119123
} else {
120124
throw new IllegalArgumentException(String.format("Base url for statistics contains an unrecognized values, baseURL %s.", url));
121125
}
122-
123-
registry.registerBeanDefinition(url, bd);
124-
}
125-
126-
@Override
127-
public void setEnvironment(Environment environment) {
128-
this.environment = environment;
129126
}
130127

131128
@Data
129+
@Builder(toBuilder = true)
130+
@AllArgsConstructor(access = AccessLevel.PRIVATE) // force Spring to not use constructor
131+
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE) // Jackson deserialize compatibility
132132
public static class UsageStats {
133133

134-
private String url;
135-
private String username;
136-
private String password;
137-
private String tableName;
134+
@Builder.Default
135+
private SpelField.String url = new SpelField.String();
136+
137+
@Builder.Default
138+
private SpelField.String username = new SpelField.String();
139+
140+
@Builder.Default
141+
private SpelField.String password = new SpelField.String();
142+
143+
@Builder.Default
144+
private SpelField.String tableName = new SpelField.String();
145+
138146
private List<UsageStatsAttribute> attributes;
139147

148+
public UsageStats resolve(SpecExpressionResolver specExpressionResolver, SpecExpressionContext specExpressionContext) {
149+
return toBuilder()
150+
.url(url.resolve(specExpressionResolver, specExpressionContext))
151+
.username(username.resolve(specExpressionResolver, specExpressionContext))
152+
.password(password.resolve(specExpressionResolver, specExpressionContext))
153+
.tableName(tableName.resolve(specExpressionResolver, specExpressionContext))
154+
.build();
155+
}
156+
140157
}
141158

142159
@Data
@@ -147,4 +164,10 @@ public static class UsageStatsAttribute {
147164

148165
}
149166

167+
private <T> void createBean(T bean, String beanName) {
168+
beanFactory.autowireBean(bean);
169+
Object initializedBean = beanFactory.initializeBean(bean, beanName);
170+
beanFactory.registerSingleton(beanName, initializedBean);
171+
}
172+
150173
}

0 commit comments

Comments
 (0)