net.oneandone.troilus.SingleReadQuery.java Source code

Java tutorial

Introduction

Here is the source code for net.oneandone.troilus.SingleReadQuery.java

Source

/*
 * Copyright 1&1 Internet AG, https://github.com/1and1/
 * 
 * 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.oneandone.troilus;

import java.util.Iterator;
import java.util.List;

import net.oneandone.troilus.java7.FetchingIterator;
import net.oneandone.troilus.java7.Record;
import net.oneandone.troilus.java7.ResultList;
import net.oneandone.troilus.java7.SingleRead;
import net.oneandone.troilus.java7.SingleReadWithUnit;
import net.oneandone.troilus.java7.interceptor.ReadQueryData;
import net.oneandone.troilus.java7.interceptor.ResultListAdapter;

import org.reactivestreams.Publisher;

import com.datastax.driver.core.ResultSet;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

/**
 * Read query implementation
 * 
 * @author Jason Westra - edited original
 * 12-12-2015: 3.x API change - ListenableFuture<Void> to ListenableFuture<ResultSet>
 */
class SingleReadQuery extends AbstractQuery<SingleReadQuery> implements SingleReadWithUnit<Record, Record> {

    private final ReadQueryData data;

    /**
     * @param ctx   the context 
     * @param data  the data
     */
    SingleReadQuery(Context ctx, ReadQueryData data) {
        super(ctx);
        this.data = data;
    }

    ////////////////////
    // factory methods

    @Override
    protected SingleReadQuery newQuery(Context newContext) {
        return new SingleReadQuery(newContext, data);
    }

    private SingleReadQuery newQuery(ReadQueryData data) {
        return new SingleReadQuery(getContext(), data);
    }

    //
    ////////////////////

    @Override
    public SingleReadQuery all() {
        return newQuery(data.columnsToFetch(ImmutableMap.<String, Boolean>of()));
    }

    @Override
    public SingleReadQuery column(String name) {
        return newQuery(data.columnsToFetch(Immutables.join(data.getColumnsToFetch(), name, false)));
    }

    @Override
    public SingleReadQuery columnWithMetadata(String name) {
        return newQuery(data.columnsToFetch(Immutables.join(data.getColumnsToFetch(), name, true)));
    }

    @Override
    public SingleReadQuery columns(String... names) {
        return columns(ImmutableList.copyOf(names));
    }

    private SingleReadQuery columns(ImmutableList<String> names) {
        SingleReadQuery read = this;
        for (String columnName : names) {
            read = read.column(columnName);
        }
        return read;
    }

    @Override
    public SingleReadQuery column(ColumnName<?> name) {
        return column(name.getName());
    }

    @Override
    public SingleReadQuery columnWithMetadata(ColumnName<?> name) {
        return column(name.getName());
    }

    @Override
    public SingleReadQuery columns(ColumnName<?>... names) {
        List<String> ns = Lists.newArrayList();
        for (ColumnName<?> name : names) {
            ns.add(name.getName());
        }
        return columns(ImmutableList.copyOf(ns));
    }

    @Override
    public <E> SingleEntityReadQuery<E> asEntity(Class<E> objectClass) {
        return new SingleEntityReadQuery<E>(getContext(), this, objectClass);
    }

    @Override
    public Record execute() {
        return ListenableFutures.getUninterruptibly(executeAsync());
    }

    @Override
    public ListenableFuture<Record> executeAsync() {
        ListenableFuture<ResultList<Record>> recordsFuture = new ListReadQuery(getContext(), data).executeAsync();
        recordsFuture = toSingleEntryResultList(recordsFuture);

        Function<ResultList<Record>, Record> fetchRecordFunction = new Function<ResultList<Record>, Record>() {

            @Override
            public Record apply(ResultList<Record> records) {
                Iterator<Record> it = records.iterator();
                if (it.hasNext()) {
                    Record record = it.next();

                    if (it.hasNext()) {
                        throw new TooManyResultsException(records, "more than one record exists");
                    }

                    return record;
                } else {
                    return null;
                }
            }
        };

        return Futures.transform(recordsFuture, fetchRecordFunction);
    }

    @Override
    public Publisher<Record> executeRx() {
        ListenableFuture<ResultList<Record>> recordsFuture = new ListReadQuery(getContext(), data).executeAsync();
        recordsFuture = toSingleEntryResultList(recordsFuture);

        return new ResultListPublisher<Record>(recordsFuture);
    }

    /**
     * Entity read query 
     * @param <E> the entity type
     */
    static class SingleEntityReadQuery<E> extends AbstractQuery<SingleEntityReadQuery<E>>
            implements SingleRead<E, E> {
        private final Class<E> clazz;
        private final SingleReadQuery query;

        /**
         * @param ctx    the context
         * @param query  the underlying query  
         * @param clazz  the entity type
         */
        SingleEntityReadQuery(Context ctx, SingleReadQuery query, Class<E> clazz) {
            super(ctx);
            this.query = query;
            this.clazz = clazz;
        }

        @Override
        protected SingleEntityReadQuery<E> newQuery(Context newContext) {
            return new SingleReadQuery(newContext, query.data).<E>asEntity(clazz);
        }

        @Override
        public E execute() {
            return ListenableFutures.getUninterruptibly(executeAsync());
        }

        @Override
        public ListenableFuture<E> executeAsync() {
            ListenableFuture<Record> future = query.executeAsync();

            Function<Record, E> mapEntity = new Function<Record, E>() {
                @Override
                public E apply(Record record) {
                    if (record == null) {
                        return null;
                    } else {
                        return getBeanMapper().fromValues(clazz, RecordImpl.toPropertiesSource(record),
                                getCatalog().getColumnNames(query.data.getTablename()));
                    }
                }
            };

            return Futures.transform(future, mapEntity);
        }

        @Override
        public Publisher<E> executeRx() {
            ListenableFuture<ResultList<E>> recordsFuture = new ListReadQuery(getContext(), query.data)
                    .asEntity(clazz).executeAsync();
            recordsFuture = toSingleEntryResultList(recordsFuture);

            return new ResultListPublisher<E>(recordsFuture);
        }
    }

    private static <T> ListenableFuture<ResultList<T>> toSingleEntryResultList(
            ListenableFuture<ResultList<T>> list) {

        Function<ResultList<T>, ResultList<T>> mapperFunction = new Function<ResultList<T>, ResultList<T>>() {

            @Override
            public ResultList<T> apply(ResultList<T> list) {
                return new SingleEntryResultList<>(list);
            }
        };

        return Futures.transform(list, mapperFunction);
    }

    private static final class SingleEntryResultList<T> extends ResultListAdapter<T> {

        public SingleEntryResultList(ResultList<T> list) {
            super(list);
        }

        @Override
        public FetchingIterator<T> iterator() {
            return new SingleFetchingIterator<>(super.iterator());
        }

        private final class SingleFetchingIterator<E> implements FetchingIterator<E> {

            private final FetchingIterator<E> iterator;

            public SingleFetchingIterator(FetchingIterator<E> iterator) {
                this.iterator = iterator;
            }

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

            @Override
            public E next() {
                E element = iterator.next();

                if (iterator.hasNext()) {
                    throw new TooManyResultsException(SingleEntryResultList.this, "more than one record exists");
                }

                return element;
            }

            @Override
            public int getAvailableWithoutFetching() {
                return iterator.getAvailableWithoutFetching();
            }

            @Override
            public ListenableFuture<ResultSet> fetchMoreResultsAsync() {
                return iterator.fetchMoreResultsAsync();
            }

            @Override
            public boolean isFullyFetched() {
                return iterator.isFullyFetched();
            }
        }
    }
}