Skip to content

Commit f1a32bf

Browse files
committed
Improve and add javadocs
Related to GH-422 and GH-606
1 parent 07a699b commit f1a32bf

File tree

4 files changed

+138
-140
lines changed

4 files changed

+138
-140
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2019 the original author or authors.
2+
* Copyright 2020-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,10 +20,22 @@
2020
import java.util.Map;
2121
import java.util.UUID;
2222

23+
import org.springframework.messaging.MessageHeaders;
2324
import org.springframework.util.StringUtils;
2425

2526

2627
/**
28+
* Utility class to assist with accessing and setting Cloud Events attributes from {@link MessageHeaders}.
29+
* <br><br>
30+
* It is effectively a wrapper over {@link MessageHeaders} which is a {@link Map}.
31+
* It also provides best effort to both discover the actual attribute name (regardless of the prefix)
32+
* as well as set appropriate attribute name.
33+
* <br><br>
34+
* For example, If there is an attribute `ce-source` or `ce_source` or 'source`, by simply calling getSource()
35+
* we'll discover it and will return its value.
36+
* <br>
37+
* Similar effort will happen during the setting of the attribute. If you provide {@link #prefixToUse} we will
38+
* use it otherwise we'll attempt to determine based on current execution context which prefix to use.
2739
*
2840
* @author Oleg Zhurakousky
2941
* @author Dave Syer
@@ -45,139 +57,131 @@ public CloudEventAttributes(Map<String, Object> headers, String prefixToUse) {
4557
this.prefixToUse = prefixToUse;
4658
}
4759

48-
4960
public CloudEventAttributes(Map<String, Object> headers) {
5061
this(headers, null);
5162
}
5263

5364
public CloudEventAttributes setId(String id) {
54-
if (StringUtils.hasText(this.prefixToUse)) {
55-
this.remove(this.getAttributeName(CloudEventMessageUtils.ID));
56-
this.put(this.prefixToUse + CloudEventMessageUtils.ID, id);
57-
}
58-
else {
59-
this.put(this.getAttributeName(CloudEventMessageUtils.ID), id);
60-
}
65+
this.setAtttribute(CloudEventMessageUtils.ID, id);
6166
return this;
6267
}
6368

64-
public CloudEventAttributes setSource(String source) {
65-
if (StringUtils.hasText(this.prefixToUse)) {
66-
this.remove(this.getAttributeName(CloudEventMessageUtils.SOURCE));
67-
this.put(this.prefixToUse + CloudEventMessageUtils.SOURCE, source);
68-
}
69-
else {
70-
this.put(this.getAttributeName(CloudEventMessageUtils.SOURCE), source);
69+
public <A> A getId() {
70+
A id = this.getAtttribute(CloudEventMessageUtils.ID);
71+
if (id instanceof UUID) {
72+
id = null;
7173
}
74+
return id;
75+
}
76+
77+
public CloudEventAttributes setSource(String source) {
78+
this.setAtttribute(CloudEventMessageUtils.SOURCE, source);
7279
return this;
7380
}
7481

82+
public <A> A getSource() {
83+
return this.getAtttribute(CloudEventMessageUtils.SOURCE);
84+
}
85+
7586
public CloudEventAttributes setSpecversion(String specversion) {
76-
if (StringUtils.hasText(this.prefixToUse)) {
77-
this.remove(this.getAttributeName(CloudEventMessageUtils.SPECVERSION));
78-
this.put(this.prefixToUse + CloudEventMessageUtils.SPECVERSION, specversion);
79-
}
80-
else {
81-
this.put(this.getAttributeName(CloudEventMessageUtils.SPECVERSION), specversion);
82-
}
87+
this.setAtttribute(CloudEventMessageUtils.SPECVERSION, specversion);
8388
return this;
8489
}
8590

91+
public <A> A getSpecversion() {
92+
return this.getAtttribute(CloudEventMessageUtils.SPECVERSION);
93+
}
94+
8695
public CloudEventAttributes setType(String type) {
87-
if (StringUtils.hasText(this.prefixToUse)) {
88-
this.remove(this.getAttributeName(CloudEventMessageUtils.TYPE));
89-
this.put(this.prefixToUse + CloudEventMessageUtils.TYPE, type);
90-
}
91-
else {
92-
this.put(this.getAttributeName(CloudEventMessageUtils.TYPE), type);
93-
}
96+
this.setAtttribute(CloudEventMessageUtils.TYPE, type);
9497
return this;
9598
}
9699

97-
@SuppressWarnings("unchecked")
98-
public <A> A getId() {
99-
if (this.containsKey(CloudEventMessageUtils.CANONICAL_ID)) {
100-
return (A) this.get(CloudEventMessageUtils.CANONICAL_ID);
101-
}
102-
else if (this.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.ID)) {
103-
return (A) this.get(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.ID);
104-
}
105-
Object id = this.get(CloudEventMessageUtils.ID);
106-
if (!(id instanceof UUID)) {
107-
return (A) id;
108-
}
109-
return null;
100+
public <A> A getType() {
101+
return this.getAtttribute(CloudEventMessageUtils.TYPE);
110102
}
111103

112-
String getAttributeName(String attributeName) {
113-
if (this.containsKey(CloudEventMessageUtils.ATTR_PREFIX + attributeName)) {
114-
return CloudEventMessageUtils.ATTR_PREFIX + attributeName;
115-
}
116-
else if (this.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + attributeName)) {
117-
return CloudEventMessageUtils.HTTP_ATTR_PREFIX + attributeName;
118-
}
119-
return attributeName;
104+
public CloudEventAttributes setDataContentType(String datacontenttype) {
105+
this.setAtttribute(CloudEventMessageUtils.DATACONTENTTYPE, datacontenttype);
106+
return this;
120107
}
121108

122-
@SuppressWarnings("unchecked")
123-
public <A> A getSource() {
124-
if (this.containsKey(CloudEventMessageUtils.CANONICAL_SOURCE)) {
125-
return (A) this.get(CloudEventMessageUtils.CANONICAL_SOURCE);
126-
}
127-
else if (this.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.SOURCE)) {
128-
return (A) this.get(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.SOURCE);
129-
}
130-
return (A) this.get(CloudEventMessageUtils.SOURCE);
109+
public <A> A getDataContentType() {
110+
return this.getAtttribute(CloudEventMessageUtils.DATACONTENTTYPE);
131111
}
132112

133-
@SuppressWarnings("unchecked")
134-
public <A> A getSpecversion() {
135-
if (this.containsKey(CloudEventMessageUtils.CANONICAL_SPECVERSION)) {
136-
return (A) this.get(CloudEventMessageUtils.CANONICAL_SPECVERSION);
137-
}
138-
else if (this.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.SPECVERSION)) {
139-
return (A) this.get(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.SPECVERSION);
140-
}
141-
return (A) this.get(CloudEventMessageUtils.SPECVERSION);
113+
public CloudEventAttributes setDataSchema(String dataschema) {
114+
this.setAtttribute(CloudEventMessageUtils.DATASCHEMA, dataschema);
115+
return this;
142116
}
143117

144-
@SuppressWarnings("unchecked")
145-
public <A> A getType() {
146-
if (this.containsKey(CloudEventMessageUtils.CANONICAL_TYPE)) {
147-
return (A) this.get(CloudEventMessageUtils.CANONICAL_TYPE);
148-
}
149-
else if (this.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.TYPE)) {
150-
return (A) this.get(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.TYPE);
151-
}
152-
return (A) this.get(CloudEventMessageUtils.TYPE);
118+
public <A> A getDataSchema() {
119+
return this.getAtttribute(CloudEventMessageUtils.DATASCHEMA);
153120
}
154121

155-
@SuppressWarnings("unchecked")
156-
public <A> A getDataContentType() {
157-
Object dataContentType;
158-
if (this.containsKey(CloudEventMessageUtils.CANONICAL_DATACONTENTTYPE)) {
159-
dataContentType = this.get(CloudEventMessageUtils.CANONICAL_DATACONTENTTYPE);
160-
}
161-
else if (this.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.DATACONTENTTYPE)) {
162-
dataContentType = this.get(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.DATACONTENTTYPE);
163-
}
164-
dataContentType = this.get(CloudEventMessageUtils.DATACONTENTTYPE);
165-
return (A) dataContentType;
122+
public CloudEventAttributes setSubject(String subject) {
123+
this.setAtttribute(CloudEventMessageUtils.SUBJECT, subject);
124+
return this;
166125
}
167126

168-
public void setDataContentType(String datacontenttype) {
169-
this.put(CloudEventMessageUtils.CANONICAL_DATACONTENTTYPE, datacontenttype);
127+
public <A> A getSubect() {
128+
return this.getAtttribute(CloudEventMessageUtils.SUBJECT);
170129
}
171130

131+
public CloudEventAttributes setTime(String time) {
132+
this.setAtttribute(CloudEventMessageUtils.TIME, time);
133+
return this;
134+
}
135+
136+
public <A> A getTime() {
137+
return this.getAtttribute(CloudEventMessageUtils.TIME);
138+
}
139+
140+
/**
141+
* Will delegate to the underlying {@link Map} returning the value for the requested attribute or null.
142+
*/
172143
@SuppressWarnings("unchecked")
173-
public <A> A getAtttribute(String name) {
174-
return (A) this.get(name);
144+
public <A> A getAtttribute(String attrName) {
145+
if (this.containsKey(CloudEventMessageUtils.ATTR_PREFIX + attrName)) {
146+
return (A) this.get(CloudEventMessageUtils.ATTR_PREFIX + attrName);
147+
}
148+
else if (this.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + attrName)) {
149+
return (A) this.get(CloudEventMessageUtils.HTTP_ATTR_PREFIX + attrName);
150+
}
151+
return (A) this.get(attrName);
175152
}
176153

