net.sf.katta.lib.mapfile.MapFileServer.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.katta.lib.mapfile.MapFileServer.java

Source

/**
 * Copyright 2009 the original author or authors.
 *
 * 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 net.sf.katta.lib.mapfile;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import net.sf.katta.node.IContentServer;
import net.sf.katta.util.NodeConfiguration;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.io.MapFile;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.MapFile.Reader;
import org.apache.log4j.Logger;

/**
 * Implements search over a set of Hadoop <code>MapFile</code>s.
 */
public class MapFileServer implements IContentServer, IMapFileServer {

    private final static Logger LOG = Logger.getLogger(MapFileServer.class);

    private final Configuration _conf = new Configuration();
    private final FileSystem _fileSystem;
    private final Map<String, MapFile.Reader> _readerByShard = new ConcurrentHashMap<String, MapFile.Reader>();
    private String _nodeName;

    public MapFileServer() throws IOException {
        _fileSystem = FileSystem.getLocal(_conf);
    }

    public long getProtocolVersion(final String protocol, final long clientVersion) throws IOException {
        return 0L;
    }

    @Override
    public void init(String nodeName, NodeConfiguration nodeConfiguration) {
        _nodeName = nodeName;
    }

    /**
     * Adds an shard index search for given name to the list of shards
     * MultiSearcher search in.
     * 
     * @param shardName
     * @throws IOException
     */
    public void addShard(final String shardName, final File shardDir) throws IOException {
        LOG.debug("LuceneServer " + _nodeName + " got shard " + shardName);
        if (!shardDir.exists()) {
            throw new IOException("Shard " + shardName + " dir " + shardDir.getAbsolutePath() + " does not exist!");
        }
        if (!shardDir.canRead()) {
            throw new IOException("Can not read shard " + shardName + " dir " + shardDir.getAbsolutePath() + "!");
        }
        try {
            final MapFile.Reader reader = new MapFile.Reader(_fileSystem, shardDir.getAbsolutePath(), _conf);
            synchronized (_readerByShard) {
                _readerByShard.put(shardName, reader);
            }
        } catch (IOException e) {
            LOG.error("Error opening shard " + shardName + " " + shardDir.getAbsolutePath(), e);
            throw e;
        }
    }

    @Override
    public Collection<String> getShards() {
        return Collections.unmodifiableCollection(_readerByShard.keySet());
    }

    /**
     * 
     * Removes a search by given shardName from the list of searchers.
     */
    public void removeShard(final String shardName) throws IOException {
        LOG.debug("LuceneServer " + _nodeName + " removing shard " + shardName);
        synchronized (_readerByShard) {
            final MapFile.Reader reader = _readerByShard.get(shardName);
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    LOG.error("Error closing shard " + shardName, e);
                    throw e;
                }
                _readerByShard.remove(shardName);
            } else {
                LOG.warn("Shard " + shardName + " not found!");
            }
        }
    }

    /**
     * Returns data about a shard. Currently the only standard key is
     * SHARD_SIZE_KEY. This value will be reported by the listIndexes command. The
     * units depend on the type of server. It is OK to return an empty map or
     * null.
     * 
     * @param shardName
     *          The name of the shard to measure. This was the name provided in
     *          addShard().
     * @return a map of key/value pairs which describe the shard.
     * @throws Exception
     */
    public Map<String, String> getShardMetaData(String shardName) throws Exception {
        final MapFile.Reader reader = _readerByShard.get(shardName);
        if (reader != null) {
            int count = 0;
            synchronized (reader) {
                reader.reset();
                WritableComparable<?> key = (WritableComparable<?>) reader.getKeyClass().newInstance();
                Writable value = (Writable) reader.getValueClass().newInstance();
                while (reader.next(key, value)) {
                    count++;
                }
            }
            Map<String, String> metaData = new HashMap<String, String>();
            metaData.put(SHARD_SIZE_KEY, Integer.toString(count));
            return metaData;
        }
        LOG.warn("Shard " + shardName + " not found!");
        throw new IllegalArgumentException("Shard " + shardName + " unknown");
    }

    /**
     * Close all MapFiles. No further calls will be made after this one.
     */
    public void shutdown() throws IOException {
        for (final MapFile.Reader reader : _readerByShard.values()) {
            try {
                reader.close();
            } catch (IOException e) {
                LOG.error("Error in shutdown", e);
            }
        }
        _readerByShard.clear();
    }

    public TextArrayWritable get(Text key, String[] shards) throws IOException {
        ExecutorService executor = Executors.newCachedThreadPool();
        Collection<Future<Text>> futures = new ArrayList<Future<Text>>();
        for (String shard : shards) {
            final MapFile.Reader reader = _readerByShard.get(shard);
            if (reader == null) {
                LOG.warn("Shard " + shard + " unknown");
                continue;
            }
            Callable<Text> callable = new MapLookup(reader, key);
            futures.add(executor.submit(callable));
        }
        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.MINUTES); // TODO: config, 10 sec?
        } catch (InterruptedException e) {
            LOG.warn("Interrupted while waiting on MapLookup threads", e);
        }
        executor.shutdownNow();
        List<Text> resultList = new ArrayList<Text>();
        for (Future<Text> future : futures) {
            try {
                Text result = future.get(0, TimeUnit.MILLISECONDS);
                if (result != null) {
                    resultList.add(result);
                }
            } catch (ExecutionException e) {
                /*
                 * This MapFile red threw an exception. Stop processing and throw an
                 * IOE.
                 */
                Throwable t = e.getCause();
                if (t instanceof IOException) {
                    // Throw the same IOException that the MapFile.Reader threw.
                    throw (IOException) t;
                }
                // Wrap MapFile.Reader's exception in an IOException.
                throw new IOException("Error in MapLookup", t);
            } catch (TimeoutException e) {
                /*
                 * Result is not ready. Should not happen, because future is done.
                 * Continue as if MapLookup had returned null.
                 */
                LOG.warn("Timed out while getting MapLookup", e);
            } catch (InterruptedException e) {
                /*
                 * Something went wrong while waiting for result. Should not happen
                 * because we wait for 0 msec, and the future is done. Continue as if
                 * the MapLookup had returned null.
                 */
                LOG.warn("Interrupted while getting RPC result", e);
            }
        }
        return new TextArrayWritable(resultList);
    }

    private class MapLookup implements Callable<Text> {

        private MapFile.Reader _reader;
        private WritableComparable<?> _key;

        public MapLookup(Reader reader, WritableComparable<?> key) {
            _reader = reader;
            _key = key;
        }

        public Text call() throws Exception {
            synchronized (_reader) {
                Writable result = (Writable) _reader.getValueClass().newInstance();
                result = _reader.get(_key, result);
                return (Text) result;
            }
        }

    }

}