[[_extensions]] == Extending the Server The {project_name} SPI framework offers the possibility to implement or override particular built-in providers. However {project_name} also provides capabilities to extend it's core functionalities and domain. This includes possibilities to: * Add custom REST endpoints to the {project_name} server * Add your own custom SPI * Add custom JPA entities to the {project_name} data model [[_extensions_rest]] === Add custom REST endpoints This is a very powerful extension, which allows you to deploy your own REST endpoints to the {project_name} server. It enables all kinds of extensions, for example the possibility to trigger functionality on the {project_name} server, which is not available through the default set of built-in {project_name} REST endpoints. To add a custom REST endpoint, you need to implement the `RealmResourceProviderFactory` and `RealmResourceProvider` interfaces. `RealmResourceProvider` has one important method: [source,java] ---- Object getResource(); ---- which allows you to return an object, which acts as a https://github.com/jax-rs[JAX-RS Resource]. For more details, see the Javadoc and our examples. There is a very simple example in the example distribution in `providers/rest` and there is a more advanced example in `providers/domain-extension`, which shows how to add an authenticated REST endpoint and other functionalities like <> or <>. For details on how to package and deploy a custom provider, refer to the <> chapter. [[_extensions_spi]] === Add your own custom SPI This is useful especially with the <>. To add your own kind of SPI, you need to implement the interface `org.keycloak.provider.Spi` and define the ID of your SPI and the `ProviderFactory` and `Provider` classes. That looks like this: [source,java] ---- package org.keycloak.examples.domainextension.spi; import ... public class ExampleSpi implements Spi { @Override public boolean isInternal() { return false; } @Override public String getName() { return "example"; } @Override public Class getProviderClass() { return ExampleService.class; } @Override @SuppressWarnings("rawtypes") public Class getProviderFactoryClass() { return ExampleServiceProviderFactory.class; } } ---- Then you need to create the file `META-INF/services/org.keycloak.provider.Spi` and add the class of your SPI to it. For example: [source] ---- org.keycloak.examples.domainextension.spi.ExampleSpi ---- The next step is to create the interfaces `ExampleServiceProviderFactory`, which extends from `ProviderFactory` and `ExampleService`, which extends from `Provider`. The `ExampleService` will usually contain the business methods you need for your use case. Note that the `ExampleServiceProviderFactory` instance is always scoped per application, however `ExampleService` is scoped per-request (or more accurately per `KeycloakSession` lifecycle). Finally you need to implement your providers in the same manner as described in the <> chapter. For more details, take a look at the example distribution at `providers/domain-extension`, which shows an Example SPI similar to the one above. [[_extensions_jpa]] === Add custom JPA entities to the {project_name} data model If the {project_name} data model does not exactly match your desired solution, or if you want to add some core functionality to {project_name}, or when you have your own REST endpoint, you might want to extend the {project_name} data model. We enable you to add your own JPA entities to the {project_name} JPA `EntityManager` . To add your own JPA entities, you need to implement `JpaEntityProviderFactory` and `JpaEntityProvider`. The `JpaEntityProvider` allows you to return a list of your custom JPA entities and provide the location and id of the liquibase changelog. An example implementation can look like this: [source,java] ---- public class ExampleJpaEntityProvider implements JpaEntityProvider { // List of your JPA entities. @Override public List> getEntities() { return Collections.>singletonList(Company.class); } // This is used to return the location of the Liquibase changelog file. // You can return null if you don't want Liquibase to create and update the DB schema. @Override public String getChangelogLocation() { return "META-INF/example-changelog.xml"; } // Helper method, which will be used internally by Liquibase. @Override public String getFactoryId() { return "sample"; } ... } ---- In the example above, we added a single JPA entity represented by class `Company`. In the code of your REST endpoint, you can then use something like this to retrieve `EntityManager` and call DB operations on it. [source,java] ---- EntityManager em = session.getProvider(JpaConnectionProvider.class).getEntityManager(); Company myCompany = em.find(Company.class, "123"); ---- The methods `getChangelogLocation` and `getFactoryId` are important to support automatic updating of your entities by Liquibase. http://www.liquibase.org/[Liquibase] is a framework for updating the database schema, which {project_name} internally uses to create the DB schema and update the DB schema among versions. You may need to use it as well and create a changelog for your entities. Note that versioning of your own liquibase changelog is independent of {project_name} versions. In other words, when you update to a new {project_name} version, you are not forced to update your schema at the same time. And vice versa, you can update your schema even without updating the {project_name} version. The Liquibase update is always done at the server startup, so to trigger a DB update of your schema, you just need to add the new changeset to your liquibase changelog file (in the example above it's the file `META-INF/example-changelog.xml` (which must be packed in same JAR as the JPA entities and `ExampleJpaEntityProvider`) and then restart server. The DB schema will be automatically updated at startup. For more details, take a look at the example distribution at example `providers/domain-extension`, which shows the `ExampleJpaEntityProvider` and `example-changelog.xml` described above. NOTE: Don't forget to always backup your database before doing any changes in the Liquibase changelog and triggering a DB update.