Package org.apache.drill.exec.cache.local

Source Code of org.apache.drill.exec.cache.local.LocalCache

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.apache.drill.exec.cache.local;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.common.util.DataOutputOutputStream;
import org.apache.drill.exec.cache.Counter;
import org.apache.drill.exec.cache.DistributedCache;
import org.apache.drill.exec.cache.DistributedMap;
import org.apache.drill.exec.cache.DistributedMultiMap;
import org.apache.drill.exec.cache.DrillSerializable;
import org.apache.drill.exec.exception.DrillbitStartupException;
import org.apache.drill.exec.memory.BufferAllocator;
import org.apache.drill.exec.memory.TopLevelAllocator;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Charsets;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import com.google.protobuf.Message;
import com.google.protobuf.Parser;

public class LocalCache implements DistributedCache {
  static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(LocalCache.class);

  private volatile ConcurrentMap<CacheConfig<?, ?>, DistributedMap<?, ?>> maps;
  private volatile ConcurrentMap<CacheConfig<?, ?>, DistributedMultiMap<?, ?>> multiMaps;
  private volatile ConcurrentMap<String, Counter> counters;
  private static final BufferAllocator allocator = new TopLevelAllocator(DrillConfig.create());

  private static final ObjectMapper mapper = DrillConfig.create().getMapper();

  @Override
  public void close() throws IOException {

  }

  @Override
  public void run() throws DrillbitStartupException {
    maps = Maps.newConcurrentMap();
    multiMaps = Maps.newConcurrentMap();
    counters = Maps.newConcurrentMap();
  }

  @Override
  public <K, V> DistributedMultiMap<K, V> getMultiMap(CacheConfig<K, V> config) {
    DistributedMultiMap<K, V> mmap = (DistributedMultiMap<K, V>) multiMaps.get(config);
    if (mmap == null) {
      multiMaps.putIfAbsent(config, new LocalDistributedMultiMapImpl<K, V>(config));
      return (DistributedMultiMap<K, V>) multiMaps.get(config);
    } else {
      return mmap;
    }
  }

  @Override
  public <K, V> DistributedMap<K, V> getMap(CacheConfig<K, V> config) {
    DistributedMap<K, V> m = (DistributedMap<K, V>) maps.get(config);
    if (m == null) {
      maps.putIfAbsent(config, new LocalDistributedMapImpl<K, V>(config));
      return (DistributedMap<K, V>) maps.get(config);
    } else {
      return m;
    }
  }

  @Override
  public Counter getCounter(String name) {
    Counter c = counters.get(name);
    if (c == null) {
      counters.putIfAbsent(name, new LocalCounterImpl());
      return counters.get(name);
    } else {
      return c;
    }
  }

  private static BytesHolder serialize(Object obj, SerializationMode mode) {
    if(obj instanceof String){
      return new BytesHolder( ((String)obj).getBytes(Charsets.UTF_8));
    }
      try{
      switch(mode){

      case DRILL_SERIALIZIABLE: {
        ByteArrayDataOutput out = ByteStreams.newDataOutput();
        OutputStream outputStream = DataOutputOutputStream.constructOutputStream(out);
        ((DrillSerializable)obj).writeToStream(outputStream);
        outputStream.flush();
        return new BytesHolder(out.toByteArray());
      }

      case JACKSON: {
        ByteArrayDataOutput out = ByteStreams.newDataOutput();
        out.write(mapper.writeValueAsBytes(obj));
        return new BytesHolder(out.toByteArray());
      }

      case PROTOBUF:
        return new BytesHolder(( (Message) obj).toByteArray());

      }
    }catch(Exception e){
      throw new RuntimeException(e);
    }

    throw new UnsupportedOperationException();
  }

  private static <V> V deserialize(BytesHolder b, SerializationMode mode, Class<V> clazz) {
    byte[] bytes = b.bytes;
    try{

      if (clazz == String.class) {
        return (V) new String(bytes, Charsets.UTF_8);
      }

      switch (mode) {

      case DRILL_SERIALIZIABLE: {
        InputStream inputStream = new ByteArrayInputStream(bytes);
        V obj = clazz.getConstructor(BufferAllocator.class).newInstance(allocator);
        ((DrillSerializable) obj).readFromStream(inputStream);
        return obj;
      }

      case JACKSON: {
        return (V) mapper.readValue(bytes, clazz);
      }

      case PROTOBUF: {
        Parser<V> parser = null;
        for (Field f : clazz.getFields()) {
          if (f.getName().equals("PARSER") && Modifier.isStatic(f.getModifiers())) {
            parser = (Parser<V>) f.get(null);
          }
        }
        if (parser == null) throw new UnsupportedOperationException(String.format("Unable to find parser for class %s.", clazz.getName()));
        InputStream inputStream = new ByteArrayInputStream(bytes);
        return parser.parseFrom(inputStream);
      }

      }
    }catch(Exception e){
      throw new RuntimeException(e);
    }

    throw new UnsupportedOperationException();
  }

