Merge pull request #1499 from stianst/master

KEYCLOAK-1542
This commit is contained in:
Stian Thorgersen 2015-07-24 17:30:29 +02:00
commit 86de481ba1
98 changed files with 940 additions and 361 deletions

View file

@ -13,10 +13,12 @@ import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
@ -29,6 +31,8 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
private volatile EntityManagerFactory emf;
private Config.Scope config;
private Map<String,String> operationalInfo;
@Override
public JpaConnectionProvider create(KeycloakSession session) {
@ -120,56 +124,73 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
if (databaseSchema != null) {
logger.trace("Updating database");
connection = getConnection();
try{
prepareOperationalInfo(connection);
if (databaseSchema != null) {
logger.trace("Updating database");
JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
if (updater == null) {
throw new RuntimeException("Can't update database: JPA updater provider not found");
}
if (databaseSchema.equals("update")) {
String currentVersion = null;
try {
ResultSet resultSet = connection.createStatement().executeQuery(updater.getCurrentVersionSql(schema));
if (resultSet.next()) {
currentVersion = resultSet.getString(1);
}
} catch (SQLException e) {
}
if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
updater.update(session, connection, schema);
} else {
logger.debug("Database is up to date");
}
} else if (databaseSchema.equals("validate")) {
updater.validate(connection, schema);
} else {
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
}
logger.trace("Database update completed");
}
logger.trace("Creating EntityManagerFactory");
emf = Persistence.createEntityManagerFactory(unitName, properties);
logger.trace("EntityManagerFactory created");
JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
if (updater == null) {
throw new RuntimeException("Can't update database: JPA updater provider not found");
}
connection = getConnection();
if (databaseSchema.equals("update")) {
String currentVersion = null;
try {
ResultSet resultSet = connection.createStatement().executeQuery(updater.getCurrentVersionSql(schema));
if (resultSet.next()) {
currentVersion = resultSet.getString(1);
}
} catch (SQLException e) {
}
if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
updater.update(session, connection, schema);
} else {
logger.debug("Database is up to date");
}
} else if (databaseSchema.equals("validate")) {
updater.validate(connection, schema);
} else {
throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
}
logger.trace("Database update completed");
}
logger.trace("Creating EntityManagerFactory");
emf = Persistence.createEntityManagerFactory(unitName, properties);
logger.trace("EntityManagerFactory created");
// Close after creating EntityManagerFactory to prevent in-mem databases from closing
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
logger.warn(e);
}
} finally {
// Close after creating EntityManagerFactory to prevent in-mem databases from closing
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
logger.warn(e);
}
}
}
}
}
}
}
protected void prepareOperationalInfo(Connection connection) {
try {
operationalInfo = new LinkedHashMap<>();
DatabaseMetaData md = connection.getMetaData();
operationalInfo.put("databaseUrl",md.getURL());
operationalInfo.put("databaseUser", md.getUserName());
operationalInfo.put("databaseProduct", md.getDatabaseProductName() + " " + md.getDatabaseProductVersion());
operationalInfo.put("databaseDriver", md.getDriverName() + " " + md.getDriverVersion());
} catch (SQLException e) {
logger.warn("Unable to prepare operational info due database exception: " + e.getMessage());
}
}
private Connection getConnection() {
try {
@ -185,5 +206,10 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
throw new RuntimeException("Failed to connect to database", e);
}
}
@Override
public Map<String,String> getOperationalInfo() {
return operationalInfo;
}
}

View file

@ -1,9 +1,10 @@
package org.keycloak.connections.jpa;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface JpaConnectionProviderFactory extends ProviderFactory<JpaConnectionProvider> {
public interface JpaConnectionProviderFactory extends ServerInfoAwareProviderFactory<JpaConnectionProvider> {
}

View file

@ -5,6 +5,7 @@ import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.mongo.api.MongoStore;
@ -18,6 +19,8 @@ import javax.net.ssl.SSLSocketFactory;
import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -57,6 +60,8 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
private MongoStore mongoStore;
private DB db;
protected Config.Scope config;
private Map<String,String> operationalInfo;
@Override
public MongoConnectionProvider create(KeycloakSession session) {
@ -159,7 +164,13 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
} else {
client = new MongoClient(new ServerAddress(host, port), clientOptions);
}
operationalInfo = new LinkedHashMap<>();
operationalInfo.put("mongoServerAddress", client.getAddress().toString());
operationalInfo.put("mongoDatabaseName", dbName);
operationalInfo.put("mongoUser", user);
operationalInfo.put("mongoDriverVersion", client.getVersion());
logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
return client;
}
@ -206,5 +217,10 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
}
}
}
@Override
public Map<String,String> getOperationalInfo() {
return operationalInfo;
}
}

View file

