官术网_书友最值得收藏!

Provision some data

At this point, we have our application. Now, we need to ensure that it has some data and, then, move on to evaluating its performance.

Without delving too much into the business details, we will implement the provisioning in two passes:

  • Find all the symbols to update
  • For each symbol found, update the price in the database

To do so, we will use two public webservices:

The first one is a plain CSV file, which we will parse without any library to keep things simple and because the format does not require special escaping/parsing. The second one will return a JSON payload, which we can read directly using the JAX-RS 2.1 client API.

Here is how we can retrieve our data:

private String[] getSymbols(final Client client) {
try (final BufferedReader stream = new BufferedReader(
new InputStreamReader(
client.target(symbolIndex)
.request(APPLICATION_OCTET_STREAM_TYPE)
.get(InputStream.class),
StandardCharsets.UTF_8))) {

return stream.lines().skip(2/*comment+header*/)
.map(line -> line.split(","))
.filter(columns -> columns.length > 2 && !columns[1].isEmpty())
.map(columns -> columns[1])
.toArray(String[]::new);
} catch (final IOException e) {
throw new IllegalArgumentException("Can't connect to find symbols", e);
}
}

Note that we directly read a buffered reader backed by the HTTP response stream. Once the symbols are extracted, we can simply iterate over them and request the price of each quote:

try {
final Data data = client.target(financialData)
.resolveTemplate("symbol", symbol)
.request(APPLICATION_JSON_TYPE)
.get(Data.class);

if (!data.hasPrice()) {
LOGGER.warning("Can't retrieve '" + symbol + "'");
return;
}

final double value = data.getQuoteSummary().getResult().get(0)
.getFinancialData().getCurrentPrice().getRaw();

final Quote quote = quoteService.mutate(symbol, quoteOrEmpty ->
quoteOrEmpty.map(q -> {
q.setValue(value);
return q;
}).orElseGet(() -> {
final Quote newQuote = new Quote();
newQuote.setName(symbol);
newQuote.setValue(value);
quoteService.create(newQuote);
return newQuote;
}));

LOGGER.info("Updated quote '" + quote.getName() + "'");
} catch (final WebApplicationException error) {
LOGGER.info("Error getting '" + symbol + "': " + error.getMessage()
+ " (HTTP " + (error.getResponse() == null ? "-" :
error.getResponse().getStatus()) + ")");
}

This piece of code sends an HTTP request, thanks to the JAX-RS client API and JSON-B, which unmarshalls a data model. Then, we use the obtained data to update our database quote if it already exists; otherwise, we use the data to create the database quote.

The code now needs to be wired to be executed. We have multiple options here:

  • Execute it at startup
  • Execute it regularly
  • Execute it when an endpoint is called

In the context of this book, we will use the first two options. The startup is common for us, even if it is not as realistic, because once started, we will get some data. The second option will use an EJB 3.2 @Schedule, which will run hourly.

The startup implementation requires a simple CDI bean with a method calling the previous logic when @ApplicationScoped is created (at startup):

@ApplicationScoped
public class InitialProvisioning {
@Inject
private ProvisioningService provisioningService;

public void onStart(@Observes @Initialized(ApplicationScoped.class) final ServletContext context) {
provisioningService.refresh();
}
}

The scheduling is done, thanks to the Enterprise Java Bean @Schedule API, which allows us, in one annotation, to request the container to regularly execute a method:

@Singleton
@Lock(WRITE)
public class DataRefresher {
@Inject
private ProvisioningService provisioningService;

@Schedule(hour = "*", persistent = false, info = "refresh-quotes")
public void refresh() {
provisioningService.refresh();
}
}
In a real application, you will probably want to configure the refresh frequency and use the  TimerService API to trigger the execution based on the application configuration. In the same spirit, the startup execution could be ignored based on the configuration in order to have a faster startup.
主站蜘蛛池模板: 宁河县| 资溪县| 博兴县| 珲春市| 镇宁| 分宜县| 克拉玛依市| 从江县| 镇平县| 海兴县| 榆林市| 巨鹿县| 龙泉市| 合山市| 新竹县| 威远县| 望谟县| 巴中市| 略阳县| 汉寿县| 都兰县| 丹寨县| 松阳县| 云浮市| 齐齐哈尔市| 平武县| 灌南县| 广安市| 突泉县| 云梦县| 蒙自县| 集安市| 巫山县| 房产| 安陆市| 额敏县| 蓬莱市| 鸡泽县| 灯塔市| 元江| 定西市|