keycloak-uncached

Merge pull request #3117 from patriot1burke/master deployer,

8/8/2016 6:46:35 PM

Changes

examples/providers/user-storage-jpa/assembly.xml 45(+0 -45)

examples/providers/user-storage-jpa/module.xml 40(+0 -40)

pom.xml 6(+6 -0)

services/pom.xml 4(+4 -0)

Details

diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems.xml
index d52c9a4..e5dc8ec 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems.xml
@@ -20,6 +20,7 @@
 <config>
    <subsystems>
       <subsystem>logging.xml</subsystem>
+      <subsystem>deployment-scanner.xml</subsystem>
       <subsystem>bean-validation.xml</subsystem>
       <subsystem supplement="default">keycloak-datasources.xml</subsystem>
       <subsystem>ee.xml</subsystem>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml
index b96cb8a..d9345a1 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml
@@ -20,6 +20,7 @@
 <config>
     <subsystems>
         <subsystem>logging.xml</subsystem>
+        <subsystem>deployment-scanner.xml</subsystem>
         <subsystem>bean-validation.xml</subsystem>
         <subsystem supplement="default">keycloak-datasources.xml</subsystem>
         <subsystem>ee.xml</subsystem>
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/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
index 42042e6..e769a7b 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml
@@ -68,5 +68,6 @@
         <module name="javax.activation.api"/>
         <module name="org.apache.httpcomponents"/>
         <module name="org.twitter4j"/>
+        <module name="javax.transaction.api"/>
     </dependencies>
 </module>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml
index 62b2616..9f44e59 100644
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml
@@ -40,6 +40,7 @@
         <module name="org.jboss.as.web-common" optional="true"/>
         <module name="org.jboss.as.web" optional="true"/>
         <module name="org.jboss.as.version" optional="true"/>
+        <module name="org.keycloak.keycloak-services"/>
         <module name="org.keycloak.keycloak-wildfly-adapter" optional="true"/>
         <module name="org.jboss.metadata"/>
     </dependencies>
