Skip to content

Commit 54781d6

Browse files
Added auto book feature (#2)
Co-authored-by: Oleg Smirnov <[email protected]>
1 parent 8a4fcf7 commit 54781d6

File tree

7 files changed

+236
-38
lines changed

7 files changed

+236
-38
lines changed

Diff for: cradle-admin-tool-http/README.md

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# cradle-admin-tool-http (1.7.2)
1+
# cradle-admin-tool-http (1.8.0)
22
Service which allows user to manage books/pages via RestAPI requests.
33
- The first page in a book can be created only if start time is more than current time.
44
- After the first page all new pages must have start time more than current time + `bookRefreshIntervalMillis` * 2
@@ -12,7 +12,10 @@ Service which allows user to manage books/pages via RestAPI requests.
1212
- **ip** - host where http cradle admin instance will be instanciated. Default value: `0.0.0.0`
1313
- **port** - port on which http server will listen user requests. Default value: `8080`
1414
- **page-recheck-interval** - interval in seconds which `PageManager` service checks if new page is required to create or not based on duration values presented in `auto-pages`. Default value: 60 seconds
15-
- **auto-pages** - defines rule for automatic pages creation for multiple books. If empty no pages will be created automatically. Default value: `empty_map`.
15+
- **auto-book** - if `true` than cradle-admin-tool creates books with first page for each value from `auto-pages` option when target book doesn't exist in cradle.
16+
Creation book time is calculate by the `current time - 1 day` formula to cover events and messages published a bit earlier than cradle-admin-tool started. Default value: `true`.
17+
Please note you can create your own book via REST API later.
18+
- **auto-pages** - defines rule for automatic pages creation for multiple books. If empty no pages will be created automatically.
1619
- **page-duration** - defines duration of the page for the book. Value uses the Java Duration format. You can read more about it [here](https://docs.oracle.com/javase/8/docsT/api/java/time/Duration.html#parse-java.lang.CharSequence-).
1720
- **page-start-time** - baseline date and time for every new page created by `PageManager` for this book.
1821

@@ -29,6 +32,7 @@ spec:
2932
custom-config:
3033
ip: 198.168.0.2
3134
port: 8080
35+
auto-book: true
3236
auto-pages:
3337
book1:
3438
page-duration: PT60S
@@ -44,6 +48,11 @@ spec:
4448
4549
## Release notes
4650
51+
### 1.8.0
52+
53+
+ Feature:
54+
+ Added auto-book functionality
55+
4756
### 1.7.2
4857
4958
+ Bug fix:

Diff for: cradle-admin-tool-http/src/main/java/com/exactpro/th2/cradle/adm/http/Application.java

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public static void main(String[] args) {
5454
resources.add(
5555
new PageManager(
5656
storage,
57+
config.isAutoBook(),
5758
config.getAutoPages(),
5859
config.getPageRecheckInterval(),
5960
settings.calculatePageActionRejectionThreshold() * 2

Diff for: cradle-admin-tool-http/src/main/java/com/exactpro/th2/cradle/adm/http/AutoPageConfiguration.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*******************************************************************************
1+
/*
22
* Copyright 2023 Exactpro (Exactpro Systems Limited)
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -12,10 +12,11 @@
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
15-
******************************************************************************/
15+
*/
1616
package com.exactpro.th2.cradle.adm.http;
1717

1818
import com.fasterxml.jackson.annotation.JsonProperty;
19+
1920
import java.time.Duration;
2021
import java.time.Instant;
2122

@@ -34,4 +35,12 @@ public Duration getPageDuration() {
3435
public Instant getPageStartTime() {
3536
return pageStartTime;
3637
}
38+
39+
public void setPageDuration(Duration pageDuration) {
40+
this.pageDuration = pageDuration;
41+
}
42+
43+
public void setPageStartTime(Instant pageStartTime) {
44+
this.pageStartTime = pageStartTime;
45+
}
3746
}

Diff for: cradle-admin-tool-http/src/main/java/com/exactpro/th2/cradle/adm/http/Configuration.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package com.exactpro.th2.cradle.adm.http;
1818

1919
import com.fasterxml.jackson.annotation.JsonProperty;
20+
21+
import java.util.Collections;
2022
import java.util.Map;
2123

2224
@SuppressWarnings("FieldMayBeFinal")
@@ -35,8 +37,11 @@ public class Configuration {
3537
@JsonProperty("page-recheck-interval")
3638
private int pageRecheckInterval = DEFAULT_PAGE_RECHECK_INTERVAL_SEC;
3739

40+
@JsonProperty("auto-book")
41+
private boolean autoBook = true;
42+
3843
@JsonProperty("auto-pages")
39-
private Map<String, AutoPageConfiguration> autoPages;
44+
private Map<String, AutoPageConfiguration> autoPages = Collections.emptyMap();
4045

4146
public String getIp() {
4247
return ip;
@@ -46,6 +51,10 @@ public int getPort() {
4651
return port;
4752
}
4853

54+
public boolean isAutoBook() {
55+
return autoBook;
56+
}
57+
4958
public Map<String, AutoPageConfiguration> getAutoPages() {
5059
return autoPages;
5160
}

Diff for: cradle-admin-tool-http/src/main/java/com/exactpro/th2/cradle/adm/http/PageManager.java

+95-20
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,35 @@
1717
package com.exactpro.th2.cradle.adm.http;
1818

1919
import com.exactpro.cradle.BookInfo;
20+
import com.exactpro.cradle.BookListEntry;
21+
import com.exactpro.cradle.BookToAdd;
2022
import com.exactpro.cradle.CradleStorage;
2123
import com.exactpro.cradle.PageInfo;
2224
import com.exactpro.cradle.utils.CradleStorageException;
25+
import org.jetbrains.annotations.NotNull;
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
29+
import java.io.IOException;
2330
import java.time.Duration;
2431
import java.time.Instant;
32+
import java.time.temporal.ChronoUnit;
2533
import java.util.Collections;
26-
import java.util.HashMap;
2734
import java.util.List;
2835
import java.util.Map;
36+
import java.util.Objects;
2937
import java.util.concurrent.Executors;
3038
import java.util.concurrent.ScheduledExecutorService;
3139
import java.util.concurrent.TimeUnit;
40+
import java.util.stream.Collectors;
3241

33-
import org.slf4j.Logger;
34-
import org.slf4j.LoggerFactory;
42+
import static org.apache.commons.lang3.StringUtils.lowerCase;
43+
import static org.apache.commons.lang3.StringUtils.trim;
3544

3645
public class PageManager implements AutoCloseable, Runnable{
37-
private static final Logger logger = LoggerFactory.getLogger(Application.class);
38-
private static final String AUTO_PAGE_COMMENT = "auto-page";
46+
private static final Logger LOGGER = LoggerFactory.getLogger(PageManager.class);
47+
static final String AUTO_PAGE_COMMENT = "auto-page";
48+
static final String AUTO_BOOK_DESCRIPTION = "auto-book";
3949

4050
private final CradleStorage storage;
4151
private final long pageActionRejectionThreshold;
@@ -44,56 +54,78 @@ public class PageManager implements AutoCloseable, Runnable{
4454

4555
public PageManager(
4656
CradleStorage storage,
57+
boolean autoBooks,
4758
Map<String, AutoPageConfiguration> autoPages,
4859
int pageRecheckInterval,
4960
long pageActionRejectionThreshold
50-
) throws CradleStorageException {
61+
) {
5162
if (autoPages == null || autoPages.isEmpty()) {
52-
logger.info("auto-page configuration is not provided, pages will not be generated automatically");
63+
LOGGER.info("auto-page configuration is not provided, pages will not be generated automatically");
5364
this.storage = null;
5465
this.pageActionRejectionThreshold = 0;
5566
this.executorService = null;
5667
this.books = Collections.emptyMap();
5768
return;
5869
}
5970

60-
this.storage = storage;
61-
this.pageActionRejectionThreshold = pageActionRejectionThreshold;
71+
Map<String, AutoPageConfiguration> normalisedBookName = autoPages.entrySet().stream()
72+
.collect(Collectors.toMap(
73+
entry -> normaliseBookName(entry.getKey()),
74+
Map.Entry::getValue
75+
));
6276

63-
books = new HashMap<>();
64-
for (String bookName : autoPages.keySet()) {
65-
books.put(bookName, new AutoPageInfo(autoPages.get(bookName), storage.refreshBook(bookName)));
77+
if (normalisedBookName.size() != autoPages.size()) {
78+
throw new IllegalArgumentException("Some of books have the same name after normalization" +
79+
", origin: " + autoPages.keySet() +
80+
", normalized: " + normalisedBookName.keySet());
6681
}
6782

68-
logger.info("Managing pages for books {} every {} sec", books.keySet().toArray(), pageRecheckInterval);
83+
books = normalisedBookName.entrySet().stream()
84+
.collect(Collectors.toUnmodifiableMap(
85+
Map.Entry::getKey,
86+
entry -> new AutoPageInfo(entry.getValue(), getOrCreateBook(storage, entry.getKey(), autoBooks))
87+
));
88+
89+
this.storage = storage;
90+
this.pageActionRejectionThreshold = pageActionRejectionThreshold;
91+
92+
LOGGER.info("Managing pages for books {} every {} sec", books.keySet().toArray(), pageRecheckInterval);
6993
executorService = Executors.newScheduledThreadPool(1);
7094
executorService.scheduleAtFixedRate(this, 0, pageRecheckInterval, TimeUnit.SECONDS);
7195
}
7296

97+
@NotNull
98+
private static String normaliseBookName(String origin) {
99+
String bookName = lowerCase(trim(origin));
100+
if (bookName == null || bookName.isEmpty()) {
101+
throw new IllegalArgumentException("One of book is null or empty");
102+
}
103+
return bookName;
104+
}
105+
73106

74107
private BookInfo checkBook(BookInfo book, AutoPageConfiguration autoPageConfiguration) throws Exception {
75108
Instant pageStartBase = autoPageConfiguration.getPageStartTime();
76109
Duration pageDuration = autoPageConfiguration.getPageDuration();
77110
Instant nowPlusThreshold = Instant.now().plusMillis(pageActionRejectionThreshold);
78-
long nowMillis = nowPlusThreshold.toEpochMilli();
79111

80112
PageInfo pageInfo = book.getLastPage();
81113

82114
if (pageInfo == null) {
83-
return storage.addPage(book.getId(), "auto-page-" + nowMillis, nowPlusThreshold, AUTO_PAGE_COMMENT);
115+
return createAutoPage(storage, book, nowPlusThreshold);
84116
}
85117

86118
Instant lastPageStart = pageInfo.getStarted();
87119
if (lastPageStart.isBefore(nowPlusThreshold)) {
88120
int comparison = nowPlusThreshold.compareTo(pageStartBase);
89121
if (comparison < 0) {
90-
return storage.addPage(book.getId(), "auto-page-" + nowMillis, pageStartBase, AUTO_PAGE_COMMENT);
122+
return createAutoPage(storage, book, pageStartBase);
91123
} else if (comparison > 0) {
92124
Duration diff = Duration.between(pageStartBase, nowPlusThreshold);
93125
Instant nextMark = pageStartBase.plus(pageDuration.multipliedBy(diff.dividedBy(pageDuration) + 1));
94-
return storage.addPage(book.getId(), "auto-page-" + nowMillis, nextMark, AUTO_PAGE_COMMENT);
126+
return createAutoPage(storage, book, nextMark);
95127
} else {
96-
return storage.addPage(book.getId(), "auto-page-" + nowMillis, pageStartBase.plus(pageDuration), AUTO_PAGE_COMMENT);
128+
return createAutoPage(storage, book, pageStartBase.plus(pageDuration));
97129
}
98130
}
99131

@@ -106,7 +138,7 @@ public void close() throws Exception {
106138
executorService.shutdown();
107139
if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
108140
List<Runnable> tasks = executorService.shutdownNow();
109-
logger.warn("Executor can't stop during 5 seconds, " + tasks + " tasks that never commenced execution");
141+
LOGGER.warn("Executor can't stop during 5 seconds, " + tasks + " tasks that never commenced execution");
110142
}
111143
}
112144
}
@@ -117,8 +149,51 @@ public void run() {
117149
try {
118150
autoPageInfo.setBookInfo(checkBook(autoPageInfo.getBookInfo(), autoPageInfo.getAutoPageConfiguration()));
119151
} catch (Exception e) {
120-
logger.error("Exception processing book {}", bookName, e);
152+
LOGGER.error("Exception processing book {}", bookName, e);
121153
}
122154
});
123155
}
156+
157+
private static BookInfo getOrCreateBook(@NotNull CradleStorage storage, String bookName, boolean autoBook) {
158+
try {
159+
BookListEntry bookListEntry = storage.listBooks().stream()
160+
.filter(entry -> Objects.equals(entry.getName(), bookName))
161+
.findFirst()
162+
.orElse(null);
163+
164+
if (bookListEntry != null) {
165+
return storage.refreshBook(bookName);
166+
}
167+
168+
if (!autoBook) {
169+
throw new IllegalStateException("Storage doesn't contain the '" + bookName + "' book. Create book manually or enable auto-book functionality in configuration");
170+
}
171+
172+
BookToAdd bookToAdd = new BookToAdd(
173+
bookName,
174+
Instant.now().minus(1, ChronoUnit.DAYS)
175+
);
176+
bookToAdd.setFullName(bookName);
177+
bookToAdd.setDesc(AUTO_BOOK_DESCRIPTION);
178+
179+
BookInfo bookInfo = storage.addBook(bookToAdd);
180+
181+
LOGGER.info("Created '{}' book, time: {}, full name: {}, description: {}",
182+
bookName,
183+
bookToAdd.getCreated(),
184+
bookToAdd.getFullName(),
185+
bookToAdd.getDesc());
186+
187+
createAutoPage(storage, bookInfo, bookInfo.getCreated());
188+
LOGGER.info("Added first page, book: {}, time: {}", bookInfo.getId().getName(), bookInfo.getCreated());
189+
return bookInfo;
190+
} catch (Exception e) {
191+
throw new RuntimeException("Book with name '" + bookName + "' can't be created", e);
192+
}
193+
}
194+
195+
private static BookInfo createAutoPage(CradleStorage storage, BookInfo book, Instant nowPlusThreshold) throws CradleStorageException, IOException {
196+
long nowMillis = nowPlusThreshold.toEpochMilli();
197+
return storage.addPage(book.getId(), "auto-page-" + nowMillis, nowPlusThreshold, AUTO_PAGE_COMMENT);
198+
}
124199
}

0 commit comments

Comments
 (0)