keycloak-developers
Changes
distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json 8(+8 -0)
examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProviderFactory.java 30(+3 -27)
server-spi/pom.xml 5(+5 -0)
services/src/main/resources/META-INF/services/org.keycloak.transaction.JtaTransactionManagerLookup 1(+1 -0)
Details
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json b/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json
index 89263bf..b2f1067 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json
@@ -75,5 +75,13 @@
"default": {
"cacheContainer" : "java:comp/env/infinispan/Keycloak"
}
+ },
+
+ "jta-lookup": {
+ "provider": "${keycloak.jta.lookup.provider:jboss}",
+ "jboss" : {
+ "enabled": true
+ }
+
}
}
\ No newline at end of file
diff --git a/examples/providers/user-storage-jpa/pom.xml b/examples/providers/user-storage-jpa/pom.xml
index 75c3a6f..9702c46 100755
--- a/examples/providers/user-storage-jpa/pom.xml
+++ b/examples/providers/user-storage-jpa/pom.xml
@@ -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,14 +75,7 @@
<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>
+ <plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<configuration>
diff --git a/examples/providers/user-storage-jpa/README.md b/examples/providers/user-storage-jpa/README.md
new file mode 100755
index 0000000..f965ef2
--- /dev/null
+++ b/examples/providers/user-storage-jpa/README.md
@@ -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.
diff --git a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProviderFactory.java b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProviderFactory.java
index 4491c98..a1db65d 100644
--- a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProviderFactory.java
+++ b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProviderFactory.java
@@ -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";
}
}
server-spi/pom.xml 5(+5 -0)
diff --git a/server-spi/pom.xml b/server-spi/pom.xml
index 62a5b0d..ad1c17b 100755
--- a/server-spi/pom.xml
+++ b/server-spi/pom.xml
@@ -37,6 +37,11 @@
<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>
<scope>provided</scope>
diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java
index 344e0a0..4027ea3 100755
--- a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java
+++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java
@@ -28,7 +28,7 @@ public class UserStorageProviderSpi implements Spi {
@Override
public boolean isInternal() {
- return true;
+ return false;
}
@Override
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
index 3b0a572..45bef3c 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
@@ -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,15 +193,18 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
}
Config.Scope scope = Config.scope(spi.getName(), provider);
- factory.init(scope);
+ if (scope.getBoolean("enabled", true)) {
- if (spi.isInternal() && !isInternal(factory)) {
- logger.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName());
- }
+ factory.init(scope);
- factories.put(factory.getId(), factory);
+ if (spi.isInternal() && !isInternal(factory)) {
+ logger.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName());
+ }
- logger.debugv("Loaded SPI {0} (provider = {1})", spi.getName(), provider);
+ 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;
}
diff --git a/services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java b/services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java
new file mode 100644
index 0000000..9d0be4a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java
@@ -0,0 +1,63 @@
+/*
+ * 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.Config;
+import org.keycloak.models.KeycloakSessionFactory;
+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 JBossJtaTransactionManagerLookup implements JtaTransactionManagerLookup {
+ private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
+ private TransactionManager tm;
+
+ @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");
+ if (tm == null) {
+ logger.debug("Could not locate TransactionManager");
+ }
+ } catch (NamingException e) {
+ logger.debug("Could not load TransactionManager", e);
+ }
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ @Override
+ public String getId() {
+ return "jboss";
+ }
+}
diff --git a/services/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java b/services/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java
new file mode 100644
index 0000000..ebcf52e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java
@@ -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();
+}
diff --git a/services/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java b/services/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java
new file mode 100755
index 0000000..f45d897
--- /dev/null
+++ b/services/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java
@@ -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;
+ }
+
+}
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 55b31a0..77cba5e 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -18,4 +18,5 @@
org.keycloak.exportimport.ClientDescriptionConverterSpi
org.keycloak.wellknown.WellKnownSpi
org.keycloak.services.clientregistration.ClientRegistrationSpi
+org.keycloak.transaction.TransactionManagerLookupSpi
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.transaction.JtaTransactionManagerLookup b/services/src/main/resources/META-INF/services/org.keycloak.transaction.JtaTransactionManagerLookup
new file mode 100644
index 0000000..bc968c4
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.transaction.JtaTransactionManagerLookup
@@ -0,0 +1 @@
+org.keycloak.transaction.JBossJtaTransactionManagerLookup
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
index 16ded24..d3f87c9 100755
--- a/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
@@ -97,5 +97,13 @@
},
"scripting": {
+ },
+
+ "jta-lookup": {
+ "provider": "${keycloak.jta.lookup.provider:jboss}",
+ "jboss" : {
+ "enabled": true
+ }
+
}
}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
index c42d254..99e8614 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
@@ -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
+ }
+
}
}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java
index aa0c1b2..5a5936f 100644
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java
@@ -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));