LoaderContext.java

/*
 * 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.tika.config.loader;


import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.apache.tika.detect.EncodingDetector;
import org.apache.tika.exception.TikaConfigException;
import org.apache.tika.renderer.Renderer;

/**
 * Shared context passed to ComponentLoaders.
 * <p>
 * Provides access to dependencies and utilities without tight coupling.
 * Component loaders can use this to:
 * <ul>
 *   <li>Access the ClassLoader for SPI loading</li>
 *   <li>Use the ObjectMapper for JSON deserialization</li>
 *   <li>Get cross-component dependencies (e.g., EncodingDetector for parsers)</li>
 *   <li>Instantiate components via the shared ComponentInstantiator</li>
 * </ul>
 */
public class LoaderContext {

    private final ClassLoader classLoader;
    private final ObjectMapper objectMapper;
    private final DependencyProvider dependencyProvider;

    /**
     * Interface for lazy access to cross-component dependencies.
     * This avoids circular dependencies during loading.
     */
    @FunctionalInterface
    public interface DependencyProvider {
        <T> T get(Class<T> componentClass) throws TikaConfigException;
    }

    public LoaderContext(ClassLoader classLoader, ObjectMapper objectMapper,
                         DependencyProvider dependencyProvider) {
        this.classLoader = classLoader;
        this.objectMapper = objectMapper;
        this.dependencyProvider = dependencyProvider;
    }

    public ClassLoader getClassLoader() {
        return classLoader;
    }

    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    /**
     * Get a dependency by class type.
     * Uses lazy loading to avoid circular dependencies.
     *
     * @param componentClass the component class to get
     * @return the component instance
     * @throws TikaConfigException if loading fails
     */
    public <T> T get(Class<T> componentClass) throws TikaConfigException {
        return dependencyProvider.get(componentClass);
    }

    /**
     * Get the EncodingDetector for injection into parsers.
     *
     * @return the encoding detector
     * @throws TikaConfigException if loading fails
     */
    public EncodingDetector getEncodingDetector() throws TikaConfigException {
        return get(EncodingDetector.class);
    }

    /**
     * Get the Renderer for injection into rendering parsers.
     *
     * @return the renderer
     * @throws TikaConfigException if loading fails
     */
    public Renderer getRenderer() throws TikaConfigException {
        return get(Renderer.class);
    }

    /**
     * Instantiate a component by name and config.
     *
     * @param name the component name (friendly name or FQCN)
     * @param configNode the JSON configuration for the component
     * @return the instantiated component
     * @throws TikaConfigException if instantiation fails
     */
    public <T> T instantiate(String name, JsonNode configNode) throws TikaConfigException {
        return ComponentInstantiator.instantiate(name, configNode, objectMapper, classLoader);
    }
}