diff --git a/distribution/server-dist/assembly.xml b/distribution/server-dist/assembly.xml
index c8dd06c..9e24567 100755
--- a/distribution/server-dist/assembly.xml
+++ b/distribution/server-dist/assembly.xml
@@ -44,8 +44,6 @@
                 <exclude>welcome-content/**</exclude>
                 <exclude>appclient</exclude>
                 <exclude>appclient/**</exclude>
-                <exclude>standalone/deployments</exclude>
-                <exclude>standalone/deployments/*</exclude>
                 <exclude>copyright.txt</exclude>
                 <exclude>README.txt</exclude>
             </excludes>
diff --git a/examples/providers/user-storage-jpa/pom.xml b/examples/providers/user-storage-jpa/pom.xml
index c1c38d2..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>
 
@@ -42,11 +42,6 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-jpa</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>org.jboss.logging</groupId>
             <artifactId>jboss-logging</artifactId>
             <scope>provided</scope>
@@ -61,6 +56,12 @@
             <artifactId>hibernate-entitymanager</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.jboss.spec.javax.ejb</groupId>
+            <artifactId>jboss-ejb-api_3.2_spec</artifactId>
+            <version>1.0.0.Final</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>
@@ -74,29 +75,12 @@
                     <target>1.8</target>
                 </configuration>
             </plugin>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-assembly-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>assemble</id>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>single</goal>
-                        </goals>
-                        <configuration>
-                            <descriptors>
-                                <descriptor>assembly.xml</descriptor>
-                            </descriptors>
-                            <recompressZippedFiles>true</recompressZippedFiles>
-                            <finalName>modules</finalName>
-                            <appendAssemblyId>false</appendAssemblyId>
-                            <outputDirectory>${project.build.directory}</outputDirectory>
-                            <workDirectory>${project.build.directory}/assembly/work</workDirectory>
-                            <tarLongFileMode>gnu</tarLongFileMode>
-                        </configuration>
-                    </execution>
-                </executions>
+             <plugin>
+                <groupId>org.wildfly.plugins</groupId>
+                <artifactId>wildfly-maven-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
             </plugin>
         </plugins>
     </build>
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/UserAdapter.java b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserAdapter.java
index f4ff5a7..6276f7f 100644
--- a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserAdapter.java
+++ b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserAdapter.java
@@ -22,7 +22,6 @@ import org.keycloak.component.ComponentModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserCredentialModel;
-import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.storage.StorageId;
 import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
 
@@ -35,7 +34,7 @@ import java.util.Map;
  * @version $Revision: 1 $
  */
 public class UserAdapter extends AbstractUserAdapterFederatedStorage {
-    private static final Logger logger = Logger.getLogger(ExampleUserStorageProvider.class);
+    private static final Logger logger = Logger.getLogger(EjbExampleUserStorageProvider.class);
     protected UserEntity entity;
     protected String keycloakId;
 
diff --git a/examples/providers/user-storage-jpa/src/main/resources/META-INF/persistence.xml b/examples/providers/user-storage-jpa/src/main/resources/META-INF/persistence.xml
index d082c23..9894af4 100644
--- a/examples/providers/user-storage-jpa/src/main/resources/META-INF/persistence.xml
+++ b/examples/providers/user-storage-jpa/src/main/resources/META-INF/persistence.xml
@@ -4,9 +4,8 @@
              xsi:schemaLocation="
         http://java.sun.com/xml/ns/persistence
         http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
-    <persistence-unit name="user-storage-jpa-example" transaction-type="RESOURCE_LOCAL">
-        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
-        <non-jta-data-source>java:jboss/datasources/ExampleDS</non-jta-data-source>
+    <persistence-unit name="user-storage-jpa-example">
+        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
 
         <class>org.keycloak.examples.storage.user.UserEntity</class>
 
diff --git a/examples/providers/user-storage-jpa/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory b/examples/providers/user-storage-jpa/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
index 1634d11..8f92432 100644
--- a/examples/providers/user-storage-jpa/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
+++ b/examples/providers/user-storage-jpa/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory
@@ -1 +1 @@
-org.keycloak.examples.storage.user.ExampleUserStorageProviderFactory
\ No newline at end of file
+org.keycloak.examples.storage.user.EjbExampleUserStorageProviderFactory
\ No newline at end of file

pom.xml 6(+6 -0)

diff --git a/pom.xml b/pom.xml
index 77d3678..c106063 100755
--- a/pom.xml
+++ b/pom.xml
@@ -63,6 +63,7 @@
         <jboss.logging.tools.version>2.0.1.Final</jboss.logging.tools.version>
         <jboss.logging.tools.wf8.version>1.2.0.Final</jboss.logging.tools.wf8.version>
         <jboss-jaxrs-api_2.0_spec>1.0.0.Final</jboss-jaxrs-api_2.0_spec>
+        <jboss-transaction-api_1.2_spec>1.0.0.Final</jboss-transaction-api_1.2_spec>
         <jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version>1.0.4.Final</jboss.spec.javax.xml.bind.jboss-jaxb-api_2.2_spec.version>
         <log4j.version>1.2.16</log4j.version>
         <resteasy.version>3.0.14.Final</resteasy.version>
@@ -228,6 +229,11 @@
                 <version>${javax.mail.version}</version>
             </dependency>
             <dependency>
+                <groupId>org.jboss.spec.javax.transaction</groupId>
+                <artifactId>jboss-transaction-api_1.2_spec</artifactId>
+                <version>${jboss-transaction-api_1.2_spec}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.jboss.spec.javax.ws.rs</groupId>
                 <artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
                 <version>${jboss-jaxrs-api_2.0_spec}</version>
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

services/pom.xml 4(+4 -0)

diff --git a/services/pom.xml b/services/pom.xml
index 180c16b..3ae57e3 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -110,6 +110,10 @@
             <artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.jboss.spec.javax.transaction</groupId>
+            <artifactId>jboss-transaction-api_1.2_spec</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.jboss.resteasy</groupId>
             <artifactId>resteasy-multipart-provider</artifactId>
         </dependency>
diff --git a/services/src/main/java/org/keycloak/provider/ProviderManager.java b/services/src/main/java/org/keycloak/provider/ProviderManager.java
index e906df9..e5d9712 100644
--- a/services/src/main/java/org/keycloak/provider/ProviderManager.java
+++ b/services/src/main/java/org/keycloak/provider/ProviderManager.java
@@ -16,8 +16,10 @@
  */
 package org.keycloak.provider;
 
+import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.services.ServicesLogger;
 
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.IdentityHashMap;
 import java.util.LinkedList;
@@ -33,7 +35,8 @@ public class ProviderManager {
     private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
 
     private List<ProviderLoader> loaders = new LinkedList<ProviderLoader>();
-    private Map<String, List<ProviderFactory>> cache = new HashMap<String, List<ProviderFactory>>();
+    private MultivaluedHashMap<Class<? extends Provider>, ProviderFactory> cache = new MultivaluedHashMap<>();
+
 
     public ProviderManager(ClassLoader baseClassLoader, String... resources) {
         List<ProviderLoaderFactory> factories = new LinkedList<ProviderLoaderFactory>();
@@ -65,6 +68,10 @@ public class ProviderManager {
         }
     }
 
+    public ProviderManager(ClassLoader baseClassLoader) {
+        loaders.add(new DefaultProviderLoader(baseClassLoader));
+    }
+
     public synchronized List<Spi> loadSpis() {
         // Use a map to prevent duplicates, since the loaders may have overlapping classpaths.
         Map<String, Spi> spiMap = new HashMap<>();
@@ -80,9 +87,7 @@ public class ProviderManager {
     }
 
     public synchronized List<ProviderFactory> load(Spi spi) {
-        List<ProviderFactory> factories = cache.get(spi.getName());
-        if (factories == null) {
-            factories = new LinkedList<ProviderFactory>();
+        if (!cache.containsKey(spi.getProviderClass())) {
             IdentityHashMap factoryClasses = new IdentityHashMap();
             for (ProviderLoader loader : loaders) {
                 List<ProviderFactory> f = loader.load(spi);
@@ -90,14 +95,26 @@ public class ProviderManager {
                     for (ProviderFactory pf: f) {
                         // make sure there are no duplicates
                         if (!factoryClasses.containsKey(pf.getClass())) {
-                            factories.add(pf);
+                            cache.add(spi.getProviderClass(), pf);
                             factoryClasses.put(pf.getClass(), pf);
                         }
                     }
                 }
             }
         }
-        return factories;
+        List<ProviderFactory> rtn = cache.get(spi.getProviderClass());
+        return rtn == null ? Collections.EMPTY_LIST : rtn;
+    }
+
+    /**
+     * returns a copy of internal factories.
+     *
+     * @return
+     */
+    public synchronized MultivaluedHashMap<Class<? extends Provider>, ProviderFactory> getLoadedFactories() {
+        MultivaluedHashMap<Class<? extends Provider>, ProviderFactory> copy = new MultivaluedHashMap<>();
+        copy.addAll(cache);
+        return copy;
     }
 
     public synchronized ProviderFactory load(Spi spi, String providerId) {
diff --git a/services/src/main/java/org/keycloak/provider/ProviderManagerDeployer.java b/services/src/main/java/org/keycloak/provider/ProviderManagerDeployer.java
new file mode 100644
index 0000000..e8ed910
--- /dev/null
+++ b/services/src/main/java/org/keycloak/provider/ProviderManagerDeployer.java
@@ -0,0 +1,26 @@
+/*
+ * 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.provider;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface ProviderManagerDeployer {
+    void deploy(ProviderManager pm);
+    void undeploy(ProviderManager pm);
+}
diff --git a/services/src/main/java/org/keycloak/provider/ProviderManagerRegistry.java b/services/src/main/java/org/keycloak/provider/ProviderManagerRegistry.java
new file mode 100644
index 0000000..69cd92e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/provider/ProviderManagerRegistry.java
@@ -0,0 +1,62 @@
+/*
+ * 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.provider;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ProviderManagerRegistry {
+    public static final ProviderManagerRegistry SINGLETON = new ProviderManagerRegistry();
+    protected List<ProviderManager> preBoot = Collections.synchronizedList(new LinkedList<>());
+    protected AtomicReference<ProviderManagerDeployer> deployerRef = new AtomicReference<>();
+
+    public void setDeployer(ProviderManagerDeployer deployer) {
+        this.deployerRef.set(deployer);
+    }
+
+    public void deploy(ProviderManager pm) {
+        ProviderManagerDeployer deployer = getDeployer();
+        if (deployer == null) {
+            preBoot.add(pm);
+        } else {
+            deployer.deploy(pm);
+        }
+
+    }
+
+    public void undeploy(ProviderManager pm) {
+        preBoot.remove(pm);
+        ProviderManagerDeployer deployer = getDeployer();
+        if (deployer != null) {
+            deployer.undeploy(pm);
+        }
+    }
+
+    public ProviderManagerDeployer getDeployer() {
+        return deployerRef.get();
+    }
+
+    public List<ProviderManager> getPreBoot() {
+        return preBoot;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
index b56af8d..45bef3c 100755
--- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
+++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java
@@ -17,6 +17,7 @@
 package org.keycloak.services;
 
 import org.keycloak.Config;
+import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.provider.Provider;
@@ -24,26 +25,31 @@ import org.keycloak.provider.ProviderEvent;
 import org.keycloak.provider.ProviderEventListener;
 import org.keycloak.provider.ProviderFactory;
 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 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;
 
-public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
+public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, ProviderManagerDeployer {
 
     private static final ServicesLogger logger = ServicesLogger.ROOT_LOGGER;
 
     private Set<Spi> spis = new HashSet<>();
-    private Map<Class<? extends Provider>, String> provider = new HashMap<Class<? extends Provider>, String>();
-    private Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<Class<? extends Provider>, Map<String, ProviderFactory>>();
-    protected CopyOnWriteArrayList<ProviderEventListener> listeners = new CopyOnWriteArrayList<ProviderEventListener>();
+    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 TransactionManager tm;
 
     // TODO: Likely should be changed to int and use Time.currentTime() to be compatible with all our "time" reps
     protected long serverStartupTimestamp;
@@ -69,15 +75,155 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
         serverStartupTimestamp = System.currentTimeMillis();
 
         ProviderManager pm = new ProviderManager(getClass().getClassLoader(), Config.scope().getArray("providers"));
-
-        // Load the SPI classes through the provider manager, so both Keycloak internal SPI's and
-        // the ones defined in deployed modules will be found.
-        loadSPIs(pm, pm.loadSpis());
+        spis.addAll(pm.loadSpis());
+        factoriesMap = loadFactories(pm);
+        for (ProviderManager manager : ProviderManagerRegistry.SINGLETON.getPreBoot()) {
+            Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoryMap = loadFactories(manager);
+            for (Map.Entry<Class<? extends Provider>,  Map<String, ProviderFactory>> entry : factoryMap.entrySet()) {
+                Map<String, ProviderFactory> factories = factoriesMap.get(entry.getKey());
+                if (factories == null) {
+                    factoriesMap.put(entry.getKey(), entry.getValue());
+                } else {
+                    factories.putAll(entry.getValue());
+                }
+            }
+        }
+        checkProvider();
         for ( Map<String, ProviderFactory> factories : factoriesMap.values()) {
             for (ProviderFactory factory : factories.values()) {
                 factory.postInit(this);
             }
         }
+        // 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<>();
+        for (Map.Entry<Class<? extends Provider>, Map<String, ProviderFactory>> entry : factoriesMap.entrySet()) {
+            Map<String, ProviderFactory> valCopy = new HashMap<>();
+            valCopy.putAll(entry.getValue());
+            copy.put(entry.getKey(), valCopy);
+        }
+        return copy;
+
+    }
+
+    @Override
+    public void deploy(ProviderManager pm) {
+        Map<Class<? extends Provider>, Map<String, ProviderFactory>> copy = getFactoriesCopy();
+        Map<Class<? extends Provider>, Map<String, ProviderFactory>> newFactories = loadFactories(pm);
+        List<ProviderFactory> undeployed = new LinkedList<>();
+
+        for (Map.Entry<Class<? extends Provider>, Map<String, ProviderFactory>> entry : newFactories.entrySet()) {
+            Map<String, ProviderFactory> current = copy.get(entry.getKey());
+            if (current == null) {
+                copy.put(entry.getKey(), entry.getValue());
+            } else {
+                for (ProviderFactory f : entry.getValue().values()) {
+                    ProviderFactory old = current.remove(f.getId());
+                    if (old != null) undeployed.add(old);
+                }
+                current.putAll(entry.getValue());
+            }
+
+        }
+        factoriesMap = copy;
+        for (ProviderFactory factory : undeployed) {
+            factory.close();
+        }
+    }
+
+    @Override
+    public void undeploy(ProviderManager pm) {
+        // we make a copy to avoid concurrent access exceptions
+        Map<Class<? extends Provider>, Map<String, ProviderFactory>> copy = getFactoriesCopy();
+        MultivaluedHashMap<Class<? extends Provider>, ProviderFactory> factories = pm.getLoadedFactories();
+        List<ProviderFactory> undeployed = new LinkedList<>();
+        for (Map.Entry<Class<? extends Provider>, List<ProviderFactory>> entry : factories.entrySet()) {
+            Map<String, ProviderFactory> registered = copy.get(entry.getKey());
+            for (ProviderFactory factory : entry.getValue()) {
+                undeployed.add(factory);
+                if (registered != null) {
+                    registered.remove(factory.getId());
+                }
+            }
+        }
+        factoriesMap = copy;
+        for (ProviderFactory factory : undeployed) {
+            factory.close();
+        }
+    }
+
+    protected void checkProvider() {
+        for (Spi spi : spis) {
+            String provider = Config.getProvider(spi.getName());
+            if (provider != null) {
+                this.provider.put(spi.getProviderClass(), provider);
+                if (getProviderFactory(spi.getProviderClass(), provider) == null) {
+                    throw new RuntimeException("Failed to find provider " + provider + " for " + spi.getName());
+                }
+            } else {
+                Map<String, ProviderFactory> factories = factoriesMap.get(spi.getProviderClass());
+                if (factories != null && factories.size() == 1) {
+                    provider = factories.values().iterator().next().getId();
+                    this.provider.put(spi.getProviderClass(), provider);
+                }
+            }
+        }
+    }
+
+    protected Map<Class<? extends Provider>, Map<String, ProviderFactory>> loadFactories(ProviderManager pm) {
+        Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoryMap = new HashMap<>();
+        Set<Spi> spiList = spis;
+
+        for (Spi spi : spiList) {
+
+            Map<String, ProviderFactory> factories = new HashMap<String, ProviderFactory>();
+            factoryMap.put(spi.getProviderClass(), factories);
+
+            String provider = Config.getProvider(spi.getName());
+            if (provider != null) {
+
+                ProviderFactory factory = pm.load(spi, provider);
+                if (factory == null) {
+                    continue;
+                }
+
+                Config.Scope scope = Config.scope(spi.getName(), provider);
+                if (scope.getBoolean("enabled", true)) {
+
+                    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);
+                }
+            } else {
+                for (ProviderFactory factory : pm.load(spi)) {
+                    Config.Scope scope = Config.scope(spi.getName(), factory.getId());
+                    if (scope.getBoolean("enabled", true)) {
+                        factory.init(scope);
+
+                        if (spi.isInternal() && !isInternal(factory)) {
+                            logger.spiMayChange(factory.getId(), factory.getClass().getName(), spi.getName());
+                        }
+
+                        factories.put(factory.getId(), factory);
+                    } else {
+                        logger.debugv("SPI {0} provider {1} disabled", spi.getName(), factory.getId());
+                    }
+                }
+            }
+        }
+        return factoryMap;
+
     }
 
     protected void loadSPIs(ProviderManager pm, List<Spi> spiList) {
@@ -135,7 +281,11 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
     }
 
     public KeycloakSession create() {
-        return new DefaultKeycloakSession(this);
+        KeycloakSession session =  new DefaultKeycloakSession(this);
+        if (tm != null) {
+            session.getTransactionManager().enlist(new JtaTransactionWrapper(tm));
+        }
+        return session;
     }
 
     <T extends Provider> String getDefaultProvider(Class<T> clazz) {
@@ -180,6 +330,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
     }
 
     public void close() {
+        ProviderManagerRegistry.SINGLETON.setDeployer(null);
         for (Map<String, ProviderFactory> factories : factoriesMap.values()) {
             for (ProviderFactory factory : factories.values()) {
                 factory.close();
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/JtaRegistration.java b/services/src/main/java/org/keycloak/transaction/JtaRegistration.java
new file mode 100644
index 0000000..1800c52
--- /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 <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));
+    }
+}
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/JtaTransactionWrapper.java b/services/src/main/java/org/keycloak/transaction/JtaTransactionWrapper.java
new file mode 100644
index 0000000..ecc3071
--- /dev/null
+++ b/services/src/main/java/org/keycloak/transaction/JtaTransactionWrapper.java
@@ -0,0 +1,111 @@
+/*
+ * 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.KeycloakTransaction;
+
+import javax.transaction.InvalidTransactionException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import javax.transaction.UserTransaction;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class JtaTransactionWrapper implements KeycloakTransaction {
+    protected TransactionManager tm;
+    protected Transaction ut;
+    protected Transaction suspended;
+
+    public JtaTransactionWrapper(TransactionManager tm) {
+        this.tm = tm;
+        try {
+            suspended = tm.suspend();
+            tm.begin();
+            ut = tm.getTransaction();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void begin() {
+    }
+
+    @Override
+    public void commit() {
+        try {
+            ut.commit();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void rollback() {
+        try {
+            ut.rollback();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        } finally {
+            end();
+        }
+
+    }
+
+    @Override
+    public void setRollbackOnly() {
+        try {
+            ut.setRollbackOnly();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean getRollbackOnly() {
+        try {
+            return ut.getStatus() == Status.STATUS_MARKED_ROLLBACK;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean isActive() {
+        try {
+            return ut.getStatus() == Status.STATUS_ACTIVE;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected void end() {
+        if (suspended != null) {
+            try {
+                tm.resume(suspended);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+    }
+}
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/java/org/keycloak/transaction/UserTransactionWrapper.java b/services/src/main/java/org/keycloak/transaction/UserTransactionWrapper.java
new file mode 100644
index 0000000..c838b18
--- /dev/null
+++ b/services/src/main/java/org/keycloak/transaction/UserTransactionWrapper.java
@@ -0,0 +1,94 @@
+/*
+ * 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.KeycloakTransaction;
+
+import javax.transaction.HeuristicMixedException;
+import javax.transaction.HeuristicRollbackException;
+import javax.transaction.NotSupportedException;
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.SystemException;
+import javax.transaction.UserTransaction;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class UserTransactionWrapper implements KeycloakTransaction {
+    protected UserTransaction ut;
+
+    public UserTransactionWrapper(UserTransaction ut) {
+        this.ut = ut;
+    }
+
+    @Override
+    public void begin() {
+        try {
+            ut.begin();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void commit() {
+        try {
+            ut.commit();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void rollback() {
+        try {
+            ut.rollback();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    @Override
+    public void setRollbackOnly() {
+        try {
+            ut.setRollbackOnly();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean getRollbackOnly() {
+        try {
+            return ut.getStatus() == Status.STATUS_MARKED_ROLLBACK;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public boolean isActive() {
+        try {
+            return ut.getStatus() == Status.STATUS_ACTIVE;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
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/pom.xml b/testsuite/integration/pom.xml
index 0e3c17c..acb0534 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -69,6 +69,10 @@
             <artifactId>jboss-jaxrs-api_2.0_spec</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.jboss.spec.javax.transaction</groupId>
+            <artifactId>jboss-transaction-api_1.2_spec</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.jboss.resteasy</groupId>
             <artifactId>async-http-servlet-3.0</artifactId>
         </dependency>
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/themes/src/main/resources/theme/base/admin/resources/js/app.js b/themes/src/main/resources/theme/base/admin/resources/js/app.js
index c2c44c0..d85c019 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -1338,18 +1338,6 @@ module.config([ '$routeProvider', function($routeProvider) {
             },
             controller : 'RealmSessionStatsCtrl'
         })
-        .when('/realms/:realm/user-storage', {
-            templateUrl : resourceUrl + '/partials/user-storage.html',
-            resolve : {
-                realm : function(RealmLoader) {
-                    return RealmLoader();
-                },
-                serverInfo : function(ServerInfoLoader) {
-                    return ServerInfoLoader();
-                }
-            },
-            controller : 'UserStorageCtrl'
-        })
         .when('/create/user-storage/:realm/providers/:provider', {
             templateUrl : resourceUrl + '/partials/user-storage-generic.html',
             resolve : {
@@ -1393,6 +1381,9 @@ module.config([ '$routeProvider', function($routeProvider) {
             resolve : {
                 realm : function(RealmLoader) {
                     return RealmLoader();
+                },
+                serverInfo : function(ServerInfoLoader) {
+                    return ServerInfoLoader();
                 }
             },
             controller : 'UserFederationCtrl'
diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
index 82072ea..88c57a7 100755
--- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
+++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js
@@ -592,36 +592,106 @@ module.controller('UserCredentialsCtrl', function($scope, realm, user, RequiredA
     };
 });
 
-module.controller('UserStorageCtrl', function($scope, $location, $route, realm, serverInfo, Components, Notifications, Dialog) {
-    console.log('UserStorageCtrl ++++****');
+module.controller('UserFederationCtrl', function($scope, $location, $route, realm, serverInfo, Components, UserFederationProviders, UserFederationInstances, Notifications, Dialog) {
+    console.log('UserFederationCtrl ++++****');
     $scope.realm = realm;
     $scope.providers = serverInfo.componentTypes['org.keycloak.storage.UserStorageProvider'];
+    for (var i = 0; i < $scope.providers.length; i++) {
+        $scope.providers[i].isUserFederationProvider = false;
+    }
+    UserFederationProviders.query({realm: realm.realm}, function(data) {
+        for (var i = 0; i < data.length; i++) {
+            data[i].isUserFederationProvider = true;
+            $scope.providers.push(data[i]);
+        }
+    });
 
     $scope.addProvider = function(provider) {
         console.log('Add provider: ' + provider.id);
-        $location.url("/create/user-storage/" + realm.realm + "/providers/" + provider.id);
+        if (provider.isUserFederationProvider) {
+            $location.url("/create/user-federation/" + realm.realm + "/providers/" + provider.id);
+        } else {
+            $location.url("/create/user-storage/" + realm.realm + "/providers/" + provider.id);
+            
+        }
     };
 
-    $scope.instances = Components.query({realm: realm.realm,
+    $scope.getInstanceLink = function(instance) {
+        if (instance.isUserFederationProvider) {
+            return "/realms/" + realm.realm + "/user-federation/providers/" + instance.providerName + "/" + instance.id;
+        } else {
+            return "/realms/" + realm.realm + "/user-storage/providers/" + instance.providerId + "/" + instance.id;
+        }
+    }
+
+    $scope.getInstanceName = function(instance) {
+        if (instance.isUserFederationProvider) {
+            return instance.displayName;
+        } else {
+            return instance.name;
+        }
+    }
+    $scope.getInstanceProvider = function(instance) {
+        if (instance.isUserFederationProvider) {
+            return instance.providerName;
+        } else {
+            return instance.providerId;
+        }
+    }
+
+    $scope.getInstancePriority = function(instance) {
+        if (instance.isUserFederationProvider) {
+            return instance.priority;
+        } else {
+            return instance.config['priority'][0];
+        }
+    }
+
+    Components.query({realm: realm.realm,
         parent: realm.id,
         type: 'org.keycloak.storage.UserStorageProvider'
+    }, function(data) {
+        $scope.instances = data;
+        for (var i = 0; i < data.length; i++) {
+            data[i].isUserFederationProvider = false;
+        }
+        UserFederationInstances.query({realm: realm.realm}, function(data) {
+            for (var i = 0; i < data.length; i++) {
+                data[i].isUserFederationProvider = true;
+                $scope.instances.push(data[i]);
+            }
+            
+        });
     });
 
-    $scope.removeUserStorage = function(instance) {
-        Dialog.confirmDelete(instance.name, 'user storage provider', function() {
-            Components.remove({
-                realm : realm.realm,
-                componentId : instance.id
-            }, function() {
-                $route.reload();
-                Notifications.success("The provider has been deleted.");
+    $scope.removeInstance = function(instance) {
+        if (instance.isUserFederationProvider) {
+            Dialog.confirmDelete(instance.displayName, 'user federation provider', function() {
+                UserFederationInstances.remove({
+                    realm : realm.realm,
+                    instance : instance.id
+                }, function() {
+                    $route.reload();
+                    Notifications.success("The provider has been deleted.");
+                });
             });
-        });
+
+        } else {
+            Dialog.confirmDelete(instance.name, 'user storage provider', function() {
+                Components.remove({
+                    realm : realm.realm,
+                    componentId : instance.id
+                }, function() {
+                    $route.reload();
+                    Notifications.success("The provider has been deleted.");
+                });
+            });
+        }
     };
 });
 
 module.controller('GenericUserStorageCtrl', function($scope, $location, Notifications, $route, Dialog, realm, serverInfo, instance, providerId, Components) {
-    console.log('GenericUserFederationCtrl');
+    console.log('GenericUserStorageCtrl');
     console.log('providerId: ' + providerId);
     $scope.create = !instance.providerId;
     console.log('create: ' + $scope.create);
@@ -737,31 +807,6 @@ module.controller('GenericUserStorageCtrl', function($scope, $location, Notifica
 });
 
 
-module.controller('UserFederationCtrl', function($scope, $location, $route, realm, UserFederationProviders, UserFederationInstances, Notifications, Dialog) {
-    console.log('UserFederationCtrl ++++****');
-    $scope.realm = realm;
-    $scope.providers = UserFederationProviders.query({realm: realm.realm});
-
-    $scope.addProvider = function(provider) {
-        console.log('Add provider: ' + provider.id);
-        $location.url("/create/user-federation/" + realm.realm + "/providers/" + provider.id);
-    };
-
-    $scope.instances = UserFederationInstances.query({realm: realm.realm});
-
-    $scope.removeUserFederation = function(instance) {
-        Dialog.confirmDelete(instance.displayName, 'user federation provider', function() {
-            UserFederationInstances.remove({
-                realm : realm.realm,
-                instance : instance.id
-            }, function() {
-                $route.reload();
-                Notifications.success("The provider has been deleted.");
-            });
-        });
-    };
-});
-
 module.controller('UserFederationTabCtrl', function(Dialog, $scope, Current, Notifications, $location) {
     $scope.removeUserFederation = function() {
         Dialog.confirmDelete($scope.instance.displayName, 'user federation provider', function() {
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-federation.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-federation.html
index fe8b1be..cdf18b8 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-federation.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-federation.html
@@ -27,11 +27,11 @@
         </thead>
         <tbody>
         <tr ng-repeat="instance in instances">
-            <td><a href="#/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">{{instance.displayName}}</a></td>
-            <td>{{instance.providerName|capitalize}}</td>
-            <td>{{instance.priority}}</td>
-            <td class="kc-action-cell" kc-open="/realms/{{realm.realm}}/user-federation/providers/{{instance.providerName}}/{{instance.id}}">{{:: 'edit' | translate}}</td>
-            <td class="kc-action-cell" data-ng-click="removeUserFederation(instance)">{{:: 'delete' | translate}}</td>
+            <td><a href="#{{getInstanceLink(instance)}}">{{getInstanceName(instance)}}</a></td>
+            <td>{{getInstanceProvider(instance)|capitalize}}</td>
+            <td>{{getInstancePriority(instance)}}</td>
+            <td class="kc-action-cell" kc-open="{{getInstanceLink(instance)}}">{{:: 'edit' | translate}}</td>
+            <td class="kc-action-cell" data-ng-click="removeInstance(instance)">{{:: 'delete' | translate}}</td>
          </tr>
         <tr data-ng-show="!instances || instances.length == 0">
             <td class="text-muted">{{:: 'no-user-federation-providers-configured' | translate}}</td>
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-generic.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-generic.html
index 0a89ac8..e3ff5f6 100755
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-generic.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-generic.html
@@ -1,6 +1,6 @@
 <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
     <ol class="breadcrumb">
-        <li><a href="#/realms/{{realm.realm}}/user-storage">{{:: 'user-storage' | translate}}</a></li>
+        <li><a href="#/realms/{{realm.realm}}/user-federation">{{:: 'user-federation' | translate}}</a></li>
         <li data-ng-hide="create">{{instance.name|capitalize}}</li>
         <li data-ng-show="create">{{:: 'add-user-storage-provider' | translate}}</li>
     </ol>
diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
index 926b43c..e766bcb 100755
--- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
+++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-menu.html
@@ -34,7 +34,6 @@
             <li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles' || (path[1] == 'role' && path[3] != 'clients')) && 'active'"><a href="#/realms/{{realm.realm}}/roles"><i class="fa fa-tasks"></i> {{:: 'roles' | translate}}</a></li>
             <li data-ng-show="access.viewIdentityProviders" data-ng-class="(path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings"><i class="fa fa-exchange"></i> {{:: 'identity-providers' | translate}}</a></li>
             <li data-ng-show="access.viewUsers" data-ng-class="(path[1] == 'user-federation' || path[2] == 'user-federation') && 'active'"><a href="#/realms/{{realm.realm}}/user-federation"><i class="fa fa-database"></i> {{:: 'user-federation' | translate}}</a></li>
-            <li data-ng-show="access.viewUsers" data-ng-class="(path[1] == 'user-storage' || path[2] == 'user-storage') && 'active'"><a href="#/realms/{{realm.realm}}/user-storage"><i class="fa fa-database"></i> {{:: 'user-storage' | translate}}</a></li>
             <li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'authentication' || path[2] == 'authentication') && 'active'"><a href="#/realms/{{realm.realm}}/authentication/flows"><i class="fa fa-lock"></i> {{:: 'authentication' | translate}}</a></li>
         </ul>
     </div>
diff --git a/wildfly/server-subsystem/pom.xml b/wildfly/server-subsystem/pom.xml
index 02c7ab1..20c9d66 100755
--- a/wildfly/server-subsystem/pom.xml
+++ b/wildfly/server-subsystem/pom.xml
@@ -78,6 +78,16 @@
             <artifactId>jboss-logging-processor</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-services</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-server-spi</artifactId>
+            <scope>provided</scope>
+        </dependency>
 
         <dependency>
             <groupId>org.wildfly.core</groupId>
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
new file mode 100644
index 0000000..5a5936f
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java
@@ -0,0 +1,110 @@
+/*
+ * 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.subsystem.server.extension;
+
+import org.jboss.as.server.deployment.Attachments;
+import org.jboss.as.server.deployment.DeploymentPhaseContext;
+import org.jboss.as.server.deployment.DeploymentUnit;
+import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
+import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.server.deployment.module.ModuleDependency;
+import org.jboss.as.server.deployment.module.ModuleSpecification;
+import org.jboss.as.server.deployment.module.ResourceRoot;
+import org.jboss.logging.Logger;
+import org.jboss.modules.Module;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoader;
+import org.jboss.vfs.VirtualFile;
+import org.jboss.vfs.util.AbstractVirtualFileFilterWithAttributes;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakProviderDependencyProcessor implements DeploymentUnitProcessor {
+    private static final ModuleIdentifier KEYCLOAK_COMMON = ModuleIdentifier.create("org.keycloak.keycloak-common");
+    private static final ModuleIdentifier KEYCLOAK_CORE = ModuleIdentifier.create("org.keycloak.keycloak-core");
+    private static final ModuleIdentifier KEYCLOAK_SERVER_SPI = ModuleIdentifier.create("org.keycloak.keycloak-server-spi");
+    private static final ModuleIdentifier KEYCLOAK_JPA = ModuleIdentifier.create("org.keycloak.keycloak-model-jpa");
+    private static final ModuleIdentifier JAXRS = ModuleIdentifier.create("javax.ws.rs.api");
+    private static final ModuleIdentifier RESTEASY = ModuleIdentifier.create("org.jboss.resteasy.resteasy-jaxrs");
+    private static final ModuleIdentifier APACHE = ModuleIdentifier.create("org.apache.httpcomponents");
+
+    private static final Logger logger = Logger.getLogger(KeycloakProviderDependencyProcessor.class);
+
+    @Override
+    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+        DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+        KeycloakAdapterConfigService config = KeycloakAdapterConfigService.INSTANCE;
+        String deploymentName = deploymentUnit.getName();
+
+        if (config.isKeycloakServerDeployment(deploymentName)) {
+            return;
+        }
+
+        if (!isKeycloakProviderDeployment(deploymentUnit)) return;
+
+        final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
+        final ModuleLoader moduleLoader = Module.getBootModuleLoader();
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_SERVER_SPI, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, JAXRS, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, RESTEASY, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE, false, false, false, false));
+        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JPA, false, false, false, false));
+
+
+    }
+
+    public KeycloakProviderDependencyProcessor() {
+        super();
+    }
+
+    public static boolean isKeycloakProviderDeployment(DeploymentUnit du) {
+        final ResourceRoot resourceRoot = du.getAttachment(Attachments.DEPLOYMENT_ROOT);
+        if (resourceRoot == null) {
+            return false;
+        }
+        final VirtualFile deploymentRoot = resourceRoot.getRoot();
+        if (deploymentRoot == null || !deploymentRoot.exists()) {
+            return false;
+        }
+        VirtualFile services = deploymentRoot.getChild("META-INF/services");
+        if (!services.exists()) return false;
+        try {
+            List<VirtualFile> archives = services.getChildren(new AbstractVirtualFileFilterWithAttributes(){
+                @Override
+                public boolean accepts(VirtualFile file) {
+                    return file.getName().startsWith("org.keycloak");
+                }
+            });
+            return !archives.isEmpty();
+        } catch (IOException e) {
+
+        }
+        return false;
+    }
+
+    @Override
+    public void undeploy(DeploymentUnit context) {
+
+    }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java
new file mode 100644
index 0000000..c573754
--- /dev/null
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java
@@ -0,0 +1,84 @@
+/*
+ * 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.subsystem.server.extension;
+
+import org.jboss.as.server.deployment.AttachmentKey;
+import org.jboss.as.server.deployment.Attachments;
+import org.jboss.as.server.deployment.DeploymentPhaseContext;
+import org.jboss.as.server.deployment.DeploymentUnit;
+import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
+import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.server.deployment.module.ModuleDependency;
+import org.jboss.as.server.deployment.module.ModuleSpecification;
+import org.jboss.as.server.deployment.module.ResourceRoot;
+import org.jboss.logging.Logger;
+import org.jboss.modules.Module;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoader;
+import org.jboss.vfs.VirtualFile;
+import org.jboss.vfs.util.AbstractVirtualFileFilterWithAttributes;
+import org.keycloak.provider.ProviderManager;
+import org.keycloak.provider.ProviderManagerRegistry;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakProviderDeploymentProcessor implements DeploymentUnitProcessor {
+
+    AttachmentKey<ProviderManager> ATTACHMENT_KEY = AttachmentKey.create(ProviderManager.class);
+
+    private static final Logger logger = Logger.getLogger(KeycloakProviderDeploymentProcessor.class);
+    @Override
+    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+        DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+        KeycloakAdapterConfigService config = KeycloakAdapterConfigService.INSTANCE;
+        String deploymentName = deploymentUnit.getName();
+
+        if (config.isKeycloakServerDeployment(deploymentName)) {
+            return;
+        }
+
+        if (!KeycloakProviderDependencyProcessor.isKeycloakProviderDeployment(deploymentUnit)) return;
+
+        logger.infof("Deploying Keycloak provider: {0}", deploymentUnit.getName());
+        final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
+        ProviderManager pm = new ProviderManager(module.getClassLoader());
+        ProviderManagerRegistry.SINGLETON.deploy(pm);
+        deploymentUnit.putAttachment(ATTACHMENT_KEY, pm);
+
+
+
+    }
+
+    public KeycloakProviderDeploymentProcessor() {
+        super();
+    }
+
+    @Override
+    public void undeploy(DeploymentUnit context) {
+        ProviderManager pm = context.getAttachment(ATTACHMENT_KEY);
+        if (pm != null) {
+            logger.infof("Undeploying Keycloak provider: {0}", context.getName());
+            ProviderManagerRegistry.SINGLETON.undeploy(pm);
+            context.removeAttachment(ATTACHMENT_KEY);
+        }
+    }
+}
diff --git a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java
index a5d9e85..3c6dc34 100755
--- a/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java
+++ b/wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakSubsystemAdd.java
@@ -44,12 +44,22 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
         context.addStep(new AbstractDeploymentChainStep() {
             @Override
             protected void execute(DeploymentProcessorTarget processorTarget) {
+                processorTarget.addDeploymentProcessor(SUBSYSTEM_NAME, Phase.DEPENDENCIES, 0, new KeycloakProviderDependencyProcessor());
+                processorTarget.addDeploymentProcessor(SUBSYSTEM_NAME,
+                        Phase.POST_MODULE, // PHASE
+                        Phase.POST_MODULE_VALIDATOR_FACTORY - 2, // PRIORITY
+                        new KeycloakProviderDeploymentProcessor());
                 processorTarget.addDeploymentProcessor(SUBSYSTEM_NAME,
                         Phase.POST_MODULE, // PHASE
                         Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY
                         new KeycloakServerDeploymentProcessor());
             }
         }, OperationContext.Stage.RUNTIME);
+        context.addStep(new AbstractDeploymentChainStep() {
+            @Override
+            protected void execute(DeploymentProcessorTarget processorTarget) {
+            }
+        }, OperationContext.Stage.RUNTIME);
     }
 
     protected void populateModel(final OperationContext context, final ModelNode operation, final Resource resource) throws  OperationFailedException {
diff --git a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-datasources.xml b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-datasources.xml
index c823e4f..326fa7f 100755
--- a/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-datasources.xml
+++ b/wildfly/server-subsystem/src/main/resources/subsystem-templates/keycloak-datasources.xml
@@ -29,7 +29,7 @@
                     <password>sa</password>
                 </security>
             </datasource>
-            <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
+            <datasource jndi-name="java:jboss/datasources/KeycloakDS" jta="false" pool-name="KeycloakDS" enabled="true" use-java-context="true">
                 <?KEYCLOAK_DS_CONNECTION_URL?>
                 <driver>h2</driver>
                 <security>