com.github.fge.jsonschema.core.processing.CachingProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.github.fge.jsonschema.core.processing.CachingProcessor.java

Source

/*
 * Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
 *
 * This software is dual-licensed under:
 *
 * - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
 *   later version;
 * - the Apache Software License (ASL) version 2.0.
 *
 * The text of this file and of both licenses is available at the root of this
 * project or, if you have the jar distribution, in directory META-INF/, under
 * the names LGPL-3.0.txt and ASL-2.0.txt respectively.
 *
 * Direct link to the sources:
 *
 * - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
 * - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
 */

package com.github.fge.jsonschema.core.processing;

import com.github.fge.jsonschema.core.exceptions.ProcessingException;
import com.github.fge.jsonschema.core.messages.JsonSchemaCoreMessageBundle;
import com.github.fge.jsonschema.core.report.ListProcessingReport;
import com.github.fge.jsonschema.core.report.LogLevel;
import com.github.fge.jsonschema.core.report.MessageProvider;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.core.util.equivalence.Equivalences;
import com.github.fge.msgsimple.bundle.MessageBundle;
import com.github.fge.msgsimple.load.MessageBundles;
import com.google.common.base.Equivalence;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.util.concurrent.ExecutionException;

/**
 * A class caching the result of a {@link Processor}
 *
 * <p>You can use this over whichever processor of your choice. Internally, it
 * uses a {@link LoadingCache} to store results.</p>
 *
 * <p>You can optionally pass an {@link Equivalence} as an argument for cache
 * keys. By default, {@link Equivalences#equals()} will be used.</p>
 *
 * @param <IN> input type for that processor
 * @param <OUT> output type for that processor
 */
public final class CachingProcessor<IN extends MessageProvider, OUT extends MessageProvider>
        implements Processor<IN, OUT> {
    private static final MessageBundle BUNDLE = MessageBundles.getBundle(JsonSchemaCoreMessageBundle.class);

    private static final int DEFAULT_CACHE_SIZE = 512;

    /**
     * The wrapped processor
     */
    private final Processor<IN, OUT> processor;

    /**
     * The equivalence to use
     */
    private final Equivalence<IN> equivalence;

    /**
     * The cache
     */
    private final LoadingCache<Equivalence.Wrapper<IN>, ProcessingResult<OUT>> cache;

    /**
     * Constructor
     *
     * <p>This is equivalent to calling {@link #CachingProcessor(Processor,
     * Equivalence)} with {@link Equivalences#equals()} as the second argument.
     * </p>
     *
     * @param processor the processor
     */
    public CachingProcessor(final Processor<IN, OUT> processor) {
        this(processor, Equivalences.<IN>equals());
    }

    public CachingProcessor(final Processor<IN, OUT> processor, final Equivalence<IN> equivalence) {
        this(processor, equivalence, DEFAULT_CACHE_SIZE);
    }

    /**
     * Main constructor
     *
     * @param processor the processor
     * @param equivalence an equivalence to use for cache keys
     * @param cacheSize the size of the cache, zero disables it
     * @throws NullPointerException processor or equivalence
     */
    public CachingProcessor(final Processor<IN, OUT> processor, final Equivalence<IN> equivalence,
            final int cacheSize) {
        BUNDLE.checkNotNull(processor, "processing.nullProcessor");
        BUNDLE.checkNotNull(equivalence, "processing.nullEquivalence");
        BUNDLE.checkArgument(cacheSize >= -1, "processing.invalidCacheSize");
        this.processor = processor;
        this.equivalence = equivalence;
        CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
        if (cacheSize != -1) {
            builder.maximumSize(cacheSize);
        }
        cache = builder.build(loader());
    }

    @Override
    public OUT process(final ProcessingReport report, final IN input) throws ProcessingException {
        final ProcessingResult<OUT> result;
        try {
            result = cache.get(equivalence.wrap(input));
        } catch (ExecutionException e) {
            throw (ProcessingException) e.getCause();
        }
        report.mergeWith(result.getReport());
        return result.getResult();
    }

    private CacheLoader<Equivalence.Wrapper<IN>, ProcessingResult<OUT>> loader() {
        return new CacheLoader<Equivalence.Wrapper<IN>, ProcessingResult<OUT>>() {
            @Override
            public ProcessingResult<OUT> load(final Equivalence.Wrapper<IN> key) throws ProcessingException {
                final IN input = key.get();
                final ListProcessingReport report = new ListProcessingReport(LogLevel.DEBUG, LogLevel.NONE);
                return ProcessingResult.of(processor, report, input);
            }
        };
    }

    @Override
    public String toString() {
        return "CACHED[" + processor + ']';
    }
}