/* * Copyright 2011 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.ipc.invalidation.ticl; import com.google.ipc.invalidation.external.client.SystemResources; import com.google.ipc.invalidation.external.client.SystemResources.Scheduler; import com.google.ipc.invalidation.external.client.SystemResources.Storage; import com.google.ipc.invalidation.external.client.types.Callback; import com.google.ipc.invalidation.external.client.types.SimplePair; import com.google.ipc.invalidation.external.client.types.Status; import com.google.ipc.invalidation.util.Bytes; import com.google.ipc.invalidation.util.InternalBase; import com.google.ipc.invalidation.util.NamedRunnable; import com.google.ipc.invalidation.util.TextBuilder; import com.google.ipc.invalidation.util.TypedUtil; import java.util.HashMap; import java.util.Map; /** * Map-based in-memory implementation of {@link Storage}. * */ public class MemoryStorageImpl extends InternalBase implements Storage { private Scheduler scheduler; private Map<String, byte[]> ticlPersistentState = new HashMap<String, byte[]>(); @Override public void writeKey(final String key, final byte[] value, final Callback<Status> callback) { // Need to schedule immediately because C++ locks aren't reentrant, and // C++ locking code assumes that this call will not return directly. // Schedule the write even if the resources are started since the // scheduler will prevent it from running in case the resources have been // stopped. scheduler.schedule(Scheduler.NO_DELAY, new NamedRunnable("MemoryStorage.writeKey") { @Override public void run() { ticlPersistentState.put(key, value); callback.accept(Status.newInstance(Status.Code.SUCCESS, "")); } }); } int numKeysForTest() { return ticlPersistentState.size(); } @Override public void setSystemResources(SystemResources resources) { this.scheduler = resources.getInternalScheduler(); } @Override public void readKey(final String key, final Callback<SimplePair<Status, byte[]>> done) { scheduler.schedule(Scheduler.NO_DELAY, new NamedRunnable("MemoryStorage.readKey") { @Override public void run() { byte[] value = TypedUtil.mapGet(ticlPersistentState, key); final SimplePair<Status, byte[]> result; if (value != null) { result = SimplePair.of(Status.newInstance(Status.Code.SUCCESS, ""), value); } else { String error = "No value present in map for " + key; result = SimplePair.of(Status.newInstance(Status.Code.PERMANENT_FAILURE, error), null); } done.accept(result); } }); } @Override public void deleteKey(final String key, final Callback<Boolean> done) { scheduler.schedule(Scheduler.NO_DELAY, new NamedRunnable("MemoryStorage.deleteKey") { @Override public void run() { TypedUtil.remove(ticlPersistentState, key); done.accept(true); } }); } @Override public void readAllKeys(final Callback<SimplePair<Status, String>> done) { scheduler.schedule(Scheduler.NO_DELAY, new NamedRunnable("MemoryStorage.readAllKeys") { @Override public void run() { Status successStatus = Status.newInstance(Status.Code.SUCCESS, ""); for (String key : ticlPersistentState.keySet()) { done.accept(SimplePair.of(successStatus, key)); } done.accept(null); } }); } /** * Same as write except without any callbacks and is NOT done on the internal thread. * Test code should typically call this before starting the client. */ void writeForTest(final String key, final byte[] value) { ticlPersistentState.put(key, value); } /** * Sets the scheduler, for tests. The Android tests use this to supply a scheduler that executes * no-delay items in-line. */ public void setSchedulerForTest(Scheduler newScheduler) { scheduler = newScheduler; } /** * Same as read except without any callbacks and is NOT done on the internal thread. */ public byte[] readForTest(final String key) { return ticlPersistentState.get(key); } @Override public void toCompactString(TextBuilder builder) { builder.append("Storage state: "); for (Map.Entry<String, byte[]> entry : ticlPersistentState.entrySet()) { builder.appendFormat("<%s, %s>, ", entry.getKey(), Bytes.toString(entry.getValue())); } } }