Skip to content

Commit

Permalink
Refactoring (#174)
Browse files Browse the repository at this point in the history
  • Loading branch information
w3stling authored Aug 20, 2024
1 parent 785fcce commit efe8dbe
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pr-agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
issues: write
pull-requests: write
contents: write
name: Run pr agent on every pull request, respond to user comments
name: Run PR agent on every pull request, respond to user comments
steps:
- name: PR Agent action step
id: pragent
Expand Down
124 changes: 54 additions & 70 deletions src/main/java/com/apptasticsoftware/rssreader/AbstractRssReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
* Abstract base class for implementing modules or extensions of RSS / Atom feeds with custom tags and attributes.
*/
public abstract class AbstractRssReader<C extends Channel, I extends Item> {
private static final String LOG_GROUP = "com.apptasticsoftware.rssreader";
private static final Logger LOGGER = Logger.getLogger("com.apptasticsoftware.rssreader");
private static final ScheduledExecutorService EXECUTOR = new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory("RssReaderWorker"));
private final HttpClient httpClient;
private DateTimeParser dateTimeParser = new DateTime();
Expand All @@ -82,7 +82,6 @@ public abstract class AbstractRssReader<C extends Channel, I extends Item> {
private final Set<String> collectChildNodesForTag = Set.of("content", "summary");
private boolean isInitialized;


/**
* Constructor
*/
Expand Down Expand Up @@ -331,6 +330,7 @@ public AbstractRssReader<C, I> setReadTimeout(Duration readTimeout) {

private void validate(Duration duration, String name) {
Objects.requireNonNull(duration, name + " must not be null");

if (duration.isNegative()) {
throw new IllegalArgumentException(name + " must not be negative");
}
Expand Down Expand Up @@ -438,16 +438,15 @@ public Stream<Item> read(Collection<String> urls) {
initialize();
isInitialized = true;
}

return urls.stream()
.parallel()
.map(url -> {
try {
return Map.entry(url, readAsync(url));
} catch (Exception e) {
var logger = Logger.getLogger(LOG_GROUP);
if (logger.isLoggable(Level.WARNING))
logger.log(Level.WARNING, () -> String.format("Failed read URL %s. Message: %s", url, e.getMessage()));
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, () -> String.format("Failed read URL %s. Message: %s", url, e.getMessage()));
}
return null;
}
})
Expand All @@ -456,10 +455,10 @@ public Stream<Item> read(Collection<String> urls) {
try {
return f.getValue().join();
} catch (Exception e) {
var logger = Logger.getLogger(LOG_GROUP);
if (logger.isLoggable(Level.WARNING))
logger.log(Level.WARNING, () -> String.format("Failed to read URL %s. Message: %s", f.getKey(), e.getMessage()));
return null;
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, () -> String.format("Failed to read URL %s. Message: %s", f.getKey(), e.getMessage()));
}
return Stream.empty();
}
});
}
Expand Down Expand Up @@ -496,7 +495,6 @@ public CompletableFuture<Stream<I>> readAsync(String url) {
initialize();
isInitialized = true;
}

return sendAsyncRequest(url).thenApply(processResponse());
}

Expand All @@ -512,27 +510,27 @@ protected CompletableFuture<HttpResponse<InputStream>> sendAsyncRequest(String u
builder.timeout(requestTimeout);
}

if (!userAgent.isBlank())
if (!userAgent.isBlank()) {
builder.header("User-Agent", userAgent);
}

headers.forEach(builder::header);

return httpClient.sendAsync(builder.GET().build(), HttpResponse.BodyHandlers.ofInputStream());
}

private Function<HttpResponse<InputStream>, Stream<I>> processResponse() {
return response -> {
try {
if (response.statusCode() >= 400 && response.statusCode() < 600) {
throw new IOException("Response http status code: " + response.statusCode());
throw new IOException(String.format("Response HTTP status code: %d", response.statusCode()));
}

var inputStream = response.body();
if (Optional.of("gzip").equals(response.headers().firstValue("Content-Encoding")))
if ("gzip".equals(response.headers().firstValue("Content-Encoding").orElse(null))) {
inputStream = new GZIPInputStream(inputStream);
}

inputStream = new BufferedInputStream(inputStream);

removeBadData(inputStream);
var itemIterator = new RssItemIterator(inputStream);
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(itemIterator, Spliterator.ORDERED), false)
Expand Down Expand Up @@ -590,10 +588,9 @@ public RssItemIterator(InputStream is) {
}
}
catch (XMLStreamException e) {
var logger = Logger.getLogger(LOG_GROUP);

if (logger.isLoggable(Level.WARNING))
logger.log(Level.WARNING, "Failed to process XML. ", e);
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "Failed to process XML. ", e);
}
}
}

