keycloak-uncached
Changes
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/configuration/standalone/subsystems-ha.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/content/standalone/configuration/keycloak-server.json 8(+8 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml 1(+1 -0)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-server-subsystem/main/module.xml 1(+1 -0)
examples/providers/user-storage-jpa/pom.xml 42(+13 -29)
examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java 22(+16 -6)
examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProviderFactory.java 65(+17 -48)
examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/UserAdapter.java 3(+1 -2)
examples/providers/user-storage-jpa/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory 2(+1 -1)
pom.xml 6(+6 -0)
server-spi/pom.xml 5(+5 -0)
services/pom.xml 4(+4 -0)
services/src/main/resources/META-INF/services/org.keycloak.transaction.JtaTransactionManagerLookup 1(+1 -0)
testsuite/integration/pom.xml 4(+4 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json 8(+8 -0)
wildfly/server-subsystem/pom.xml 10(+10 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDependencyProcessor.java 110(+110 -0)
wildfly/server-subsystem/src/main/java/org/keycloak/subsystem/server/extension/KeycloakProviderDeploymentProcessor.java 84(+84 -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>
examples/providers/user-storage-jpa/pom.xml 42(+13 -29)
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>
server-spi/pom.xml 5(+5 -0)
diff --git a/server-spi/pom.xml b/server-spi/pom.xml
index 62a5b0d..ad1c17b 100755
--- a/server-spi/pom.xml
+++ b/server-spi/pom.xml
@@ -37,6 +37,11 @@
<dependencies>
<dependency>
+ <groupId>org.jboss.spec.javax.transaction</groupId>
+ <artifactId>jboss-transaction-api_1.2_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<scope>provided</scope>
diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java
index 344e0a0..4027ea3 100755
--- a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java
+++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java
@@ -28,7 +28,7 @@ public class UserStorageProviderSpi implements Spi {
@Override
public boolean isInternal() {
- return true;
+ return false;
}
@Override
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
testsuite/integration/pom.xml 4(+4 -0)
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>
wildfly/server-subsystem/pom.xml 10(+10 -0)
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>