001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2014-2015 ForgeRock AS
026 */
027package org.opends.guitools.controlpanel.datamodel;
028
029import java.util.ArrayList;
030import java.util.Collection;
031import java.util.Comparator;
032import java.util.HashSet;
033import java.util.LinkedHashSet;
034import java.util.Set;
035import java.util.TreeSet;
036
037import org.forgerock.i18n.LocalizableMessage;
038
039import static org.opends.guitools.controlpanel.util.Utilities.*;
040import static org.opends.messages.AdminToolMessages.*;
041import static org.opends.server.util.CollectionUtils.*;
042
043/** The table model used to display all the database monitoring information. */
044public class DatabaseMonitoringTableModel extends SortableTableModel implements Comparator<BackendDescriptor>
045{
046  private static final long serialVersionUID = 548035716525600536L;
047  private Set<BackendDescriptor> data = new HashSet<>();
048  private ArrayList<String[]> dataArray = new ArrayList<>();
049
050  private String[] columnNames = {};
051  private LocalizableMessage NO_VALUE_SET = INFO_CTRL_PANEL_NO_MONITORING_VALUE.get();
052  private LocalizableMessage NOT_IMPLEMENTED = INFO_CTRL_PANEL_NOT_IMPLEMENTED.get();
053
054  /** The fields to be displayed. */
055  private LinkedHashSet<String> attributes = new LinkedHashSet<>();
056  /** The sort column of the table. */
057  private int sortColumn;
058  /** Whether the sorting is ascending or descending. */
059  private boolean sortAscending = true;
060
061  /**
062   * Sets the data for this table model.
063   * @param newData the data for this table model.
064   */
065  public void setData(Set<BackendDescriptor> newData)
066  {
067    if (!newData.equals(data))
068    {
069      data.clear();
070      data.addAll(newData);
071      updateDataArray();
072      fireTableDataChanged();
073    }
074  }
075
076  /**
077   * Updates the table model contents and sorts its contents depending on the
078   * sort options set by the user.
079   */
080  @Override
081  public void forceResort()
082  {
083    updateDataArray();
084    fireTableDataChanged();
085  }
086
087  /**
088   * Updates the table model contents, sorts its contents depending on the
089   * sort options set by the user and updates the column structure.
090   */
091  public void forceDataStructureChange()
092  {
093    updateDataArray();
094    fireTableStructureChanged();
095    fireTableDataChanged();
096  }
097
098  /** {@inheritDoc} */
099  @Override
100  public int getColumnCount()
101  {
102    return columnNames.length;
103  }
104
105  /** {@inheritDoc} */
106  @Override
107  public int getRowCount()
108  {
109    return dataArray.size();
110  }
111
112  /** {@inheritDoc} */
113  @Override
114  public Object getValueAt(int row, int col)
115  {
116    return dataArray.get(row)[col];
117  }
118
119  /** {@inheritDoc} */
120  @Override
121  public String getColumnName(int col) {
122    return columnNames[col];
123  }
124
125  /** {@inheritDoc} */
126  @Override
127  public int compare(BackendDescriptor desc1, BackendDescriptor desc2)
128  {
129    CustomSearchResult monitor1 = desc1.getMonitoringEntry();
130    CustomSearchResult monitor2 = desc2.getMonitoringEntry();
131
132    ArrayList<Integer> possibleResults = newArrayList(getName(desc1).compareTo(getName(desc2)));
133    computeMonitoringPossibleResults(monitor1, monitor2, possibleResults, attributes);
134
135    int result = possibleResults.get(getSortColumn());
136    if (result == 0)
137    {
138      result = getFirstNonZero(possibleResults);
139    }
140    if (!isSortAscending())
141    {
142      result = -result;
143    }
144    return result;
145  }
146
147  private int getFirstNonZero(ArrayList<Integer> possibleResults)
148  {
149    for (int i : possibleResults)
150    {
151      if (i != 0)
152      {
153        return i;
154      }
155    }
156    return 0;
157  }
158
159  /**
160   * Returns whether the sort is ascending or descending.
161   * @return <CODE>true</CODE> if the sort is ascending and <CODE>false</CODE>
162   * otherwise.
163   */
164  @Override
165  public boolean isSortAscending()
166  {
167    return sortAscending;
168  }
169
170  /**
171   * Sets whether to sort ascending of descending.
172   * @param sortAscending whether to sort ascending or descending.
173   */
174  @Override
175  public void setSortAscending(boolean sortAscending)
176  {
177    this.sortAscending = sortAscending;
178  }
179
180  /**
181   * Returns the column index used to sort.
182   * @return the column index used to sort.
183   */
184  @Override
185  public int getSortColumn()
186  {
187    return sortColumn;
188  }
189
190  /**
191   * Sets the column index used to sort.
192   * @param sortColumn column index used to sort..
193   */
194  @Override
195  public void setSortColumn(int sortColumn)
196  {
197    this.sortColumn = sortColumn;
198  }
199
200  /**
201   * Returns the fields displayed by this table model.
202   * @return the fields displayed by this table model.
203   */
204  public Collection<String> getAttributes()
205  {
206    return attributes;
207  }
208
209  /**
210   * Sets the fields displayed by this table model.
211   * @param fields the statistic fields displayed by this table model.
212   */
213  public void setAttributes(LinkedHashSet<String> fields)
214  {
215    this.attributes.clear();
216    this.attributes.addAll(fields);
217    columnNames = new String[fields.size() + 1];
218    columnNames[0] = INFO_CTRL_PANEL_DB_HEADER.get().toString();
219    int i = 1;
220    for (String field : fields)
221    {
222      columnNames[i] = field;
223      i++;
224    }
225  }
226
227  /**
228   * Updates the array data.  This includes resorting it.
229   */
230  private void updateDataArray()
231  {
232    TreeSet<BackendDescriptor> sortedSet = new TreeSet<>(this);
233    sortedSet.addAll(data);
234    dataArray.clear();
235    for (BackendDescriptor ach : sortedSet)
236    {
237      String[] s = getLine(ach);
238      dataArray.add(s);
239    }
240
241    // Add the total: always at the end
242
243    String[] line = new String[attributes.size() + 1];
244    line[0] = "<html><b>" + INFO_CTRL_PANEL_TOTAL_LABEL.get() + "</b>";
245    for (int i=1; i<line.length; i++)
246    {
247      boolean valueSet = false;
248      boolean notImplemented = false;
249      long totalValue = 0;
250      for (String[] l : dataArray)
251      {
252        String value = l[i];
253        try
254        {
255          long v = Long.parseLong(value);
256          totalValue += v;
257          valueSet = true;
258        }
259        catch (Throwable t)
260        {
261          try
262          {
263            double v = Double.parseDouble(value);
264            totalValue += v;
265            valueSet = true;
266          }
267          catch (Throwable t2)
268          {
269            notImplemented = NOT_IMPLEMENTED.toString().equals(value);
270          }
271        }
272      }
273      if (notImplemented)
274      {
275        line[i] = NOT_IMPLEMENTED.toString();
276      }
277      else if (valueSet)
278      {
279        line[i] = String.valueOf(totalValue);
280      }
281      else
282      {
283        line[i] = NO_VALUE_SET.toString();
284      }
285    }
286    dataArray.add(line);
287  }
288
289  /**
290   * Returns the label to be used for the provided backend.
291   * @param backend the backend.
292   * @return the label to be used for the provided backend.
293   */
294  protected String getName(BackendDescriptor backend)
295  {
296    return backend.getBackendID();
297  }
298
299  /**
300   * Returns the monitoring entry associated with the provided backend.
301   * @param backend the backend.
302   * @return the monitoring entry associated with the provided backend.  Returns
303   * <CODE>null</CODE> if there is no monitoring entry associated.
304   */
305  protected CustomSearchResult getMonitoringEntry(BackendDescriptor backend)
306  {
307    return backend.getMonitoringEntry();
308  }
309
310  private String[] getLine(BackendDescriptor backend)
311  {
312    String[] line = new String[attributes.size() + 1];
313    line[0] = getName(backend);
314    int i = 1;
315    CustomSearchResult monitoringEntry = getMonitoringEntry(backend);
316    for (String attr : attributes)
317    {
318      String o = getFirstValueAsString(monitoringEntry, attr);
319      if (o != null)
320      {
321        line[i] = o;
322      }
323      else
324      {
325        line[i] = NO_VALUE_SET.toString();
326      }
327      i++;
328    }
329    return line;
330  }
331
332}