Expand All @@ -605,19 +602,17 @@ public void close() {
reader.close();
is.close();
} catch (XMLStreamException | IOException e) {
var logger = Logger.getLogger(LOG_GROUP);

if (logger.isLoggable(Level.WARNING))
logger.log(Level.WARNING, "Failed to close XML stream. ", e);
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "Failed to close XML stream. ", e);
}
}
}

void peekNext() {
private void peekNext() {
if (nextItem == null) {
try {
nextItem = next();
}
catch (NoSuchElementException e) {
} catch (NoSuchElementException e) {
nextItem = null;
}
}
Expand All @@ -635,7 +630,6 @@ public I next() {
if (nextItem != null) {
var next = nextItem;
nextItem = null;

return next;
}

Expand All @@ -646,30 +640,27 @@ public I next() {

if (type == CHARACTERS || type == CDATA) {
parseCharacters();
}
else if (type == START_ELEMENT) {
} else if (type == START_ELEMENT) {
parseStartElement();
parseAttributes();
}
else if (type == END_ELEMENT) {
} else if (type == END_ELEMENT) {
var itemParsed = parseEndElement();

if (itemParsed)
if (itemParsed) {
return item;
}
}
}
} catch (XMLStreamException e) {
var logger = Logger.getLogger(LOG_GROUP);

if (logger.isLoggable(Level.WARNING))
logger.log(Level.WARNING, "Failed to parse XML. ", e);
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, "Failed to parse XML. ", e);
}
}

close();
throw new NoSuchElementException();
}

