diff --git a/topics/providers.adoc b/topics/providers.adoc index 619f67bf7c..0b028c3c39 100755 --- a/topics/providers.adoc +++ b/topics/providers.adoc @@ -175,12 +175,29 @@ public class MyEventListenerProviderFactory implements EventListenerProviderFact === Registering provider implementations -Keycloak can load provider implementations from JBoss Modules or directly from the file-system. +There are two ways to register provider implementations. The easiest way is to just throw your provider jar within +the Keycloak `deploy/` directory. Keycloak supports hot deployment as well in this scenario. This is also the best +solution. + +The alternative is not really recommended, but exists for legacy purposes as the Keycloak deployer didn't exist in +previous versions of the project. Keycloak can load provider implementations from JBoss Modules or directly from the file-system. Using Modules is recommended as you can control exactly what classes are available to your provider. -Any providers loaded from the file-system uses a classloader with the Keycloak classloader as its parent. +Any providers loaded from the file-system uses a classloader with the Keycloak classloader as its parent. + +==== Using the Keycloak Deployer + +If you throw your provider jar within the Keycloak `deploy/` directory, your provider will automatically be deployed. +Hot deployment works too. Additionally, your provider jar works similarly to other components deployed in a JBoss/Wildfly +environment in that they can use facilities like the `jboss-deployment-structure.xml` file. This file allows you to +set up dependencies on other components and load third-party jars and modules. + +Provider jars can also be contained within other deployable units like EARs and WARs. Deploying with a EAR actually makes +it really easy to use third party jars as you can just put these libraries in the EAR's `lib/` directory. ==== Register a provider using Modules +WARNING: We don't recommend this approach. + To register a provider using Modules first create a module. To do this you can either use the jboss-cli script or manually create a folder inside `KEYCLOAK_HOME/modules` and add your jar and a `module.xml`. For example to add the event listener sysout example provider using the `jboss-cli` script execute: @@ -275,7 +292,83 @@ For example to disable the Infinispan user cache provider add: ----- +---- + +=== Leveraging Java EE + +The can be packaged within any Java EE component so long as you set up the `META-INF/services` +file correctly to point to your providers. For example, if your provider needs to use third party libraries, you +can package up your provider within an ear and store these third pary libraries in the ear's `lib/` directory. +Also note that provider jars can make use of the `jboss-deployment-structure.xml` file that EJBs, WARS, and EARs +can use in a JBoss/Wildfly environment. See the JBoss/Wildfly documentation for more details on this file. It +allows you to pull in external dependencies among other fine grain actions. + +`ProviderFactory` implementations are required to be plain java objects. But, we also currently support +implementing provider classes as Stateful EJBs. TThis is how you would do it: + +[source,java] +---- +@Stateful +@Local(EjbExampleUserStorageProvider.class) +public class EjbExampleUserStorageProvider implements UserStorageProvider, + UserLookupProvider, + UserRegistrationProvider, + UserQueryProvider, + CredentialInputUpdater, + CredentialInputValidator, + OnUserCache +{ + @PersistenceContext + protected EntityManager em; + + protected ComponentModel model; + protected KeycloakSession session; + + public void setModel(ComponentModel model) { + this.model = model; + } + + public void setSession(KeycloakSession session) { + this.session = session; + } + + + @Remove + @Override + public void close() { + } +... +} +---- + +You have to define the `@Local` annotation and specify your provider class there. If you don't do this, EJB will +not proxy the provider instance correctly and your provider won't work. + +You must put the `@Remove` annotation on the `close()` method of your provider. If you don't, the stateful bean +will never be cleaned up and you may eventually see error messages. + +Implementations of `ProviderFactory` are required to be plain java objects. Your factory class would +perform a JNDI lookup of the Stateful EJB in its create() method. + +[source,java] +---- +public class EjbExampleUserStorageProviderFactory + implements UserStorageProviderFactory { + + @Override + public EjbExampleUserStorageProvider create(KeycloakSession session, ComponentModel model) { + try { + InitialContext ctx = new InitialContext(); + EjbExampleUserStorageProvider provider = (EjbExampleUserStorageProvider)ctx.lookup( + "java:global/user-storage-jpa-example/" + EjbExampleUserStorageProvider.class.getSimpleName()); + provider.setModel(model); + provider.setSession(session); + return provider; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +---- === Available SPIs