1 /* 2 * Copyright (C) Azureus Software, Inc, All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details ( see the LICENSE file ). 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 17 */ 18 19 package org.gudy.azureus2.ui.swt.views.clientstats; 20 21 import java.net.InetAddress; 22 import java.text.SimpleDateFormat; 23 import java.util.*; 24 import java.util.List; 25 26 import org.eclipse.swt.SWT; 27 import org.eclipse.swt.layout.*; 28 import org.eclipse.swt.widgets.*; 29 import org.gudy.azureus2.core3.download.DownloadManager; 30 import org.gudy.azureus2.core3.download.DownloadManagerPeerListener; 31 import org.gudy.azureus2.core3.download.DownloadManagerState; 32 import org.gudy.azureus2.core3.global.GlobalManagerListener; 33 import org.gudy.azureus2.core3.peer.PEPeer; 34 import org.gudy.azureus2.core3.peer.PEPeerListener; 35 import org.gudy.azureus2.core3.peer.PEPeerManager; 36 import org.gudy.azureus2.core3.peer.impl.PEPeerTransport; 37 import org.gudy.azureus2.core3.util.*; 38 import org.gudy.azureus2.plugins.ui.UIManager; 39 import org.gudy.azureus2.plugins.ui.tables.TableColumn; 40 import org.gudy.azureus2.plugins.ui.tables.TableColumnCreationListener; 41 import org.gudy.azureus2.plugins.ui.tables.TableManager; 42 import org.gudy.azureus2.pluginsimpl.local.PluginInitializer; 43 import org.gudy.azureus2.ui.swt.Utils; 44 import org.gudy.azureus2.ui.swt.mainwindow.ClipboardCopy; 45 import org.gudy.azureus2.ui.swt.views.table.TableViewSWT; 46 import org.gudy.azureus2.ui.swt.views.table.impl.TableViewFactory; 47 import org.gudy.azureus2.ui.swt.views.table.impl.TableViewTab; 48 49 import com.aelitis.azureus.core.AzureusCore; 50 import com.aelitis.azureus.core.AzureusCoreFactory; 51 import com.aelitis.azureus.core.AzureusCoreRunningListener; 52 import com.aelitis.azureus.core.peermanager.peerdb.PeerItem; 53 import com.aelitis.azureus.core.peermanager.piecepicker.util.BitFlags; 54 import com.aelitis.azureus.core.util.bloom.BloomFilter; 55 import com.aelitis.azureus.core.util.bloom.BloomFilterFactory; 56 import com.aelitis.azureus.ui.common.table.TableColumnCore; 57 import com.aelitis.azureus.ui.common.table.TableLifeCycleListener; 58 import com.aelitis.azureus.ui.common.table.TableRowCore; 59 import com.aelitis.azureus.ui.common.table.impl.TableColumnManager; 60 import com.aelitis.azureus.util.MapUtils; 61 62 public class ClientStatsView 63 extends TableViewTab<ClientStatsDataSource> 64 implements TableLifeCycleListener, GlobalManagerListener, 65 DownloadManagerPeerListener 66 { 67 private static final String CONFIG_FILE = "ClientStats.dat"; 68 69 private static final String CONFIG_FILE_ARCHIVE = "ClientStats_%1.dat"; 70 71 private static final int BLOOMFILTER_SIZE = 100000; 72 73 private static final int BLOOMFILTER_PEERID_SIZE = 50000; 74 75 private static final String TABLEID = "ClientStats"; 76 77 private AzureusCore core; 78 79 private TableViewSWT<ClientStatsDataSource> tv; 80 81 private boolean columnsAdded; 82 83 private final Map<String, ClientStatsDataSource> mapData = new HashMap<String, ClientStatsDataSource>(); 84 85 private Composite parent; 86 87 private BloomFilter bloomFilter; 88 89 private BloomFilter bloomFilterPeerId; 90 91 private ClientStatsOverall overall; 92 93 private long startedListeningOn; 94 95 private long totalTime; 96 97 private long lastAdd; 98 99 private GregorianCalendar calendar = new GregorianCalendar(); 100 101 private int lastAddMonth; 102 103 private static boolean registered = false; 104 ClientStatsView()105 public ClientStatsView() { 106 super("ClientStats"); 107 108 initAndLoad(); 109 110 AzureusCoreFactory.addCoreRunningListener(new AzureusCoreRunningListener() { 111 public void azureusCoreRunning(AzureusCore core) { 112 initColumns(core); 113 register(core); 114 } 115 }); 116 } 117 initComposite(Composite composite)118 public Composite initComposite(Composite composite) { 119 parent = new Composite(composite, SWT.BORDER); 120 parent.setLayout(new FormLayout()); 121 Layout layout = composite.getLayout(); 122 if (layout instanceof GridLayout) { 123 Utils.setLayoutData(parent, new GridData(SWT.FILL, SWT.FILL, true, true)); 124 } else if (layout instanceof FormLayout) { 125 Utils.setLayoutData(parent, Utils.getFilledFormData()); 126 } 127 128 return parent; 129 } 130 tableViewTabInitComplete()131 public void tableViewTabInitComplete() { 132 Composite cTV = (Composite) parent.getChildren()[0]; 133 Composite cBottom = new Composite(parent, SWT.None); 134 FormData fd; 135 fd = Utils.getFilledFormData(); 136 fd.bottom = new FormAttachment(cBottom); 137 Utils.setLayoutData(cTV, fd); 138 fd = Utils.getFilledFormData(); 139 fd.top = null; 140 Utils.setLayoutData(cBottom, fd); 141 cBottom.setLayout(new FormLayout()); 142 143 Button btnCopy = new Button(cBottom, SWT.PUSH); 144 Utils.setLayoutData(btnCopy, new FormData()); 145 btnCopy.setText("Copy"); 146 btnCopy.addListener(SWT.Selection, new Listener() { 147 public void handleEvent(Event event) { 148 TableRowCore[] rows = tv.getRows(); 149 StringBuilder sb = new StringBuilder(); 150 151 sb.append(new SimpleDateFormat("MMM yyyy").format(new Date())); 152 sb.append("\n"); 153 154 sb.append("Hits,Client,Bytes Sent,Bytes Received,Bad Bytes\n"); 155 for (TableRowCore row : rows) { 156 ClientStatsDataSource stat = (ClientStatsDataSource) row.getDataSource(); 157 if (stat == null) { 158 continue; 159 } 160 sb.append(stat.count); 161 sb.append(","); 162 sb.append(stat.client.replaceAll(",", "")); 163 sb.append(","); 164 sb.append(stat.bytesSent); 165 sb.append(","); 166 sb.append(stat.bytesReceived); 167 sb.append(","); 168 sb.append(stat.bytesDiscarded); 169 sb.append("\n"); 170 } 171 ClipboardCopy.copyToClipBoard(sb.toString()); 172 } 173 }); 174 175 Button btnCopyShort = new Button(cBottom, SWT.PUSH); 176 Utils.setLayoutData(btnCopyShort, new FormData()); 177 btnCopyShort.setText("Copy > 1%"); 178 btnCopyShort.addListener(SWT.Selection, new Listener() { 179 public void handleEvent(Event event) { 180 StringBuilder sb = new StringBuilder(); 181 182 sb.append(new SimpleDateFormat("MMM ''yy").format(new Date())); 183 sb.append("] "); 184 sb.append(overall.count); 185 sb.append(": "); 186 187 ClientStatsDataSource[] stats; 188 189 synchronized (mapData) { 190 191 stats = mapData.values().toArray(new ClientStatsDataSource[0]); 192 } 193 194 Arrays.sort(stats, new Comparator<ClientStatsDataSource>() { 195 public int compare(ClientStatsDataSource o1, ClientStatsDataSource o2) { 196 if (o1.count == o2.count) { 197 return 0; 198 } 199 return o1.count > o2.count ? -1 : 1; 200 } 201 }); 202 203 boolean first = true; 204 for (ClientStatsDataSource stat : stats) { 205 int pct = (int) (stat.count * 1000 / overall.count); 206 if (pct < 10) { 207 continue; 208 } 209 if (first) { 210 first = false; 211 } else { 212 sb.append(", "); 213 } 214 sb.append(DisplayFormatters.formatPercentFromThousands(pct)); 215 sb.append(" "); 216 sb.append(stat.client); 217 } 218 219 Arrays.sort(stats, new Comparator<ClientStatsDataSource>() { 220 public int compare(ClientStatsDataSource o1, ClientStatsDataSource o2) { 221 float v1 = (float) o1.bytesReceived / o1.count; 222 float v2 = (float) o2.bytesReceived / o2.count; 223 if (v1 == v2) { 224 return 0; 225 } 226 return v1 > v2 ? -1 : 1; 227 } 228 }); 229 230 int top = 5; 231 first = true; 232 sb.append("\nBest Seeders ("); 233 long total = 0; 234 for (ClientStatsDataSource stat : stats) { 235 total += stat.bytesReceived; 236 } 237 sb.append(DisplayFormatters.formatByteCountToKiBEtc(total, false, true, 238 0)); 239 sb.append(" Downloaded): "); 240 for (ClientStatsDataSource stat : stats) { 241 if (first) { 242 first = false; 243 } else { 244 sb.append(", "); 245 } 246 sb.append(DisplayFormatters.formatByteCountToKiBEtc( 247 stat.bytesReceived / stat.count, false, true, 0)); 248 sb.append(" per "); 249 sb.append(stat.client); 250 sb.append("(x"); 251 sb.append(stat.count); 252 sb.append(")"); 253 if (--top <= 0) { 254 break; 255 } 256 } 257 258 Arrays.sort(stats, new Comparator<ClientStatsDataSource>() { 259 public int compare(ClientStatsDataSource o1, ClientStatsDataSource o2) { 260 float v1 = (float) o1.bytesDiscarded / o1.count; 261 float v2 = (float) o2.bytesDiscarded / o2.count; 262 if (v1 == v2) { 263 return 0; 264 } 265 return v1 > v2 ? -1 : 1; 266 } 267 }); 268 top = 5; 269 first = true; 270 sb.append("\nMost Discarded ("); 271 total = 0; 272 for (ClientStatsDataSource stat : stats) { 273 total += stat.bytesDiscarded; 274 } 275 sb.append(DisplayFormatters.formatByteCountToKiBEtc(total, false, true, 276 0)); 277 sb.append(" Discarded): "); 278 for (ClientStatsDataSource stat : stats) { 279 if (first) { 280 first = false; 281 } else { 282 sb.append(", "); 283 } 284 sb.append(DisplayFormatters.formatByteCountToKiBEtc( 285 stat.bytesDiscarded / stat.count, false, true, 0)); 286 sb.append(" per "); 287 sb.append(stat.client); 288 sb.append("(x"); 289 sb.append(stat.count); 290 sb.append(")"); 291 if (--top <= 0) { 292 break; 293 } 294 } 295 296 Arrays.sort(stats, new Comparator<ClientStatsDataSource>() { 297 public int compare(ClientStatsDataSource o1, ClientStatsDataSource o2) { 298 float v1 = (float) o1.bytesSent / o1.count; 299 float v2 = (float) o2.bytesSent / o2.count; 300 if (v1 == v2) { 301 return 0; 302 } 303 return v1 > v2 ? -1 : 1; 304 } 305 }); 306 top = 5; 307 first = true; 308 sb.append("\nMost Fed ("); 309 total = 0; 310 for (ClientStatsDataSource stat : stats) { 311 total += stat.bytesSent; 312 } 313 sb.append(DisplayFormatters.formatByteCountToKiBEtc(total, false, true, 314 0)); 315 sb.append(" Sent): "); 316 for (ClientStatsDataSource stat : stats) { 317 if (first) { 318 first = false; 319 } else { 320 sb.append(", "); 321 } 322 sb.append(DisplayFormatters.formatByteCountToKiBEtc( 323 stat.bytesSent / stat.count, false, true, 0)); 324 sb.append(" per "); 325 sb.append(stat.client); 326 sb.append("(x"); 327 sb.append(stat.count); 328 sb.append(")"); 329 if (--top <= 0) { 330 break; 331 } 332 } 333 334 ClipboardCopy.copyToClipBoard(sb.toString()); 335 } 336 }); 337 fd = new FormData(); 338 fd.left = new FormAttachment(btnCopy, 5); 339 Utils.setLayoutData(btnCopyShort, fd); 340 } 341 initYourTableView()342 public TableViewSWT<ClientStatsDataSource> initYourTableView() { 343 tv = TableViewFactory.createTableViewSWT(ClientStatsDataSource.class, 344 TABLEID, getPropertiesPrefix(), new TableColumnCore[0], 345 ColumnCS_Count.COLUMN_ID, SWT.MULTI | SWT.FULL_SELECTION | SWT.VIRTUAL); 346 /* 347 tv.addTableDataSourceChangedListener(this, true); 348 tv.addRefreshListener(this, true); 349 tv.addSelectionListener(this, false); 350 tv.addMenuFillListener(this); 351 */ 352 tv.addLifeCycleListener(this); 353 354 return tv; 355 } 356 initColumns(AzureusCore core)357 private void initColumns(AzureusCore core) { 358 synchronized (ClientStatsView.class) { 359 360 if (columnsAdded) { 361 362 return; 363 } 364 365 columnsAdded = true; 366 } 367 368 UIManager uiManager = PluginInitializer.getDefaultInterface().getUIManager(); 369 370 TableManager tableManager = uiManager.getTableManager(); 371 372 tableManager.registerColumn(ClientStatsDataSource.class, 373 ColumnCS_Name.COLUMN_ID, new TableColumnCreationListener() { 374 public void tableColumnCreated(TableColumn column) { 375 new ColumnCS_Name(column); 376 } 377 }); 378 tableManager.registerColumn(ClientStatsDataSource.class, 379 ColumnCS_Count.COLUMN_ID, new TableColumnCreationListener() { 380 public void tableColumnCreated(TableColumn column) { 381 new ColumnCS_Count(column); 382 } 383 }); 384 tableManager.registerColumn(ClientStatsDataSource.class, 385 ColumnCS_Discarded.COLUMN_ID, new TableColumnCreationListener() { 386 public void tableColumnCreated(TableColumn column) { 387 new ColumnCS_Discarded(column); 388 } 389 }); 390 tableManager.registerColumn(ClientStatsDataSource.class, 391 ColumnCS_Received.COLUMN_ID, new TableColumnCreationListener() { 392 public void tableColumnCreated(TableColumn column) { 393 new ColumnCS_Received(column); 394 } 395 }); 396 tableManager.registerColumn(ClientStatsDataSource.class, 397 ColumnCS_ReceivedPer.COLUMN_ID, new TableColumnCreationListener() { 398 public void tableColumnCreated(TableColumn column) { 399 new ColumnCS_ReceivedPer(column); 400 } 401 }); 402 tableManager.registerColumn(ClientStatsDataSource.class, 403 ColumnCS_Sent.COLUMN_ID, new TableColumnCreationListener() { 404 public void tableColumnCreated(TableColumn column) { 405 new ColumnCS_Sent(column); 406 } 407 }); 408 tableManager.registerColumn(ClientStatsDataSource.class, 409 ColumnCS_Pct.COLUMN_ID, new TableColumnCreationListener() { 410 public void tableColumnCreated(TableColumn column) { 411 new ColumnCS_Pct(column); 412 } 413 }); 414 415 for (final String network : AENetworkClassifier.AT_NETWORKS) { 416 tableManager.registerColumn(ClientStatsDataSource.class, network + "." 417 + ColumnCS_Sent.COLUMN_ID, new TableColumnCreationListener() { 418 public void tableColumnCreated(TableColumn column) { 419 column.setUserData("network", network); 420 new ColumnCS_Sent(column); 421 } 422 }); 423 tableManager.registerColumn(ClientStatsDataSource.class, network + "." 424 + ColumnCS_Discarded.COLUMN_ID, new TableColumnCreationListener() { 425 public void tableColumnCreated(TableColumn column) { 426 column.setUserData("network", network); 427 new ColumnCS_Discarded(column); 428 } 429 }); 430 tableManager.registerColumn(ClientStatsDataSource.class, network + "." 431 + ColumnCS_Received.COLUMN_ID, new TableColumnCreationListener() { 432 public void tableColumnCreated(TableColumn column) { 433 column.setUserData("network", network); 434 new ColumnCS_Received(column); 435 } 436 }); 437 tableManager.registerColumn(ClientStatsDataSource.class, network + "." 438 + ColumnCS_Count.COLUMN_ID, new TableColumnCreationListener() { 439 public void tableColumnCreated(TableColumn column) { 440 column.setUserData("network", network); 441 new ColumnCS_Count(column); 442 } 443 }); 444 } 445 446 TableColumnManager tcManager = TableColumnManager.getInstance(); 447 tcManager.setDefaultColumnNames(TABLEID, new String[] { 448 ColumnCS_Name.COLUMN_ID, 449 ColumnCS_Pct.COLUMN_ID, 450 ColumnCS_Count.COLUMN_ID, 451 ColumnCS_Received.COLUMN_ID, 452 ColumnCS_Sent.COLUMN_ID, 453 ColumnCS_Discarded.COLUMN_ID, 454 }); 455 } 456 tableViewDestroyed()457 public void tableViewDestroyed() { 458 } 459 460 initAndLoad()461 private void initAndLoad() { 462 synchronized (mapData) { 463 Map map = FileUtil.readResilientConfigFile(CONFIG_FILE); 464 465 totalTime = MapUtils.getMapLong(map, "time", 0); 466 467 lastAdd = MapUtils.getMapLong(map, "lastadd", 0); 468 if (lastAdd != 0) { 469 calendar.setTimeInMillis(lastAdd); 470 lastAddMonth = calendar.get(Calendar.MONTH); 471 472 Map mapBloom = MapUtils.getMapMap(map, "bloomfilter", null); 473 if (mapBloom != null) { 474 bloomFilter = BloomFilterFactory.deserialiseFromMap(mapBloom); 475 } 476 mapBloom = MapUtils.getMapMap(map, "bloomfilterPeerId", null); 477 if (mapBloom != null) { 478 bloomFilterPeerId = BloomFilterFactory.deserialiseFromMap(mapBloom); 479 } 480 } 481 if (bloomFilter == null) { 482 bloomFilter = BloomFilterFactory.createRotating( 483 BloomFilterFactory.createAddOnly(BLOOMFILTER_SIZE), 2); 484 } 485 if (bloomFilterPeerId == null) { 486 bloomFilterPeerId = BloomFilterFactory.createRotating( 487 BloomFilterFactory.createAddOnly(BLOOMFILTER_PEERID_SIZE), 2); 488 } 489 490 overall = new ClientStatsOverall(); 491 492 List listSavedData = MapUtils.getMapList(map, "data", null); 493 if (listSavedData != null) { 494 for (Object val : listSavedData) { 495 try { 496 Map mapVal = (Map) val; 497 if (mapVal != null) { 498 ClientStatsDataSource ds = new ClientStatsDataSource(mapVal); 499 ds.overall = overall; 500 501 if (!mapData.containsKey(ds.client)) { 502 mapData.put(ds.client, ds); 503 overall.count += ds.count; 504 } 505 } 506 507 } catch (Exception e) { 508 // ignore 509 } 510 } 511 } 512 } 513 } 514 save(String filename)515 private void save(String filename) { 516 Map<String, Object> map = new HashMap<String, Object>(); 517 synchronized (mapData) { 518 map.put("data", new ArrayList(mapData.values())); 519 map.put("bloomfilter", bloomFilter.serialiseToMap()); 520 map.put("bloomfilterPeerId", bloomFilterPeerId.serialiseToMap()); 521 map.put("lastadd", SystemTime.getCurrentTime()); 522 if (startedListeningOn > 0) { 523 map.put("time", totalTime 524 + (SystemTime.getCurrentTime() - startedListeningOn)); 525 } else { 526 map.put("time", totalTime); 527 } 528 } 529 FileUtil.writeResilientConfigFile(filename, map); 530 } 531 tableViewInitialized()532 public void tableViewInitialized() { 533 synchronized (mapData) { 534 if (mapData.values().size() > 0) { 535 tv.addDataSources(mapData.values().toArray(new ClientStatsDataSource[0])); 536 } 537 } 538 } 539 register(AzureusCore core)540 protected void register(AzureusCore core) { 541 this.core = core; 542 core.getGlobalManager().addListener(this); 543 synchronized (mapData) { 544 startedListeningOn = SystemTime.getCurrentTime(); 545 } 546 registered = true; 547 } 548 549 // @see org.gudy.azureus2.core3.global.GlobalManagerListener#destroyInitiated() destroyInitiated()550 public void destroyInitiated() { 551 if (core == null) { 552 return; 553 } 554 core.getGlobalManager().removeListener(this); 555 List downloadManagers = core.getGlobalManager().getDownloadManagers(); 556 for (Object object : downloadManagers) { 557 ((DownloadManager) object).removePeerListener(this); 558 } 559 registered = false; 560 save(CONFIG_FILE); 561 } 562 563 // @see org.gudy.azureus2.core3.global.GlobalManagerListener#destroyed() destroyed()564 public void destroyed() { 565 } 566 downloadManagerAdded(DownloadManager dm)567 public void downloadManagerAdded(DownloadManager dm) { 568 if (!dm.getDownloadState().getFlag(DownloadManagerState.FLAG_LOW_NOISE)) { 569 dm.addPeerListener(this, true); 570 } 571 } 572 downloadManagerRemoved(DownloadManager dm)573 public void downloadManagerRemoved(DownloadManager dm) { 574 dm.removePeerListener(this); 575 } 576 seedingStatusChanged(boolean seedingOnlyMode, boolean potentiallySeedingOnlyMode)577 public void seedingStatusChanged(boolean seedingOnlyMode, 578 boolean potentiallySeedingOnlyMode) { 579 } 580 peerAdded(PEPeer peer)581 public void peerAdded(PEPeer peer) { 582 peer.addListener(new PEPeerListener() { 583 584 public void stateChanged(PEPeer peer, int newState) { 585 if (newState == PEPeer.TRANSFERING) { 586 addPeer(peer); 587 } else if (newState == PEPeer.CLOSING 588 || newState == PEPeer.DISCONNECTED) { 589 peer.removeListener(this); 590 } 591 } 592 593 public void sentBadChunk(PEPeer peer, int pieceNum, int totalBadChunks) { 594 } 595 596 public void removeAvailability(PEPeer peer, BitFlags peerHavePieces) { 597 } 598 599 public void addAvailability(PEPeer peer, BitFlags peerHavePieces) { 600 } 601 }); 602 } 603 addPeer(PEPeer peer)604 protected void addPeer(PEPeer peer) { 605 byte[] bloomId; 606 long now = SystemTime.getCurrentTime(); 607 608 // Bloom Filter is based on the first 8 bytes of peer id + ip address 609 // This captures more duplicates than peer id because most clients 610 // randomize their peer id on restart. IP address, however, changes 611 // less often. 612 byte[] address = null; 613 byte[] peerId = peer.getId(); 614 InetAddress ip = peer.getAlternativeIPv6(); 615 if (ip == null) { 616 try { 617 ip = AddressUtils.getByName(peer.getIp()); 618 address = ip.getAddress(); 619 } catch (Throwable e) { 620 String ipString = peer.getIp(); 621 if (ipString != null) { 622 address = ByteFormatter.intToByteArray(ipString.hashCode()); 623 } 624 } 625 } else { 626 address = ip.getAddress(); 627 } 628 if (address == null) { 629 bloomId = peerId; 630 } else { 631 bloomId = new byte[8 + address.length]; 632 System.arraycopy(peerId, 0, bloomId, 0, 8); 633 System.arraycopy(address, 0, bloomId, 8, address.length); 634 } 635 636 synchronized (mapData) { 637 // break on month.. assume user didn't last use this on the same month in a different year 638 calendar.setTimeInMillis(now); 639 int thisMonth = calendar.get(Calendar.MONTH); 640 if (thisMonth != lastAddMonth) { 641 if (lastAddMonth == 0) { 642 lastAddMonth = thisMonth; 643 } else { 644 String s = new SimpleDateFormat("yyyy-MM").format(new Date(lastAdd)); 645 String filename = CONFIG_FILE_ARCHIVE.replace("%1", s); 646 save(filename); 647 648 lastAddMonth = thisMonth; 649 lastAdd = 0; 650 bloomFilter = BloomFilterFactory.createRotating( 651 BloomFilterFactory.createAddOnly(BLOOMFILTER_SIZE), 2); 652 bloomFilterPeerId = BloomFilterFactory.createRotating( 653 BloomFilterFactory.createAddOnly(BLOOMFILTER_PEERID_SIZE), 2); 654 overall = new ClientStatsOverall(); 655 mapData.clear(); 656 if (tv != null) { 657 tv.removeAllTableRows(); 658 } 659 totalTime = 0; 660 startedListeningOn = 0; 661 } 662 } 663 664 String id = getID(peer); 665 ClientStatsDataSource stat; 666 stat = mapData.get(id); 667 boolean needNew = stat == null; 668 if (needNew) { 669 stat = new ClientStatsDataSource(); 670 stat.overall = overall; 671 stat.client = id; 672 mapData.put(id, stat); 673 } 674 675 boolean inBloomFilter = bloomFilter.contains(bloomId) || bloomFilterPeerId.contains(peerId); 676 677 if (!inBloomFilter) { 678 bloomFilter.add(bloomId); 679 bloomFilterPeerId.add(peerId); 680 681 lastAdd = now; 682 synchronized (overall) { 683 684 overall.count++; 685 } 686 stat.count++; 687 } 688 689 stat.current++; 690 691 long existingBytesReceived = peer.getStats().getTotalDataBytesReceived(); 692 long existingBytesSent = peer.getStats().getTotalDataBytesSent(); 693 long existingBytesDiscarded = peer.getStats().getTotalBytesDiscarded(); 694 695 if (existingBytesReceived > 0) { 696 stat.bytesReceived -= existingBytesReceived; 697 if (stat.bytesReceived < 0) { 698 stat.bytesReceived = 0; 699 } 700 } 701 if (existingBytesSent > 0) { 702 stat.bytesSent -= existingBytesSent; 703 if (stat.bytesSent < 0) { 704 stat.bytesSent = 0; 705 } 706 } 707 if (existingBytesDiscarded > 0) { 708 stat.bytesDiscarded -= existingBytesDiscarded; 709 if (stat.bytesDiscarded < 0) { 710 stat.bytesDiscarded = 0; 711 } 712 } 713 714 if (peer instanceof PEPeerTransport) { 715 PeerItem identity = ((PEPeerTransport) peer).getPeerItemIdentity(); 716 if (identity != null) { 717 String network = identity.getNetwork(); 718 if (network != null) { 719 Map<String, Object> map = stat.perNetworkStats.get(network); 720 if (map == null) { 721 map = new HashMap<String, Object>(); 722 stat.perNetworkStats.put(network, map); 723 } 724 if (!inBloomFilter) { 725 long count = MapUtils.getMapLong(map, "count", 0); 726 map.put("count", count + 1); 727 } 728 729 if (existingBytesReceived > 0) { 730 long bytesReceived = MapUtils.getMapLong(map, "bytesReceived", 731 0); 732 bytesReceived -= existingBytesReceived; 733 if (bytesReceived < 0) { 734 bytesReceived = 0; 735 } 736 map.put("bytesReceived", bytesReceived); 737 } 738 if (existingBytesSent > 0) { 739 long bytesSent = MapUtils.getMapLong(map, "bytesSent", 0); 740 bytesSent -= existingBytesSent; 741 if (bytesSent < 0) { 742 bytesSent = 0; 743 } 744 map.put("bytesSent", bytesSent); 745 } 746 if (existingBytesDiscarded > 0) { 747 long bytesDiscarded = MapUtils.getMapLong(map, 748 "bytesDiscarded", 0); 749 bytesDiscarded -= existingBytesDiscarded; 750 if (bytesDiscarded < 0) { 751 bytesDiscarded = 0; 752 } 753 map.put("bytesDiscarded", bytesDiscarded); 754 } 755 756 } 757 } 758 } 759 760 if (tv != null) { 761 if (needNew) { 762 tv.addDataSource(stat); 763 } else { 764 TableRowCore row = tv.getRow(stat); 765 if (row != null) { 766 row.invalidate(); 767 } 768 } 769 } 770 } 771 } 772 peerManagerAdded(PEPeerManager manager)773 public void peerManagerAdded(PEPeerManager manager) { 774 } 775 peerManagerRemoved(PEPeerManager manager)776 public void peerManagerRemoved(PEPeerManager manager) { 777 } 778 peerManagerWillBeAdded(PEPeerManager manager)779 public void peerManagerWillBeAdded(PEPeerManager manager) { 780 } 781 peerRemoved(PEPeer peer)782 public void peerRemoved(PEPeer peer) { 783 synchronized (mapData) { 784 ClientStatsDataSource stat = mapData.get(getID(peer)); 785 if (peer.getStats().getTotalDataBytesSent() > 0) 786 if (stat != null) { 787 stat.current--; 788 789 String network = null; 790 if (peer instanceof PEPeerTransport) { 791 PeerItem identity = ((PEPeerTransport) peer).getPeerItemIdentity(); 792 if (identity != null) { 793 network = identity.getNetwork(); 794 } 795 } 796 797 798 stat.bytesReceived += peer.getStats().getTotalDataBytesReceived(); 799 stat.bytesSent += peer.getStats().getTotalDataBytesSent(); 800 stat.bytesDiscarded += peer.getStats().getTotalBytesDiscarded(); 801 802 if (network != null) { 803 Map<String, Object> map = stat.perNetworkStats.get(network); 804 if (map == null) { 805 map = new HashMap<String, Object>(); 806 stat.perNetworkStats.put(network, map); 807 } 808 long bytesReceived = MapUtils.getMapLong(map, "bytesReceived", 0); 809 map.put("bytesReceived", bytesReceived 810 + peer.getStats().getTotalDataBytesReceived()); 811 long bytesSent = MapUtils.getMapLong(map, "bytesSent", 0); 812 map.put("bytesSent", bytesSent 813 + peer.getStats().getTotalDataBytesSent()); 814 long bytesDiscarded = MapUtils.getMapLong(map, "bytesDiscarded", 0); 815 map.put("bytesDiscarded", bytesDiscarded 816 + peer.getStats().getTotalBytesDiscarded()); 817 } 818 819 if (tv != null) { 820 TableRowCore row = tv.getRow(stat); 821 if (row != null) { 822 row.invalidate(); 823 } 824 } 825 } 826 } 827 } 828 getID(PEPeer peer)829 private String getID(PEPeer peer) { 830 String s = peer.getClientNameFromPeerID(); 831 if (s == null) { 832 s = peer.getClient(); 833 if (s.startsWith("HTTP Seed")) { 834 return "HTTP Seed"; 835 } 836 } 837 return s.replaceAll(" v?[0-9._]+", ""); 838 } 839 } 840