/* * eXist Open Source Native XML Database * Copyright (C) 2001-10 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * $Id$ */ package org.exist.management.impl; import java.util.ArrayList; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Properties; import javax.management.AttributeChangeNotification; import javax.management.MBeanNotificationInfo; import javax.management.Notification; import javax.management.NotificationBroadcasterSupport; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenDataException; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; import javax.management.openmbean.TabularType; import org.apache.log4j.Logger; import org.exist.EXistException; import org.exist.backup.ErrorReport; import org.exist.management.TaskStatus; import org.exist.security.SecurityManager; import org.exist.security.xacml.AccessContext; import org.exist.source.StringSource; import org.exist.storage.BrokerPool; import org.exist.storage.ConsistencyCheckTask; import org.exist.storage.DBBroker; import org.exist.storage.SystemTask; import org.exist.storage.XQueryPool; import org.exist.xquery.CompiledXQuery; import org.exist.xquery.XQuery; import org.exist.xquery.XQueryContext; public class SanityReport extends NotificationBroadcasterSupport implements SanityReportMBean { private final static Logger LOG = Logger.getLogger(SanityReport.class.getName()); public final static String STATUS_OK = "OK"; public final static String STATUS_FAIL = "FAIL"; public final static StringSource TEST_XQUERY = new StringSource("<r>{current-dateTime()}</r>"); public final static int PING_WAITING = -1; public final static int PING_ERROR = -2; private static String[] itemNames = { "errcode", "description" }; private static String[] itemDescriptions = { "Error code", "Description of the error" }; private static String[] indexNames = { "errcode" }; private static List<ErrorReport> NO_ERRORS = new LinkedList<ErrorReport>(); private int seqNum = 0; private Date actualCheckStart = null; private Date lastCheckStart = null; private Date lastCheckEnd = null; private String lastActionInfo = "nothing done"; private long lastPingRespTime = 0; private String output = ""; private TaskStatus taskstatus = new TaskStatus(TaskStatus.Status.NEVER_RUN); private List<ErrorReport> errors = NO_ERRORS; private BrokerPool pool; public SanityReport(BrokerPool pool) { this.pool = pool; } public MBeanNotificationInfo[] getNotificationInfo() { String[] types = new String[] { AttributeChangeNotification.ATTRIBUTE_CHANGE }; String name = AttributeChangeNotification.class.getName(); String description = "The status attribute of this MBean has changed"; MBeanNotificationInfo info = new MBeanNotificationInfo(types, name, description); return new MBeanNotificationInfo[] { info }; } public Date getLastCheckEnd() { return lastCheckEnd; } public Date getLastCheckStart() { return lastCheckStart; } public Date getActualCheckStart() { return actualCheckStart; } public String getStatus() { return taskstatus.getStatusString(); } public String getLastActionInfo() { return lastActionInfo; } public long getPingTime() { return lastPingRespTime; } public TabularData getErrors() { OpenType[] itemTypes = { SimpleType.STRING, SimpleType.STRING }; CompositeType infoType; try { infoType = new CompositeType("errorInfo", "Provides information on a consistency check error", itemNames, itemDescriptions, itemTypes); TabularType tabularType = new TabularType("errorList", "List of consistency check errors", infoType, indexNames); TabularDataSupport data = new TabularDataSupport(tabularType); for (ErrorReport error : errors) { Object[] itemValues = { error.getErrcodeString(), error.getMessage() }; data.put(new CompositeDataSupport(infoType, itemNames, itemValues)); } return data; } catch (OpenDataException e) { LOG.warn(e.getMessage(), e); return null; } } public void triggerCheck(String output, String backup, String incremental) { try { this.output = output; SystemTask task = new ConsistencyCheckTask(); Properties properties = parseParameter(output, backup, incremental); task.configure(pool.getConfiguration(), properties); pool.triggerSystemTask(task); } catch (EXistException existException) { taskstatus.setStatus(TaskStatus.Status.STOPPED_ERROR); List<ErrorReport> errors = new ArrayList<ErrorReport>(); errors.add( new ErrorReport( ErrorReport.CONFIGURATION_FAILD, existException.getMessage(), existException)); taskstatus.setReason(errors); changeStatus(taskstatus); taskstatus.setStatusChangeTime(); taskstatus.setReason(existException.toString()); LOG.warn("Failed to trigger db sanity check: " + existException.getMessage(), existException); } } public long ping(boolean checkQueryEngine) { long start = System.currentTimeMillis(); lastPingRespTime = -1; lastActionInfo = "Ping"; taskstatus.setStatus(TaskStatus.Status.PING_WAIT); DBBroker broker = null; try { // try to acquire a broker. If the db is deadlocked or not responsive, // this will block forever. broker = pool.get(pool.getSecurityManager().getUser(SecurityManager.GUEST_USER)); if (checkQueryEngine) { XQuery xquery = broker.getXQueryService(); XQueryPool xqPool = xquery.getXQueryPool(); CompiledXQuery compiled = xqPool.borrowCompiledXQuery(broker, TEST_XQUERY); if (compiled == null) { XQueryContext context = xquery.newContext(AccessContext.TEST); compiled = xquery.compile(context, TEST_XQUERY); } try { xquery.execute(compiled, null); } finally { xqPool.returnCompiledXQuery(TEST_XQUERY, compiled); } } } catch (Exception e) { lastPingRespTime = -2; taskstatus.setStatus(TaskStatus.Status.PING_ERROR); taskstatus.setStatusChangeTime(); taskstatus.setReason(e.getMessage()); changeStatus(taskstatus); } finally { pool.release(broker); lastPingRespTime = System.currentTimeMillis() - start; taskstatus.setStatus(TaskStatus.Status.PING_OK); taskstatus.setStatusChangeTime(); taskstatus.setReason("ping response time: " + lastPingRespTime); changeStatus(taskstatus); } return lastPingRespTime; } private Properties parseParameter(String output, String backup, String incremental) { Properties properties = new Properties(); final boolean doBackup = backup.equalsIgnoreCase("YES"); if (backup != null && (doBackup) || backup.equalsIgnoreCase("no")) { properties.put("backup", backup); } if (incremental != null && (incremental.equalsIgnoreCase("YES") || incremental.equalsIgnoreCase("no"))) { properties.put("incremental", incremental); } if (output != null) { properties.put("output", output); } else { properties.put("backup", "no"); } return properties; } protected void updateErrors(List<ErrorReport> errorList) { try { if (errorList == null || errorList.isEmpty()) { taskstatus.setStatus(TaskStatus.Status.STOPPED_OK); this.errors = NO_ERRORS; } else { this.errors = errorList; taskstatus.setStatus(TaskStatus.Status.STOPPED_ERROR); } } catch (Exception e) { // ignore } } protected void changeStatus(TaskStatus status) { status.setStatusChangeTime(); switch (status.getStatus()) { case INIT: actualCheckStart = status.getStatusChangeTime(); break; case STOPPED_ERROR: case STOPPED_OK: lastCheckStart = actualCheckStart; actualCheckStart = null; lastCheckEnd = status.getStatusChangeTime(); if (status.getReason() != null) { this.errors = (List<ErrorReport>) status.getReason(); } lastActionInfo = taskstatus.toString() + " to [" + output + "] ended with status [" + status.toString() + "]"; break; default: break; } TaskStatus oldState = taskstatus; try { taskstatus = status; Notification event = new AttributeChangeNotification(this, seqNum++, taskstatus.getStatusChangeTime().getTime(), "Status change", "status", "String", oldState.toString(), taskstatus.toString()); event.setUserData(taskstatus.getCompositeData()); sendNotification(event); } catch (Exception e) { // ignore } } protected void updateStatus(int percentage) { try { int oldPercentage = taskstatus.getPercentage(); taskstatus.setPercentage(percentage); Notification event = new AttributeChangeNotification(this, seqNum++, taskstatus.getStatusChangeTime().getTime(), "Work percentage change", "status", "int", String.valueOf(oldPercentage), String.valueOf(taskstatus .getPercentage())); event.setUserData(taskstatus.getCompositeData()); sendNotification(event); } catch (Exception e) { // ignore } } }