154+
/**
155+
* Determines if this instance of {@link CloudEventAttributes} represents valid Cloud Event.
156+
* This implies that it contains all 4 required attributes (id, source, type & specversion)
157+
*
158+
* @return true if this instance represents a valid Cloud Event
159+
*/
177160
public boolean isValidCloudEvent() {
178161
return StringUtils.hasText(this.getId())
179162
&& StringUtils.hasText(this.getSource())
180163
&& StringUtils.hasText(this.getSpecversion())
181164
&& StringUtils.hasText(this.getType());
182165
}
166+
167+
String getAttributeName(String attributeName) {
168+
if (this.containsKey(CloudEventMessageUtils.ATTR_PREFIX + attributeName)) {
169+
return CloudEventMessageUtils.ATTR_PREFIX + attributeName;
170+
}
171+
else if (this.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + attributeName)) {
172+
return CloudEventMessageUtils.HTTP_ATTR_PREFIX + attributeName;
173+
}
174+
return attributeName;
175+
}
176+
177+
private CloudEventAttributes setAtttribute(String attrName, String attrValue) {
178+
if (StringUtils.hasText(this.prefixToUse)) {
179+
this.remove(this.getAttributeName(attrName));
180+
this.put(this.prefixToUse + attrName, attrValue);
181+
}
182+
else {
183+
this.put(this.getAttributeName(attrName), attrValue);
184+
}
185+
return this;
186+
}
183187
}

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventAttributesProvider.java

