/*
* JBoss, Home of Professional Open Source
* Copyright 2015, Red Hat, Inc. and/or its affiliates, and individual
* contributors by the @authors tag. See the copyright.txt in the
* distribution for a full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.as.quickstarts.datagrid.hotrod.query;
import org.infinispan.client.hotrod.Flag;
import org.infinispan.client.hotrod.RemoteCache;
import org.infinispan.client.hotrod.RemoteCacheManager;
import org.infinispan.client.hotrod.Search;
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder;
import org.infinispan.client.hotrod.marshall.ProtoStreamMarshaller;
import org.infinispan.protostream.SerializationContext;
import org.infinispan.protostream.annotations.ProtoSchemaBuilder;
import org.infinispan.query.api.continuous.ContinuousQuery;
import org.infinispan.query.api.continuous.ContinuousQueryListener;
import org.infinispan.query.dsl.Expression;
import org.infinispan.query.dsl.Query;
import org.infinispan.query.dsl.QueryFactory;
import org.infinispan.query.remote.client.ProtobufMetadataManagerConstants;
import org.jboss.as.quickstarts.datagrid.hotrod.query.domain.Forecast;
import java.io.BufferedReader;
import java.io.Console;
import java.io.IOError;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
/**
* A simple demo for aggregations and continuous query capabilities on a remote cache.
*
* @author Adrian Nistor
*/
public class SnowForecast {
private static final String SERVER_HOST = "jdg.host";
private static final String HOTROD_PORT = "jdg.hotrod.port";
private static final String CACHE_NAME = "jdg.cache";
private static final String PROPERTIES_FILE = "jdg.properties";
private static final String APP_MENU = "\nAvailable actions:\n" +
"0. Display available actions\n" +
"1. Add/update forecast\n" +
"2. Remove forecast\n" +
"3. Add continuous query listener\n" +
"4. Remove continuous query listener\n" +
"5. Display snow report\n" +
"6. Display all cache entries\n" +
"7. Clear cache\n" +
"8. Quit\n";
private RemoteCacheManager cacheManager;
/**
* A cache that holds Forecast objects.
*/
private RemoteCache<Integer, Forecast> remoteCache;
private ContinuousQuery<Integer, Forecast> continuousQuery;
public SnowForecast() throws Exception {
final String host = jdgProperty(SERVER_HOST);
final int hotrodPort = Integer.parseInt(jdgProperty(HOTROD_PORT));
final String cacheName = jdgProperty(CACHE_NAME); // The name of the address book cache, as defined in your server config.
System.out.printf("Using cache %s on %s:%d\n\n", cacheName, host, hotrodPort);
ConfigurationBuilder builder = new ConfigurationBuilder();
builder.addServer()
.host(host)
.port(hotrodPort)
.marshaller(new ProtoStreamMarshaller()); // The Protobuf based marshaller is required for query capabilities
cacheManager = new RemoteCacheManager(builder.build());
remoteCache = cacheManager.getCache(cacheName);
if (remoteCache == null) {
throw new RuntimeException("Cache '" + cacheName + "' not found. Please make sure the server is properly configured");
}
registerSchemasAndMarshallers();
}
/**
* Register the Protobuf schemas and marshallers with the client and then register the schemas with the server too.
*/
private void registerSchemasAndMarshallers() throws IOException {
// Register entity marshallers on the client side ProtoStreamMarshaller instance associated with the remote cache manager.
SerializationContext ctx = ProtoStreamMarshaller.getSerializationContext(cacheManager);
// generate the 'memo.proto' schema file based on the annotations on Memo class and register it with the SerializationContext of the client
ProtoSchemaBuilder protoSchemaBuilder = new ProtoSchemaBuilder();
String forecastSchemaFile = protoSchemaBuilder
.fileName("forecast.proto")
.packageName("quickstart")
.addClass(Forecast.class)
.build(ctx);
// register the schemas with the server too
RemoteCache<String, String> metadataCache = cacheManager.getCache(ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME);
metadataCache.put("forecast.proto", forecastSchemaFile);
String errors = metadataCache.get(ProtobufMetadataManagerConstants.ERRORS_KEY_SUFFIX);
if (errors != null) {
throw new IllegalStateException("Some Protobuf schema files contain errors:\n" + errors);
}
}
private void addContinuousQueryListener() {
if (continuousQuery == null) {
QueryFactory qf = Search.getQueryFactory(remoteCache);
Query query = qf.from(Forecast.class)
.having("humidity").lte(75.0f)
.and().having("temperature").lte(3.0f)
.and().having("rain").eq(0)
.and().having("snowfall").gte(0)
.toBuilder().build();
continuousQuery = Search.getContinuousQuery(remoteCache);
ContinuousQueryListener<Integer, Forecast> cqListener = new ContinuousQueryListener<Integer, Forecast>() {
@Override
public void resultJoining(Integer key, Forecast forecast) {
System.out.printf("Great news! Found perfect ski conditions at %s in %d-%d-%d%n", forecast.getLocation(), forecast.getYear(), forecast.getMonth(), forecast.getDay());
}
@Override
public void resultLeaving(Integer key) {
System.out.printf("The forecast %s was updated (or removed) and it no longer predicts good ski conditions%n", key);
}
};
continuousQuery.addContinuousQueryListener(query, cqListener);
System.out.println("Continuous query listener added.");
}
}
private void removeContinuousQueryListener() {
if (continuousQuery != null) {
continuousQuery.removeAllListeners();
continuousQuery = null;
System.out.println("Continuous query listener removed.");
}
}
private void addForecast() throws ParseException {
int id = Integer.parseInt(readConsole("Enter forecast id (int): "));
String location = readConsole("Enter location (string): ");
int year = Integer.parseInt(readConsole("Enter year (int): "));
int month = Integer.parseInt(readConsole("Enter month (int): "));
int day = Integer.parseInt(readConsole("Enter day (int): "));
float rain = Float.parseFloat(readConsole("Rain (float): "));
float snow = Float.parseFloat(readConsole("Snowfall (float): "));
float temperature = Float.parseFloat(readConsole("Temperature (float): "));
float humidity = Float.parseFloat(readConsole("Humidity (float): "));
Forecast forecast = new Forecast();
forecast.setId(id);
forecast.setLocation(location);
forecast.setYear(year);
forecast.setMonth(month);
forecast.setDay(day);
forecast.setRain(rain);
forecast.setSnowfall(snow);
forecast.setTemperature(temperature);
forecast.setHumidity(humidity);
if (remoteCache.containsKey(forecast.getId())) {
System.out.println("Updating forecast with id " + forecast.getId());
}
// put the Forecast in cache
remoteCache.put(forecast.getId(), forecast);
}
private void removeForecast() {
int id = Integer.parseInt(readConsole("Enter forecast id to remove (int): "));
// remove from cache
Forecast prevValue = remoteCache.withFlags(Flag.FORCE_RETURN_VALUE).remove(id);
System.out.println("Removed: " + prevValue);
}
private void printSnowReport() {
QueryFactory qf = Search.getQueryFactory(remoteCache);
Query q = qf.from(Forecast.class)
.select(Expression.property("location"), Expression.property("year"), Expression.property("month"),
Expression.property("day"), Expression.sum("rain"), Expression.sum("snowfall"),
Expression.avg("humidity"), Expression.avg("temperature"))
.groupBy("location", "year", "month", "day")
.build();
List<Object[]> list = q.list();
System.out.println("[Location, year, month, day, rain, snowfall, humidity, temperature]");
for (Object[] row : list) {
System.out.println(Arrays.asList(row));
}
}
private void printAllEntries() {
for (Object key : remoteCache.keySet()) {
System.out.println("key=" + key + " value=" + remoteCache.get(key));
}
}
private void cleaCache() {
remoteCache.clear();
System.out.println("Cache cleared.");
}
private void stop() {
cacheManager.stop();
}
public static void main(String[] args) throws Exception {
SnowForecast forecast = new SnowForecast();
System.out.println(APP_MENU);
while (true) {
try {
String action = readConsole("> ");
if (action == null) {
continue;
}
action = action.trim();
if (action.isEmpty()) {
continue;
}
if ("0".equals(action)) {
System.out.println(APP_MENU);
} else if ("1".equals(action)) {
forecast.addForecast();
} else if ("2".equals(action)) {
forecast.removeForecast();
} else if ("3".equals(action)) {
forecast.addContinuousQueryListener();
} else if ("4".equals(action)) {
forecast.removeContinuousQueryListener();
} else if ("5".equals(action)) {
forecast.printSnowReport();
} else if ("6".equals(action)) {
forecast.printAllEntries();
} else if ("7".equals(action)) {
forecast.cleaCache();
} else if ("8".equals(action)) {
System.out.println("Bye!");
break;
} else {
System.out.println("\nUnrecognized action!");
System.out.println(APP_MENU);
}
} catch (Exception e) {
e.printStackTrace();
}
}
forecast.stop();
}
private static String readConsole(String prompt) {
// this method is intended to be as simple as possible rather than
// being efficient by caching a reference to the console/buffered reader
Console con = System.console();
if (con != null) {
return con.readLine(prompt);
}
System.out.print(prompt);
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
return reader.readLine();
} catch (IOException ex) {
throw new IOError(ex);
}
}
private String jdgProperty(String name) {
InputStream res = null;
try {
res = getClass().getClassLoader().getResourceAsStream(PROPERTIES_FILE);
Properties props = new Properties();
props.load(res);
return props.getProperty(name);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
if (res != null) {
try {
res.close();
} catch (IOException e) {
// ignore
}
}
}
}
}