  private static class BytesHolder {
    final byte[] bytes;
    public BytesHolder(byte[] bytes){
      this.bytes = bytes;
    }
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + Arrays.hashCode(bytes);
      return result;
    }
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      BytesHolder other = (BytesHolder) obj;
      if (!Arrays.equals(bytes, other.bytes))
        return false;
      return true;
    }


  }

  static class LocalDistributedMultiMapImpl<K, V> implements DistributedMultiMap<K, V> {
    private ListMultimap<BytesHolder, BytesHolder> mmap;
    private CacheConfig<K, V> config;

    public LocalDistributedMultiMapImpl(CacheConfig<K, V> config) {
      ListMultimap<BytesHolder, BytesHolder> innerMap = ArrayListMultimap.create();
      mmap = Multimaps.synchronizedListMultimap(innerMap);
      this.config = config;
    }

    @Override
    public Collection<V> get(K key) {
      List<V> list = Lists.newArrayList();
      for (BytesHolder o : mmap.get(serialize(key, config.getMode()))) {
        list.add(deserialize(o, config.getMode(), config.getValueClass()));
      }
      return list;
    }

    @Override
    public Future<Boolean> put(K key, V value) {
      mmap.put(serialize(key, config.getMode()), serialize(value, config.getMode()));
      return new LocalCacheFuture(true);
    }
  }

  public static class LocalCacheFuture<V> implements Future<V> {

    V value;

    public LocalCacheFuture(V value) {
      this.value = value;
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
      return false;
    }

    @Override
    public boolean isCancelled() {
      return false;
    }

    @Override
    public boolean isDone() {
      return true;
    }

    @Override
    public V get() throws InterruptedException, ExecutionException {
      return value;
    }

    @Override
    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
      return value;
    }
  }

  public static class LocalDistributedMapImpl<K, V> implements DistributedMap<K, V> {
    protected ConcurrentMap<BytesHolder, BytesHolder> m;
    protected CacheConfig<K, V> config;

    public LocalDistributedMapImpl(CacheConfig<K, V> config) {
      m = Maps.newConcurrentMap();
      this.config = config;
    }

    @Override
    public V get(K key) {
      BytesHolder b = m.get(serialize(key, config.getMode()));
      if(b == null) return null;
      return (V) deserialize(b, config.getMode(), config.getValueClass());
    }

    @Override
    public Iterable<Entry<K, V>> getLocalEntries() {
      return new Iterable<Entry<K, V>>(){
        @Override
        public Iterator<Entry<K, V>> iterator() {
          return new DeserializingTransformer(m.entrySet().iterator());
        }
      };

    }

    @Override
    public Future<V> put(K key, V value) {
      m.put(serialize(key, config.getMode()), serialize(value, config.getMode()));
      return new LocalCacheFuture(value);
    }


    @Override
    public Future<V> putIfAbsent(K key, V value) {
      m.putIfAbsent(serialize(key, config.getMode()), serialize(value, config.getMode()));
      return new LocalCacheFuture(value);
    }

    @Override
    public Future<V> delete(K key) {
      V value = get(key);
      m.remove(serialize(key, config.getMode()));
      return new LocalCacheFuture(value);
    }

    @Override
    public Future<V> putIfAbsent(K key, V value, long ttl, TimeUnit timeUnit) {
      m.putIfAbsent(serialize(key, config.getMode()), serialize(value, config.getMode()));
      logger.warn("Expiration not implemented in local map cache");
      return new LocalCacheFuture<V>(value);
    }

    private class DeserializingTransformer implements Iterator<Map.Entry<K, V>> {
      private Iterator<Map.Entry<BytesHolder, BytesHolder>> inner;

      public DeserializingTransformer(Iterator<Entry<BytesHolder, BytesHolder>> inner) {
        super();
        this.inner = inner;
      }

      @Override
      public boolean hasNext() {
        return inner.hasNext();
      }

      @Override
      public Entry<K, V> next() {
        return newEntry(inner.next());
      }

      @Override
      public void remove() {
        throw new UnsupportedOperationException();
      }

      public Entry<K, V> newEntry(final Entry<BytesHolder, BytesHolder> input) {
        return new Map.Entry<K, V>() {

          @Override
          public K getKey() {
            return deserialize(input.getKey(), config.getMode(), config.getKeyClass());
          }

          @Override
          public V getValue() {
            return deserialize(input.getValue(), config.getMode(), config.getValueClass());
          }

          @Override
          public V setValue(V value) {
            throw new UnsupportedOperationException();
          }

        };
      }

    }


  }

  public static class LocalCounterImpl implements Counter {
    private AtomicLong al = new AtomicLong();

    @Override
    public long get() {
      return al.get();
    }

    @Override
    public long incrementAndGet() {
      return al.incrementAndGet();
    }

    @Override
    public long decrementAndGet() {
      return al.decrementAndGet();
    }
  }
}
TOP

Related Classes of org.apache.drill.exec.cache.local.LocalCache

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.