package org.geoserver.web.admin;
import java.awt.GraphicsEnvironment;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
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.apache.wicket.markup.html.panel.Panel;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.catalog.Predicates;
import org.geoserver.catalog.util.CloseableIterator;
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 org.geotools.util.logging.Logging;
import org.opengis.filter.Filter;
import com.sun.media.imageioimpl.common.PackageUtil;
import com.sun.media.jai.util.CacheDiagnostics;
public class StatusPanel extends Panel {
private static final long serialVersionUID = 7732030199323990637L;
/**
* The map used as the model source so the label contents are updated
*/
private 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";
private static final String KEY_JAVA_RENDERER = "renderer";
private static final Logger LOGGER = Logging.getLogger(StatusPanel.class);
private AbstractStatusPage parent;
public StatusPanel(String id, AbstractStatusPage parent) {
super(id);
this.parent = parent;
initUI();
}
public void initUI() {
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 Label("renderer", new MapModel(values, KEY_JAVA_RENDERER)));
//serialization error here
add(new Link("free.locks") {
private static final long serialVersionUID = -2889353495319211391L;
public void onClick() {
// TODO: see GEOS-2130
updateModel();
}
});
add(new Link("free.memory") {
private static final long serialVersionUID = 3695369177295089346L;
public void onClick() {
System.gc();
System.runFinalization();
updateModel();
}
});
add(new Link("free.memory.jai") {
private static final long serialVersionUID = -3556725607958589003L;
public void onClick() {
TileCache jaiCache = parent.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") {
private static final long serialVersionUID = 2663650174059497376L;
@Override
public void onClick(AjaxRequestTarget target) {
try {
parent.getGeoServer().reset();
info(getLocalizer().getString("resourceCacheClearedSuccessfully", this));
} catch(Throwable t) {
LOGGER.log(Level.SEVERE, "Error resetting resource caches", t);
error(t);
}
target.add(parent.feedbackPanel);
}
});
add(new AjaxLink("reload.catalogConfig") {
private static final long serialVersionUID = -7476556423889306321L;
@Override
public void onClick(AjaxRequestTarget target) {
try {
parent.getGeoServer().reload();
info(getLocalizer().getString("catalogConfigReloadedSuccessfully", StatusPanel.this));
} catch(Throwable t) {
LOGGER.log(Level.SEVERE, "An error occurred while reloading the catalog", t);
error(t);
}
target.add(parent.feedbackPanel);
}
});
}
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 = parent.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, Integer.toString( (int)( 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()));
values.put(KEY_JAVA_RENDERER, checkRenderer());
}
/**
* Retrieves the GeoServer data directory
*/
private String getDataDirectory() {
GeoServerDataDirectory dd = parent.getGeoServerApplication().getBeanOfType(GeoServerDataDirectory.class);
return dd.root().getAbsolutePath();
}
private String checkRenderer() {
try {
String renderer = sun.java2d.pipe.RenderingEngine.getInstance().getClass().getName();
return renderer;
} catch(Throwable e) {
return "Unknown";
}
}
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);
String formattedMaxMemory = formatMemory(runtime.maxMemory());
return formattedUsedMemory + " / " + formattedMaxMemory;
}
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((float)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;
CloseableIterator<DataStoreInfo> i = getDataStores();
try{
for (; 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;
}
}
} finally {
i.close();
}
return count;
}
private synchronized int getConnectionCount() {
int count = 0;
CloseableIterator<DataStoreInfo> i = getDataStores();
try{
for (; i.hasNext(); ) {
DataStoreInfo meta = 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;
}
}finally{
i.close();
}
return count;
}
private CloseableIterator<DataStoreInfo> getDataStores() {
Catalog catalog = parent.getGeoServer().getCatalog();
Filter filter = Predicates.acceptAll();
CloseableIterator<DataStoreInfo> stores = catalog.list(DataStoreInfo.class, filter);
return stores;
}
}