/* * JBoss, Home of Professional Open Source * Copyright 2013, 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.FileDescriptorSource; import org.infinispan.protostream.SerializationContext; import org.infinispan.protostream.annotations.ProtoSchemaBuilder; 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.Memo; import org.jboss.as.quickstarts.datagrid.hotrod.query.domain.Person; import org.jboss.as.quickstarts.datagrid.hotrod.query.domain.PhoneNumber; import org.jboss.as.quickstarts.datagrid.hotrod.query.domain.PhoneType; import org.jboss.as.quickstarts.datagrid.hotrod.query.marshallers.PersonMarshaller; import org.jboss.as.quickstarts.datagrid.hotrod.query.marshallers.PhoneNumberMarshaller; import org.jboss.as.quickstarts.datagrid.hotrod.query.marshallers.PhoneTypeMarshaller; 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.io.Reader; import java.io.StringWriter; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Properties; /** * A simple demo for remote query capabilities. * * @author Adrian Nistor */ public class AddressBookManager { 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 PROTOBUF_DEFINITION_RESOURCE = "/quickstart/addressbook.proto"; private static final String APP_MENU = "\nAvailable actions:\n" + "0. Display available actions\n" + "1. Add person\n" + "2. Remove person\n" + "3. Add phone to person\n" + "4. Remove phone from person\n" + "5. Query persons by name\n" + "6. Query persons by phone\n" + "7. Add memo\n" + "8. Query memo by author\n" + "9. Display all cache entries\n" + "10. Clear cache\n" + "11. Quit\n"; private RemoteCacheManager cacheManager; /** * A cache that holds both Person and Memo objects. */ private RemoteCache<Integer, Object> remoteCache; public AddressBookManager() 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); ctx.registerProtoFiles(FileDescriptorSource.fromResources(PROTOBUF_DEFINITION_RESOURCE)); ctx.registerMarshaller(new PersonMarshaller()); ctx.registerMarshaller(new PhoneNumberMarshaller()); ctx.registerMarshaller(new PhoneTypeMarshaller()); // 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 memoSchemaFile = protoSchemaBuilder .fileName("memo.proto") .packageName("quickstart") .addClass(Memo.class) .build(ctx); // register the schemas with the server too RemoteCache<String, String> metadataCache = cacheManager.getCache(ProtobufMetadataManagerConstants.PROTOBUF_METADATA_CACHE_NAME); metadataCache.put(PROTOBUF_DEFINITION_RESOURCE, readResource(PROTOBUF_DEFINITION_RESOURCE)); metadataCache.put("memo.proto", memoSchemaFile); String errors = metadataCache.get(ProtobufMetadataManagerConstants.ERRORS_KEY_SUFFIX); if (errors != null) { throw new IllegalStateException("Some Protobuf schema files contain errors:\n" + errors); } } private void queryPersonByName() { String namePattern = readConsole("Enter person name pattern: "); QueryFactory qf = Search.getQueryFactory(remoteCache); Query query = qf.from(Person.class) .having("name").like(namePattern).toBuilder() .build(); List<Person> results = query.list(); System.out.println("Found " + results.size() + " matches:"); for (Person p : results) { System.out.println(">> " + p); } } private void queryPersonByPhone() { String phoneNumber = readConsole("Enter phone number: "); QueryFactory qf = Search.getQueryFactory(remoteCache); Query query = qf.from(Person.class) .having("phone.number").eq(phoneNumber).toBuilder() .build(); List<Person> results = query.list(); System.out.println("Found " + results.size() + " matches:"); for (Person p : results) { System.out.println(">> " + p); } } private void addPerson() { int id = Integer.parseInt(readConsole("Enter person id (int): ")); String name = readConsole("Enter person name (string): "); String email = readConsole("Enter person email (string): "); Person person = new Person(); person.setId(id); person.setName(name); person.setEmail(email); if (remoteCache.containsKey(person.getId())) { System.out.println("Updating person with id " + person.getId()); } // put the Person in cache remoteCache.put(person.getId(), person); } private void removePerson() { int id = Integer.parseInt(readConsole("Enter person id to remove (int): ")); // remove from cache Person prevValue = (Person) remoteCache.withFlags(Flag.FORCE_RETURN_VALUE).remove(id); System.out.println("Removed: " + prevValue); } private void addPhone() { System.out.println("Adding a phone number to a person"); int id = Integer.parseInt(readConsole("Enter person id (int): ")); Person person = (Person) remoteCache.get(id); if (person == null) { System.out.println("Person not found"); return; } System.out.println("> " + person); String number = readConsole("Enter phone number (string): "); PhoneType type = PhoneType.valueOf(readConsole("Enter phone type " + EnumSet.allOf(PhoneType.class) + ": ").toUpperCase()); List<PhoneNumber> phones = person.getPhones(); if (phones == null) { phones = new ArrayList<PhoneNumber>(); } PhoneNumber phoneNumber = new PhoneNumber(); phoneNumber.setNumber(number); phoneNumber.setType(type); phones.add(phoneNumber); person.setPhones(phones); // update the Person in cache remoteCache.put(person.getId(), person); } private void removePhone() { System.out.println("Removing a phone number from a person"); int id = Integer.parseInt(readConsole("Enter person id (int): ")); Person person = (Person) remoteCache.get(id); if (person == null) { System.out.println("Person not found"); return; } System.out.println("> " + person); if (person.getPhones() != null && !person.getPhones().isEmpty()) { int idx = Integer.parseInt(readConsole("Enter phone index [0.." + (person.getPhones().size() - 1) + "]: ")); if (idx < 0 || idx >= person.getPhones().size()) { System.out.println("Index out of range"); return; } person.getPhones().remove(idx); // update the Person in cache remoteCache.put(person.getId(), person); } else { System.out.println("The person does not have any phones"); } } private void printAllEntries() { for (Object key : remoteCache.keySet()) { System.out.println("key=" + key + " value=" + remoteCache.get(key)); } } private void clearCache() { remoteCache.clear(); System.out.println("Cache cleared."); } private void addMemo() { int id = Integer.parseInt(readConsole("Enter memo id (int): ")); String text = readConsole("Enter memo text (string): "); Memo.Priority priority = Memo.Priority.valueOf(readConsole("Enter priority " + EnumSet.allOf(Memo.Priority.class) + ": ").toUpperCase()); int authorId = Integer.parseInt(readConsole("Enter author id (int): ")); Person author = (Person) remoteCache.get(authorId); if (author == null) { System.out.println("Person not found"); return; } System.out.println("> " + author); Memo memo = new Memo(); memo.setId(id); memo.setText(text); memo.setPriority(priority); memo.setAuthor(author); // put the Memo in cache remoteCache.put(memo.getId(), memo); } private void queryMemoByAuthor() { String namePattern = readConsole("Enter person name pattern: "); QueryFactory qf = Search.getQueryFactory(remoteCache); Query query = qf.from(Memo.class) .having("author.name").like(namePattern).toBuilder() .build(); List<Memo> results = query.list(); System.out.println("Found " + results.size() + " matches:"); for (Memo p : results) { System.out.println(">> " + p); } } private void stop() { cacheManager.stop(); } public static void main(String[] args) throws Exception { AddressBookManager manager = new AddressBookManager(); 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)) { manager.addPerson(); } else if ("2".equals(action)) { manager.removePerson(); } else if ("3".equals(action)) { manager.addPhone(); } else if ("4".equals(action)) { manager.removePhone(); } else if ("5".equals(action)) { manager.queryPersonByName(); } else if ("6".equals(action)) { manager.queryPersonByPhone(); } else if ("7".equals(action)) { manager.addMemo(); } else if ("8".equals(action)) { manager.queryMemoByAuthor(); } else if ("9".equals(action)) { manager.printAllEntries(); } else if ("10".equals(action)) { manager.clearCache(); } else if ("11".equals(action)) { System.out.println("Bye!"); break; } else { System.out.println("\nUnrecognized action!"); System.out.println(APP_MENU); } } catch (Exception e) { e.printStackTrace(); } } manager.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 } } } } private String readResource(String resourcePath) throws IOException { InputStream is = getClass().getResourceAsStream(resourcePath); try { final Reader reader = new InputStreamReader(is, "UTF-8"); StringWriter writer = new StringWriter(); char[] buf = new char[1024]; int len; while ((len = reader.read(buf)) != -1) { writer.write(buf, 0, len); } return writer.toString(); } finally { is.close(); } } }