| // Copyright 2010 Google Inc. |
| // |
| // 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 com.google.gwtorm.nosql.heap; |
| |
| import com.google.gwtorm.nosql.generic.GenericDatabase; |
| import com.google.gwtorm.server.OrmException; |
| import com.google.gwtorm.server.Schema; |
| import com.google.protobuf.InvalidProtocolBufferException; |
| import com.google.protobuf.UnknownFieldSet; |
| |
| import java.io.PrintWriter; |
| import java.util.Map; |
| import java.util.SortedMap; |
| import java.util.TreeMap; |
| import java.util.concurrent.locks.Lock; |
| import java.util.concurrent.locks.ReentrantLock; |
| |
| /** |
| * Toy in-memory implementation of a NoSQL database. |
| * <p> |
| * Implements a simple NoSQL database with a standard {@link java.util.TreeMap} |
| * held inside of this JVM process. All operations occur on the TreeMap, with no |
| * durability across database restarts. Therefore this implementation is only |
| * suitable for simple tests. |
| * |
| * @param <T> type of the application schema. |
| */ |
| abstract class TreeMapDatabase<T extends Schema, S extends TreeMapSchema, A extends TreeMapAccess> |
| extends GenericDatabase<T, S, A> { |
| |
| /** Lock that protects reads and writes of {@link #table}. */ |
| final Lock lock; |
| |
| /** The NoSQL database storage. */ |
| final SortedMap<byte[], byte[]> table; |
| |
| /** |
| * Initialize a new database and generate the implementation. |
| * |
| * @param schemaBaseType class that the generated Schema implementation should |
| * extend in order to provide data store connectivity. |
| * @param accessBaseType class that the generated Access implementations |
| * should extend in order to provide single-relation access for each |
| * schema instance. |
| * @param appSchema the application schema interface that must be implemented |
| * and constructed on demand. |
| * @throws OrmException the schema cannot be created because of an annotation |
| * error in the interface definitions. |
| */ |
| protected TreeMapDatabase(final Class<S> schemaBaseType, |
| final Class<A> accessBaseType, final Class<T> appSchema) |
| throws OrmException { |
| super(schemaBaseType, accessBaseType, appSchema); |
| |
| lock = new ReentrantLock(true); |
| table = new TreeMap<byte[], byte[]>(HeapKeyComparator.INSTANCE); |
| } |
| |
| /** |
| * Try to print the database contents in human readable format. |
| * |
| * @param pw writer to print the database out to. |
| */ |
| public void dump(PrintWriter pw) { |
| lock.lock(); |
| try { |
| for (Map.Entry<byte[], byte[]> ent : table.entrySet()) { |
| String key = format(ent.getKey()); |
| |
| String val; |
| try { |
| UnknownFieldSet proto = UnknownFieldSet.parseFrom(ent.getValue()); |
| val = proto.toString(); |
| } catch (InvalidProtocolBufferException notProto) { |
| val = format(ent.getValue()); |
| } |
| |
| if (val.contains("\n")) { |
| pw.println(key + ":\n" + " " + val.replaceAll("\n", "\n ")); |
| } else { |
| pw.println(key + ": " + val); |
| } |
| } |
| } finally { |
| lock.unlock(); |
| } |
| } |
| |
| private static String format(byte[] bin) { |
| StringBuilder s = new StringBuilder(bin.length); |
| for (int i = 0; i < bin.length; i++) { |
| byte b = bin[i]; |
| switch (b) { |
| case 0x00: |
| s.append("\\0"); |
| break; |
| |
| case 0x01: |
| s.append("\\1"); |
| break; |
| |
| case -1: |
| s.append("\\xff"); |
| break; |
| |
| case '\r': |
| s.append("\\r"); |
| break; |
| |
| default: |
| s.append((char) b); |
| break; |
| } |
| } |
| return s.toString(); |
| } |
| } |