001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ui.apps.logfile; 029 030import org.opencms.main.CmsLog; 031import org.opencms.ui.A_CmsUI; 032import org.opencms.ui.CmsCssIcon; 033import org.opencms.ui.CmsVaadinUtils; 034import org.opencms.ui.FontOpenCms; 035import org.opencms.ui.apps.Messages; 036import org.opencms.ui.components.OpenCmsTheme; 037import org.opencms.ui.contextmenu.CmsContextMenu; 038import org.opencms.ui.contextmenu.CmsContextMenu.ContextMenuItem; 039import org.opencms.ui.contextmenu.CmsContextMenu.ContextMenuItemClickEvent; 040import org.opencms.ui.contextmenu.CmsContextMenu.ContextMenuItemClickListener; 041import org.opencms.util.CmsStringUtil; 042 043import java.util.ArrayList; 044import java.util.List; 045import java.util.Set; 046 047import org.apache.commons.logging.Log; 048import org.apache.logging.log4j.Level; 049import org.apache.logging.log4j.core.Logger; 050import org.apache.logging.log4j.core.config.LoggerConfig; 051 052import com.vaadin.event.MouseEvents; 053import com.vaadin.server.Resource; 054import com.vaadin.shared.MouseEventDetails.MouseButton; 055import com.vaadin.v7.data.Item; 056import com.vaadin.v7.data.util.IndexedContainer; 057import com.vaadin.v7.data.util.filter.Or; 058import com.vaadin.v7.data.util.filter.SimpleStringFilter; 059import com.vaadin.v7.event.ItemClickEvent; 060import com.vaadin.v7.event.ItemClickEvent.ItemClickListener; 061import com.vaadin.v7.ui.Table; 062 063/** 064 * Class for table to display and edit Log channels.<p> 065 */ 066@SuppressWarnings("deprecation") 067public class CmsLogChannelTable extends Table { 068 069 /** 070 * Table column generator for Level-buttons.<p> 071 * */ 072 class LevelIcon implements Table.ColumnGenerator { 073 074 /**vaadin serial id. */ 075 private static final long serialVersionUID = 7258796583481183276L; 076 077 /** 078 * @see com.vaadin.ui.Table.ColumnGenerator#generateCell(com.vaadin.ui.Table, java.lang.Object, java.lang.Object) 079 */ 080 public Object generateCell(Table source, final Object itemId, Object columnId) { 081 082 return ((LoggerLevel)(source.getItem(itemId).getItemProperty(columnId).getValue())).getLevelString(); 083 } 084 085 } 086 087 /** 088 * Enumeration of Table Columns.<p> 089 */ 090 enum TableColumn { 091 092 /**Channel column.*/ 093 Channel(Messages.GUI_LOGFILE_LOGSETTINGS_CHANNEL_0, String.class, ""), 094 095 /**Log file column. */ 096 File(Messages.GUI_LOGFILE_LOGSETTINGS_FILE_0, String.class, ""), 097 098 /**Icon column.*/ 099 Icon(null, Resource.class, new CmsCssIcon(OpenCmsTheme.ICON_LOG)), 100 101 /**Level column.*/ 102 Level(Messages.GUI_LOGFILE_LOGSETTINGS_LEVEL_0, LoggerLevel.class, null), 103 104 /**Parent channel column.*/ 105 ParentChannel(Messages.GUI_LOGFILE_LOGSETTINGS_PARENT_CHANNEL_0, String.class, ""); 106 107 /**Default value for column.*/ 108 private Object m_defaultValue; 109 110 /**Header Message key.*/ 111 private String m_headerMessage; 112 113 /**Type of column property.*/ 114 private Class<?> m_type; 115 116 /** 117 * constructor. 118 * 119 * @param headerMessage key 120 * @param type to property 121 * @param defaultValue of column 122 */ 123 TableColumn(String headerMessage, Class<?> type, Object defaultValue) { 124 125 m_headerMessage = headerMessage; 126 m_type = type; 127 m_defaultValue = defaultValue; 128 } 129 130 /** 131 * Returns list of all properties with non-empty header.<p> 132 * 133 * @return list of properties 134 */ 135 static List<TableColumn> withHeader() { 136 137 List<TableColumn> props = new ArrayList<TableColumn>(); 138 139 for (TableColumn prop : TableColumn.values()) { 140 if (prop.m_headerMessage != null) { 141 props.add(prop); 142 } 143 } 144 return props; 145 } 146 147 /** 148 * Returns the default value of property.<p> 149 * 150 * @return object 151 */ 152 Object getDefaultValue() { 153 154 return m_defaultValue; 155 } 156 157 /** 158 * Returns localized header.<p> 159 * 160 * @return string for header 161 */ 162 String getLocalizedMessage() { 163 164 if (m_headerMessage == null) { 165 return ""; 166 } 167 return CmsVaadinUtils.getMessageText(m_headerMessage); 168 } 169 170 /** 171 * Returns tye of value for given property.<p> 172 * 173 * @return type 174 */ 175 Class<?> getType() { 176 177 return m_type; 178 } 179 180 } 181 182 /** 183 * Enumeration of Logger Level and corresponging icon paths.<p> 184 */ 185 private enum LoggerLevel { 186 187 /**Debug level.*/ 188 Debug(Level.DEBUG, OpenCmsTheme.TABLE_COLUMN_BOX_RED, null), 189 190 /**Error level. */ 191 Error(Level.ERROR, OpenCmsTheme.TABLE_COLUMN_BOX_CYAN, "Default"), 192 193 /**Fatal level.*/ 194 Fatal(Level.FATAL, OpenCmsTheme.TABLE_COLUMN_BOX_BLUE_LIGHT, null), 195 196 /**Info level. */ 197 Info(Level.INFO, OpenCmsTheme.TABLE_COLUMN_BOX_ORANGE_DARK, null), 198 199 /**Off level. */ 200 Off(Level.OFF, OpenCmsTheme.TABLE_COLUMN_BOX_GRAY, null), 201 202 /**Warning level. */ 203 Warn(Level.WARN, OpenCmsTheme.TABLE_COLUMN_BOX_ORANGE, null); 204 205 /**Caption for logger level.*/ 206 private String m_caption; 207 208 /**CSS class.*/ 209 private String m_css; 210 211 /**Corresponging log4j Level.*/ 212 private Level m_level; 213 214 /** 215 * constructor.<p> 216 * 217 * @param level of logger 218 * @param css class 219 * @param caption for the level 220 */ 221 private LoggerLevel(Level level, String css, String caption) { 222 223 m_css = css; 224 m_level = level; 225 m_caption = caption; 226 } 227 228 /** 229 * Returns LoggerLevel object from given logger.<p> 230 * 231 * @param logger to fing enumeration object for 232 * @return LoggerLevel 233 */ 234 protected static LoggerLevel fromLogger(Logger logger) { 235 236 for (LoggerLevel item : LoggerLevel.values()) { 237 if (item.getLevel().equals(logger.getLevel())) { 238 return item; 239 } 240 } 241 return null; 242 } 243 244 /** 245 * Returns path to icon.<p> 246 * 247 * @return path to icon 248 */ 249 protected String getCssClass() { 250 251 return m_css; 252 } 253 254 /** 255 * Returns level. <p> 256 * @return log4j Level 257 */ 258 protected Level getLevel() { 259 260 return m_level; 261 } 262 263 /** 264 * Returns the string representation for level.<p> 265 * 266 * @return string 267 */ 268 protected String getLevelString() { 269 270 if (m_caption == null) { 271 String out = m_level.toString(); 272 return out.substring(0, 1).toUpperCase() + out.substring(1).toLowerCase(); 273 } 274 return m_caption; 275 } 276 277 /** 278 * Returns an extenden string representation with log level name added in case of having caption set.<p> 279 * 280 * @return string 281 */ 282 protected String getLevelStringComplet() { 283 284 if (m_caption == null) { 285 return getLevelString(); 286 } 287 String level = m_level.toString(); 288 level = level.substring(0, 1).toUpperCase() + level.substring(1).toLowerCase(); 289 return m_caption + " (" + level + ")"; 290 } 291 292 } 293 294 /** Channel name for logging logger configuration changes. */ 295 public static final String LOGCHANGES_NAME = "logchanges"; 296 297 /** Logger for logging logger configuration changes. */ 298 private static final Log LOGCHANGES = CmsLog.getLog(LOGCHANGES_NAME); 299 300 /**vaadin serial id.*/ 301 private static final long serialVersionUID = 5467369614234190999L; 302 303 /**Container holding table data. */ 304 private IndexedContainer m_container; 305 306 /**Context menu. */ 307 private CmsContextMenu m_menu; 308 309 /**Instance of the app */ 310 private CmsLogFileApp m_app; 311 312 /** 313 * constructor.<p> 314 * @param app the app instance 315 */ 316 protected CmsLogChannelTable(CmsLogFileApp app) { 317 318 m_app = app; 319 m_container = new IndexedContainer(); 320 321 setContainerDataSource(m_container); 322 323 for (TableColumn prop : TableColumn.values()) { 324 m_container.addContainerProperty(prop, prop.getType(), prop.getDefaultValue()); 325 setColumnHeader(prop, prop.getLocalizedMessage()); 326 } 327 328 setVisibleColumns(TableColumn.Level, TableColumn.Channel, TableColumn.ParentChannel, TableColumn.File); 329 330 setItemIconPropertyId(TableColumn.Icon); 331 setColumnWidth(null, 40); 332 setRowHeaderMode(RowHeaderMode.ICON_ONLY); 333 334 setColumnWidth(TableColumn.Level, 80); 335 336 setSelectable(true); 337 setMultiSelect(true); 338 m_menu = new CmsContextMenu(); 339 m_menu.setAsTableContextMenu(this); 340 addItemClickListener(new ItemClickListener() { 341 342 private static final long serialVersionUID = 1L; 343 344 public void itemClick(ItemClickEvent event) { 345 346 onItemClick(event, event.getItemId(), event.getPropertyId()); 347 } 348 }); 349 setCellStyleGenerator(new CellStyleGenerator() { 350 351 private static final long serialVersionUID = 1L; 352 353 public String getStyle(Table source, Object itemId, Object propertyId) { 354 355 if (TableColumn.Channel.equals(propertyId)) { 356 return " " + OpenCmsTheme.HOVER_COLUMN; 357 } 358 359 if (TableColumn.Level.equals(propertyId)) { 360 return ((LoggerLevel)source.getItem(itemId).getItemProperty(propertyId).getValue()).getCssClass(); 361 } 362 363 return null; 364 } 365 }); 366 367 addGeneratedColumn(TableColumn.Level, new LevelIcon()); 368 369 fillTable(); 370 } 371 372 /** 373 * Adds a container item for the given logger.<p> 374 * 375 * @param logger the logger for which to generate a container item 376 */ 377 public void addItemForLogger(Logger logger) { 378 379 Item item = m_container.addItem(logger); 380 if (item != null) { 381 item.getItemProperty(TableColumn.Channel).setValue(logger.getName()); 382 String parentChannelName = getParentLogChannelName(logger); 383 item.getItemProperty(TableColumn.ParentChannel).setValue( 384 parentChannelName != null ? parentChannelName : "none"); 385 item.getItemProperty(TableColumn.File).setValue(m_app.getLogFile(logger)); 386 item.getItemProperty(TableColumn.Level).setValue(LoggerLevel.fromLogger(logger)); 387 } 388 } 389 390 /** 391 * Filters the table according to given search string.<p> 392 * 393 * @param search string to be looked for. 394 */ 395 @SuppressWarnings("unchecked") 396 public void filterTable(String search) { 397 398 m_container.removeAllContainerFilters(); 399 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(search)) { 400 m_container.addContainerFilter( 401 new Or( 402 new SimpleStringFilter(TableColumn.Channel, search, true, false), 403 new SimpleStringFilter(TableColumn.ParentChannel, search, true, false), 404 new SimpleStringFilter(TableColumn.File, search, true, false))); 405 } 406 if ((getValue() != null) & !((Set<Logger>)getValue()).isEmpty()) { 407 setCurrentPageFirstItemId(((Set<Logger>)getValue()).iterator().next()); 408 } 409 } 410 411 /** 412 * Toggles the log file of a given log channel.<p> 413 * 414 * @param logchannel to set or remove log file to 415 */ 416 protected void toggleOwnFile(Logger logchannel) { 417 418 m_app.toggleOwnFileForLogger(logchannel); 419 m_app.updateTable(); 420 } 421 422 /** 423 * Sets a given Level to a Set of Loggers.<p> 424 * 425 * @param clickedLevel to be set 426 * @param clickedLogger to get level changed 427 */ 428 void changeLoggerLevel(LoggerLevel clickedLevel, Set<Logger> clickedLogger) { 429 430 for (Logger logger : clickedLogger) { 431 432 logLogLevelChange(logger, clickedLevel.getLevel()); 433 logger.setLevel(clickedLevel.getLevel()); 434 m_app.writeElement(logger); 435 } 436 m_app.updateTable(); 437 } 438 439 /** 440 * Handles the table item clicks, including clicks on images inside of a table item.<p> 441 * 442 * @param event the click event 443 * @param itemId of the clicked row 444 * @param propertyId column id 445 */ 446 @SuppressWarnings("unchecked") 447 void onItemClick(MouseEvents.ClickEvent event, Object itemId, Object propertyId) { 448 449 if (!event.isCtrlKey() && !event.isShiftKey()) { 450 451 if (event.getButton().equals(MouseButton.LEFT)) { 452 setValue(null); 453 } 454 changeValueIfNotMultiSelect(itemId); 455 // don't interfere with multi-selection using control key 456 if (event.getButton().equals(MouseButton.RIGHT) || (propertyId == null)) { 457 m_menu.removeAllItems(); 458 fillContextMenu((Set<Logger>)getValue()); 459 m_menu.openForTable(event, itemId, propertyId, this); 460 } 461 462 } 463 } 464 465 /** 466 * Checks value of table and sets it new if needed:<p> 467 * if multiselect: new itemId is in current Value? -> no change of value<p> 468 * no multiselect and multiselect, but new item not selected before: set value to new item<p> 469 * 470 * @param itemId if of clicked item 471 */ 472 private void changeValueIfNotMultiSelect(Object itemId) { 473 474 @SuppressWarnings("unchecked") 475 Set<String> value = (Set<String>)getValue(); 476 if (value == null) { 477 select(itemId); 478 } else if (!value.contains(itemId)) { 479 setValue(null); 480 select(itemId); 481 } 482 } 483 484 /** 485 * Fills the context menu.<p> 486 * 487 * @param loggerSet Set of logger to open context menu for 488 */ 489 private void fillContextMenu(final Set<Logger> loggerSet) { 490 491 for (LoggerLevel level : LoggerLevel.values()) { 492 final LoggerLevel currentLevel = level; 493 ContextMenuItem item = m_menu.addItem(level.getLevelStringComplet()); 494 item.setData(loggerSet); 495 item.addItemClickListener(new ContextMenuItemClickListener() { 496 497 public void contextMenuItemClicked(ContextMenuItemClickEvent event) { 498 499 changeLoggerLevel(currentLevel, loggerSet); 500 501 } 502 }); 503 if (loggerSet.size() == 1) { 504 if (level.getLevel().equals(loggerSet.iterator().next().getLevel())) { 505 item.setIcon(FontOpenCms.CHECK_SMALL); 506 } 507 } 508 } 509 if (loggerSet.size() == 1) { 510 String message = CmsVaadinUtils.getMessageText(Messages.GUI_LOGFILE_LOGSETTINGS_NEWFILE_0); 511 if (CmsLogFileApp.isloggingactivated(loggerSet.iterator().next())) { 512 message = CmsVaadinUtils.getMessageText(Messages.GUI_LOGFILE_LOGSETTINGS_REMOVEFILE_0); 513 } 514 ContextMenuItem item = m_menu.addItem(message); 515 item.setData(loggerSet); 516 item.addItemClickListener(new ContextMenuItemClickListener() { 517 518 public void contextMenuItemClicked(ContextMenuItemClickEvent event) { 519 520 toggleOwnFile(loggerSet.iterator().next()); 521 522 } 523 }); 524 } 525 } 526 527 /** 528 * Populate table.<p> 529 */ 530 private void fillTable() { 531 532 removeAllItems(); 533 for (Logger logger : m_app.getAllElements()) { 534 addItemForLogger(logger); 535 } 536 } 537 538 /** 539 * Gets the parent log channel name of a logger. 540 * 541 * @param logger the logger 542 * @return the parent log channel name 543 */ 544 private String getParentLogChannelName(Logger logger) { 545 546 LoggerConfig parentConfig = null; 547 if (logger.getName().equals(logger.get().getName())) { 548 parentConfig = logger.get().getParent(); 549 } else { 550 parentConfig = logger.get(); 551 } 552 return parentConfig != null ? parentConfig.getName() : null; 553 } 554 555 /** 556 * Helper method for logging user actions. 557 * 558 * @param message the message to write 559 */ 560 private void log(String message) { 561 562 String user = A_CmsUI.getCmsObject().getRequestContext().getCurrentUser().getName(); 563 LOGCHANGES.info("[User: " + user + "] " + message); 564 } 565 566 /** 567 * Logs a log level change. 568 * 569 * @param logger the logger 570 * @param newLevel the new level to be set on the logger 571 */ 572 private void logLogLevelChange(Logger logger, Level newLevel) { 573 574 String oldLevelDesc = null; 575 LoggerConfig config = logger.get(); 576 if (logger.getName().equals(config.getName())) { 577 oldLevelDesc = config.getLevel().toString(); 578 } else { 579 oldLevelDesc = config.getLevel().toString() + " (inherited from '" + config.getName() + "')"; 580 } 581 log("Switching channel '" + logger.getName() + "' from " + oldLevelDesc + " to " + newLevel.toString()); 582 } 583 584}