REST API w module kontrolera Floodlight. Implementacja kolektora statystyk OpenFlow.

Podobne dokumenty
Wprowadzenie do tworzenia własnego modułu w kontrolerze Floodlight.

W celu uruchomienia kontrolera należy w katalogu głównym kontrolera z wiersza poleceń wydać następujące polecenie: $ java -jar target/floodlight.

Narzędzia i aplikacje Java EE. Usługi sieciowe Paweł Czarnul pczarnul@eti.pg.gda.pl

Sposoby tworzenia projektu zawierającego aplet w środowisku NetBeans. Metody zabezpieczenia komputera użytkownika przed działaniem apletu.

Aplikacje WWW - laboratorium

Ćwiczenie 1. Kolejki IBM Message Queue (MQ)

Aplikacje internetowe i rozproszone - laboratorium

Aplikacje RMI Lab4

Obiektowe programowanie rozproszone Java RMI. Krzysztof Banaś Systemy rozproszone 1

Materiały oryginalne: ZAWWW-2st1.2-l11.tresc-1.0kolor.pdf. Materiały poprawione

Aplikacje RMI

Zaawansowane aplikacje internetowe - laboratorium Web Services (część 1).

Instrukcja implementacji sterownika wirtualnego portu szeregowego dla systemu Android. Opracowanie: Elzab Soft sp. z o.o.

Zaawansowane aplikacje WWW - laboratorium

Wątki. Definiowanie wątków jako klas potomnych Thread. Nadpisanie metody run().

Tworzenie i wykorzystanie usług

2) W wyświetlonym oknie należy zaznaczyć chęć utworzenia nowej aplikacji (wygląd okna może się różnić od powyższego); kliknąć OK

Programowanie Obiektowe GUI

D:\DYDAKTYKA\ZAI_BIS\_Ćwiczenia_wzorce\04\04_poprawiony.doc 2009-lis-23, 17:44

Współbieżność w środowisku Java

Zaawansowane aplikacje internetowe - laboratorium Architektura CORBA.

Git, Bitbucket, IntelliJ IDEA

ZAPOZNANIE SIĘ Z TWORZENIEM

1. Czynności przygotowujące aplikację działającą na platformie Java SE Biblioteka5 (należy ją pobrać z załącznika z p.1)

Programowanie obiektowe

Zapoznanie ze środowiskiem Mininet. Instalacja zewnętrznego kontrolera SDN.

Wprowadzenie do laboratorium. Zasady obowiązujące na zajęciach. Wprowadzenie do narzędzi wykorzystywanych podczas laboratorium.

Programowanie obiektowe

Java Platform Micro Edition

Wielowątkowość. Programowanie w środowisku rozproszonym. Wykład 1.

Zaawansowane aplikacje internetowe

Aplikacja webowa w Javie szybkie programowanie biznesowych aplikacji Spring Boot + Vaadin

ASP.NET MVC. Podstawy. Zaawansowane programowanie internetowe Instrukcja nr 3

Java. Wykład. Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Projektowanie aplikacji internetowych laboratorium

Aplikacje w środowisku Java

Wprowadzenie do projektu QualitySpy

Systemy Rozproszone - Ćwiczenie 6

WYKONANIE APLIKACJI OKIENKOWEJ OBLICZAJĄCEJ SUMĘ DWÓCH LICZB W ŚRODOWISKU PROGRAMISTYCZNYM. NetBeans. Wykonał: Jacek Ventzke informatyka sem.

Laboratorium 7 Blog: dodawanie i edycja wpisów

Microsoft.NET: ASP.NET MVC + Entity Framework (Code First)

Architektury Usług Internetowych. Laboratorium 2. Usługi sieciowe

Instrukcja tworzenia aplikacji EE na bazie aplikacji prezentowanej na zajęciach lab.4 z PIO umożliwiająca przez sieć dostęp wielu użytkownikom.

Język JAVA podstawy. wykład 2, część 2. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

KLASY, INTERFEJSY, ITP

Instrukcja 10 Laboratorium 13 Testy akceptacyjne z wykorzystaniem narzędzia FitNesse

Załącznik 1 instrukcje instalacji

Architektury usług internetowych. Laboratorium 5. JADE

Architektury Usług Internetowych. Laboratorium 3. Usługi w środowisku wielo-agentowym

Programowanie w języku Java - Wyjątki, obsługa wyjątków, generowanie wyjątków

