blob: c31b868b701c971bef57443adf2e28d264dd1320 [file] [log] [blame]
/*
* 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.directory.scim.example.spring;
import jakarta.enterprise.inject.Instance;
import jakarta.enterprise.util.TypeLiteral;
import jakarta.ws.rs.core.Application;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.directory.scim.server.provider.Provider;
import org.apache.directory.scim.server.provider.ProviderRegistry;
import org.apache.directory.scim.server.provider.SelfIdResolver;
import org.apache.directory.scim.server.rest.RequestContext;
import org.apache.directory.scim.server.rest.ScimResourceHelper;
import org.apache.directory.scim.server.rest.UserResourceImpl;
import org.apache.directory.scim.server.schema.Registry;
import org.apache.directory.scim.server.rest.EtagGenerator;
import org.apache.directory.scim.spec.extension.ScimExtensionRegistry;
import org.apache.directory.scim.spec.protocol.UserResource;
import org.apache.directory.scim.spec.resources.ScimResource;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.annotation.Annotation;
import java.util.*;
/**
* This class would be moved to a Spring-Boot module, and used to autoconfigure an application.
*/
@Configuration
public class ScimpleSpringConfiguration {
// TODO: ServerConfiguration should be configured based on application.properties?
// TODO: commented out for now, @ConditionalOnMissingBean would only work if this were an AutoConfig from another module
// @Bean
// @ConditionalOnMissingBean
// ServerConfiguration serverConfiguration() {
// return new ServerConfiguration();
// }
@Bean
@ConditionalOnMissingBean
Registry registry() {
return new Registry();
}
@Bean
@ConditionalOnMissingBean
Instance<SelfIdResolver> selfIdResolverInstance(SelfIdResolver selfIdResolver) {
return SpringInstance.of(selfIdResolver);
}
@Bean
@ConditionalOnMissingBean
ProviderRegistry providerRegistry(Registry registry, ScimExtensionRegistry scimExtensionRegistry, List<Provider<? extends ScimResource>> scimResources) {
ProviderRegistry providerRegistry = new ProviderRegistry(registry, scimExtensionRegistry, new SpringInstance<>(scimResources));
providerRegistry.configure();
return providerRegistry;
}
@Bean
@ConditionalOnMissingBean
ScimExtensionRegistry scimExtensionRegistry() {
return ScimExtensionRegistry.getInstance();
}
@Bean
@ConditionalOnMissingBean
Application jaxrsApplication() {
return new ScimpleJaxRsApplication();
}
@Bean
@ConditionalOnMissingBean
public ResourceConfig conf(Application app) {
ResourceConfig config = ResourceConfig.forApplication(app);
config.register(new AbstractBinder() {
@Override
protected void configure() {
bind(RequestContext.class).to(RequestContext.class); // needs to load from HK2 to include @Context
bind(UserResourceImpl.class).to(UserResource.class); // Used by SelfResource
// basic beans, this could also be defined as @Beans above too
bind(EtagGenerator.class).to(EtagGenerator.class);
// bind(ServerConfiguration.class).to(ServerConfiguration.class);
}
});
return config;
}
/**
* Basic JAX-RS application that includes the required SCIMple classes.
*/
static class ScimpleJaxRsApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
return new HashSet<>(ScimResourceHelper.getScimClassesToLoad());
}
}
/**
* An implementation of {@code Instance} to expose Spring beans.
* @param <T> the required bean type
*/
static class SpringInstance<T> implements Instance<T> {
private final List<T> beans;
public SpringInstance(T[] beans) {
this(Arrays.asList(beans));
}
public SpringInstance(List<T> beans) {
this.beans = beans;
}
@Override
public Instance<T> select(Annotation... qualifiers) {
throw new NotImplementedException("This implementation does not support the `select` method.");
}
@Override
public <U extends T> Instance<U> select(Class<U> subtype, Annotation... qualifiers) {
throw new NotImplementedException("This implementation does not support the `select` method.");
}
@Override
public <U extends T> Instance<U> select(TypeLiteral<U> subtype, Annotation... qualifiers) {
throw new NotImplementedException("This implementation does not support the `select` method.");
}
@Override
public boolean isUnsatisfied() {
return beans.isEmpty();
}
@Override
public boolean isAmbiguous() {
return beans.size() > 1;
}
@Override
public void destroy(T instance) {
beans.remove(instance);
}
@Override
public T get() {
if (isAmbiguous()) {
throw new IllegalStateException("Multiple bean instances found, expecting only one.");
}
return beans.stream()
.findFirst()
.orElse(null);
}
@Override
public Iterator<T> iterator() {
return beans.iterator();
}
public static <T> SpringInstance<T> of(T... beans) {
return new SpringInstance<>(beans);
}
}
}