keycloak-memoizeit
Changes
connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java 399(+226 -173)
connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProviderFactory.java 5(+3 -2)
connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java 410(+219 -191)
connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProviderFactory.java 4(+2 -2)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html 130(+96 -34)
services/pom.xml 15(+15 -0)
Details
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
index 61bbafa..36f3c09 100755
--- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
@@ -1,189 +1,242 @@
package org.keycloak.connections.jpa;
-import org.hibernate.ejb.AvailableSettings;
-import org.jboss.logging.Logger;
-import org.keycloak.Config;
-import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.KeycloakSessionFactory;
-
-import javax.naming.InitialContext;
-import javax.persistence.EntityManager;
-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.Map;
+import javax.naming.InitialContext;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.Persistence;
+import javax.sql.DataSource;
+
+import org.hibernate.ejb.AvailableSettings;
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderOperationalInfo;
+
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultJpaConnectionProviderFactory implements JpaConnectionProviderFactory {
- private static final Logger logger = Logger.getLogger(DefaultJpaConnectionProviderFactory.class);
-
- private volatile EntityManagerFactory emf;
-
- private Config.Scope config;
-
- @Override
- public JpaConnectionProvider create(KeycloakSession session) {
- lazyInit(session);
-
- EntityManager em = emf.createEntityManager();
- em = PersistenceExceptionConverter.create(em);
- session.getTransaction().enlist(new JpaKeycloakTransaction(em));
- return new DefaultJpaConnectionProvider(em);
- }
-
- @Override
- public void close() {
- if (emf != null) {
- emf.close();
- }
- }
-
- @Override
- public String getId() {
- return "default";
- }
-
- @Override
- public void init(Config.Scope config) {
- this.config = config;
- }
-
- @Override
- public void postInit(KeycloakSessionFactory factory) {
-
- }
-
- private void lazyInit(KeycloakSession session) {
- if (emf == null) {
- synchronized (this) {
- if (emf == null) {
- logger.debug("Initializing JPA connections");
-
- Connection connection = null;
-
- String databaseSchema = config.get("databaseSchema");
-
- Map<String, Object> properties = new HashMap<String, Object>();
-
- String unitName = "keycloak-default";
-
- String dataSource = config.get("dataSource");
- if (dataSource != null) {
- if (config.getBoolean("jta", false)) {
- properties.put(AvailableSettings.JTA_DATASOURCE, dataSource);
- } else {
- properties.put(AvailableSettings.NON_JTA_DATASOURCE, dataSource);
- }
- } else {
- properties.put(AvailableSettings.JDBC_URL, config.get("url"));
- properties.put(AvailableSettings.JDBC_DRIVER, config.get("driver"));
-
- String user = config.get("user");
- if (user != null) {
- properties.put(AvailableSettings.JDBC_USER, user);
- }
- String password = config.get("password");
- if (password != null) {
- properties.put(AvailableSettings.JDBC_PASSWORD, password);
- }
- }
-
- String driverDialect = config.get("driverDialect");
- if (driverDialect != null && driverDialect.length() > 0) {
- properties.put("hibernate.dialect", driverDialect);
- }
-
- String schema = config.get("schema");
- if (schema != null) {
- properties.put("hibernate.default_schema", schema);
- }
-
- if (databaseSchema != null) {
- if (databaseSchema.equals("development-update")) {
- properties.put("hibernate.hbm2ddl.auto", "update");
- databaseSchema = null;
- } else if (databaseSchema.equals("development-validate")) {
- properties.put("hibernate.hbm2ddl.auto", "validate");
- databaseSchema = null;
- }
- }
-
- 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");
-
- 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);
- }
- }
- }
- }
- }
- }
-
- private Connection getConnection() {
- try {
- String dataSourceLookup = config.get("dataSource");
- if (dataSourceLookup != null) {
- DataSource dataSource = (DataSource) new InitialContext().lookup(dataSourceLookup);
- return dataSource.getConnection();
- } else {
- Class.forName(config.get("driver"));
- return DriverManager.getConnection(config.get("url"), config.get("user"), config.get("password"));
- }
- } catch (Exception e) {
- throw new RuntimeException("Failed to connect to database", e);
- }
- }
+ private static final Logger logger = Logger.getLogger(DefaultJpaConnectionProviderFactory.class);
+
+ private volatile EntityManagerFactory emf;
+
+ private Config.Scope config;
+
+ private DatabaseInfo databaseInfo;
+
+ @Override
+ public JpaConnectionProvider create(KeycloakSession session) {
+ lazyInit(session);
+
+ EntityManager em = emf.createEntityManager();
+ em = PersistenceExceptionConverter.create(em);
+ session.getTransaction().enlist(new JpaKeycloakTransaction(em));
+ return new DefaultJpaConnectionProvider(em);
+ }
+
+ @Override
+ public void close() {
+ if (emf != null) {
+ emf.close();
+ }
+ }
+
+ @Override
+ public String getId() {
+ return "default";
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ this.config = config;
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ private void lazyInit(KeycloakSession session) {
+ if (emf == null) {
+ synchronized (this) {
+ if (emf == null) {
+ logger.debug("Initializing JPA connections");
+
+ Connection connection = null;
+
+ String databaseSchema = config.get("databaseSchema");
+
+ Map<String, Object> properties = new HashMap<String, Object>();
+
+ String unitName = "keycloak-default";
+
+ String dataSource = config.get("dataSource");
+ if (dataSource != null) {
+ if (config.getBoolean("jta", false)) {
+ properties.put(AvailableSettings.JTA_DATASOURCE, dataSource);
+ } else {
+ properties.put(AvailableSettings.NON_JTA_DATASOURCE, dataSource);
+ }
+ } else {
+ properties.put(AvailableSettings.JDBC_URL, config.get("url"));
+ properties.put(AvailableSettings.JDBC_DRIVER, config.get("driver"));
+
+ String user = config.get("user");
+ if (user != null) {
+ properties.put(AvailableSettings.JDBC_USER, user);
+ }
+ String password = config.get("password");
+ if (password != null) {
+ properties.put(AvailableSettings.JDBC_PASSWORD, password);
+ }
+ }
+
+ String driverDialect = config.get("driverDialect");
+ if (driverDialect != null && driverDialect.length() > 0) {
+ properties.put("hibernate.dialect", driverDialect);
+ }
+
+ String schema = config.get("schema");
+ if (schema != null) {
+ properties.put("hibernate.default_schema", schema);
+ }
+
+ if (databaseSchema != null) {
+ if (databaseSchema.equals("development-update")) {
+ properties.put("hibernate.hbm2ddl.auto", "update");
+ databaseSchema = null;
+ } else if (databaseSchema.equals("development-validate")) {
+ properties.put("hibernate.hbm2ddl.auto", "validate");
+ databaseSchema = null;
+ }
+ }
+
+ properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
+ properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
+
+ connection = getConnection();
+ prepareDatabaseInfo(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");
+
+ // 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 prepareDatabaseInfo(Connection connection) {
+ try {
+ databaseInfo = new DatabaseInfo();
+ DatabaseMetaData md = connection.getMetaData();
+ databaseInfo.databaseDriver = md.getDriverName() + " " + md.getDriverVersion();
+ databaseInfo.databaseProduct = md.getDatabaseProductName() + " " + md.getDatabaseProductVersion();
+ databaseInfo.databaseUser = md.getUserName();
+ databaseInfo.jdbcUrl = md.getURL();
+ } catch (SQLException e) {
+ logger.warn("Unable to get database info due " + e.getMessage());
+ }
+ }
+
+ private Connection getConnection() {
+ try {
+ String dataSourceLookup = config.get("dataSource");
+ if (dataSourceLookup != null) {
+ DataSource dataSource = (DataSource) new InitialContext().lookup(dataSourceLookup);
+ return dataSource.getConnection();
+ } else {
+ Class.forName(config.get("driver"));
+ return DriverManager.getConnection(config.get("url"), config.get("user"), config.get("password"));
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to connect to database", e);
+ }
+ }
+
+ @Override
+ public DatabaseInfo getOperationalInfo() {
+ return databaseInfo;
+ }
+
+ public static class DatabaseInfo implements ProviderOperationalInfo {
+ protected String jdbcUrl;
+ protected String databaseUser;
+ protected String databaseProduct;
+ protected String databaseDriver;
+
+ public String getJdbcUrl() {
+ return jdbcUrl;
+ }
+
+ public String getDatabaseDriver() {
+ return databaseDriver;
+ }
+
+ public String getDatabaseUser() {
+ return databaseUser;
+ }
+
+ public String getDatabaseProduct() {
+ return databaseProduct;
+ }
+
+ @Override
+ public boolean isOk() {
+ // TODO KEYCLOAK-1578 - implement operational monitoring of JPA DB connection
+ return true;
+ }
+ }
}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProviderFactory.java
index 1cf4a5f..ab2b119 100644
--- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProviderFactory.java
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/JpaConnectionProviderFactory.java
@@ -1,9 +1,10 @@
package org.keycloak.connections.jpa;
-import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.MonitorableProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
-public interface JpaConnectionProviderFactory extends ProviderFactory<JpaConnectionProvider> {
+public interface JpaConnectionProviderFactory extends MonitorableProviderFactory<JpaConnectionProvider> {
+
}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index 36be680..c8d1cba 100755
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -1,10 +1,11 @@
package org.keycloak.connections.mongo;
-import com.mongodb.DB;
-import com.mongodb.MongoClient;
-import com.mongodb.MongoClientOptions;
-import com.mongodb.MongoCredential;
-import com.mongodb.ServerAddress;
+import java.lang.reflect.Method;
+import java.net.UnknownHostException;
+import java.util.Collections;
+
+import javax.net.ssl.SSLSocketFactory;
+
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.mongo.api.MongoStore;
@@ -13,198 +14,225 @@ import org.keycloak.connections.mongo.impl.context.TransactionMongoStoreInvocati
import org.keycloak.connections.mongo.updater.MongoUpdaterProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.provider.ProviderOperationalInfo;
-import javax.net.ssl.SSLSocketFactory;
-import java.lang.reflect.Method;
-import java.net.UnknownHostException;
-import java.util.Collections;
+import com.mongodb.DB;
+import com.mongodb.MongoClient;
+import com.mongodb.MongoClientOptions;
+import com.mongodb.MongoCredential;
+import com.mongodb.ServerAddress;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultMongoConnectionFactoryProvider implements MongoConnectionProviderFactory {
- // TODO Make configurable
- private String[] entities = new String[]{
- "org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity",
- "org.keycloak.models.mongo.keycloak.entities.MongoUserEntity",
- "org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity",
- "org.keycloak.models.entities.IdentityProviderEntity",
- "org.keycloak.models.entities.ClientIdentityProviderMappingEntity",
- "org.keycloak.models.entities.RequiredCredentialEntity",
- "org.keycloak.models.entities.CredentialEntity",
- "org.keycloak.models.entities.FederatedIdentityEntity",
- "org.keycloak.models.mongo.keycloak.entities.MongoClientEntity",
- "org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity",
- "org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity",
- "org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity",
- "org.keycloak.models.entities.UserFederationProviderEntity",
- "org.keycloak.models.entities.UserFederationMapperEntity",
- "org.keycloak.models.entities.ProtocolMapperEntity",
- "org.keycloak.models.entities.IdentityProviderMapperEntity",
- "org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity",
- "org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity",
- "org.keycloak.models.entities.AuthenticationExecutionEntity",
- "org.keycloak.models.entities.AuthenticationFlowEntity",
- "org.keycloak.models.entities.AuthenticatorConfigEntity",
- "org.keycloak.models.entities.RequiredActionProviderEntity",
- };
-
- private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
-
- private volatile MongoClient client;
-
- private MongoStore mongoStore;
- private DB db;
- protected Config.Scope config;
-
- @Override
- public MongoConnectionProvider create(KeycloakSession session) {
- lazyInit(session);
-
- TransactionMongoStoreInvocationContext invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
- session.getTransaction().enlist(new MongoKeycloakTransaction(invocationContext));
- return new DefaultMongoConnectionProvider(db, mongoStore, invocationContext);
- }
-
- @Override
- public void init(Config.Scope config) {
- this.config = config;
- }
-
- @Override
- public void postInit(KeycloakSessionFactory factory) {
-
- }
-
-
- private void lazyInit(KeycloakSession session) {
- if (client == null) {
- synchronized (this) {
- if (client == null) {
- try {
- this.client = createMongoClient();
-
- String dbName = config.get("db", "keycloak");
- this.db = client.getDB(dbName);
-
- String databaseSchema = config.get("databaseSchema");
- if (databaseSchema != null) {
- if (databaseSchema.equals("update")) {
- MongoUpdaterProvider mongoUpdater = session.getProvider(MongoUpdaterProvider.class);
-
- if (mongoUpdater == null) {
- throw new RuntimeException("Can't update database: Mongo updater provider not found");
- }
-
- mongoUpdater.update(session, db);
- } else {
- throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
- }
- }
-
- this.mongoStore = new MongoStoreImpl(db, getManagedEntities());
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
- }
- }
-
- private Class[] getManagedEntities() throws ClassNotFoundException {
- Class[] entityClasses = new Class[entities.length];
- for (int i = 0; i < entities.length; i++) {
- entityClasses[i] = Thread.currentThread().getContextClassLoader().loadClass(entities[i]);
- }
- return entityClasses;
- }
-
- @Override
- public void close() {
- if (client != null) {
- client.close();
- }
- }
-
- @Override
- public String getId() {
- return "default";
- }
-
- /**
- * Override this method if you want more possibility to configure Mongo client. It can be also used to inject mongo client
- * from different source.
- *
- * This method can assume that "config" is already set and can use it.
- *
- * @return mongoClient instance, which will be shared for whole Keycloak
- *
- * @throws UnknownHostException
- */
- protected MongoClient createMongoClient() throws UnknownHostException {
- String host = config.get("host", ServerAddress.defaultHost());
- int port = config.getInt("port", ServerAddress.defaultPort());
- String dbName = config.get("db", "keycloak");
-
- String user = config.get("user");
- String password = config.get("password");
-
- MongoClientOptions clientOptions = getClientOptions();
-
- MongoClient client;
- if (user != null && password != null) {
- MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
- client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
- } else {
- client = new MongoClient(new ServerAddress(host, port), clientOptions);
- }
-
- logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
- return client;
- }
-
- protected MongoClientOptions getClientOptions() {
- MongoClientOptions.Builder builder = MongoClientOptions.builder();
- checkIntOption("connectionsPerHost", builder);
- checkIntOption("threadsAllowedToBlockForConnectionMultiplier", builder);
- checkIntOption("maxWaitTime", builder);
- checkIntOption("connectTimeout", builder);
- checkIntOption("socketTimeout", builder);
- checkBooleanOption("socketKeepAlive", builder);
- checkBooleanOption("autoConnectRetry", builder);
- if (config.getLong("maxAutoConnectRetryTime") != null) {
- builder.maxAutoConnectRetryTime(config.getLong("maxAutoConnectRetryTime"));
- }
- if(config.getBoolean("ssl", false)) {
- builder.socketFactory(SSLSocketFactory.getDefault());
- }
-
- return builder.build();
- }
-
- protected void checkBooleanOption(String optionName, MongoClientOptions.Builder builder) {
- Boolean val = config.getBoolean(optionName);
- if (val != null) {
- try {
- Method m = MongoClientOptions.Builder.class.getMethod(optionName, boolean.class);
- m.invoke(builder, val);
- } catch (Exception e) {
- throw new IllegalStateException("Problem configuring boolean option " + optionName + " for mongo client. Ensure you used correct value true or false and if this option is supported by mongo driver", e);
- }
- }
- }
-
- protected void checkIntOption(String optionName, MongoClientOptions.Builder builder) {
- Integer val = config.getInt(optionName);
- if (val != null) {
- try {
- Method m = MongoClientOptions.Builder.class.getMethod(optionName, int.class);
- m.invoke(builder, val);
- } catch (Exception e) {
- throw new IllegalStateException("Problem configuring int option " + optionName + " for mongo client. Ensure you used correct value (number) and if this option is supported by mongo driver", e);
- }
- }
- }
+ // TODO Make configurable
+ private String[] entities = new String[] { "org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity", "org.keycloak.models.mongo.keycloak.entities.MongoUserEntity", "org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity",
+ "org.keycloak.models.entities.IdentityProviderEntity", "org.keycloak.models.entities.ClientIdentityProviderMappingEntity", "org.keycloak.models.entities.RequiredCredentialEntity", "org.keycloak.models.entities.CredentialEntity",
+ "org.keycloak.models.entities.FederatedIdentityEntity", "org.keycloak.models.mongo.keycloak.entities.MongoClientEntity", "org.keycloak.models.sessions.mongo.entities.MongoUsernameLoginFailureEntity",
+ "org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity", "org.keycloak.models.sessions.mongo.entities.MongoClientSessionEntity", "org.keycloak.models.entities.UserFederationProviderEntity",
+ "org.keycloak.models.entities.UserFederationMapperEntity", "org.keycloak.models.entities.ProtocolMapperEntity", "org.keycloak.models.entities.IdentityProviderMapperEntity",
+ "org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity", "org.keycloak.models.mongo.keycloak.entities.MongoMigrationModelEntity", "org.keycloak.models.entities.AuthenticationExecutionEntity",
+ "org.keycloak.models.entities.AuthenticationFlowEntity", "org.keycloak.models.entities.AuthenticatorConfigEntity", "org.keycloak.models.entities.RequiredActionProviderEntity", };
+
+ private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
+
+ private volatile MongoClient client;
+
+ private MongoStore mongoStore;
+ private DB db;
+ protected Config.Scope config;
+
+ private MongoDbInfo mongoDbInfo;
+
+ @Override
+ public MongoConnectionProvider create(KeycloakSession session) {
+ lazyInit(session);
+
+ TransactionMongoStoreInvocationContext invocationContext = new TransactionMongoStoreInvocationContext(mongoStore);
+ session.getTransaction().enlist(new MongoKeycloakTransaction(invocationContext));
+ return new DefaultMongoConnectionProvider(db, mongoStore, invocationContext);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ this.config = config;
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+
+ private void lazyInit(KeycloakSession session) {
+ if (client == null) {
+ synchronized (this) {
+ if (client == null) {
+ try {
+ this.client = createMongoClient();
+
+ String dbName = config.get("db", "keycloak");
+ this.db = client.getDB(dbName);
+
+ String databaseSchema = config.get("databaseSchema");
+ if (databaseSchema != null) {
+ if (databaseSchema.equals("update")) {
+ MongoUpdaterProvider mongoUpdater = session.getProvider(MongoUpdaterProvider.class);
+
+ if (mongoUpdater == null) {
+ throw new RuntimeException("Can't update database: Mongo updater provider not found");
+ }
+
+ mongoUpdater.update(session, db);
+ } else {
+ throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
+ }
+ }
+
+ this.mongoStore = new MongoStoreImpl(db, getManagedEntities());
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+
+ private Class[] getManagedEntities() throws ClassNotFoundException {
+ Class[] entityClasses = new Class[entities.length];
+ for (int i = 0; i < entities.length; i++) {
+ entityClasses[i] = Thread.currentThread().getContextClassLoader().loadClass(entities[i]);
+ }
+ return entityClasses;
+ }
+
+ @Override
+ public void close() {
+ if (client != null) {
+ client.close();
+ }
+ }
+
+ @Override
+ public String getId() {
+ return "default";
+ }
+
+ /**
+ * Override this method if you want more possibility to configure Mongo client. It can be also used to inject mongo
+ * client from different source.
+ *
+ * This method can assume that "config" is already set and can use it.
+ *
+ * @return mongoClient instance, which will be shared for whole Keycloak
+ *
+ * @throws UnknownHostException
+ */
+ protected MongoClient createMongoClient() throws UnknownHostException {
+ String host = config.get("host", ServerAddress.defaultHost());
+ int port = config.getInt("port", ServerAddress.defaultPort());
+ String dbName = config.get("db", "keycloak");
+
+ String user = config.get("user");
+ String password = config.get("password");
+
+ MongoClientOptions clientOptions = getClientOptions();
+
+ MongoClient client;
+ if (user != null && password != null) {
+ MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
+ client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential), clientOptions);
+ } else {
+ client = new MongoClient(new ServerAddress(host, port), clientOptions);
+ }
+
+ mongoDbInfo = new MongoDbInfo();
+ mongoDbInfo.driverVersion = client.getVersion();
+ mongoDbInfo.address = client.getAddress().toString();
+ mongoDbInfo.database = dbName;
+ mongoDbInfo.user = user;
+
+ logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
+ return client;
+ }
+
+ protected MongoClientOptions getClientOptions() {
+ MongoClientOptions.Builder builder = MongoClientOptions.builder();
+ checkIntOption("connectionsPerHost", builder);
+ checkIntOption("threadsAllowedToBlockForConnectionMultiplier", builder);
+ checkIntOption("maxWaitTime", builder);
+ checkIntOption("connectTimeout", builder);
+ checkIntOption("socketTimeout", builder);
+ checkBooleanOption("socketKeepAlive", builder);
+ checkBooleanOption("autoConnectRetry", builder);
+ if (config.getLong("maxAutoConnectRetryTime") != null) {
+ builder.maxAutoConnectRetryTime(config.getLong("maxAutoConnectRetryTime"));
+ }
+ if (config.getBoolean("ssl", false)) {
+ builder.socketFactory(SSLSocketFactory.getDefault());
+ }
+
+ return builder.build();
+ }
+
+ protected void checkBooleanOption(String optionName, MongoClientOptions.Builder builder) {
+ Boolean val = config.getBoolean(optionName);
+ if (val != null) {
+ try {
+ Method m = MongoClientOptions.Builder.class.getMethod(optionName, boolean.class);
+ m.invoke(builder, val);
+ } catch (Exception e) {
+ throw new IllegalStateException("Problem configuring boolean option " + optionName + " for mongo client. Ensure you used correct value true or false and if this option is supported by mongo driver", e);
+ }
+ }
+ }
+
+ protected void checkIntOption(String optionName, MongoClientOptions.Builder builder) {
+ Integer val = config.getInt(optionName);
+ if (val != null) {
+ try {
+ Method m = MongoClientOptions.Builder.class.getMethod(optionName, int.class);
+ m.invoke(builder, val);
+ } catch (Exception e) {
+ throw new IllegalStateException("Problem configuring int option " + optionName + " for mongo client. Ensure you used correct value (number) and if this option is supported by mongo driver", e);
+ }
+ }
+ }
+
+ @Override
+ public ProviderOperationalInfo getOperationalInfo() {
+ return mongoDbInfo;
+ }
+
+ public static class MongoDbInfo implements ProviderOperationalInfo {
+
+ public String address;
+ public String database;
+ public String driverVersion;
+ public String user;
+
+ @Override
+ public boolean isOk() {
+ // TODO KEYCLOAK-1578 - implement operational monitoring of Mongo DB connection
+ return true;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public String getDatabase() {
+ return database;
+ }
+
+ public String getDriverVersion() {
+ return driverVersion;
+ }
+
+ public String getUser() {
+ return user;
+ }
+ }
}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProviderFactory.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProviderFactory.java
index e787ce6..3b3a00e 100644
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProviderFactory.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/MongoConnectionProviderFactory.java
@@ -1,9 +1,9 @@
package org.keycloak.connections.mongo;
-import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.MonitorableProviderFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
-public interface MongoConnectionProviderFactory extends ProviderFactory<MongoConnectionProvider> {
+public interface MongoConnectionProviderFactory extends MonitorableProviderFactory<MongoConnectionProvider> {
}
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html
index 5590f95..f4e4c6d 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html
@@ -1,21 +1,47 @@
<div class="col-md-12">
- <h1>Server Info</h1>
+ <h1>Server Info</h1>
- <table class="table table-striped table-bordered">
- <tr>
- <td>Version</td>
- <td>{{serverInfo.version}}</td>
- </tr>
- <tr>
- <td>Server Time</td>
- <td>{{serverInfo.serverTime}} (<a style="cursor: pointer" data-ng-click="serverInfoUpdate()">update</a>)</td>
- </tr>
- <tr>
- <td>Server Uptime</td>
- <td>{{serverInfo.serverUptime}}</td>
- </tr>
+ <table class="table table-striped table-bordered">
+ <tr>
+ <td width="20%">Keycloak Version</td>
+ <td>{{serverInfo.version}}</td>
+ </tr>
+ <tr>
+ <td>Server Time</td>
+ <td>{{serverInfo.serverTime}} (<a style="cursor: pointer" data-ng-click="serverInfoUpdate()">update</a>)</td>
+ </tr>
+ <tr>
+ <td>Server Uptime</td>
+ <td>{{serverInfo.serverUptime}}</td>
+ </tr>
+ </table>
+
+ <fieldset>
+ <legend>Java VM Memory Statistics</legend>
+ <div class="form-group">
+ <table class="table table-striped table-bordered" style="margin-top: 0px;">
+ <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>
+ </div>
+ </fieldset>
+
+ <fieldset>
+ <legend collapsed>System Info</legend>
+ <div class="form-group">
+ <table class="table table-striped table-bordered" style="margin-top: 0px;">
<tr>
- <td>Current Working Directory</td>
+ <td width="20%">Current Working Directory</td>
<td>{{serverInfo.systemInfo.userDir}}</td>
</tr>
<tr>
@@ -66,23 +92,59 @@
<td>OS Architecture</td>
<td>{{serverInfo.systemInfo.osArchitecture}}</td>
</tr>
- </table>
-
- <h3>Java VM Memory Statistics</h3>
- <table class="table table-striped table-bordered">
- <tr>
- <td>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>
+ </table>
+ </div>
+ </fieldset>
+
+
+ <fieldset ng-show="serverInfo.jpaInfo">
+ <legend collapsed>Database Info</legend>
+ <div class="form-group">
+ <table class="table table-striped table-bordered" style="margin-top: 0px;">
+ <tr>
+ <td width="20%">Database URL</td>
+ <td>{{serverInfo.jpaInfo.jdbcUrl}}</td>
+ </tr>
+ <tr>
+ <td>Database User</td>
+ <td>{{serverInfo.jpaInfo.databaseUser}}</td>
+ </tr>
+ <tr>
+ <td>Database Type</td>
+ <td>{{serverInfo.jpaInfo.databaseProduct}}</td>
+ </tr>
+ <tr>
+ <td>Database Driver</td>
+ <td>{{serverInfo.jpaInfo.databaseDriver}}</td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+
+ <fieldset ng-show="serverInfo.mongoDbInfo">
+ <legend collapsed>Mongo DB Info</legend>
+ <div class="form-group">
+ <table class="table table-striped table-bordered" style="margin-top: 0px;">
+ <tr width="20%">
+ <td>Address</td>
+ <td>{{serverInfo.mongoDbInfo.address}}</td>
+ </tr>
+ <tr>
+ <td>Database</td>
+ <td>{{serverInfo.mongoDbInfo.database}}</td>
+ </tr>
+ <tr>
+ <td>User</td>
+ <td>{{serverInfo.mongoDbInfo.user}}</td>
+ </tr>
+ <tr>
+ <td>Driver Version</td>
+ <td>{{serverInfo.mongoDbInfo.driverVersion}}</td>
+ </tr>
+ </table>
+ </div>
+ </fieldset>
+
<fieldset>
<legend collapsed>Providers</legend>
@@ -93,7 +155,7 @@
<table class="table table-striped table-bordered">
<thead>
<tr>
- <th>SPI</th>
+ <th width="20%">SPI</th>
<th>Providers</th>
</tr>
</thead>
@@ -117,7 +179,7 @@
<table class="table table-striped table-bordered">
<thead>
<tr>
- <th>SPI</th>
+ <th width="20%">SPI</th>
<th>Providers</th>
</tr>
</thead>
diff --git a/model/api/src/main/java/org/keycloak/provider/MonitorableProviderFactory.java b/model/api/src/main/java/org/keycloak/provider/MonitorableProviderFactory.java
new file mode 100644
index 0000000..7b9c95c
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/provider/MonitorableProviderFactory.java
@@ -0,0 +1,18 @@
+package org.keycloak.provider;
+
+/**
+ * Provider factory for provider which is monitorable. It means some info about it can be shown on "Server Info" page or accessed over Operational monitoring endpoint.
+ *
+ * @author Vlastimil Elias (velias at redhat dot com)
+ */
+public interface MonitorableProviderFactory<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).
+ * Is used to be shown on "Server Info" page or in Operational monitoring endpoint.
+ *
+ * @return extendion of {@link ProviderOperationalInfo}
+ */
+ public ProviderOperationalInfo getOperationalInfo();
+
+}
diff --git a/model/api/src/main/java/org/keycloak/provider/ProviderOperationalInfo.java b/model/api/src/main/java/org/keycloak/provider/ProviderOperationalInfo.java
new file mode 100644
index 0000000..ca3e8a6
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/provider/ProviderOperationalInfo.java
@@ -0,0 +1,22 @@
+package org.keycloak.provider;
+
+import java.io.Serializable;
+
+/**
+ * Operational info about given Provider.
+ * Contains info about Provider that can be shown on "Server Info" page or accessed over Operational monitoring endpoint.
+ *
+ * @author Vlastimil Elias (velias at redhat dot com)
+ * @see MonitorableProviderFactory
+ */
+public interface ProviderOperationalInfo extends Serializable {
+
+ /**
+ * Return true if provider is OK from operation point of view. It means it is able to perform necessary work.
+ * It can return false for example if remote DB of JPA provider is not available, or LDAP server of LDAP based user federation provider is not available.
+ *
+ * @return true if provider is OK to perform his operation.
+ */
+ boolean isOk();
+
+}
services/pom.xml 15(+15 -0)
diff --git a/services/pom.xml b/services/pom.xml
index da71e9a..16fef06 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -41,6 +41,21 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-connections-jpa</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-connections-mongo</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-forms-common-freemarker</artifactId>
<scope>provided</scope>
</dependency>
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
index c600b92..91610db 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/ServerInfoAdminResource.java
@@ -1,5 +1,6 @@
package org.keycloak.services.resources.admin;
+import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -13,9 +14,13 @@ import java.util.Set;
import javax.ws.rs.GET;
import javax.ws.rs.core.Context;
+import org.jboss.logging.Logger;
import org.keycloak.Version;
import org.keycloak.broker.provider.IdentityProvider;
import org.keycloak.broker.provider.IdentityProviderFactory;
+import org.keycloak.connections.jpa.JpaConnectionProvider;
+import org.keycloak.connections.mongo.DefaultMongoConnectionFactoryProvider.MongoDbInfo;
+import org.keycloak.connections.mongo.MongoConnectionProvider;
import org.keycloak.events.EventListenerProvider;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.OperationType;
@@ -29,8 +34,10 @@ import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.LoginProtocolFactory;
import org.keycloak.protocol.ProtocolMapper;
+import org.keycloak.provider.MonitorableProviderFactory;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.ProviderOperationalInfo;
import org.keycloak.provider.Spi;
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import org.keycloak.representations.idm.ProtocolMapperRepresentation;
@@ -41,6 +48,8 @@ import org.keycloak.social.SocialIdentityProvider;
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ServerInfoAdminResource {
+
+ private static final Logger logger = Logger.getLogger(ServerInfoAdminResource.class);
private static final Map<String, List<String>> ENUMS = createEnumsMap(EventType.class, OperationType.class);
@@ -58,6 +67,8 @@ public class ServerInfoAdminResource {
info.version = Version.VERSION;
info.serverTime = new Date().toString();
info.serverStartupTime = session.getKeycloakSessionFactory().getServerStartupTimestamp();
+ info.memoryInfo = (new MemoryInfo()).init(Runtime.getRuntime());
+ info.systemInfo = (new SystemInfo()).init();
setSocialProviders(info);
setIdentityProviders(info);
setThemes(info);
@@ -68,6 +79,21 @@ public class ServerInfoAdminResource {
setProtocolMapperTypes(info);
setBuiltinProtocolMappers(info);
info.setEnums(ENUMS);
+
+ ProviderFactory<JpaConnectionProvider> jpf = session.getKeycloakSessionFactory().getProviderFactory(JpaConnectionProvider.class);
+ if(jpf!=null && jpf instanceof MonitorableProviderFactory){
+ info.jpaInfo = ((MonitorableProviderFactory<?>)jpf).getOperationalInfo();
+ } else {
+ logger.debug("JPA provider not found or is not monitorable");
+ }
+
+ ProviderFactory<MongoConnectionProvider> mpf = session.getKeycloakSessionFactory().getProviderFactory(MongoConnectionProvider.class);
+ if(mpf!=null && mpf instanceof MonitorableProviderFactory){
+ info.mongoDbInfo = ((MonitorableProviderFactory<?>)mpf).getOperationalInfo();
+ } else {
+ logger.debug("Mongo provider not found or is not monitorable");
+ }
+
return info;
}
@@ -191,10 +217,28 @@ public class ServerInfoAdminResource {
}
}
- public static class MemoryInfo{
+ public static class MemoryInfo implements Serializable {
+
+ protected long total;
+
+ protected long used;
+
+ public MemoryInfo(){
+ }
+
+ /**
+ * Fill object fwith info.
+ * @param runtime used to get memory info from.
+ * @return itself for chaining
+ */
+ public MemoryInfo init(Runtime runtime){
+ total = runtime.maxMemory();
+ used = runtime.totalMemory() - runtime.freeMemory();
+ return this;
+ }
public long getTotal(){
- return Runtime.getRuntime().maxMemory();
+ return total;
}
public String getTotalFormated(){
@@ -210,7 +254,7 @@ public class ServerInfoAdminResource {
}
public long getUsed(){
- return Runtime.getRuntime().totalMemory();
+ return used;
}
public String getUsedFormated(){
@@ -218,7 +262,7 @@ public class ServerInfoAdminResource {
}
public long getFreePercentage(){
- return getFree()*100/getTotal();
+ return getFree() * 100 / getTotal();
}
private String formatMemory(long bytes){
@@ -233,71 +277,109 @@ public class ServerInfoAdminResource {
}
- public static class SystemInfo {
+ public static class SystemInfo implements Serializable {
+
+ protected String javaVersion;
+ protected String javaVendor;
+ protected String javaVm;
+ protected String javaVmVersion;
+ protected String javaRuntime;
+ protected String javaHome;
+ protected String osName;
+ protected String osArchitecture;
+ protected String osVersion;
+ protected String fileEncoding;
+ protected String userName;
+ protected String userDir;
+ protected String userTimezone;
+ protected String userLocale;
+
+ public SystemInfo() {
+ }
+
+ /**
+ * Fill object with info about current system loaded from {@link System} properties.
+ * @return object itself for chaining
+ */
+ protected SystemInfo init(){
+ javaVersion = System.getProperty("java.version");
+ javaVendor = System.getProperty("java.vendor");
+ javaVm = System.getProperty("java.vm.name");
+ javaVmVersion = System.getProperty("java.vm.version");
+ javaRuntime = System.getProperty("java.runtime.name");
+ javaHome = System.getProperty("java.home");
+ osName = System.getProperty("os.name");
+ osArchitecture = System.getProperty("os.arch");
+ osVersion = System.getProperty("os.version");
+ fileEncoding = System.getProperty("file.encoding");
+ userName = System.getProperty("user.name");
+ userDir = System.getProperty("user.dir");
+ userTimezone = System.getProperty("user.timezone");
+ userLocale = (new Locale(System.getProperty("user.country"),System.getProperty("user.language")).toString());
+ return this;
+ }
+
public String getJavaVersion(){
- return System.getProperty("java.version");
+ return javaVersion;
}
public String getJavaVendor(){
- return System.getProperty("java.vendor");
+ return javaVendor;
}
public String getJavaVm(){
- return System.getProperty("java.vm.name");
+ return javaVm;
}
public String getJavaVmVersion(){
- return System.getProperty("java.vm.version");
+ return javaVmVersion;
}
public String getJavaRuntime(){
- return System.getProperty("java.runtime.name");
+ return javaRuntime;
}
public String getJavaHome(){
- return System.getProperty("java.home");
+ return javaHome;
}
public String getOsName(){
- return System.getProperty("os.name");
+ return osName;
}
public String getOsArchitecture(){
- return System.getProperty("os.arch");
+ return osArchitecture;
}
public String getOsVersion(){
- return System.getProperty("os.version");
+ return osVersion;
}
public String getFileEncoding(){
- return System.getProperty("file.encoding");
+ return fileEncoding;
}
public String getUserName(){
- return System.getProperty("user.name");
+ return userName;
}
public String getUserDir(){
- return System.getProperty("user.dir");
+ return userDir;
}
public String getUserTimezone(){
- return System.getProperty("user.timezone");
+ return userTimezone;
}
public String getUserLocale(){
- return (new Locale(System.getProperty("user.country"),System.getProperty("user.language")).toString());
+ return userLocale;
}
-
}
- public static class ServerInfoRepresentation {
+ public static class ServerInfoRepresentation implements Serializable {
private String version;
-
private String serverTime;
-
private long serverStartupTime;
private Map<String, List<String>> themes;
@@ -314,26 +396,44 @@ public class ServerInfoAdminResource {
private Map<String, List<ProtocolMapperRepresentation>> builtinProtocolMappers;
private Map<String, List<String>> enums;
+
+ private MemoryInfo memoryInfo;
+ private SystemInfo systemInfo;
+
+ private ProviderOperationalInfo jpaInfo;
+ private ProviderOperationalInfo mongoDbInfo;
public ServerInfoRepresentation() {
}
public SystemInfo getSystemInfo(){
- return new SystemInfo();
+ return systemInfo;
}
public MemoryInfo getMemoryInfo(){
- return new MemoryInfo();
+ return memoryInfo;
+ }
+
+ public ProviderOperationalInfo getJpaInfo() {
+ return jpaInfo;
+ }
+
+ public ProviderOperationalInfo getMongoDbInfo() {
+ return mongoDbInfo;
}
public String getServerTime() {
return serverTime;
}
+ public long getServerStartupTime() {
+ return serverStartupTime;
+ }
+
/**
* @return server startup time formatted
*/
- public String getServerStartupTime() {
+ public String getServerStartupTimeFormatted() {
return (new Date(serverStartupTime)).toString();
}
@@ -420,7 +520,7 @@ public class ServerInfoAdminResource {
}
}
- public static class SpiInfoRepresentation {
+ public static class SpiInfoRepresentation implements Serializable {
private String name;
private boolean internal;
private Set<String> implementations;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
index 3bde1cb..85a81bb 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
@@ -21,10 +21,27 @@
*/
package org.keycloak.testsuite.admin;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.ClientRequestContext;
+import javax.ws.rs.client.ClientRequestFilter;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
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;
@@ -40,22 +57,8 @@ import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.admin.AdminRoot;
-import org.keycloak.testsuite.rule.AbstractKeycloakRule;
import org.keycloak.testsuite.KeycloakServer;
-
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
-import javax.ws.rs.client.ClientRequestContext;
-import javax.ws.rs.client.ClientRequestFilter;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.UriBuilder;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
/**
* Tests Undertow Adapter
@@ -295,4 +298,34 @@ 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);
+
+ Assert.assertNotNull(response);
+ Assert.assertEquals(Version.VERSION, response.get("version"));
+ Assert.assertNotNull(response.get("serverTime"));
+ Assert.assertNotNull(response.get("serverStartupTime"));
+
+ Assert.assertNotNull(response.get("memoryInfo"));
+ Assert.assertNotNull(response.get("jpaInfo"));
+ Assert.assertNull(response.get("mongoDbInfo"));
+
+ System.out.println(response);
+
+ }
+
}