Programowanie obiektowe

Zaawansowane aplikacje internetowe - laboratorium Architektura CORBA.

Programowanie obiektowe zastosowanie języka Java SE

Programowanie w Javie

Interfejsy w Java. Przetwarzanie równoległe. Wątki.

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Aplikacje w środowisku Java

Język Java część 2 (przykładowa aplikacja)

Katalog książek cz. 2

Aplikacje w Javie- wykład 11 Wątki-podstawy

Programowanie rozproszone w języku Java

akademia androida Składowanie danych część VI

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

Laboratorium 1. Wzorce oprogramowania lab1, Zofia Kruczkiewicz

Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

PWSG Ćwiczenia 12. Wszystkie ukończone zadania należy wysłać na adres: lub

Wątki w Javie. Piotr Tokarski

Multimedia JAVA. Historia

Singleton. Cel: Przykład: Zastosowanie: Zapewnienie, że klasa ma tylko jedną instancję i dostarczenie globalnego dostępu do niej.

Programowanie obiektowe

namespace HostedReceiver { public class Receiver: IConfigureThisEndpoint, AsA_Server {

1 Wątki 1. 2 Tworzenie wątków 1. 3 Synchronizacja 3. 4 Dodatki 3. 5 Algorytmy sortowania 4

1 LINQ. Zaawansowane programowanie internetowe Instrukcja nr 1

Budowa aplikacji wielowarstwowych. Obsługa zdarzeń

Spring Web MVC, Spring DI

Programowanie obiektowe

Aplikacje w środowisku Java

Db4o obiektowa baza danych wersja.net

SIP Studia Podyplomowe Ćwiczenie laboratoryjne Instrukcja

Klasy i obiekty cz II

Java Zadanie 1. Aby poprawnie uruchomić aplikację desktopową, należy zaimplementować główną metodę zapewniającą punkt wejścia do programu.

Protokół JDBC współpraca z relacyjnymi bazami danych lab4. Dr inż. Zofia Kruczkiewicz Programowanie aplikacji internetowych

akademia androida Service, BroadcastReceiver, ContentProvider część IV

Ćwiczenia 9 - Swing - część 1

WSNHiD, Programowanie 2 Lab. 2 Język Java struktura programu, dziedziczenie, abstrakcja, polimorfizm, interfejsy

1. Co można powiedzieć o poniższym kodzie? public interface I { void m1() {}; static public void m2() {}; void abstract m3();

Podstawy tworzenia aplikacji z wykorzystaniem języka Java ME ćwiczenia 1

Dokumentacja do API Javy.

Enterprise JavaBeans (EJB)

Komunikatory typu TCP/IP lab2. Dr inż. Zofia Kruczkiewicz Programowanie aplikacji internetowych

Języki i metody programowania Java Lab2 podejście obiektowe

Kurs programowania. Wykład 8. Wojciech Macyna. 10 maj 2017

Język Java część 2 (przykładowa aplikacja)

Warsztaty AVR. Instalacja i konfiguracja środowiska Eclipse dla mikrokontrolerów AVR. Dariusz Wika

Java pierwszy program w Eclipse «Grzegorz Góralski strona własna

Platformy Technologiczne

Programowanie obiektowe. Literatura: Autor: dr inŝ. Zofia Kruczkiewicz

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Transkrypt:

Instrukcja do laboratorium 6 REST API w module kontrolera Floodlight. Implementacja kolektora statystyk OpenFlow. 1. Cel ćwiczenia Celem ćwiczenia jest implementacja własnego rutera REST API pozwalającego na sprawdzanie i modyfikację czasów IdleTimeout oraz HardTimeout dla nowo dodawanych przepływów. Zaimplementowana zostanie także prosta aplikacja pozwalająca na zbieranie statystyk z przełączników OpenFlow oraz obliczanie przepustowości łączy. 2. Przygotowanie środowiska 2.1 Instalacja wymaganych zależności Przed zbudowaniem i uruchomieniem środowiska należy zainstalować potrzebne repozytoria poleceniem: $ sudo apt-get update $ sudo apt-get install build-essential ant maven python-dev eclipse 2.2 Pobieranie i budowanie kontrolera Proszę pobrać kontroler ze strony: http://www.kt.agh.edu.pl/~rzym/lectures/ti-sdn/floodlight-1.2- lab6.zip a następnie rozpakować go (lub kontynuować implementację z wykorzystaniem kodu z poprzedniego laboratorium). Kontroler zawiera już kilka klas w paczce pl.edu.agh.kt. Dodawanie klas i edycja kodu źródłowego podczas zajęć następuje tylko dla klas w tej paczce! 2.3 Import projektu do środowiska Eclipse IDE Proszę uruchomić środowisko Eclipse i utworzyć nowe Workspace. W celu importu projektu proszę wybrać kolejno: File -> Import -> General -> Existing Projects into Workspace Kliknąć na przycisk Browse obok Select root directory i odnaleźć katalog, do którego został pobrany wcześniej projekt Floodlight W oknie Projects zaznaczyć Floodlight Kliknąć przycisk Finish. Po tym procesie cały projekt powinien zostać zaimportowany. W lewej części środowiska Eclipse w oknie Package Explorer powinno pojawić się drzewo katalogów projektu Floodlight. 2.4 Budowanie i uruchamianie projektu w środowsku Eclipse Aby zbudować projekt (np. po zmianach w kodzie źródłowym) należy utworzyć cel: Wybrać Run->Run Configurations Prawym przyciskiem myszy wybrać Java Application->New W polu nazwy wpisać FloodlightLaunch W polu Project wybrać Floodlight W polu Main Class wybrać net.floodlightcontroller.core.main Kliknąć przycisk Apply 1

3. Implementacja własnego REST API Dla własnego modułu istnieje możliwość implementacji interfejsu REST API. Taki interfejs pozwala na interakcję z modułem już w trakcie jego pracy, np. zmianę parametrów czy wartości zmiennych zapisanych w kodzie. Ponieważ kontroler Floodlight już implementuje potrzebne interfejsy, bazowy serwer HTTP oraz przetwarzanie i kierowanie żądań do odpowiednich metod w kontrolerze dopisanie własnego REST API jest uproszczone. Ta usługa jest wspierana przez framework o nazwie Restlet. W kolejnych krokach tego ćwiczenia: Dokonana zostanie rejestracja własnego modułu w IRestApiService, Zdefiniowane zostaną URI dla własnego modułu, Zaimplementowane zostaną klasy i metody dla danych URI, Dokonany zostanie start własnego REST API wraz ze startem modułu. 3.1 Rejestracja własnego modułu w IRestApiService Tworzenie własnego REST API należy rozpocząć od rejestracji własnego modułu w usłudze IRestApiService. Usługa ta zapewnia mechanizm rejestracji REST API dla wszystkich modułów kontrolera Floodligh. Każdy moduł, który implementuje interfejs REST API, po prostu rejestruje interfejs API za pomocą IRestApiService, a usługa IRestApiService pobiera go z tego interfejsu, obsługując dla nas wszystkie szczegóły niskiego poziomu. Implementując REST API należy zapewnić, że usługa IRestApiService zostanie załadowana jeszcze przed inicjalizacją modułu (metoda init()). W tym celu należy do metody getmodulesdependencies() klasy SdnLabListener dodać: import net.floodlightcontroller.restserver.irestapiservice; protected IRestApiService restapiservice; public Collection<Class<? extends IFloodlightService>> getmoduledependencies() { Collection<Class<? extends IFloodlightService>> l = new ArrayList<Class<? extends IFloodlightService>>(); l.add(irestapiservice.class); return l; Następnie należy uzyskać referencję do IRestApiService. W tym celu w metodzie init() klasy SdnLabListener proszę dodać: public void init(floodlightmodulecontext context) throws FloodlightModuleException { restapiservice = context.getserviceimpl(irestapiservice.class); 2

3.2 Definicja URI Kolejnym krokiem jest definicja URI, dla którego zostanie wystawiony interfejs REST. Tylko te URI będą do wykorzystania przez nasz moduł. Taka definicja pozwala Restletowi na przekierowywanie odpowiednich żądań do naszego modułu. W tym celu należy stworzyć nową klasę w paczce pl.edu.agh.kt o nazwie RestLab i implementującą interfejs RestletRoutable. W klasie tej, po stworzeniu, pojawią się dwie adnotacje (), które w następnych krokach będą uzupełniane. Kod źródłowy po utworzeniu tej klasy przedstawia się następująco: package pl.edu.agh.kt; import org.restlet.context; import org.restlet.restlet; import net.floodlightcontroller.restserver.restletroutable; public class RestLab implements RestletRoutable { public Restlet getrestlet(context context) { // TODO Auto-generated method stub return null; public String basepath() { // TODO Auto-generated method stub return null; Teraz już wystarczy zdefiniować ścieżkę podstawową (bazową) oraz odpowiednie ścieżki dostępu (URIs). W tym celu proszę dokonać edycji kodu tej klasy: public Restlet getrestlet(context context) { Router router = new Router(context); router.attach("/timeout", LabRestServer.class); return router; public String basepath() { return "/sdnlab"; Uwaga 1: W powyższym przykładzie zdefiniowaliśmy tylko jedno URI ( /timeout ). Nic nie szkodzi na przeszkodzie, aby do rutera dodać kolejne URI implementujące inne ścieżki oraz wykorzystujące inne klasy. 3

Uwaga 2: Ten kod nie skompiluje się jeszcze poprawnie. Jak łatwo można zauważyć nie mamy klasy LabRestServer, która to zostanie zaimplementowana w następnym punkcie. 3.3 Implementacja klasy zadanego URI W kolejnym kroku należy zdefiniować (zaimplementować) klasę i jej metody dla zadanego URI w metodzie getrestlet(). Podczas tych zajęć zaimplementowana zostanie klasa pomocnicza, dzięki której możliwa będzie dynamiczna zmiana liczników idletimeout oraz hardtimeout dla nowo dodawanych przepływów. W tym celu należy w paczce pl.edu.agh.kt stworzyć nowa klasę o nazwie LabRestServer dziedziczącą z klasy ServerResource (opcja Superclass podczas tworzenia klasy). Po dodaniu odpowiednich importów (wymaganych później) i loggera klasa ta powinna wyglądać jak poniżej: package pl.edu.agh.kt; import java.io.ioexception; import org.restlet.resource.get; import org.restlet.resource.post; import org.restlet.resource.serverresource; import org.slf4j.logger; import org.slf4j.loggerfactory; import com.fasterxml.jackson.core.jsonprocessingexception; import com.fasterxml.jackson.databind.objectmapper; import com.fasterxml.jackson.databind.serializationfeature; public class LabRestServer extends ServerResource { protected static Logger log = LoggerFactory.getLogger(LabRestServer.class); Zanim jednak zaimplementowane zostaną w niej odpowiednie metody nasłuchujące żądań @Post and @Get należy przygotować jeszcze kolejną klasę, która to będzie wywoływana z tych żądań. W tym celu proszę dodać nowa klasę o nazwie Timeout w paczce pl.edu.agh.kt. Poniżej pełen kod tej klasy. Klasa ta ma dwie zmienne (idletimeout oraz hardtimeout), dwa konstruktory (parametryczny i bez parametrów) oraz metody służące do ustawiania i pobierania wartości zmiennych (podstawowy poziom programowania, bez dalszych wyjaśnień). package pl.edu.agh.kt; public class Timeout { private short hardtimeout = 0; private short idletimeout = 0; public Timeout() { this.idletimeout = Flows.getIdleTimeout(); this.hardtimeout = Flows.getHardTimeout(); public Timeout(short idle, short hard){ this.idletimeout = idle; 4

this.hardtimeout = hard; Flows.setIdleTimeout(idle); Flows.setHardTimeout(hard); public short gethardtimeout() { return hardtimeout; public void sethardtimeout(short hardtimeout) { this.hardtimeout = hardtimeout; public short getidletimeout() { return idletimeout; public void setidletimeout(short idletimeout) { this.idletimeout = idletimeout; Klasa ta implementuje metody Flows.getIdleTimeout(), Flows.setIdleTimeout(), Flows.getHardTimeout(), Flows.setHardTimeout(). Ponieważ klasa Flows do tej pory nie posiadała tych metod oraz zmiennych należy je dodać (do klasy Flows). Należy wkleić poniższy kod: public static short idletimeout = 5; public static short hardtimeout = 0; public static short getidletimeout() { return idletimeout; public static void setidletimeout(short idletimeout) { Flows.idleTimeout = idletimeout; public static short gethardtimeout() { return hardtimeout; public static void sethardtimeout(short hardtimeout) { Flows.hardTimeout = hardtimeout; Po tym można dokończyć implementację klasy LabRestServer. Poniżej zamieszczono kod dla adnotacji @Get oraz @Post. Metody oznaczone tymi adnotacjami są wywołane, jeśli na skonfigurowane REST API (tj. /sdnlab/timeout ) zostanie wysłane żądanie GET lub POST (np. za pomocą znanej aplikacji curl lub wtyczki Postman). W przypadku zapytania GET tworzony jest nowy obiekt Timeout, dla którego wartości liczników (idle/hard timeout) są ustawiane na takie, jakie obecne są w klasie Flows (pobrania wartości z klasy Flows). Następnie obiekt ten jest serializowany i wysyłany jako String. Resztą zajmuje się ruter i Restlet zaimplementowany w kontrolerze Floodlight. Dla metody POST odbierany jest string (JSON), na podstawie którego tworzony jest nowy obiekt 5

Timeout z deserializowanych danych. Następnie wartości obiektu timeout wykorzystywane są do ustawienia wartości liczników w klasie Flows. Dwie metody serialize() oraz deserialize() są przykładową implementacją serializowania i deserializowania obiektów typu Timeout do i z Stringów z wykorzystaniem ObjectMappera. @Get("json") public String handleget() throws JsonProcessingException { log.info("handleget"); return serialize(new Timeout(Flows.getIdleTimeout(), Flows.getHardTimeout())); @Post("json") public String handlepost(string text) throws JsonProcessingException, IOException { log.info("handlepost"); Timeout timeout = new Timeout(); timeout = deserialize(text, Timeout.class); Flows.setIdleTimeout(timeout.getIdleTimeout()); Flows.setHardTimeout(timeout.getHardTimeout()); return serialize(timeout); private static final ObjectMapper mapper; static { mapper = new ObjectMapper(); mapper.configure(serializationfeature.fail_on_empty_beans, false); public static String serialize(timeout t) throws JsonProcessingException { return mapper.writevalueasstring(t); public static Timeout deserialize(string text, Class<Timeout> clazz) throws IOException { return mapper.readvalue(text, clazz); 3.4 Start własnego REST API Teraz już wystarczy tylko wystartować własne REST API wraz ze startem własnego modułu. W tym celu do metody startup() klasy SdnLabListener należy dodać: public void startup(floodlightmodulecontext context) throws FloodlightModuleException { restapiservice.addrestletroutable(new RestLab()); 6

Zadanie1: Proszę uruchomić kontroler i w przeglądarce wejść na stronę http://127.0.0.1:8080/sdnlab/timeout Proszę sprawdzić pobieranie aktualnych wartości liczników. Zadanie 2: Proszę zmienić wartości tych liczników wysyłając odpowiedniego JSONa na podany adres (np. z wykorzystaniem aplikacji curl). Proszę sprawdzić czy wartości te zostały nadpisane (ponownym ich pobraniem). W tym celu można wykorzystać polecenie: $ curl -X POST -H 'Content-Type: application/json' -i 'http://127.0.0.1:8080/sdnlab/timeout' --data '{"hardtimeout":10,"idletimeout":15' Zadanie 3: Proszę tak zmodyfikować klasę Flows, aby dodawanie nowych przepływów następowało z wykorzystaniem ustawionych wartości przez REST API (sugeruję wartości rzędu kilku sekund). Proszę odczekać chwilę na wygaśnięcie przepływów, a następnie zmienić wartości liczników i jeszcze raz puścić ruch, aby zaobserwować nowe przepływy z nowymi wartościami. Pomocne może być wykorzystanie komendy: #ovs-ofctl dump-flows s1 4. Pobieranie statystyk za pomocą protokołu Openflow OpenFlow dostarcza możliwość pobrania statystyk per port, per kolejka, per tablica, per przepływ. W niniejszym przykładzie zostaną pobrane statystyki dla portów przełącznika. Należy zaznaczyć, że synchroniczne zbieranie statystyk (nie tylko dla protokołu OpenFlow) nie powinno być implementowane w żadnym wątku kodu wykonującym wywołania wejścia lub wyjścia oraz w żadnym wątku, w którym jest implementacja metod nasłuchujących (listener thread). Taka implementacja może spowodować, że oczekiwanie na odpowiedź zablokuje inne zadania. Rekomendowana jest implementacja osobnego wątku, w którym następuje obsługa zapytań i odpowiedzi wiadomości OpenFlow dotyczących statystyk. 4.1. Implementacja klasy kolektora statystyk OpenFlow Poniżej zaprezentowano przykładową klasę o nazwie StatisticsCollector, w której zaimplementowano zbieranie statystyk ze wszystkich portów przełącznika OpenFlow. Klasa ta jest singletonem, tj. może powstać tylko jeden obiekt tej klasy. Proszę zwrócić uwagę, że konstruktor jest prywatny a sam dostęp do obiektu tej klasy następuje przez metodę getinstance(), która to może wywołać ten konstruktor o ile obiekt singleton nie istnieje. W klasie tej zaimplementowano kolejną klasę (PortStatisticsPoller) dziedziczącą z klasy TimerTask. W klasie PortStatisticsPoller jest wymagane implementacja jednej metody run() z adnotacją. Metoda ta jest odpowiedzialna za wysyłanie zapytań typu OFStatsRequest o statystyki portów oraz odbieranie i obsługę odpowiedzi OFStatsReply. Poniżej pełny kod tej klasy. Proszę ją umieścić w paczce pl.edu.agh.kt. package pl.edu.agh.kt; import java.util.list; import java.util.timer; import java.util.timertask; import java.util.concurrent.executionexception; import java.util.concurrent.timeunit; import java.util.concurrent.timeoutexception; import org.projectfloodlight.openflow.protocol.ofportstatsentry; import org.projectfloodlight.openflow.protocol.ofportstatsreply; 7

import org.projectfloodlight.openflow.protocol.ofstatsreply; import org.projectfloodlight.openflow.protocol.ofstatsrequest; import org.projectfloodlight.openflow.types.ofport; import org.slf4j.logger; import org.slf4j.loggerfactory; import com.google.common.util.concurrent.listenablefuture; import net.floodlightcontroller.core.iofswitch; public class StatisticsCollector { private static final Logger logger = LoggerFactory.getLogger(StatisticsCollector.class); private IOFSwitch sw; public class PortStatisticsPoller extends TimerTask { private final Logger logger = LoggerFactory.getLogger(PortStatisticsPoller.class); private static final int TIMEOUT = PORT_STATISTICS_POLLING_INTERVAL / 2; public void run() { logger.debug("run() begin"); synchronized (StatisticsCollector.this) { if (sw == null) { // no switch logger.error("run() end (no switch)"); return; ListenableFuture<?> future; List<OFStatsReply> values = null; OFStatsRequest<?> req = null; req = sw.getoffactory().buildportstatsrequest().setportno(ofport.any).build(); try { if (req!= null) { future = sw.writestatsrequest(req); values = (List<OFStatsReply>) future.get(port_statistics_polling_interval * 1000 / 2, TimeUnit.MILLISECONDS); OFPortStatsReply psr = (OFPortStatsReply) values.get(0); for (OFPortStatsEntry pse : psr.getentries()) { if (pse.getportno().getportnumber() > 0) { logger.info("port number: {, txpackets: {", pse.getportno().getportnumber(), pse.gettxpackets().getvalue()); ex) { catch (InterruptedException ExecutionException TimeoutException logger.error("error during statistics polling", ex); logger.debug("run() end"); public static final int PORT_STATISTICS_POLLING_INTERVAL = 3000; // in ms private static StatisticsCollector singleton; 8

private StatisticsCollector(IOFSwitch sw) { this.sw = sw; new Timer().scheduleAtFixedRate(new PortStatisticsPoller(), 0, PORT_STATISTICS_POLLING_INTERVAL); public static StatisticsCollector getinstance(iofswitch sw) { logger.debug("getinstance() begin"); synchronized (StatisticsCollector.class) { if (singleton == null) { logger.debug("creating StatisticsCollector singleton"); singleton = new StatisticsCollector(sw); logger.debug("getinstance() end"); return singleton; 4.2 Uruchomienie kolektora statystyk Teraz już wystarczy tylko utworzyć obiekt tej klasy (stworzony zostanie tylko jeden raz, singleton), aby zacząć zbierać i wyświetlać statystyki. W tym celu w klasie SdnLabListener w metodzie receive() proszę dodać linię jak poniżej: public net.floodlightcontroller.core.ilistener.command receive(iofswitch sw, OFMessage msg, FloodlightContext cntx) { StatisticsCollector.getInstance(sw); Zadanie 4: Proszę tak zmodyfikować klasy StatisticsCollector oraz PortStatisticsPoller, aby wyświetlać szybkość transmisji na poszczególnych łączach. 9