package org.geoserver.web.admin; import java.awt.GraphicsEnvironment; import java.text.NumberFormat; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; import javax.media.jai.JAI; import javax.media.jai.TileCache; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.link.BookmarkablePageLink; import org.apache.wicket.markup.html.link.Link; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.config.CoverageAccessInfo; import org.geoserver.config.GeoServerDataDirectory; import org.geoserver.config.GeoServerInfo; import org.geoserver.config.JAIInfo; import org.geoserver.web.util.MapModel; import org.geoserver.web.wicket.ParamResourceModel; import org.geotools.data.DataAccess; import org.geotools.data.DataStore; import org.geotools.data.LockingManager; import com.sun.media.imageioimpl.common.PackageUtil; import com.sun.media.jai.util.CacheDiagnostics; import com.sun.media.jai.util.SunTileCache; public class StatusPage extends ServerAdminPage { /** * The map used as the model source so the label contents are updated */ private final Map<String, String> values; private static final String KEY_DATA_DIR = "dataDir"; private static final String KEY_LOCKS = "locks"; private static final String KEY_CONNECTIONS = "connections"; private static final String KEY_MEMORY = "memory"; private static final String KEY_JVM_VERSION = "jvm_version"; private static final String KEY_JAI_AVAILABLE = "jai_available"; private static final String KEY_JAI_IMAGEIO_AVAILABLE = "jai_imageio_available"; private static final String KEY_JAI_MAX_MEM = "jai_max_mem"; private static final String KEY_JAI_MEM_USAGE = "jai_mem_usage"; private static final String KEY_JAI_MEM_THRESHOLD = "jai_mem_threshold"; private static final String KEY_JAI_TILE_THREADS = "jai_tile_threads"; private static final String KEY_JAI_TILE_THREAD_PRIORITY = "jai_tile_thread_priority"; private static final String KEY_COVERAGEACCESS_CORE_POOL_SIZE = "coverage_thread_corepoolsize"; private static final String KEY_COVERAGEACCESS_MAX_POOL_SIZE = "coverage_thread_maxpoolsize"; private static final String KEY_COVERAGEACCESS_KEEP_ALIVE_TIME = "coverage_thread_keepalivetime"; private static final String KEY_UPDATE_SEQUENCE = "update_sequence"; public StatusPage() { values = new HashMap<String, String>(); updateModel(); // TODO: if we just provide the values directly as the models they won't // be refreshed on a page reload (ugh). add(new Label("dataDir", new MapModel(values, KEY_DATA_DIR))); add(new Label("locks", new MapModel(values, KEY_LOCKS))); add(new Label("connections", new MapModel(values, KEY_CONNECTIONS))); add(new Label("memory", new MapModel(values, KEY_MEMORY))); add(new Label("jvm.version", new MapModel(values, KEY_JVM_VERSION))); add(new Label("jai.available", new MapModel(values, KEY_JAI_AVAILABLE))); add(new Label("jai.imageio.available", new MapModel(values, KEY_JAI_IMAGEIO_AVAILABLE))); add(new Label("jai.memory.available", new MapModel(values, KEY_JAI_MAX_MEM))); add(new Label("jai.memory.used", new MapModel(values, KEY_JAI_MEM_USAGE))); add(new Label("jai.memory.threshold", new MapModel(values, KEY_JAI_MEM_THRESHOLD))); add(new Label("jai.tile.threads", new MapModel(values, KEY_JAI_TILE_THREADS))); add(new Label("jai.tile.priority", new MapModel(values, KEY_JAI_TILE_THREAD_PRIORITY))); add(new Label("coverage.corepoolsize", new MapModel(values, KEY_COVERAGEACCESS_CORE_POOL_SIZE))); add(new Label("coverage.maxpoolsize", new MapModel(values, KEY_COVERAGEACCESS_MAX_POOL_SIZE))); add(new Label("coverage.keepalivetime", new MapModel(values, KEY_COVERAGEACCESS_KEEP_ALIVE_TIME))); add(new Label("updateSequence", new MapModel(values, KEY_UPDATE_SEQUENCE))); add(new Link("free.locks") { private static final long serialVersionUID = 1L; public void onClick() { // TODO: see GEOS-2130 updateModel(); } }); add(new Link("free.memory") { private static final long serialVersionUID = 1L; public void onClick() { System.gc(); System.runFinalization(); updateModel(); } }); add(new Link("free.memory.jai") { private static final long serialVersionUID = 1L; public void onClick() { TileCache jaiCache = getGeoServer().getGlobal().getJAI().getTileCache(); final long capacityBefore = jaiCache.getMemoryCapacity(); jaiCache.flush(); jaiCache.setMemoryCapacity(0); // to be sure we realease all tiles System.gc(); System.runFinalization(); jaiCache.setMemoryCapacity(capacityBefore); updateModel(); } }); int fontCount = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts().length; add(new Label("fontCount", new ParamResourceModel("StatusPage.fontCount", this, fontCount))); add(new BookmarkablePageLink("show.fonts", JVMFontsPage.class)); add(new AjaxLink("clear.resourceCache") { @Override public void onClick(AjaxRequestTarget target) { try { getGeoServer().reset(); info(getLocalizer().getString("resourceCacheClearedSuccessfully", this)); } catch(Throwable t) { LOGGER.log(Level.SEVERE, "Error resetting resource caches", t); error(t); } target.addComponent(feedbackPanel); } }); add(new AjaxLink("reload.catalogConfig") { @Override public void onClick(AjaxRequestTarget target) { try { getGeoServer().reload(); info(getLocalizer().getString("catalogConfigReloadedSuccessfully", StatusPage.this)); } catch(Throwable t) { LOGGER.log(Level.SEVERE, "An error occurred while reloading the catalog", t); error(t); } target.addComponent(feedbackPanel); } }); add(new Label("reload.date.geoserver", "Jul 14, 3:07 PM")); add(new Label("reload.date.configuration", "Jul 14, 3:07 PM")); add(new Label("reload.date.xml", "Mar 14, 2:15 PM")); } private void updateModel() { values.put(KEY_DATA_DIR, getDataDirectory()); values.put(KEY_LOCKS, Long.toString(getLockCount())); values.put(KEY_CONNECTIONS, Long.toString(getConnectionCount())); values.put(KEY_MEMORY, formatUsedMemory()); values.put(KEY_JVM_VERSION, System.getProperty("java.vendor") + ": " + System.getProperty("java.version") + " (" + System.getProperty("java.vm.name") + ")"); values.put(KEY_JAI_AVAILABLE, Boolean.toString(isNativeJAIAvailable())); values.put(KEY_JAI_IMAGEIO_AVAILABLE, Boolean.toString(PackageUtil.isCodecLibAvailable())); GeoServerInfo geoServerInfo = getGeoServer().getGlobal(); JAIInfo jaiInfo = geoServerInfo.getJAI(); JAI jai = jaiInfo.getJAI(); CoverageAccessInfo coverageAccess = geoServerInfo.getCoverageAccess(); TileCache jaiCache = jaiInfo.getTileCache(); values.put(KEY_JAI_MAX_MEM, formatMemory(jaiCache.getMemoryCapacity())); if(jaiCache instanceof CacheDiagnostics) { values.put(KEY_JAI_MEM_USAGE, formatMemory(((CacheDiagnostics) jaiCache).getCacheMemoryUsed())); } else { values.put(KEY_JAI_MEM_USAGE, "-"); } values.put(KEY_JAI_MEM_THRESHOLD, Float.toString(100.0f * jaiCache.getMemoryThreshold())); values.put(KEY_JAI_TILE_THREADS, Integer.toString(jai.getTileScheduler().getParallelism())); values.put(KEY_JAI_TILE_THREAD_PRIORITY, Integer.toString(jai.getTileScheduler() .getPriority())); values.put(KEY_COVERAGEACCESS_CORE_POOL_SIZE, Integer.toString(coverageAccess.getCorePoolSize())); values.put(KEY_COVERAGEACCESS_MAX_POOL_SIZE, Integer.toString(coverageAccess.getMaxPoolSize())); values.put(KEY_COVERAGEACCESS_KEEP_ALIVE_TIME, Integer.toString(coverageAccess.getKeepAliveTime())); values.put(KEY_UPDATE_SEQUENCE, Long.toString(geoServerInfo.getUpdateSequence())); } /** * Retrieves the GeoServer data directory * @return */ private String getDataDirectory() { GeoServerDataDirectory dd = getGeoServerApplication().getBeanOfType(GeoServerDataDirectory.class); return dd.root().getAbsolutePath(); } boolean isNativeJAIAvailable() { // we directly access the Mlib Image class, if in the classpath it will tell us if // the native extensions are available, if not, an Error will be thrown try { Class image = Class.forName("com.sun.medialib.mlib.Image"); return (Boolean) image.getMethod("isAvailable").invoke(null); } catch(Throwable e) { return false; } } /** * @return a human friendly string for the VM used memory */ private String formatUsedMemory() { final Runtime runtime = Runtime.getRuntime(); final long usedBytes = runtime.totalMemory() - runtime.freeMemory(); String formattedUsedMemory = formatMemory(usedBytes); return formattedUsedMemory; } private String formatMemory(final long bytes) { final long KB = 1024; final long MB = KB * KB; final long GB = KB * MB; final NumberFormat formatter = NumberFormat.getInstance(); formatter.setMaximumFractionDigits(2); String formattedUsedMemory; if (bytes > GB) { formattedUsedMemory = formatter.format(bytes / GB) + " GB"; } else if (bytes > MB) { formattedUsedMemory = formatter.format(bytes / MB) + " MB"; } else { formattedUsedMemory = formatter.format(bytes / KB) + " KB"; } return formattedUsedMemory; } private synchronized int getLockCount() { int count = 0; for (Iterator i = getDataStores().iterator(); i.hasNext();) { DataStoreInfo meta = (DataStoreInfo) i.next(); if (!meta.isEnabled()) { // Don't count locks from disabled datastores. continue; } try { DataAccess store = meta.getDataStore(null); if(store instanceof DataStore) { LockingManager lockingManager = ((DataStore) store).getLockingManager(); if (lockingManager != null){ // we can't actually *count* locks right now? // count += lockingManager.getLockSet().size(); } } } catch (IllegalStateException notAvailable) { continue; } catch (Throwable huh) { continue; } } return count; } private synchronized int getConnectionCount() { int count = 0; for (Iterator i = getDataStores().iterator(); i.hasNext();) { DataStoreInfo meta = (DataStoreInfo) i.next(); if (!meta.isEnabled()) { // Don't count connections from disabled datastores. continue; } try { meta.getDataStore(null); } catch (Throwable notAvailable) { // TODO: Logging. continue; } count += 1; } return count; } private List<DataStoreInfo> getDataStores() { return getGeoServer().getCatalog().getDataStores(); } }