All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openremote.manager.setup.KeycloakInitService Maven / Gradle / Ivy

/*
 * Copyright 2024, OpenRemote Inc.
 *
 * See the CONTRIBUTORS.txt file in the distribution for a
 * full listing of individual contributors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see .
 */

package org.openremote.manager.setup;

import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriBuilder;
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget;
import org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl;
import org.openremote.container.persistence.PersistenceService;
import org.openremote.container.security.keycloak.KeycloakResource;
import org.openremote.container.web.WebClient;
import org.openremote.container.web.WebTargetBuilder;
import org.openremote.model.Container;
import org.openremote.model.ContainerService;
import org.openremote.model.util.TextUtil;

import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import static jakarta.ws.rs.core.Response.Status.Family.REDIRECTION;
import static jakarta.ws.rs.core.Response.Status.Family.SUCCESSFUL;
import static org.openremote.container.security.IdentityService.OR_IDENTITY_PROVIDER;
import static org.openremote.container.security.IdentityService.OR_IDENTITY_PROVIDER_DEFAULT;
import static org.openremote.container.security.keycloak.KeycloakIdentityProvider.*;
import static org.openremote.container.util.MapAccess.getInteger;
import static org.openremote.container.util.MapAccess.getString;

/**
 * Service just to wait for keycloak availability
 *
 */
// TODO Not a great way to block startup while we wait for other services (Hystrix?)
public class KeycloakInitService implements ContainerService {

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

	private static final int PRIORITY = PersistenceService.PRIORITY - 10;

    @Override
    public void init(Container container) throws Exception {
        String identityProviderType = getString(container.getConfig(), OR_IDENTITY_PROVIDER, OR_IDENTITY_PROVIDER_DEFAULT);

        if (!identityProviderType.equalsIgnoreCase("keycloak")) {
            return;
        }

        waitForKeycloak(container);
    }

    @Override
    public void start(Container container) throws Exception {

    }

    @Override
    public void stop(Container container) throws Exception {

    }

    @Override
    public int getPriority() {
        return PRIORITY;
    }

    public static void waitForKeycloak(Container container) {
        UriBuilder keycloakServiceUri =
            UriBuilder.fromPath("/")
                .scheme("http")
                .host(getString(container.getConfig(), OR_KEYCLOAK_HOST, OR_KEYCLOAK_HOST_DEFAULT))
                .port(getInteger(container.getConfig(), OR_KEYCLOAK_PORT, OR_KEYCLOAK_PORT_DEFAULT));

        String keycloakPath = getString(container.getConfig(), OR_KEYCLOAK_PATH, OR_KEYCLOAK_PATH_DEFAULT);

        if (!TextUtil.isNullOrEmpty(keycloakPath)) {
            keycloakServiceUri.path(keycloakPath);
        }

        ResteasyClientBuilderImpl clientBuilder = new ResteasyClientBuilderImpl()
            .connectTimeout(
                getInteger(container.getConfig(), KEYCLOAK_CONNECT_TIMEOUT, KEYCLOAK_CONNECT_TIMEOUT_DEFAULT),
                TimeUnit.MILLISECONDS
            )
            .readTimeout(
                getInteger(container.getConfig(), KEYCLOAK_REQUEST_TIMEOUT, KEYCLOAK_REQUEST_TIMEOUT_DEFAULT),
                TimeUnit.MILLISECONDS
            )
            .connectionPoolSize(
                getInteger(container.getConfig(), KEYCLOAK_CLIENT_POOL_SIZE, KEYCLOAK_CLIENT_POOL_SIZE_DEFAULT)
            );
        ResteasyClient httpClient = WebClient.registerDefaults(clientBuilder).build();

        boolean keycloakAvailable = false;
        WebTargetBuilder targetBuilder = new WebTargetBuilder(httpClient, keycloakServiceUri.build());
        ResteasyWebTarget target = targetBuilder.build();
        KeycloakResource keycloakResource = target.proxy(KeycloakResource.class);

        while (!keycloakAvailable) {
            LOG.info("Connecting to Keycloak server: " + keycloakServiceUri.build());
            try {
                pingKeycloak(keycloakResource);
                keycloakAvailable = true;
	            LOG.info("Successfully connected to Keycloak server: " + keycloakServiceUri.build());
            } catch (Exception ex) {
                LOG.info("Keycloak server not available, waiting...");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    protected static void pingKeycloak(KeycloakResource resource) throws Exception {
        Response response = null;

        try {
            response = resource.getWelcomePage();
            if (response != null &&
                (response.getStatusInfo().getFamily() == SUCCESSFUL
                    || response.getStatusInfo().getFamily() == REDIRECTION)) {
                return;
            }
            throw new Exception();
        } finally {
            if (response != null)
                response.close();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy