HibernateRepositoryResolver.java

/*******************************************************************************
 * Copyright (c) 2026 Carsten Hammer.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Carsten Hammer
 *******************************************************************************/
package org.eclipse.jgit.server.resolver;

import java.io.IOException;
import java.util.Objects;

import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.server.repository.CopiedHibernateRepositoryService;
import org.eclipse.jgit.server.repository.SandboxRepositoryService;
import org.eclipse.jgit.storage.hibernate.config.HibernateSessionFactoryProvider;
import org.eclipse.jgit.transport.resolver.RepositoryResolver;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.hibernate.SessionFactory;

import jakarta.servlet.http.HttpServletRequest;

/** Resolves Smart HTTP repositories through the application-owned service boundary. */
public class HibernateRepositoryResolver
		implements RepositoryResolver<HttpServletRequest>, AutoCloseable {

	private final HibernateSessionFactoryProvider sessionFactoryProvider;
	private final SandboxRepositoryService repositories;
	private final boolean ownsRepositoryService;

	/** Creates a resolver that owns a copied adapter for the native session factory. */
	public HibernateRepositoryResolver(SessionFactory sessionFactory) {
		this(null, new CopiedHibernateRepositoryService(sessionFactory), true);
	}

	/**
	 * Creates a resolver using and owning the temporary copied-backend adapter.
	 *
	 * @deprecated application wiring should pass its application-owned
	 *             {@link SandboxRepositoryService}
	 */
	@Deprecated(forRemoval = true)
	public HibernateRepositoryResolver(HibernateSessionFactoryProvider sessionFactoryProvider) {
		this(sessionFactoryProvider, new CopiedHibernateRepositoryService(sessionFactoryProvider), true);
	}

	/**
	 * Creates a resolver for an application-owned repository service without
	 * taking lifecycle ownership.
	 */
	public HibernateRepositoryResolver(SandboxRepositoryService repositories) {
		this(null, repositories, false);
	}

	private HibernateRepositoryResolver(HibernateSessionFactoryProvider sessionFactoryProvider,
			SandboxRepositoryService repositories, boolean ownsRepositoryService) {
		this.sessionFactoryProvider= sessionFactoryProvider;
		this.repositories= Objects.requireNonNull(repositories, "repositories"); //$NON-NLS-1$
		this.ownsRepositoryService= ownsRepositoryService;
	}

	@Override
	public Repository open(HttpServletRequest request, String name) throws ServiceNotEnabledException {
		try {
			return repositories.openOrCreate(name);
		} catch (IOException | RuntimeException exception) {
			throw new ServiceNotEnabledException(exception.getMessage());
		}
	}

	/**
	 * Transitional source-compatible access for existing server tests and callers.
	 * New application code must use {@link #getRepositoryService()} and the public
	 * {@link Repository} type. The generic return erases to {@code Repository} and
	 * introduces no copied-backend type into this resolver.
	 */
	@Deprecated(forRemoval = true)
	@SuppressWarnings("unchecked")
	public <R extends Repository> R getOrCreateRepository(String name) throws IOException {
		return (R) repositories.openOrCreate(name);
	}

	/** Returns whether this resolver already owns an open repository handle. */
	public boolean hasRepository(String name) {
		return repositories.isOpen(name);
	}

	/** Returns the application-owned repository service used by REST resources. */
	public SandboxRepositoryService getRepositoryService() {
		return repositories;
	}

	/**
	 * Returns the legacy provider while Search and analytics still use the copied
	 * implementation. Repository lifecycle code must use {@link #getRepositoryService()}.
	 */
	@Deprecated(forRemoval = true)
	public HibernateSessionFactoryProvider getSessionFactoryProvider() {
		if (sessionFactoryProvider == null) {
			throw new IllegalStateException("This resolver was created without a legacy session factory provider."); //$NON-NLS-1$
		}
		return sessionFactoryProvider;
	}

	@Override
	public void close() {
		if (ownsRepositoryService) {
			repositories.close();
		}
	}
}