@ -1,9 +1,9 @@
package org.keycloak.connections.mongo;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface MongoConnectionProviderFactory extends ProviderFactory<MongoConnectionProvider> {
public interface MongoConnectionProviderFactory extends ServerInfoAwareProviderFactory<MongoConnectionProvider> {
}

View file

@ -1,8 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="net.iharder.base64">
<module xmlns="urn:jboss:module:1.3" name="net.iharder.base64">
<resources>
<artifact name="${net.iharder:base64}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-core">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-adapter-core">
<resources>
<artifact name="${org.keycloak:keycloak-adapter-core}"/>
</resources>

View file

@ -22,7 +22,7 @@
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-adapter-subsystem">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-adapter-subsystem">
<resources>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-core">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-core">
<resources>
<artifact name="${org.keycloak:keycloak-core}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-jboss-adapter-core">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-jboss-adapter-core">
<resources>
<artifact name="${org.keycloak:keycloak-jboss-adapter-core}"/>
</resources>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-servlet-oauth-client">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-servlet-oauth-client">
<resources>
<artifact name="${org.keycloak:keycloak-servlet-oauth-client}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-undertow-adapter">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-undertow-adapter">
<resources>
<artifact name="${org.keycloak:keycloak-undertow-adapter}"/>
</resources>

View file

@ -22,7 +22,7 @@
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wf9-subsystem">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-wf9-subsystem">
<resources>
<artifact name="${org.keycloak:keycloak-wf9-subsystem}"/>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wildfly-adapter">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-wildfly-adapter">
<resources>
<artifact name="${org.keycloak:keycloak-wildfly-adapter}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="com.google.zxing.core">
<module xmlns="urn:jboss:module:1.3" name="com.google.zxing.core">
<resources>
<artifact name="${com.google.zxing:core}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="com.google.zxing.javase">
<module xmlns="urn:jboss:module:1.3" name="com.google.zxing.javase">
<resources>
<artifact name="${com.google.zxing:javase}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="de.idyl.winzipaes">
<module xmlns="urn:jboss:module:1.3" name="de.idyl.winzipaes">
<resources>
<artifact name="${de.idyl:winzipaes}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="net.iharder.base64">
<module xmlns="urn:jboss:module:1.3" name="net.iharder.base64">
<resources>
<artifact name="${net.iharder:base64}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.freemarker">
<module xmlns="urn:jboss:module:1.3" name="org.freemarker">
<resources>
<artifact name="${org.freemarker:freemarker}"/>
</resources>

View file

@ -22,7 +22,7 @@
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.1" name="org.jboss.as.product" slot="keycloak">
<module xmlns="urn:jboss:module:1.3" name="org.jboss.as.product" slot="keycloak">
<resources>
<resource-root path="dir"/>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-account-api">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-account-api">
<resources>
<artifact name="${org.keycloak:keycloak-account-api}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-account-freemarker">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-account-freemarker">
<resources>
<artifact name="${org.keycloak:keycloak-account-freemarker}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-broker-core">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-broker-core">
<resources>
<artifact name="${org.keycloak:keycloak-broker-core}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-broker-oidc">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-broker-oidc">
<resources>
<artifact name="${org.keycloak:keycloak-broker-oidc}"/>
</resources>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-broker-saml">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-broker-saml">
<resources>
<artifact name="${org.keycloak:keycloak-broker-saml}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-connections-file">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-connections-file">
<resources>
<artifact name="${org.keycloak:keycloak-connections-file}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-connections-http-client">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-connections-http-client">
<resources>
<artifact name="${org.keycloak:keycloak-connections-http-client}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-connections-infinispan">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-connections-infinispan">
<resources>
<artifact name="${org.keycloak:keycloak-connections-infinispan}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-connections-jpa-liquibase">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-connections-jpa-liquibase">
<resources>
<artifact name="${org.keycloak:keycloak-connections-jpa-liquibase}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-connections-jpa">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-connections-jpa">
<resources>
<artifact name="${org.keycloak:keycloak-connections-jpa}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-connections-mongo-update">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-connections-mongo-update">
<resources>
<artifact name="${org.keycloak:keycloak-connections-mongo-update}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-connections-mongo">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-connections-mongo">
<resources>
<artifact name="${org.keycloak:keycloak-connections-mongo}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-core-jaxrs">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-core-jaxrs">
<resources>
<artifact name="${org.keycloak:keycloak-core-jaxrs}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-core">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-core">
<resources>
<artifact name="${org.keycloak:keycloak-core}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-email-api">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-email-api">
<resources>
<artifact name="${org.keycloak:keycloak-email-api}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-email-freemarker">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-email-freemarker">
<resources>
<artifact name="${org.keycloak:keycloak-email-freemarker}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-events-api">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-events-api">
<resources>
<artifact name="${org.keycloak:keycloak-events-api}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-events-email">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-events-email">
<resources>
<artifact name="${org.keycloak:keycloak-events-email}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-events-jboss-logging">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-events-jboss-logging">
<resources>
<artifact name="${org.keycloak:keycloak-events-jboss-logging}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-events-jpa">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-events-jpa">
<resources>
<artifact name="${org.keycloak:keycloak-events-jpa}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-events-mongo">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-events-mongo">
<resources>
<artifact name="${org.keycloak:keycloak-events-mongo}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-export-import-api">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-export-import-api">
<resources>
<artifact name="${org.keycloak:keycloak-export-import-api}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-export-import-dir">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-export-import-dir">
<resources>
<artifact name="${org.keycloak:keycloak-export-import-dir}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-export-import-single-file">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-export-import-single-file">
<resources>
<artifact name="${org.keycloak:keycloak-export-import-single-file}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-export-import-zip">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-export-import-zip">
<resources>
<artifact name="${org.keycloak:keycloak-export-import-zip}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-forms-common-freemarker">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-forms-common-freemarker">
<resources>
<artifact name="${org.keycloak:keycloak-forms-common-freemarker}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-forms-common-themes">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-forms-common-themes">
<resources>
<artifact name="${org.keycloak:keycloak-forms-common-themes}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-invalidation-cache-infinispan">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-invalidation-cache-infinispan">
<resources>
<artifact name="${org.keycloak:keycloak-invalidation-cache-infinispan}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-invalidation-cache-model">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-invalidation-cache-model">
<resources>
<artifact name="${org.keycloak:keycloak-invalidation-cache-model}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-js-adapter">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-js-adapter">
<resources>
<artifact name="${org.keycloak:keycloak-js-adapter}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-kerberos-federation">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-kerberos-federation">
<resources>
<artifact name="${org.keycloak:keycloak-kerberos-federation}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-ldap-federation">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-ldap-federation">
<resources>
<artifact name="${org.keycloak:keycloak-ldap-federation}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-login-api">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-login-api">
<resources>
<artifact name="${org.keycloak:keycloak-login-api}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-login-freemarker">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-login-freemarker">
<resources>
<artifact name="${org.keycloak:keycloak-login-freemarker}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-model-api">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-model-api">
<resources>
<artifact name="${org.keycloak:keycloak-model-api}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-model-file">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-model-file">
<resources>
<artifact name="${org.keycloak:keycloak-model-file}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-model-jpa">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-model-jpa">
<resources>
<artifact name="${org.keycloak:keycloak-model-jpa}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-model-mongo">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-model-mongo">
<resources>
<artifact name="${org.keycloak:keycloak-model-mongo}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-model-sessions-infinispan">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-model-sessions-infinispan">
<resources>
<artifact name="${org.keycloak:keycloak-model-sessions-infinispan}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-model-sessions-jpa">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-model-sessions-jpa">
<resources>
<artifact name="${org.keycloak:keycloak-model-sessions-jpa}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-model-sessions-mem">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-model-sessions-mem">
<resources>
<artifact name="${org.keycloak:keycloak-model-sessions-mem}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-model-sessions-mongo">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-model-sessions-mongo">
<resources>
<artifact name="${org.keycloak:keycloak-model-sessions-mongo}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-core">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-saml-core">
<resources>
<artifact name="${org.keycloak:keycloak-saml-core}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-protocol">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-saml-protocol">
<resources>
<artifact name="${org.keycloak:keycloak-saml-protocol}"/>
</resources>

View file

@ -22,7 +22,7 @@
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-server-subsystem">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-server-subsystem">
<resources>
<resource-root path="."/>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-services">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-services">
<resources>
<artifact name="${org.keycloak:keycloak-services}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-social-core">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-social-core">
<resources>
<artifact name="${org.keycloak:keycloak-social-core}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-social-facebook">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-social-facebook">
<resources>
<artifact name="${org.keycloak:keycloak-social-facebook}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-social-github">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-social-github">
<resources>
<artifact name="${org.keycloak:keycloak-social-github}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-social-google">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-social-google">
<resources>
<artifact name="${org.keycloak:keycloak-social-google}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-social-linkedin">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-social-linkedin">
<resources>
<artifact name="${org.keycloak:keycloak-social-linkedin}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-social-stackoverflow">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-social-stackoverflow">
<resources>
<artifact name="${org.keycloak:keycloak-social-stackoverflow}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-social-twitter">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-social-twitter">
<resources>
<artifact name="${org.keycloak:keycloak-social-twitter}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-timer-api">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-timer-api">
<resources>
<artifact name="${org.keycloak:keycloak-timer-api}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-timer-basic">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-timer-basic">
<resources>
<artifact name="${org.keycloak:keycloak-timer-basic}"/>
</resources>

View file

@ -22,7 +22,7 @@
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wf9-server-subsystem">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-wf9-server-subsystem">
<resources>
<resource-root path="."/>

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wildfly-extensions">
<module xmlns="urn:jboss:module:1.3" name="org.keycloak.keycloak-wildfly-extensions">
<resources>
<artifact name="${org.keycloak:keycloak-wildfly-extensions}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.liquibase">
<module xmlns="urn:jboss:module:1.3" name="org.liquibase">
<resources>
<artifact name="${org.liquibase:liquibase-core}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.mongodb.mongo-java-driver">
<module xmlns="urn:jboss:module:1.3" name="org.mongodb.mongo-java-driver">
<resources>
<artifact name="${org.mongodb:mongo-java-driver}"/>
</resources>

View file

@ -2,7 +2,7 @@
<module xmlns="urn:jboss:module:1.1" name="org.twitter4j">
<module xmlns="urn:jboss:module:1.3" name="org.twitter4j">
<resources>
<artifact name="${org.twitter4j:twitter4j-core}"/>
</resources>

View file

@ -2,9 +2,8 @@
<module xmlns="urn:jboss:module:1.1" name="sun.jdk.jgss">
<module xmlns="urn:jboss:module:1.3" name="sun.jdk.jgss">
<resources>
<!-- Insert resources here -->
</resources>
<dependencies>
<system export="true">

View file

@ -82,8 +82,8 @@ module.config([ '$routeProvider', function($routeProvider) {
realm : function(RealmLoader) {
return RealmLoader();
},
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
serverInfo : function(ServerInfo) {
return ServerInfo.delay;
}
},
controller : 'RealmLoginSettingsCtrl'
@ -1133,7 +1133,22 @@ module.config([ '$routeProvider', function($routeProvider) {
controller : 'AuthenticationConfigCreateCtrl'
})
.when('/server-info', {
templateUrl : resourceUrl + '/partials/server-info.html'
templateUrl : resourceUrl + '/partials/server-info.html',
resolve : {
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ServerInfoCtrl'
})
.when('/server-info/providers', {
templateUrl : resourceUrl + '/partials/server-info-providers.html',
resolve : {
serverInfo : function(ServerInfoLoader) {
return ServerInfoLoader();
}
},
controller : 'ServerInfoCtrl'
})
.when('/logout', {
templateUrl : resourceUrl + '/partials/home.html',
@ -1867,4 +1882,4 @@ module.directive( 'kcOpen', function ( $location ) {
});
});
};
});
});