void collectChildNodes(int type) {
private void collectChildNodes(int type) {
if (type == START_ELEMENT) {
var nsTagName = toNsName(reader.getPrefix(), reader.getLocalName());

Expand Down Expand Up @@ -702,11 +693,9 @@ void collectChildNodes(int type) {
if (collectChildNodesForTag.contains(nsTagName)) {
childNodeTextBuilder.put(nsTagName, new StringBuilder());
}
}
else if (type == CHARACTERS || type == CDATA) {
} else if (type == CHARACTERS || type == CDATA) {
childNodeTextBuilder.forEach((k, builder) -> builder.append(reader.getText()));
}
else if (type == END_ELEMENT) {
} else if (type == END_ELEMENT) {
var nsTagName = toNsName(reader.getPrefix(), reader.getLocalName());
var endTag = "</" + nsTagName + ">";
childNodeTextBuilder.entrySet()
Expand All @@ -717,7 +706,7 @@ else if (type == END_ELEMENT) {
}

@SuppressWarnings("java:S5738")
void parseStartElement() {
private void parseStartElement() {
textBuilder.setLength(0);
var nsTagName = toNsName(reader.getPrefix(), reader.getLocalName());
elementStack.addLast(nsTagName);
Expand All @@ -728,8 +717,7 @@ void parseStartElement() {
channel.setDescription("");
channel.setLink("");
isChannelPart = true;
}
else if (isItem(nsTagName)) {
} else if (isItem(nsTagName)) {
item = Objects.requireNonNullElse(createItem(dateTimeParser), createItem());
item.setChannel(channel);
isChannelPart = false;
Expand All @@ -745,16 +733,15 @@ protected boolean isItem(String tagName) {
return "item".equals(tagName) || "entry".equals(tagName);
}

void parseAttributes() {
private void parseAttributes() {
var nsTagName = toNsName(reader.getPrefix(), reader.getLocalName());
var elementFullPath = getElementFullPath();

if (isChannelPart) {
// Map channel attributes
mapChannelAttributes(nsTagName);
mapChannelAttributes(elementFullPath);
}
else if (isItemPart) {
} else if (isItemPart) {
onItemTags.computeIfPresent(nsTagName, (k, f) -> { f.accept(item); return f; });
onItemTags.computeIfPresent(getElementFullPath(), (k, f) -> { f.accept(item); return f; });
// Map item attributes
Expand All @@ -763,7 +750,7 @@ else if (isItemPart) {
}
}

void mapChannelAttributes(String key) {
private void mapChannelAttributes(String key) {
var consumers = channelAttributes.get(key);
if (consumers != null && channel != null) {
consumers.forEach((attributeName, consumer) -> {
Expand All @@ -773,7 +760,7 @@ void mapChannelAttributes(String key) {
}
}

void mapItemAttributes(String key) {
private void mapItemAttributes(String key) {
var consumers = itemAttributes.get(key);
if (consumers != null && item != null) {
consumers.forEach((attributeName, consumer) -> {
Expand All @@ -783,65 +770,63 @@ void mapItemAttributes(String key) {
}
}

boolean parseEndElement() {
private boolean parseEndElement() {
var nsTagName = toNsName(reader.getPrefix(), reader.getLocalName());
var text = textBuilder.toString().trim();
var elementFullPath = getElementFullPath();
elementStack.removeLast();

if (isChannelPart)
if (isChannelPart) {
parseChannelCharacters(channel, nsTagName, elementFullPath, text);
else
} else {
parseItemCharacters(item, nsTagName, elementFullPath, text);
}

textBuilder.setLength(0);

return isItem(nsTagName);
}

void parseCharacters() {
private void parseCharacters() {
var text = reader.getText();

if (text.isBlank())
if (text.isBlank()) {
return;

}
textBuilder.append(text);
}

void parseChannelCharacters(C channel, String nsTagName, String elementFullPath, String text) {
if (channel == null || text.isEmpty())
private void parseChannelCharacters(C channel, String nsTagName, String elementFullPath, String text) {
if (channel == null || text.isEmpty()) {
return;

}
channelTags.computeIfPresent(nsTagName, (k, f) -> { f.accept(channel, text); return f; });
channelTags.computeIfPresent(elementFullPath, (k, f) -> { f.accept(channel, text); return f; });
}

void parseItemCharacters(final I item, String nsTagName, String elementFullPath, final String text) {
private void parseItemCharacters(final I item, String nsTagName, String elementFullPath, final String text) {
var builder = childNodeTextBuilder.remove(nsTagName);
if (item == null || (text.isEmpty() && builder == null))
if (item == null || (text.isEmpty() && builder == null)) {
return;

}
var textValue = (builder != null) ? builder.toString().trim() : text;
itemTags.computeIfPresent(nsTagName, (k, f) -> { f.accept(item, textValue); return f; });
itemTags.computeIfPresent(elementFullPath, (k, f) -> { f.accept(item, text); return f; });
}

String toNsName(String prefix, String name) {
private String toNsName(String prefix, String name) {
return prefix.isEmpty() ? name : prefix + ":" + name;
}

String toNamespacePrefix(String prefix) {
private String toNamespacePrefix(String prefix) {
return prefix == null || prefix.isEmpty() ? "xmlns" : "xmlns" + ":" + prefix;
}

String getElementFullPath() {
private String getElementFullPath() {
return "/" + String.join("/", elementStack);
}
}

private HttpClient createHttpClient() {
HttpClient client;

try {
var context = SSLContext.getInstance("TLSv1.3");
context.init(null, null, null);
Expand All @@ -861,7 +846,6 @@ private HttpClient createHttpClient() {
}
client = builder.build();
}

return client;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
* Provides methods for mapping field
*/
public final class Mapper {
private static final String LOG_GROUP = "com.apptasticsoftware.rssreader.util";
private static final Logger LOGGER = Logger.getLogger("com.apptasticsoftware.rssreader.util");

private Mapper() { }

Expand Down Expand Up @@ -52,9 +52,9 @@ private static <T> void mapNumber(String text, Consumer<T> func, Function<String
try {
func.accept(convert.apply(text));
} catch (NumberFormatException e) {
var logger = Logger.getLogger(LOG_GROUP);
if (logger.isLoggable(Level.WARNING))
logger.log(Level.WARNING, () -> String.format("Failed to convert %s. Message: %s", text, e.getMessage()));
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING, () -> String.format("Failed to convert %s. Message: %s", text, e.getMessage()));
}
}
}
}
Expand Down
Loading

0 comments on commit efe8dbe

Please sign in to comment.