blob: fc3a02f18a5c5af519693ed48e2ecd7d98522f0d [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.felix.mosgi.console.component;
import org.apache.felix.mosgi.console.ifc.CommonPlugin;
import org.apache.felix.mosgi.console.ifc.Plugin;
import org.apache.felix.mosgi.console.component.MyTree;
import org.osgi.framework.BundleContext;
import java.beans.PropertyChangeEvent;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JScrollBar;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.JOptionPane;
import javax.swing.JDialog;
import java.awt.Component;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
import java.awt.event.ActionEvent;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.MBeanServerConnection;
import javax.management.Attribute;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Date;
import java.util.Enumeration;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
//import org.osgi.service.prefs.Preferences;
public class RemoteLogger_jtree extends DefaultTreeModel implements CommonPlugin, NotificationListener, MouseListener {
private static final String OLDLOG_THIS_TIME ="This time";
private static final String OLDLOG_NOT_THIS_TIME ="Not this time";
private static final String OLDLOG_ALWAYS ="Always";
private static final String OLDLOG_NEVER ="Never";
private static final String[] LOG_LVL=new String[] {"Error", "Warning", "Info", "Debug"};
private String oldLogChoice=OLDLOG_THIS_TIME;
private Hashtable ht_connectedGateway=new Hashtable(); // connString/mbsc
protected Hashtable ht_logLvl=new Hashtable(); // DefaultMutableTreeNode/Integer_logLvl
protected Vector v_ul=new Vector(); // tree node containing not visible log yet (placer ce vecteur dans le renderer ???)
private MyTree logTree;
private TreePath selPath;
private JPanel jp;
private DefaultMutableTreeNode rootNode=new DefaultMutableTreeNode("");
private JScrollBar jsb_horizontal=null;
private JScrollBar jsb_vertical=null;
public RemoteLogger_jtree (BundleContext bdlCtx){
super(null);
setRoot(rootNode);
this.jp=new JPanel();
this.jp.setLayout(new BorderLayout());
this.logTree=new MyTree(this);
JtreeCellRenderer treeCellRenderer=new JtreeCellRenderer(bdlCtx, this);
this.logTree.setCellRenderer(treeCellRenderer);
this.logTree.setLargeModel(true);
this.logTree.setToggleClickCount(-1);
this.logTree.setScrollsOnExpand(false);
// if I do this.logTree.setRootVisible(false) => Create an invisible tree, even if I use an "expand"
// then need to expand after the first insert into the tree so i give up with root not visible.
this.logTree.addMouseListener(this);
JScrollPane jsp=new JScrollPane(logTree);
this.jsb_horizontal=jsp.getHorizontalScrollBar();
this.jsb_vertical=jsp.getVerticalScrollBar();
jp.add(jsp, BorderLayout.CENTER);
jp.setMinimumSize(new Dimension(500,25));
}
/////////////////////////////////////////////////////
// Mouse Listener Interface //
/////////////////////////////////////////////////////
public void mouseEntered(MouseEvent e){}
public void mouseClicked(MouseEvent e) {}
public void mouseExited(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mousePressed(MouseEvent e) {
final int selRow = logTree.getRowForLocation(e.getX(), e.getY());
selPath = logTree.getPathForLocation(e.getX(), e.getY());
if ( e.getClickCount()==1 & selRow!=-1 & e.getButton()>1 ) { // show JPopupMenu
String nodeString="\""+((DefaultMutableTreeNode) selPath.getLastPathComponent()).getUserObject()+"\"";
JPopupMenu jpopup=new JPopupMenu();
JMenuItem jmiRemove=new JMenuItem("Remove logs \""+nodeString.substring(0,Math.min(15,nodeString.length()))+((nodeString.length()>15)?"...\"":"\""));
jmiRemove.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
removeLog_actionPerformed(selRow);
}
});
jpopup.add(jmiRemove);
if (selPath.getPath().length==3) {
JMenuItem jmiLogLvl=new JMenuItem("Set log level");
jmiLogLvl.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
setLogLvl(selPath);
}
});
jpopup.add(jmiLogLvl);
}
jpopup.show(jp, e.getX()-jsb_horizontal.getValue(), e.getY()-jsb_vertical.getValue());
/*} else if ( e.getClickCount()==2 & selPath!=null) { // expand selected path
if (logTree.isExpanded(selPath)) {
logTree.collapsePath(selPath);
} else {
logTree.expandPath(selPath);
} */
} else if ( e.getClickCount()==1 & selPath!=null ) { // reload logTree and let selected path location
reloadTree(selRow);
}
}
private void reloadTree(int selRow) {
int horizontal_jsb_init_value = jsb_horizontal.getValue();
int vertical_jsb_init_value = jsb_vertical.getValue();
int row_y_init_loc = (int) ((logTree.getRowBounds(selRow)).getY());
Enumeration enu = logTree.getExpandedDescendants(new TreePath(rootNode));
reload();
if ( enu != null ) { // necessaire ce test ?
while (enu.hasMoreElements()) {
logTree.expandPath((TreePath) enu.nextElement());
}
}
// Redefini tous les noeuds rmiport/profilName comme a jour
this.v_ul.removeAllElements();
// without next line if scrollbar_value=scrollbar_max it's generate a bad shift (may be vertical scrollbar height)
logTree.scrollPathToVisible(selPath);
int row_y_new_loc = (int) ((logTree.getRowBounds(logTree.getRowForPath(selPath))).getY());
int vertical_jsb_new_value = vertical_jsb_init_value+(row_y_new_loc-row_y_init_loc);
jsb_vertical.setValue(vertical_jsb_new_value);
jsb_horizontal.setValue(horizontal_jsb_init_value);
logTree.setSelectionPath(selPath);
logTree.repaint();
}
/////////////////////////////////////
// Plugin Interface //
/////////////////////////////////////
public String getName(){
return "JTree Remote Logger";
}
public Component getGUI(){
return this.jp;
}
/* a supprimer si on enleve l'heritage CommonPlugin -> Plugin */
public String pluginLocation(){
return null;
}
public void registerServicePlugin(){}
public void unregisterServicePlugin(){}
/* fin a supprimer */
public void propertyChange(PropertyChangeEvent e){
if (e.getPropertyName().equals(Plugin.NEW_NODE_CONNECTION)){
try {
MBeanServerConnection mbsc=(MBeanServerConnection)e.getNewValue();
if ( !ht_connectedGateway.containsValue(mbsc) ) {
String jmxsurl = (String) e.getOldValue();
mbsc.addNotificationListener(Activator.REMOTE_LOGGER_ON, this, null, jmxsurl);
// At gateway connection time : add into the tree a "port/profileName" node under an "ip" node
String ref = jmxsurl.substring(jmxsurl.lastIndexOf(":")+1);
String ip_tmp = jmxsurl.substring(0, jmxsurl.lastIndexOf(":"));
String ip = ip_tmp.substring(ip_tmp.lastIndexOf("/")+1);
String connString = jmxsurl.substring(ip_tmp.lastIndexOf("/")+1);
ht_connectedGateway.put(connString, mbsc);
DefaultMutableTreeNode dmtn_ip=createIfNeed(ip, rootNode);
DefaultMutableTreeNode dmtn_ref=createIfNeed(ref, dmtn_ip);
Integer lL = this.getLogLvl(connString);
ht_logLvl.put(dmtn_ref, lL);
// ask for old log management choice :
if (oldLogChoice==OLDLOG_THIS_TIME | oldLogChoice==OLDLOG_NOT_THIS_TIME) {
JOptionPane jop = new JOptionPane("Do you want old log from gateway :\n"+jmxsurl+" ?", JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION, null, new String[] {OLDLOG_THIS_TIME, OLDLOG_NOT_THIS_TIME, OLDLOG_ALWAYS, OLDLOG_NEVER}, OLDLOG_THIS_TIME);
JDialog dialog = jop.createDialog(jp, "Old log management");
//dialog.setModal(true);
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
dialog.show();
oldLogChoice = (String) jop.getValue();
if (oldLogChoice==JOptionPane.UNINITIALIZED_VALUE) {
oldLogChoice=OLDLOG_THIS_TIME; // *1)
}
}
if (oldLogChoice==OLDLOG_THIS_TIME | oldLogChoice==OLDLOG_ALWAYS) {
mbsc.invoke(Activator.REMOTE_LOGGER_ON, "sendOldLog", new Object[]{}, new String[]{});
}
}
} catch(Exception ex){
System.out.println("[RemoteLogger_jtree] error : "+ex);
}
}
}
private static final DateFormat dateFormat = new SimpleDateFormat("dd-MM-yy");
private static final DateFormat timeFormat = new SimpleDateFormat("HH:mm:ss:SSS");
///////////////////////////////////////////////////
// NotificationListener implementation //
///////////////////////////////////////////////////
public void handleNotification(Notification notification, Object handback) {
String jmxsurl = handback.toString();
String ref = jmxsurl.substring(jmxsurl.lastIndexOf(":")+1);
String ip_tmp = jmxsurl.substring(0, jmxsurl.lastIndexOf(":"));
String ip = ip_tmp.substring(ip_tmp.lastIndexOf("/")+1);
StringTokenizer st = new StringTokenizer(notification.getMessage(), "*");
long ts=notification.getTimeStamp();
String time=JtreeCellRenderer.UNKNOWN_TIME;
String date=JtreeCellRenderer.UNKNOWN_DATE;
if (ts!=0) {
Date timeDate=new Date(ts);
// if I use local date format there are indentations problems
//DateFormat df = DateFormat.getTimeInstance(DateFormat.MEDIUM);
//DateFormat df2 = DateFormat.getDateInstance(DateFormat.SHORT);
time=timeFormat.format(timeDate);
date=dateFormat.format(timeDate);
}
String id=st.nextToken();
String name=st.nextToken();
String idname=new String(id+" : "+name);
// bundle state juste after remote loger received a the log entry (in old log case do state="")
String state=(String) JtreeCellRenderer.ht_num2string.get(new Integer((int) Integer.parseInt(st.nextToken())));
String lvl=st.nextToken();
String msg=st.nextToken();
// Get and maybe create parents nodes : ip / ref / idname
DefaultMutableTreeNode dmtn_ip=createIfNeed(ip, rootNode);
DefaultMutableTreeNode dmtn_ref=createIfNeed(ref, dmtn_ip);
DefaultMutableTreeNode dmtn_idname=createIfNeed(idname, dmtn_ref);
// insert the leaf with message under id/ref/idname
DefaultMutableTreeNode dmtn=new DefaultMutableTreeNode(time+" | "+date+" | "+state+" | "+lvl+" | "+msg,false);
this.insertNodeInto(dmtn, dmtn_idname, 0);
// if usefull save nodes which contains new log
if ( !v_ul.contains(dmtn_ip) ) {
v_ul.add(dmtn_ip);
v_ul.add(dmtn_ref);
v_ul.add(dmtn_idname);
} else if ( !v_ul.contains(dmtn_ref) ) {
v_ul.add(dmtn_ref);
v_ul.add(dmtn_idname);
} else if ( !v_ul.contains(dmtn_idname) ) {
v_ul.add(dmtn_idname);
}
this.logTree.repaint();
}
//////////////////////////////////////////
// MBean attribute manipulation //
//////////////////////////////////////////
private Integer getLogLvl(String connString) {
Integer val=new Integer(0);
try {
MBeanServerConnection mb=(MBeanServerConnection) ht_connectedGateway.get(connString);
val=(Integer) mb.getAttribute(Activator.REMOTE_LOGGER_ON, "LogLvl");
} catch (Exception exc) {
exc.printStackTrace();
}
return val;
}
private void setLogLvl(TreePath tp) {
Object[] o=tp.getPath();
String connString = o[1]+":"+o[2];
try {
MBeanServerConnection mb=(MBeanServerConnection) ht_connectedGateway.get(connString);
Integer curentVal=(Integer) mb.getAttribute(Activator.REMOTE_LOGGER_ON, "LogLvl");
int val = JOptionPane.showOptionDialog(jp, "Select a log level for \"..."+connString+"\" :", "Log level", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, LOG_LVL, LOG_LVL[curentVal.intValue()-1]);
if ( val == JOptionPane.CLOSED_OPTION ) { return; }
Integer newVal = new Integer(val+1);
mb.setAttribute(Activator.REMOTE_LOGGER_ON, new Attribute("LogLvl", newVal));
DefaultMutableTreeNode ddmmttnn=(DefaultMutableTreeNode) tp.getLastPathComponent();
ht_logLvl.put(ddmmttnn, newVal);
} catch (Exception ex) {
JOptionPane.showMessageDialog(jp,"Error with \"..."+connString+"\" :\n"+ex, "Error :", JOptionPane.ERROR_MESSAGE);
ex.printStackTrace();
}
}
//////////////////////////////////////////
// PRIVATE TOOLS //
//////////////////////////////////////////
private void removeLog_actionPerformed(int selRow) {
//System.out.println("selected path="+this.selPath);
Object[] o= this.selPath.getPath();
DefaultMutableTreeNode selectedDmtn=(DefaultMutableTreeNode) this.selPath.getLastPathComponent();
if (o.length==5) {
// Can't remove first child of a bundle to avoid modify tree node color
if ( ((DefaultMutableTreeNode) selectedDmtn.getParent()).getFirstChild()!=selectedDmtn ) {
removeNodeFromParent(selectedDmtn);
}
} else if (o.length==4) {
removeNodeFromParent(selectedDmtn);
} else if (o.length==3) {
selectedDmtn.removeAllChildren();
reloadTree(selRow);
} else if (o.length==2) {
Enumeration enu_1=selectedDmtn.children();
while (enu_1.hasMoreElements()) {
DefaultMutableTreeNode dmtn_child=(DefaultMutableTreeNode) enu_1.nextElement();
dmtn_child.removeAllChildren();
}
reloadTree(selRow);
} else if (o.length==1) {
Enumeration enume=rootNode.children();
while (enume.hasMoreElements()) {
DefaultMutableTreeNode dmtn_child=(DefaultMutableTreeNode) enume.nextElement();
Enumeration enume2=dmtn_child.children();
while (enume2.hasMoreElements()) {
DefaultMutableTreeNode dmtn_child_child=(DefaultMutableTreeNode) enume2.nextElement();
dmtn_child_child.removeAllChildren();
}
}
reloadTree(selRow);
}
}
private DefaultMutableTreeNode createIfNeed(String nodeToCreateAndGet, DefaultMutableTreeNode parent) {
int childNumber=this.getChildCount(parent);
DefaultMutableTreeNode theNode=null;
for (int i=0 ; i<childNumber ; i++){ // is node even exist ?
String string_pool=((DefaultMutableTreeNode)(this.getChild(parent, i))).toString();
if (string_pool.equals(nodeToCreateAndGet)){
theNode=(DefaultMutableTreeNode) (this.getChild(parent, i));
break;
}
}
if (theNode==null){ // create the node
theNode=new DefaultMutableTreeNode(nodeToCreateAndGet);
this.insertNodeInto(theNode, parent, 0);
if ( parent==rootNode ) {
v_ul.add(rootNode);
}
}
return theNode;
}
protected String getLogLvl(DefaultMutableTreeNode dmtn) {
// used by treeCellRenderer
return LOG_LVL[ ((Integer) ht_logLvl.get(dmtn)).intValue() - 1 ];
}
protected void fireTreeNodesInserted(Object source, Object path[], int childIndices[], Object children[]) {
// Do nothing to avoid refresh jtree after each log.
}
}