jta transaction abstraction
This commit is contained in:
parent
f14f303dfe
commit
83306963e8
18 changed files with 224 additions and 63 deletions
|
@ -75,5 +75,13 @@
|
|||
"default": {
|
||||
"cacheContainer" : "java:comp/env/infinispan/Keycloak"
|
||||
}
|
||||
},
|
||||
|
||||
"jta-lookup": {
|
||||
"provider": "${keycloak.jta.lookup.provider:jboss}",
|
||||
"jboss" : {
|
||||
"enabled": true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
13
examples/providers/user-storage-jpa/README.md
Executable file
13
examples/providers/user-storage-jpa/README.md
Executable file
|
@ -0,0 +1,13 @@
|
|||
Example User Storage Provider with EJB and JPA
|
||||
===================================================
|
||||
|
||||
This is an example of the User Storage SPI implemented using EJB and JPA. To deploy this provider you must have Keycloak
|
||||
running in standalone or standalone-ha mode. Then type the follow maven command:
|
||||
|
||||
mvn clean install wildfly:deploy
|
||||
|
||||
Login and go to the User Federation tab and you should now see your deployed provider in the add-provider list box.
|
||||
Add the provider, save it, then any new user you create will be stored and in the custom store you implemented. You
|
||||
can modify the example and hot deploy it using the above maven command again.
|
||||
|
||||
This example uses the built in in-memory datasource that comes with keycloak: ExampleDS.
|
|
@ -23,7 +23,7 @@
|
|||
<version>2.1.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<name>Properties Authentication Provider Example</name>
|
||||
<name>User Storage JPA Provider Exapmle</name>
|
||||
<description/>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
@ -75,13 +75,6 @@
|
|||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jboss.as.plugins</groupId>
|
||||
<artifactId>jboss-as-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>false</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.wildfly.plugins</groupId>
|
||||
<artifactId>wildfly-maven-plugin</artifactId>
|
||||
|
|
|
@ -49,35 +49,11 @@ public class EjbExampleUserStorageProviderFactory implements UserStorageProvider
|
|||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "example-user-storage";
|
||||
return "example-user-storage-jpa";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
}
|
||||
|
||||
static List<ProviderConfigProperty> OPTIONS = new LinkedList<>();
|
||||
static {
|
||||
ProviderConfigProperty prop = new ProviderConfigProperty("propertyFile", "Property File", "file that contains name value pairs", ProviderConfigProperty.STRING_TYPE, null);
|
||||
OPTIONS.add(prop);
|
||||
prop = new ProviderConfigProperty("federatedStorage", "User Federated Storage", "use federated storage?", ProviderConfigProperty.BOOLEAN_TYPE, null);
|
||||
OPTIONS.add(prop);
|
||||
|
||||
}
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return OPTIONS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
public String getHelpText() {
|
||||
return "JPA Example User Storage Provider";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,11 @@
|
|||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.spec.javax.transaction</groupId>
|
||||
<artifactId>jboss-transaction-api_1.2_spec</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
|
|
|
@ -28,7 +28,7 @@ public class UserStorageProviderSpi implements Spi {
|
|||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -28,15 +28,16 @@ import org.keycloak.provider.ProviderManager;
|
|||
import org.keycloak.provider.ProviderManagerDeployer;
|
||||
import org.keycloak.provider.ProviderManagerRegistry;
|
||||
import org.keycloak.provider.Spi;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
import org.keycloak.transaction.JtaRegistration;
|
||||
import org.keycloak.transaction.JtaTransactionManagerLookup;
|
||||
import org.keycloak.transaction.JtaTransactionWrapper;
|
||||
|
||||
import java.util.Collections;
|
||||
import javax.transaction.TransactionManager;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
|
@ -48,7 +49,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
|||
private Map<Class<? extends Provider>, String> provider = new HashMap<>();
|
||||
private volatile Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<>();
|
||||
protected CopyOnWriteArrayList<ProviderEventListener> listeners = new CopyOnWriteArrayList<>();
|
||||
private JtaRegistration jta;
|
||||
private TransactionManager tm;
|
||||
|
||||
// TODO: Likely should be changed to int and use Time.currentTime() to be compatible with all our "time" reps
|
||||
protected long serverStartupTimestamp;
|
||||
|
@ -72,7 +73,6 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
|||
|
||||
public void init() {
|
||||
serverStartupTimestamp = System.currentTimeMillis();
|
||||
jta = new JtaRegistration();
|
||||
|
||||
ProviderManager pm = new ProviderManager(getClass().getClassLoader(), Config.scope().getArray("providers"));
|
||||
spis.addAll(pm.loadSpis());
|
||||
|
@ -96,6 +96,9 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
|||
}
|
||||
// make the session factory ready for hot deployment
|
||||
ProviderManagerRegistry.SINGLETON.setDeployer(this);
|
||||
|
||||
JtaTransactionManagerLookup lookup = (JtaTransactionManagerLookup)getProviderFactory(JtaTransactionManagerLookup.class);
|
||||
if (lookup != null) tm = lookup.getTransactionManager();
|
||||
}
|
||||
protected Map<Class<? extends Provider>, Map<String, ProviderFactory>> getFactoriesCopy() {
|
||||
Map<Class<? extends Provider>, Map<String, ProviderFactory>> copy = new HashMap<>();
|
||||
|
@ -190,6 +193,8 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
|||
}
|
||||
|
||||
Config.Scope scope = Config.scope(spi.getName(), provider);
|
||||
if (scope.getBoolean("enabled", true)) {
|
||||
|
||||
factory.init(scope);
|
||||
|
||||
if (spi.isInternal() && !isInternal(factory)) {
|
||||
|
@ -199,6 +204,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
|||
factories.put(factory.getId(), factory);
|
||||
|
||||
logger.debugv("Loaded SPI {0} (provider = {1})", spi.getName(), provider);
|
||||
}
|
||||
} else {
|
||||
for (ProviderFactory factory : pm.load(spi)) {
|
||||
Config.Scope scope = Config.scope(spi.getName(), factory.getId());
|
||||
|
@ -276,7 +282,9 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
|
|||
|
||||
public KeycloakSession create() {
|
||||
KeycloakSession session = new DefaultKeycloakSession(this);
|
||||
jta.begin(session);
|
||||
if (tm != null) {
|
||||
session.getTransactionManager().enlist(new JtaTransactionWrapper(tm));
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,25 +14,31 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.services;
|
||||
package org.keycloak.transaction;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.transaction.Transaction;
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class JtaRegistration {
|
||||
public class JBossJtaTransactionManagerLookup implements JtaTransactionManagerLookup {
|
||||
private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
|
||||
|
||||
private TransactionManager tm;
|
||||
|
||||
public JtaRegistration() {
|
||||
@Override
|
||||
public TransactionManager getTransactionManager() {
|
||||
return tm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
try {
|
||||
InitialContext ctx = new InitialContext();
|
||||
tm = (TransactionManager)ctx.lookup("java:jboss/TransactionManager");
|
||||
|
@ -45,9 +51,13 @@ public class JtaRegistration {
|
|||
|
||||
}
|
||||
|
||||
public void begin(KeycloakSession session) {
|
||||
if (tm == null) return;
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
session.getTransactionManager().enlist(new JtaTransactionWrapper(tm));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "jboss";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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.keycloak.transaction;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.services.ServicesLogger;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class JtaRegistration {
|
||||
|
||||
|
||||
|
||||
public void begin(KeycloakSession session) {
|
||||
TransactionManager tm = session.getProvider(JtaTransactionManagerLookup.class).getTransactionManager();
|
||||
if (tm == null) return;
|
||||
|
||||
session.getTransactionManager().enlist(new JtaTransactionWrapper(tm));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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.keycloak.transaction;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
import javax.transaction.TransactionManager;
|
||||
|
||||
/**
|
||||
* JTA TransactionManager lookup
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface JtaTransactionManagerLookup extends Provider, ProviderFactory<JtaTransactionManagerLookup> {
|
||||
@Override
|
||||
default void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
default JtaTransactionManagerLookup create(KeycloakSession session) {
|
||||
return this;
|
||||
}
|
||||
|
||||
TransactionManager getTransactionManager();
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.services;
|
||||
package org.keycloak.transaction;
|
||||
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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.keycloak.transaction;
|
||||
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class TransactionManagerLookupSpi implements Spi {
|
||||
|
||||
@Override
|
||||
public boolean isInternal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "jta-lookup";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Provider> getProviderClass() {
|
||||
return JtaTransactionManagerLookup.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||
return JtaTransactionManagerLookup.class;
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.services;
|
||||
package org.keycloak.transaction;
|
||||
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
|
|
@ -18,4 +18,5 @@
|
|||
org.keycloak.exportimport.ClientDescriptionConverterSpi
|
||||
org.keycloak.wellknown.WellKnownSpi
|
||||
org.keycloak.services.clientregistration.ClientRegistrationSpi
|
||||
org.keycloak.transaction.TransactionManagerLookupSpi
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.transaction.JBossJtaTransactionManagerLookup
|
|
@ -130,5 +130,13 @@
|
|||
"hostname-verification-policy": "${keycloak.truststore.policy:WILDCARD}",
|
||||
"disabled": "${keycloak.truststore.disabled:false}"
|
||||
}
|
||||
},
|
||||
|
||||
"jta-lookup": {
|
||||
"provider": "${keycloak.jta.lookup.provider:jboss}",
|
||||
"jboss" : {
|
||||
"enabled": true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,5 +97,13 @@
|
|||
},
|
||||
|
||||
"scripting": {
|
||||
},
|
||||
|
||||
"jta-lookup": {
|
||||
"provider": "${keycloak.jta.lookup.provider:jboss}",
|
||||
"jboss" : {
|
||||
"enabled": true
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -61,8 +61,6 @@ public class KeycloakProviderDependencyProcessor implements DeploymentUnitProces
|
|||
|
||||
if (!isKeycloakProviderDeployment(deploymentUnit)) return;
|
||||
|
||||
logger.info("FOUND KEYCLOAK PROVIDER DEPLOYMENT!!!!: " + deploymentUnit.getName());
|
||||
|
||||
final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
|
||||
final ModuleLoader moduleLoader = Module.getBootModuleLoader();
|
||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false));
|
||||
|
|
Loading…
Reference in a new issue