+14
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,20 @@
1818

1919

2020
/**
21+
* Strategy that should be implemented by the user to help with outgoing Cloud Event attributes.
22+
* <br><br>
23+
* The provided `attributes` are already initialized with default values, so you can only set the ones that you need.
24+
* <br>
25+
* Once implemented, simply configure it as a bean and the framework will invoke it before the outbound Cloud Event Message is finalized.
26+
*
27+
* <pre>{@code
28+
* @Bean
29+
* public CloudEventAttributesProvider cloudEventAttributesProvider() {
30+
* return attributes -> {
31+
* attributes.setSource("https://interface21.com/").setType("com.interface21");
32+
* };
33+
* }}
34+
* </pre>
2135
*
2236
* @author Oleg Zhurakousky
2337
* @author Dave Syer

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtils.java

+27-41
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
/**
3636
* Miscellaneous utility methods to deal with Cloud Events - https://cloudevents.io/.
3737
* <br>
38-
* Mainly for internal use within the framework;
38+
* Primarily intended for the internal use within the framework;
3939
*
4040
* @author Oleg Zhurakousky
4141
* @author Dave Syer
@@ -202,20 +202,6 @@ public static CloudEventAttributes get(String ce_source, String ce_type) {
202202
return get(UUID.randomUUID().toString(), "1.0", ce_source, ce_type);
203203
}
204204

205-
// /**
206-
// * Will construct instance of {@link CloudEventAttributes} from {@link MessageHeaders}.
207-
// *
208-
// * Should copy Cloud Event related headers into an instance of {@link CloudEventAttributes}
209-
// * NOTE: Certain headers must not be copied.
210-
// *
211-
// * @param headers instance of {@link MessageHeaders}
212-
// * @return modifiable instance of {@link CloudEventAttributes}
213-
// */
214-
// public static CloudEventAttributes get(MessageHeaders headers) {
215-
// return new CloudEventAttributes(headers);
216-
// }
217-
218-
219205
@SuppressWarnings("unchecked")
220206
public static Message<?> toBinary(Message<?> inputMessage, MessageConverter messageConverter) {
221207

@@ -252,32 +238,6 @@ else if (StringUtils.hasText(attributes.getDataContentType())) {
252238
return inputMessage;
253239
}
254240

255-
private static Message<?> buildCeMessageFromStructured(Map<String, Object> structuredCloudEvent, MessageHeaders originalHeaders) {
256-
String prefixToUse = determinePrefixToUse(originalHeaders);
257-
Object data = null;
258-
if (structuredCloudEvent.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.DATA)) {
259-
data = structuredCloudEvent.get(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.DATA);
260-
structuredCloudEvent.remove(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.DATA);
261-
}
262-
else if (structuredCloudEvent.containsKey(CloudEventMessageUtils.CANONICAL_DATA)) {
263-
data = structuredCloudEvent.get(CloudEventMessageUtils.CANONICAL_DATA);
264-
structuredCloudEvent.remove(CloudEventMessageUtils.CANONICAL_DATA);
265-
}
266-
else if (structuredCloudEvent.containsKey(CloudEventMessageUtils.DATA)) {
267-
data = structuredCloudEvent.get(CloudEventMessageUtils.DATA);
268-
structuredCloudEvent.remove(CloudEventMessageUtils.DATA);
269-
}
270-
Assert.notNull(data, "'data' must not be null");
271-
MessageBuilder<?> builder = MessageBuilder.withPayload(data);
272-
CloudEventAttributes attributes = new CloudEventAttributes(structuredCloudEvent);
273-
builder.setHeader(prefixToUse + CloudEventMessageUtils.ID, attributes.getId());
274-
builder.setHeader(prefixToUse + CloudEventMessageUtils.SOURCE, attributes.getSource());
275-
builder.setHeader(prefixToUse + CloudEventMessageUtils.TYPE, attributes.getType());
276-
builder.setHeader(prefixToUse + CloudEventMessageUtils.SPECVERSION, attributes.getSpecversion());
277-
builder.copyHeaders(originalHeaders);
278-
return builder.build();
279-
}
280-
281241
public static String determinePrefixToUse(MessageHeaders messageHeaders) {
282242
Set<String> keys = messageHeaders.keySet();
283243
if (keys.contains("user-agent")) {
@@ -304,6 +264,32 @@ public static CloudEventAttributes generateAttributes(Message<?> inputMessage, S
304264
return generateDefaultAttributeValues(attributes, typeName, sourceName);
305265
}
306266

267+
private static Message<?> buildCeMessageFromStructured(Map<String, Object> structuredCloudEvent, MessageHeaders originalHeaders) {
268+
String prefixToUse = determinePrefixToUse(originalHeaders);
269+
Object data = null;
270+
if (structuredCloudEvent.containsKey(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.DATA)) {
271+
data = structuredCloudEvent.get(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.DATA);
272+
structuredCloudEvent.remove(CloudEventMessageUtils.HTTP_ATTR_PREFIX + CloudEventMessageUtils.DATA);
273+
}
274+
else if (structuredCloudEvent.containsKey(CloudEventMessageUtils.CANONICAL_DATA)) {
275+
data = structuredCloudEvent.get(CloudEventMessageUtils.CANONICAL_DATA);
276+
structuredCloudEvent.remove(CloudEventMessageUtils.CANONICAL_DATA);
277+
}
278+
else if (structuredCloudEvent.containsKey(CloudEventMessageUtils.DATA)) {
279+
data = structuredCloudEvent.get(CloudEventMessageUtils.DATA);
280+
structuredCloudEvent.remove(CloudEventMessageUtils.DATA);
281+
}
282+
Assert.notNull(data, "'data' must not be null");
283+
MessageBuilder<?> builder = MessageBuilder.withPayload(data);
284+
CloudEventAttributes attributes = new CloudEventAttributes(structuredCloudEvent);
285+
builder.setHeader(prefixToUse + CloudEventMessageUtils.ID, attributes.getId());
286+
builder.setHeader(prefixToUse + CloudEventMessageUtils.SOURCE, attributes.getSource());
287+
builder.setHeader(prefixToUse + CloudEventMessageUtils.TYPE, attributes.getType());
288+
builder.setHeader(prefixToUse + CloudEventMessageUtils.SPECVERSION, attributes.getSpecversion());
289+
builder.copyHeaders(originalHeaders);
290+
return builder.build();
291+
}
292+
307293
private static CloudEventAttributes generateDefaultAttributeValues(CloudEventAttributes attributes, String source, String type) {
308294
if (attributes.isValidCloudEvent()) {
309295
return attributes

spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java

-6
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,6 @@ public class ContextFunctionCatalogAutoConfiguration {
7070

7171
static final String PREFERRED_MAPPER_PROPERTY = "spring.http.converters.preferred-json-mapper";
7272

73-
// @Bean
74-
// @ConditionalOnMissingBean
75-
// public CloudEventAttributesProvider cloudEventAttributesProvider() {
76-
// return new DefaultCloudEventAttributesProvider();
77-
// }
78-
7973
@Bean
8074
public FunctionRegistry functionCatalog(List<MessageConverter> messageConverters, JsonMapper jsonMapper, ConfigurableApplicationContext context) {
8175
ConfigurableConversionService conversionService = (ConfigurableConversionService) context.getBeanFactory().getConversionService();

0 commit comments

Comments
 (0)