View file

@ -549,7 +549,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, $route, se
"bearer-only"
];
$scope.protocols = serverInfo.protocols;
$scope.protocols = Object.keys(serverInfo.providers['login-protocol'].providers).sort();
$scope.signatureAlgorithms = [
"RSA_SHA1",

View file

@ -7,9 +7,6 @@ module.controller('GlobalCtrl', function($scope, $http, Auth, WhoAmI, Current, $
$scope.resourceUrl = resourceUrl;
$scope.auth = Auth;
$scope.serverInfo = ServerInfo.get();
$scope.serverInfoUpdate = function() {
$scope.serverInfo = ServerInfo.get();
};
function hasAnyAccess() {
var realmAccess = Auth.user && Auth.user['realm_access'];
@ -125,6 +122,25 @@ module.controller('RealmTabCtrl', function(Dialog, $scope, Current, Realm, Notif
};
});
module.controller('ServerInfoCtrl', function($scope, ServerInfo) {
ServerInfo.reload();
$scope.serverInfo = ServerInfo.get();
$scope.$watch($scope.serverInfo, function() {
$scope.providers = [];
for(var spi in $scope.serverInfo.providers) {
var p = angular.copy($scope.serverInfo.providers[spi]);
p.name = spi;
$scope.providers.push(p)
}
});
$scope.serverInfoReload = function() {
ServerInfo.reload();
}
});
module.controller('RealmListCtrl', function($scope, Realm, Current) {
$scope.realms = Realm.query();
Current.realms = $scope.realms;
@ -1217,7 +1233,7 @@ module.controller('RealmEventsConfigCtrl', function($scope, eventsConfig, RealmE
}
});
$scope.eventListeners = serverInfo.eventListeners;
$scope.eventListeners = Object.keys(serverInfo.providers.eventsListener.providers);
$scope.eventSelectOptions = {
'multiple': true,

View file

@ -35,8 +35,10 @@ module.factory('RealmListLoader', function(Loader, Realm, $q) {
return Loader.get(Realm);
});
module.factory('ServerInfoLoader', function(Loader, ServerInfo, $q) {
return Loader.get(ServerInfo);
module.factory('ServerInfoLoader', function(Loader, ServerInfo) {
return function() {
return ServerInfo.promise;
};
});
module.factory('RealmLoader', function(Loader, Realm, $route, $q) {

View file

@ -215,10 +215,27 @@ module.factory('RealmLDAPConnectionTester', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/testLDAPConnection');
});
module.factory('ServerInfo', function($resource) {
return $resource(authUrl + '/admin/serverinfo');
});
module.service('ServerInfo', function($resource, $q, $http) {
var info = {};
var delay = $q.defer();
$http.get(authUrl + '/admin/serverinfo').success(function(data) {
info = data;
delay.resolve(info);
});
return {
get: function() {
return info;
},
reload: function() {
$http.get(authUrl + '/admin/serverinfo').success(function(data) {
angular.copy(data, info);
});
},
promise: delay.promise
}
});
module.factory('ClientProtocolMapper', function($resource) {

View file

@ -0,0 +1,55 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1>
Server Info
<i id="serverInfoReload" class="pficon pficon-restart clickable" data-ng-click="serverInfoReload()"></i>
</h1>
<ul class="nav nav-tabs">
<li><a href="#/server-info">Info</a></li>
<li class="active"><a href="#/server-info/providers">Providers</a></li>
</ul>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="kc-table-actions" colspan="5">
<div class="form-inline">
<div class="form-group">
<div class="input-group">
<input type="text" placeholder="Search..." data-ng-model="search" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
</div>
</div>
</div>
</th>
</tr>
<tr>
<th width="20%">SPI</th>
<th>Providers</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="spi in (providers | filter:search)">
<td>{{spi.name}}</td>
<td>
<div data-ng-repeat="(providerName, provider) in spi.providers">
{{providerName}}
<span ng-show="provider.operationalInfo">
<button type="button" class="btn btn-default btn-xs" ng-click="collapseRep = !collapseRep">
<span class="glyphicon glyphicon-plus" data-ng-show="!collapseRep"></span>
<span class="glyphicon glyphicon-minus" data-ng-show="collapseRep"></span>
</button>
<table ng-show="collapseRep" class="table table-striped table-bordered" style="margin-top: 0px;">
<tr ng-repeat="(key, value) in provider.operationalInfo">
<td width="20%">{{key}}</td>
<td>{{value}}</td>
</tr>
</table>
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<kc-menu></kc-menu>

View file

@ -1,66 +1,104 @@
<div class="col-md-12">
<h1>Server Info</h1>
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1>
Server Info
<i id="serverInfoReload" class="pficon pficon-restart clickable" data-ng-click="serverInfoReload()"></i>
</h1>
<ul class="nav nav-tabs">
<li class="active"><a href="#/server-info">Info</a></li>
<li><a href="#/server-info/providers">Providers</a></li>
</ul>
<table class="table table-striped table-bordered">
<tr>
<td>Version</td>
<td>{{serverInfo.version}}</td>
<td width="20%">Keycloak Version</td>
<td>{{serverInfo.systemInfo.version}}</td>
</tr>
<tr>
<td>Server Time</td>
<td>{{serverInfo.serverTime}} (<a data-ng-click="serverInfoUpdate()">update</a>)</td>
<td>{{serverInfo.systemInfo.serverTime}}</td>
</tr>
<tr>
<td>Server Uptime</td>
<td>{{serverInfo.systemInfo.uptime}}</td>
</tr>
</table>
<fieldset>
<legend collapsed>Providers</legend>
<div class="form-group">
<h3>Public SPIs</h3>
<kc-tooltip>For public SPIs there are built-in providers, but it's also supported to write your own custom providers.</kc-tooltip>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>SPI</th>
<th>Providers</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="spi in (serverInfo.providers | filter:{internal:false} | orderBy:'name')">
<td>{{spi.name}}</td>
<td>
<div data-ng-repeat="provider in (spi.implementations | orderBy:'toString()')">
{{provider}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="form-group">
<h3>Internal SPIs</h3>
<kc-tooltip>For internal SPIs there are only built-in providers. It's not recommended to write your own custom providers as internal SPIs may change or be removed without notice.</kc-tooltip>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>SPI</th>
<th>Providers</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="spi in (serverInfo.providers | filter:{internal:true} | orderBy:'name')">
<td>{{spi.name}}</td>
<td>
<div data-ng-repeat="provider in (spi.implementations | orderBy:'toString()')">
{{provider}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
<legend>Memory</legend>
<table class="table table-striped table-bordered" style="margin-top: 0;">
<tr>
<td width="20%">Total Memory</td>
<td>{{serverInfo.memoryInfo.totalFormated}}</td>
</tr>
<tr>
<td>Free Memory</td>
<td>{{serverInfo.memoryInfo.freeFormated}} ({{serverInfo.memoryInfo.freePercentage}}%)</td>
</tr>
<tr>
<td>Used Memory</td>
<td>{{serverInfo.memoryInfo.usedFormated}}</td>
</tr>
</table>
</fieldset>
</div>
<fieldset>
<legend>System</legend>
<table class="table table-striped table-bordered" style="margin-top: 0;">
<tr>
<td width="20%">Current Working Directory</td>
<td>{{serverInfo.systemInfo.userDir}}</td>
</tr>
<tr>
<td>Java Version</td>
<td>{{serverInfo.systemInfo.javaVersion}}</td>
</tr>
<tr>
<td>Java Vendor</td>
<td>{{serverInfo.systemInfo.javaVendor}}</td>
</tr>
<tr>
<td>Java Runtime</td>
<td>{{serverInfo.systemInfo.javaRuntime}}</td>
</tr>
<tr>
<td>Java VM</td>
<td>{{serverInfo.systemInfo.javaVm}}</td>
</tr>
<tr>
<td>Java VM Version</td>
<td>{{serverInfo.systemInfo.javaVmVersion}}</td>
</tr>
<tr>
<td>Java Home</td>
<td>{{serverInfo.systemInfo.javaHome}}</td>
</tr>
<tr>
<td>User Name</td>
<td>{{serverInfo.systemInfo.userName}}</td>
</tr>
<tr>
<td>User Timezone</td>
<td>{{serverInfo.systemInfo.userTimezone}}</td>
</tr>
<tr>
<td>User Locale</td>
<td>{{serverInfo.systemInfo.userLocale}}</td>
</tr>
<tr>
<td>System Encoding</td>
<td>{{serverInfo.systemInfo.fileEncoding}}</td>
</tr>
<tr>
<td>Operating System</td>
<td>{{serverInfo.systemInfo.osName}} {{serverInfo.systemInfo.osVersion}}</td>
</tr>
<tr>
<td>OS Architecture</td>
<td>{{serverInfo.systemInfo.osArchitecture}}</td>
</tr>
</table>
</fieldset>
</div>
<kc-menu></kc-menu>

View file

@ -18,6 +18,8 @@ public interface KeycloakSessionFactory extends ProviderEventManager {
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id);
List<ProviderFactory> getProviderFactories(Class<? extends Provider> clazz);
long getServerStartupTimestamp();
void close();
}

View file

@ -0,0 +1,20 @@
package org.keycloak.provider;
import java.util.Map;
/**
* Marker interface for ProviderFactory of Provider which wants to show some info on "Server Info" page in Admin console.
*
* @author Vlastimil Elias (velias at redhat dot com)
*/
public interface ServerInfoAwareProviderFactory<T extends Provider> extends ProviderFactory<T> {
/**
* Get operational info about given provider. This info contains informations about providers configuration and operational conditions (eg. errors in connection to remote systems etc) which is
* shown on "Server Info" page.
*
* @return Map with keys describing value and relevant values itself
*/
public Map<String, String> getOperationalInfo();
}

View file

@ -28,6 +28,8 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
private Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<Class<? extends Provider>, Map<String, ProviderFactory>>();
protected CopyOnWriteArrayList<ProviderEventListener> listeners = new CopyOnWriteArrayList<ProviderEventListener>();
protected long serverStartupTimestamp;
@Override
public void register(ProviderEventListener listener) {
listeners.add(listener);
@ -46,6 +48,8 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
}
public void init() {
serverStartupTimestamp = System.currentTimeMillis();
ProviderManager pm = new ProviderManager(getClass().getClassLoader(), Config.scope().getArray("providers"));
for (Spi spi : ServiceLoader.load(Spi.class, getClass().getClassLoader())) {
@ -148,4 +152,12 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory {
return factory.getClass().getPackage().getName().startsWith("org.keycloak");
}
/**
* @return timestamp of Keycloak server startup
*/
@Override
public long getServerStartupTimestamp() {
return serverStartupTimestamp;
}
}

View file

@ -20,6 +20,7 @@ import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.admin.info.ServerInfoAdminResource;
import javax.ws.rs.GET;
import javax.ws.rs.Path;

View file

@ -0,0 +1,54 @@
package org.keycloak.services.resources.admin.info;
public class MemoryInfoRepresentation {
protected long total;
protected long used;
public static MemoryInfoRepresentation create() {
MemoryInfoRepresentation rep = new MemoryInfoRepresentation();
Runtime runtime = Runtime.getRuntime();
rep.total = runtime.maxMemory();
rep.used = runtime.totalMemory() - runtime.freeMemory();
return rep;
}
public long getTotal() {
return total;
}
public String getTotalFormated() {
return formatMemory(getTotal());
}
public long getFree() {
return getTotal() - getUsed();
}
public String getFreeFormated() {
return formatMemory(getFree());
}
public long getUsed() {
return used;
}
public String getUsedFormated() {
return formatMemory(getUsed());
}
public long getFreePercentage() {
return getFree() * 100 / getTotal();
}
private String formatMemory(long bytes) {
if (bytes > 1024L * 1024L) {
return bytes / (1024L * 1024L) + " MB";
} else if (bytes > 1024L) {
return bytes / (1024L) + " kB";
} else {
return bytes + " B";
}
}
}

View file

@ -0,0 +1,17 @@
package org.keycloak.services.resources.admin.info;
import java.util.Map;
public class ProviderRepresentation {
private Map<String, String> operationalInfo;
public Map<String, String> getOperationalInfo() {
return operationalInfo;
}
public void setOperationalInfo(Map<String, String> operationalInfo) {
this.operationalInfo = operationalInfo;
}
}

View file

@ -1,9 +1,7 @@
package org.keycloak.services.resources.admin;
package org.keycloak.services.resources.admin.info;
import org.keycloak.Version;
import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.broker.provider.IdentityProviderFactory;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.OperationType;
import org.keycloak.exportimport.ClientImporter;
@ -18,6 +16,7 @@ import org.keycloak.protocol.LoginProtocolFactory;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
import org.keycloak.provider.Spi;
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@ -26,14 +25,7 @@ import org.keycloak.social.SocialIdentityProvider;
import javax.ws.rs.GET;
import javax.ws.rs.core.Context;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.*;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -53,13 +45,12 @@ public class ServerInfoAdminResource {
@GET
public ServerInfoRepresentation getInfo() {
ServerInfoRepresentation info = new ServerInfoRepresentation();
info.version = Version.VERSION;
info.serverTime = new Date().toString();
info.setSystemInfo(SystemInfoRepresentation.create(session));
info.setMemoryInfo(MemoryInfoRepresentation.create());
setSocialProviders(info);
setIdentityProviders(info);
setThemes(info);
setEventListeners(info);
setProtocols(info);
setClientImporters(info);
setProviders(info);
setProtocolMapperTypes(info);
@ -69,42 +60,70 @@ public class ServerInfoAdminResource {
}
private void setProviders(ServerInfoRepresentation info) {
List<SpiInfoRepresentation> providers = new LinkedList<>();
LinkedHashMap<String, SpiInfoRepresentation> spiReps = new LinkedHashMap<>();
List<Spi> spis = new LinkedList<>();
for (Spi spi : ServiceLoader.load(Spi.class)) {
SpiInfoRepresentation spiRep = new SpiInfoRepresentation();
spiRep.setName(spi.getName());
spiRep.setInternal(spi.isInternal());
spiRep.setImplementations(session.listProviderIds(spi.getProviderClass()));
providers.add(spiRep);
spis.add(spi);
}
info.providers = providers;
Collections.sort(spis, new Comparator<Spi>() {
@Override
public int compare(Spi s1, Spi s2) {
return s1.getName().compareTo(s2.getName());
}
});
for (Spi spi : spis) {
SpiInfoRepresentation spiRep = new SpiInfoRepresentation();
spiRep.setInternal(spi.isInternal());
spiRep.setSystemInfo(ServerInfoAwareProviderFactory.class.isAssignableFrom(spi.getProviderFactoryClass()));
List<String> providerIds = new LinkedList<>(session.listProviderIds(spi.getProviderClass()));
Collections.sort(providerIds);
Map<String, ProviderRepresentation> providers = new HashMap<>();
if (providerIds != null) {
for (String name : providerIds) {
ProviderRepresentation provider = new ProviderRepresentation();
if (spiRep.isSystemInfo()) {
provider.setOperationalInfo(((ServerInfoAwareProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(spi.getProviderClass(), name)).getOperationalInfo());
}
providers.put(name, provider);
}
}
spiRep.setProviders(providers);
spiReps.put(spi.getName(), spiRep);
}
info.setProviders(spiReps);
}
private void setThemes(ServerInfoRepresentation info) {
ThemeProvider themeProvider = session.getProvider(ThemeProvider.class, "extending");
info.themes = new HashMap<String, List<String>>();
info.setThemes(new HashMap<String, List<String>>());
for (Theme.Type type : Theme.Type.values()) {
List<String> themes = new LinkedList<String>(themeProvider.nameSet(type));
Collections.sort(themes);
info.themes.put(type.toString().toLowerCase(), themes);
info.getThemes().put(type.toString().toLowerCase(), themes);
}
}
private void setSocialProviders(ServerInfoRepresentation info) {
info.socialProviders = new LinkedList<>();
info.setSocialProviders(new LinkedList<Map<String, String>>());
List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class);
setIdentityProviders(providerFactories, info.socialProviders, "Social");
setIdentityProviders(providerFactories, info.getSocialProviders(), "Social");
}
private void setIdentityProviders(ServerInfoRepresentation info) {
info.identityProviders = new LinkedList<>();
info.setIdentityProviders(new LinkedList<Map<String, String>>());
List<ProviderFactory> providerFactories = session.getKeycloakSessionFactory().getProviderFactories(IdentityProvider.class);
setIdentityProviders(providerFactories, info.identityProviders, "User-defined");
setIdentityProviders(providerFactories, info.getIdentityProviders(), "User-defined");
providerFactories = session.getKeycloakSessionFactory().getProviderFactories(SocialIdentityProvider.class);
setIdentityProviders(providerFactories, info.identityProviders, "Social");
setIdentityProviders(providerFactories, info.getIdentityProviders(), "Social");
}
public void setIdentityProviders(List<ProviderFactory> factories, List<Map<String, String>> providers, String groupName) {
@ -119,31 +138,14 @@ public class ServerInfoAdminResource {
}
}
private void setEventListeners(ServerInfoRepresentation info) {
info.eventListeners = new LinkedList<String>();
Set<String> providers = session.listProviderIds(EventListenerProvider.class);
if (providers != null) {
info.eventListeners.addAll(providers);
}
}
private void setProtocols(ServerInfoRepresentation info) {
info.protocols = new LinkedList<String>();
for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class)) {
info.protocols.add(p.getId());
}
Collections.sort(info.protocols);
}
private void setProtocolMapperTypes(ServerInfoRepresentation info) {
info.protocolMapperTypes = new HashMap<String, List<ProtocolMapperTypeRepresentation>>();
info.setProtocolMapperTypes(new HashMap<String, List<ProtocolMapperTypeRepresentation>>());
for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(ProtocolMapper.class)) {
ProtocolMapper mapper = (ProtocolMapper)p;
List<ProtocolMapperTypeRepresentation> types = info.protocolMapperTypes.get(mapper.getProtocol());
List<ProtocolMapperTypeRepresentation> types = info.getProtocolMapperTypes().get(mapper.getProtocol());
if (types == null) {
types = new LinkedList<ProtocolMapperTypeRepresentation>();
info.protocolMapperTypes.put(mapper.getProtocol(), types);
info.getProtocolMapperTypes().put(mapper.getProtocol(), types);
}
ProtocolMapperTypeRepresentation rep = new ProtocolMapperTypeRepresentation();
rep.setId(mapper.getId());
@ -166,136 +168,25 @@ public class ServerInfoAdminResource {
}
private void setBuiltinProtocolMappers(ServerInfoRepresentation info) {
info.builtinProtocolMappers = new HashMap<>();
info.setBuiltinProtocolMappers(new HashMap<String, List<ProtocolMapperRepresentation>>());
for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(LoginProtocol.class)) {
LoginProtocolFactory factory = (LoginProtocolFactory)p;
List<ProtocolMapperRepresentation> mappers = new LinkedList<>();
for (ProtocolMapperModel mapper : factory.getBuiltinMappers()) {
mappers.add(ModelToRepresentation.toRepresentation(mapper));
}
info.builtinProtocolMappers.put(p.getId(), mappers);
info.getBuiltinProtocolMappers().put(p.getId(), mappers);
}
}
private void setClientImporters(ServerInfoRepresentation info) {
info.clientImporters = new LinkedList<Map<String, String>>();
info.setClientImporters(new LinkedList<Map<String, String>>());
for (ProviderFactory p : session.getKeycloakSessionFactory().getProviderFactories(ClientImporter.class)) {
ClientImporterFactory factory = (ClientImporterFactory)p;
Map<String, String> data = new HashMap<String, String>();
data.put("id", factory.getId());
data.put("name", factory.getDisplayName());
info.clientImporters.add(data);
}
}
public static class ServerInfoRepresentation {
private String version;
private String serverTime;
private Map<String, List<String>> themes;
private List<Map<String, String>> socialProviders;
public List<Map<String, String>> identityProviders;
private List<String> protocols;
private List<Map<String, String>> clientImporters;
private List<SpiInfoRepresentation> providers;
private List<String> eventListeners;
private Map<String, List<ProtocolMapperTypeRepresentation>> protocolMapperTypes;
private Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers;
private Map<String, List<String>> enums;
public ServerInfoRepresentation() {
}
public String getServerTime() {
return serverTime;
}
public String getVersion() {
return version;
}
public Map<String, List<String>> getThemes() {
return themes;
}
public List<Map<String, String>> getSocialProviders() {
return socialProviders;
}
public List<Map<String, String>> getIdentityProviders() {
return this.identityProviders;
}
public List<String> getEventListeners() {
return eventListeners;
}
public List<String> getProtocols() {
return protocols;
}
public List<Map<String, String>> getClientImporters() {
return clientImporters;
}
public List<SpiInfoRepresentation> getProviders() {
return providers;
}
public Map<String, List<ProtocolMapperTypeRepresentation>> getProtocolMapperTypes() {
return protocolMapperTypes;
}
public Map<String, List<ProtocolMapperRepresentation>> getBuiltinProtocolMappers() {
return builtinProtocolMappers;
}
public void setBuiltinProtocolMappers(Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers) {
this.builtinProtocolMappers = builtinProtocolMappers;
}
public Map<String, List<String>> getEnums() {
return enums;
}
public void setEnums(Map<String, List<String>> enums) {
this.enums = enums;
}
}
public static class SpiInfoRepresentation {
private String name;
private boolean internal;
private Set<String> implementations;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isInternal() {
return internal;
}
public void setInternal(boolean internal) {
this.internal = internal;
}
public Set<String> getImplementations() {
return implementations;
}
public void setImplementations(Set<String> implementations) {
this.implementations = implementations;
info.getClientImporters().add(data);
}
}

View file

@ -0,0 +1,108 @@
package org.keycloak.services.resources.admin.info;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
import org.keycloak.representations.idm.ProtocolMapperTypeRepresentation;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ServerInfoRepresentation {
private SystemInfoRepresentation systemInfo;
private MemoryInfoRepresentation memoryInfo;
private Map<String, List<String>> themes;
private List<Map<String, String>> socialProviders;
private List<Map<String, String>> identityProviders;
private List<Map<String, String>> clientImporters;
private Map<String, SpiInfoRepresentation> providers;
private Map<String, List<ProtocolMapperTypeRepresentation>> protocolMapperTypes;
private Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers;
private Map<String, List<String>> enums;
public SystemInfoRepresentation getSystemInfo() {
return systemInfo;
}
public void setSystemInfo(SystemInfoRepresentation systemInfo) {
this.systemInfo = systemInfo;
}
public MemoryInfoRepresentation getMemoryInfo() {
return memoryInfo;
}
public void setMemoryInfo(MemoryInfoRepresentation memoryInfo) {
this.memoryInfo = memoryInfo;
}
public Map<String, List<String>> getThemes() {
return themes;
}
public void setThemes(Map<String, List<String>> themes) {
this.themes = themes;
}
public List<Map<String, String>> getSocialProviders() {
return socialProviders;
}
public void setSocialProviders(List<Map<String, String>> socialProviders) {
this.socialProviders = socialProviders;
}
public List<Map<String, String>> getIdentityProviders() {
return identityProviders;
}
public void setIdentityProviders(List<Map<String, String>> identityProviders) {
this.identityProviders = identityProviders;
}
public List<Map<String, String>> getClientImporters() {
return clientImporters;
}
public void setClientImporters(List<Map<String, String>> clientImporters) {
this.clientImporters = clientImporters;
}
public Map<String, SpiInfoRepresentation> getProviders() {
return providers;
}
public void setProviders(Map<String, SpiInfoRepresentation> providers) {
this.providers = providers;
}
public Map<String, List<ProtocolMapperTypeRepresentation>> getProtocolMapperTypes() {
return protocolMapperTypes;
}
public void setProtocolMapperTypes(Map<String, List<ProtocolMapperTypeRepresentation>> protocolMapperTypes) {
this.protocolMapperTypes = protocolMapperTypes;
}
public Map<String, List<ProtocolMapperRepresentation>> getBuiltinProtocolMappers() {
return builtinProtocolMappers;
}
public void setBuiltinProtocolMappers(Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers) {
this.builtinProtocolMappers = builtinProtocolMappers;
}
public Map<String, List<String>> getEnums() {
return enums;
}
public void setEnums(Map<String, List<String>> enums) {
this.enums = enums;
}
}

View file

@ -0,0 +1,39 @@
package org.keycloak.services.resources.admin.info;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class SpiInfoRepresentation {
private boolean internal;
private boolean systemInfo;
private Map<String, ProviderRepresentation> providers;
public boolean isInternal() {
return internal;
}
public void setInternal(boolean internal) {
this.internal = internal;
}
public boolean isSystemInfo() {
return systemInfo;
}
public void setSystemInfo(boolean systemInfo) {
this.systemInfo = systemInfo;
}
public Map<String, ProviderRepresentation> getProviders() {
return providers;
}
public void setProviders(Map<String, ProviderRepresentation> providers) {
this.providers = providers;
}
}

View file

@ -0,0 +1,217 @@
package org.keycloak.services.resources.admin.info;
import org.keycloak.Version;
import org.keycloak.models.KeycloakSession;
import java.util.Date;
import java.util.Locale;
public class SystemInfoRepresentation {
private String version;
private String serverTime;
private String uptime;
private long uptimeMillis;
private String javaVersion;
private String javaVendor;
private String javaVm;
private String javaVmVersion;
private String javaRuntime;
private String javaHome;
private String osName;
private String osArchitecture;
private String osVersion;
private String fileEncoding;
private String userName;
private String userDir;
private String userTimezone;
private String userLocale;
public static SystemInfoRepresentation create(KeycloakSession session) {
SystemInfoRepresentation rep = new SystemInfoRepresentation();
rep.version = Version.VERSION;
rep.serverTime = new Date().toString();
rep.uptimeMillis = System.currentTimeMillis() - session.getKeycloakSessionFactory().getServerStartupTimestamp();
rep.uptime = formatUptime(rep.uptimeMillis);
rep.javaVersion = System.getProperty("java.version");
rep.javaVendor = System.getProperty("java.vendor");
rep.javaVm = System.getProperty("java.vm.name");
rep.javaVmVersion = System.getProperty("java.vm.version");
rep.javaRuntime = System.getProperty("java.runtime.name");
rep.javaHome = System.getProperty("java.home");
rep.osName = System.getProperty("os.name");
rep.osArchitecture = System.getProperty("os.arch");
rep.osVersion = System.getProperty("os.version");
rep.fileEncoding = System.getProperty("file.encoding");
rep.userName = System.getProperty("user.name");
rep.userDir = System.getProperty("user.dir");
rep.userTimezone = System.getProperty("user.timezone");
rep.userLocale = (new Locale(System.getProperty("user.country"), System.getProperty("user.language")).toString());
return rep;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getServerTime() {
return serverTime;
}
public void setServerTime(String serverTime) {
this.serverTime = serverTime;
}
public String getUptime() {
return uptime;
}
public void setUptime(String uptime) {
this.uptime = uptime;
}
public long getUptimeMillis() {
return uptimeMillis;
}
public void setUptimeMillis(long uptimeMillis) {
this.uptimeMillis = uptimeMillis;
}
public String getJavaVersion() {
return javaVersion;
}
public void setJavaVersion(String javaVersion) {
this.javaVersion = javaVersion;
}
public String getJavaVendor() {
return javaVendor;
}
public void setJavaVendor(String javaVendor) {
this.javaVendor = javaVendor;
}
public String getJavaVm() {
return javaVm;
}
public void setJavaVm(String javaVm) {
this.javaVm = javaVm;
}
public String getJavaVmVersion() {
return javaVmVersion;
}
public void setJavaVmVersion(String javaVmVersion) {
this.javaVmVersion = javaVmVersion;
}
public String getJavaRuntime() {
return javaRuntime;
}
public void setJavaRuntime(String javaRuntime) {
this.javaRuntime = javaRuntime;
}
public String getJavaHome() {
return javaHome;
}
public void setJavaHome(String javaHome) {
this.javaHome = javaHome;
}
public String getOsName() {
return osName;
}
public void setOsName(String osName) {
this.osName = osName;
}
public String getOsArchitecture() {
return osArchitecture;
}
public void setOsArchitecture(String osArchitecture) {
this.osArchitecture = osArchitecture;
}
public String getOsVersion() {
return osVersion;
}
public void setOsVersion(String osVersion) {
this.osVersion = osVersion;
}
public String getFileEncoding() {
return fileEncoding;
}
public void setFileEncoding(String fileEncoding) {
this.fileEncoding = fileEncoding;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserDir() {
return userDir;
}
public void setUserDir(String userDir) {
this.userDir = userDir;
}
public String getUserTimezone() {
return userTimezone;
}
public void setUserTimezone(String userTimezone) {
this.userTimezone = userTimezone;
}
public String getUserLocale() {
return userLocale;
}
public void setUserLocale(String userLocale) {
this.userLocale = userLocale;
}
private static String formatUptime(long uptime) {
long diffInSeconds = uptime / 1000;
long diff[] = new long[]{0, 0, 0, 0}; // sec
diff[3] = (diffInSeconds >= 60 ? diffInSeconds % 60 : diffInSeconds); // min
diff[2] = (diffInSeconds = (diffInSeconds / 60)) >= 60 ? diffInSeconds % 60 : diffInSeconds; // hours
diff[1] = (diffInSeconds = (diffInSeconds / 60)) >= 24 ? diffInSeconds % 24 : diffInSeconds; // days
diff[0] = (diffInSeconds = (diffInSeconds / 24));
return String.format(
"%d day%s, %d hour%s, %d minute%s, %d second%s",
diff[0],
diff[0] != 1 ? "s" : "",
diff[1],
diff[1] != 1 ? "s" : "",
diff[2],
diff[2] != 1 ? "s" : "",
diff[3],
diff[3] != 1 ? "s" : "");
}
}

View file

@ -25,6 +25,7 @@ import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.keycloak.Config;
import org.keycloak.Version;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
@ -55,6 +56,7 @@ import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
@ -295,4 +297,37 @@ public class AdminAPITest {
testCreateRealm("/admin-test/testrealm.json");
}
@Test
public void testServerInfo() {
String token = createToken();
final String authHeader = "Bearer " + token;
ClientRequestFilter authFilter = new ClientRequestFilter() {
@Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, authHeader);
}
};
Client client = ClientBuilder.newBuilder().register(authFilter).build();
UriBuilder authBase = UriBuilder.fromUri("http://localhost:8081/auth");
WebTarget target = client.target(AdminRoot.adminBaseUrl(authBase).path("serverinfo"));
Map<?, ?> response = target.request().accept("application/json").get(Map.class);
System.out.println(response.keySet().toString());
Assert.assertNotNull(response);
Assert.assertNotNull(response.get("providers"));
Assert.assertNotNull(response.get("themes"));
Assert.assertNotNull(response.get("enums"));
Assert.assertNotNull(response.get("memoryInfo"));
Assert.assertNotNull(response.get("systemInfo"));
Map<?, ?> systemInfo = (Map<?, ?>) response.get("systemInfo");
Assert.assertEquals(Version.VERSION, systemInfo.get("version"));
Assert.assertNotNull(systemInfo.get("serverTime"));
Assert.assertNotNull(systemInfo.get("uptime"));
}
}