keycloak-memoizeit

KEYCLOAK-1542 - Server Info page extended by info about DB and

7/21/2015 11:09:47 AM

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);
+
+    }
+
 }