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 89263bf4e59..b2f106792ab 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/README.md b/examples/providers/user-storage-jpa/README.md
new file mode 100755
index 00000000000..f965ef254ab
--- /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/pom.xml b/examples/providers/user-storage-jpa/pom.xml
index 75c3a6f9808..9702c46af08 100755
--- a/examples/providers/user-storage-jpa/pom.xml
+++ b/examples/providers/user-storage-jpa/pom.xml
@@ -23,7 +23,7 @@
2.1.0-SNAPSHOT
- Properties Authentication Provider Example
+ User Storage JPA Provider Exapmle
4.0.0
@@ -75,14 +75,7 @@
1.8
-
- org.jboss.as.plugins
- jboss-as-maven-plugin
-
- false
-
-
-
+
org.wildfly.plugins
wildfly-maven-plugin
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 4491c98f195..a1db65d1b80 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 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 getConfigProperties() {
- return OPTIONS;
- }
-
-
-
- @Override
- public void close() {
-
+ public String getHelpText() {
+ return "JPA Example User Storage Provider";
}
}
diff --git a/server-spi/pom.xml b/server-spi/pom.xml
index 62a5b0d82f8..ad1c17b79b1 100755
--- a/server-spi/pom.xml
+++ b/server-spi/pom.xml
@@ -36,6 +36,11 @@
+
+ org.jboss.spec.javax.transaction
+ jboss-transaction-api_1.2_spec
+ provided
+
org.jboss.resteasy
resteasy-jaxrs
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 344e0a06808..4027ea3942b 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 3b0a572396a..45bef3c3ea4 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, String> provider = new HashMap<>();
private volatile Map, Map> factoriesMap = new HashMap<>();
protected CopyOnWriteArrayList 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, Map> getFactoriesCopy() {
Map, Map> 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);
+
+ if (spi.isInternal() && !isInternal(factory)) {
+ logger.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName());
+ }
+
+ factories.put(factory.getId(), factory);
+
+ 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/services/JtaRegistration.java b/services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java
similarity index 72%
rename from services/src/main/java/org/keycloak/services/JtaRegistration.java
rename to services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java
index cdd6e94d632..9d0be4a46eb 100644
--- a/services/src/main/java/org/keycloak/services/JtaRegistration.java
+++ b/services/src/main/java/org/keycloak/transaction/JBossJtaTransactionManagerLookup.java
@@ -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 Bill Burke
* @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";
}
}
diff --git a/services/src/main/java/org/keycloak/transaction/JtaRegistration.java b/services/src/main/java/org/keycloak/transaction/JtaRegistration.java
new file mode 100644
index 00000000000..1800c52c47c
--- /dev/null
+++ b/services/src/main/java/org/keycloak/transaction/JtaRegistration.java
@@ -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 Bill Burke
+ * @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));
+ }
+}
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 00000000000..ebcf52ec880
--- /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 Bill Burke
+ * @version $Revision: 1 $
+ */
+public interface JtaTransactionManagerLookup extends Provider, ProviderFactory {
+ @Override
+ default void close() {
+
+ }
+
+ @Override
+ default JtaTransactionManagerLookup create(KeycloakSession session) {
+ return this;
+ }
+
+ TransactionManager getTransactionManager();
+}
diff --git a/services/src/main/java/org/keycloak/services/JtaTransactionWrapper.java b/services/src/main/java/org/keycloak/transaction/JtaTransactionWrapper.java
similarity index 98%
rename from services/src/main/java/org/keycloak/services/JtaTransactionWrapper.java
rename to services/src/main/java/org/keycloak/transaction/JtaTransactionWrapper.java
index 5eb1233c26b..ecc3071a0d0 100644
--- a/services/src/main/java/org/keycloak/services/JtaTransactionWrapper.java
+++ b/services/src/main/java/org/keycloak/transaction/JtaTransactionWrapper.java
@@ -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;
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 00000000000..f45d897ee46
--- /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 Stian Thorgersen
+ */
+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/java/org/keycloak/services/UserTransactionWrapper.java b/services/src/main/java/org/keycloak/transaction/UserTransactionWrapper.java
similarity index 98%
rename from services/src/main/java/org/keycloak/services/UserTransactionWrapper.java
rename to services/src/main/java/org/keycloak/transaction/UserTransactionWrapper.java
index a1063210ee9..c838b188c48 100644
--- a/services/src/main/java/org/keycloak/services/UserTransactionWrapper.java
+++ b/services/src/main/java/org/keycloak/transaction/UserTransactionWrapper.java
@@ -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;
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 55b31a09f9a..77cba5e1efb 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 00000000000..bc968c4cb5d
--- /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-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 c42d254a3f3..99e8614d0ed 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/testsuite/integration/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration/src/test/resources/META-INF/keycloak-server.json
index 16ded246e31..d3f87c98d1e 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/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 aa0c1b28ccf..5a5936f9a01 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));