Ohjelmointi

Seuraa vastuun ketjua

Vaihdoin äskettäin Windowsista Mac OS X: ään ja olen innoissaan tuloksista. Mutta taas kerran vietin vain lyhyen viiden vuoden jakson Windows NT: ssä ja XP: ssä; ennen sitä olin tiukasti Unix-kehittäjä 15 vuotta, enimmäkseen Sun Microsystemsin koneilla. Minulla oli myös onni kehittää ohjelmistoja Nextstepin alla, joka on rehevä Unix-pohjainen edeltäjä Mac OS X: ssä, joten olen hieman puolueellinen.

Kauniin Aqua-käyttöliittymän lisäksi Mac OS X on Unix, epäilemättä paras olemassa oleva käyttöjärjestelmä. Unixilla on monia hienoja ominaisuuksia; yksi tunnetuimmista on putki, jonka avulla voit luoda komentojen yhdistelmiä vetämällä yhden komennon lähdön toisen syötteeseen. Oletetaan esimerkiksi, että haluat luetella lähdekoodit Struts-lähdejakelusta, jotka kutsuvat tai määrittelevät nimetyn menetelmän suorittaa(). Tässä on yksi tapa tehdä se putken kanssa:

 grep "execute (" `etsi $ STRUTS_SRC_DIR -name" * .java "" | awk -F: '{print}' 

grep komento etsii tiedostoista säännöllisiä lausekkeita; täällä käytän sitä etsimään merkkijonon esiintymät suorittaa( tiedostoissa, jotka löytö komento. greptuotos putkistoon awk, joka tulostaa ensimmäisen merkin - erotettuna kaksoispisteellä - kullekin riville greptuotos (pystysuora palkki tarkoittaa putkea). Tämä tunniste on tiedostonimi, joten pääsen luetteloon tiedostonimistä, jotka sisältävät merkkijonon suorittaa(.

Nyt kun minulla on luettelo tiedostonimistä, voin lajitella luettelon toisella putkella:

 grep "execute (" `etsi $ STRUTS_SRC_DIR -name" * .java "` | awk -F: '{print}' | järjestellä

Tällä kertaa olen lähettänyt luettelon tiedostonimistä osoitteeseen järjestellä. Entä jos haluat tietää, kuinka monessa tiedostossa merkkijono on suorittaa(? Toisella putkella on helppoa:

 grep "execute (" `etsi $ STRUTS_SRC_DIR -name" * .java "` | awk -F: '{print}' | lajittelu -u | wc -l 

WC komento laskee sanat, rivit ja tavut. Tässä tapauksessa määritin - vaihtoehto laskea rivejä, yksi rivi kullekin tiedostolle. Lisäsin myös a -u vaihtoehto järjestellä varmistaa jokaisen tiedostonimen ainutlaatuisuus ( -u suodattaa kaksoiskappaleet).

Putket ovat tehokkaita, koska niiden avulla voit säveltää dynaamisesti toimintaketjun. Ohjelmistojärjestelmissä käytetään usein vastaavia putkia (esim. Sähköpostisuodattimet tai suodinsarja servletille). Putkien ja suodattimien sydämessä on suunnittelukuvio: Vastuuketju (CoR).

merkintä: Voit ladata tämän artikkelin lähdekoodin Resursseista.

Alueiden komitean esittely

Vastuuketju-malli käyttää objektiketjua pyynnön käsittelemiseen, joka on tyypillisesti tapahtuma. Ketjun objektit välittävät pyynnön ketjussa, kunnes yksi esineistä käsittelee tapahtumaa. Käsittely loppuu tapahtuman käsittelyn jälkeen.

Kuva 1 kuvaa, miten AK: n malli käsittelee pyynnöt.

Sisään Suunnittelumalleja, kirjoittajat kuvaavat vastuullisuusketjun mallia näin:

Vältä pyynnön lähettäjän liittämistä vastaanottimeen antamalla useammalle kohteelle mahdollisuus käsitellä pyyntöä. Ketjua vastaanottavat esineet ja välitä pyyntö ketjua pitkin, kunnes esine käsittelee sitä.

Vastuuketjumalli on sovellettavissa, jos:

  • Haluat irrottaa pyynnön lähettäjän ja vastaanottajan
  • Useat ajon aikana määritetyt objektit ovat ehdokkaita, jotka käsittelevät pyyntöä
  • Et halua määrittää käsittelijöitä erikseen koodissasi

Jos käytät AK: n mallia, muista:

  • Vain yksi ketjun esine käsittelee pyynnön
  • Joitakin pyyntöjä ei ehkä käsitellä

Nämä rajoitukset koskevat tietysti AK: n klassista täytäntöönpanoa. Käytännössä nämä säännöt ovat taipuneet; esimerkiksi servlet-suodattimet ovat CoR-toteutus, jonka avulla useat suodattimet voivat käsitellä HTTP-pyyntöä.

Kuvassa 2 on esitetty AK: n malliluokka.

Tyypillisesti pyynnön käsittelijät ovat perusluokan laajennuksia, jotka ylläpitävät viittausta ketjun seuraavaan käsittelijään, joka tunnetaan nimellä seuraaja. Perusluokka saattaa toteuttaa kahvaRequest () kuten tämä:

 julkinen abstrakti luokka HandlerBase {... public void handleRequest (SomeRequestObject sro) {if (seuraaja! = null) seuraaja.handleRequest (sro); }} 

Joten käsittelijät välittävät pyynnön oletusarvoisesti ketjun seuraavalle käsittelijälle. Konkreettinen jatko HandlerBase saattaa näyttää tältä:

 public class SpamFilter laajentaa HandlerBase {public void handleRequest (SomeRequestObject mailMessage) {if (isSpam (mailMessage)) {// Jos viesti on roskapostia // tee roskapostiin liittyviä toimintoja. Älä lähetä viestiä eteenpäin. } else {// Viesti ei ole roskapostia. super.handleRequest (mailMessage); // Lähetä viesti ketjun seuraavaan suodattimeen. }}} 

Roskapostisuodatin käsittelee pyynnön (oletettavasti uuden sähköpostin vastaanottamisen), jos viesti on roskapostia, joten pyyntö ei mene pidemmälle; muuten luotettavat viestit välitetään seuraavalle käsittelijälle, oletettavasti toiselle sähköpostisuodattimelle, joka pyrkii poistamaan ne. Lopulta ketjun viimeinen suodatin saattaa tallentaa viestin sen jälkeen, kun se on läpäissyt kokoontumispaikan siirtymällä useiden suodattimien läpi.

Huomaa, että edellä käsitellyt hypoteettiset sähköpostisuodattimet sulkevat toisensa pois: Viime kädessä vain yksi suodatin käsittelee pyynnön. Voit halutessasi kääntää tämän ylösalaisin antamalla usean suodattimen käsitellä yhtä pyyntöä, mikä on parempi analogia Unix-putkiin. Joko niin, taustalla oleva moottori on AK: n malli.

Tässä artikkelissa käsittelen kahta vastuullisuusketjumallin toteutusta: servlet-suodattimet, suosittu AK-toteutus, joka sallii useiden suodattimien käsittelevän pyyntöä, ja alkuperäinen Abstract Window Toolkit (AWT) -tapahtumamalli. .

Servlet-suodattimet

Java 2 Platform, Enterprise Editionin (J2EE) alkuaikoina jotkut servlet-kontit tarjosivat kätevän ominaisuuden, joka tunnetaan nimellä servlet-ketjutus, jolloin olennaisesti voitiin käyttää suodatinluetteloa servlet-sovellukseen. Servlet-suodattimet ovat suosittuja, koska ne ovat hyödyllisiä tietoturvan, pakkaamisen, lokien kirjaamisen ja muun suhteen. Ja tietysti voit laatia suodatinketjun tehdäksesi kaikki tai kaikki näistä ajonaikaisista olosuhteista riippuen.

Java Servlet Specification -ohjelman version 2.3 myötä suodattimista tuli vakiokomponentteja. Toisin kuin perinteinen CoR, servlet-suodattimet sallivat ketjussa olevien useiden objektien (suodattimien) käsitellä pyyntöä.

Servlet-suodattimet ovat tehokas lisäys J2EE: hen. Suunnittelumallien näkökulmasta ne tarjoavat mielenkiintoisen käänteen: Jos haluat muokata pyyntöä tai vastausta, käytä AK: n lisäksi Decorator-mallia. Kuva 3 näyttää, kuinka servlet-suodattimet toimivat.

Yksinkertainen servlet-suodatin

Sinun on tehtävä kolme asiaa servletin suodattamiseksi:

  • Ota käyttöön servlet
  • Ota suodatin käyttöön
  • Yhdistä suodatin ja servlet

Esimerkit 1-3 suorittavat kaikki kolme vaihetta peräkkäin:

Esimerkki 1. Servlet

tuo java.io.PrintWriter; tuo javax.servlet. *; tuo javax.servlet.http. *; public class FilteredServlet laajentaa HttpServlet {public void doGet (HttpServletRequest-pyyntö, HttpServletResponse vastaus) heittää ServletException, java.io.IOException {PrintWriter out = response.getWriter (); out.println ("Suodatettu palvelinsovellus käynnistetty"); }} 

Esimerkki 2. Suodatin

tuo java.io.PrintWriter; tuo javax.servlet. *; tuo javax.servlet.http.HttpServletRequest; public class AuditFilter toteuttaa suodattimen {private ServletContext app = null; public void init (FilterConfig config) {app = config.getServletContext (); } julkinen mitätöinti doFilter(ServletRequest-pyyntö, ServletResponse-vastaus, FilterChain-ketju) heittää java.io.IOException, javax.servlet.ServletException {app.log ((((HttpServletRequest) pyyntö) .getServletPath ()); chain.doFilter(pyyntö, vastaus); } julkinen mitätöinti () {}} 

Esimerkki 3. Käyttöönoton kuvaaja

    auditFilter AuditFilter <suodatinkartoitus>auditFilter/ filteredServlet</ suodatinkartoitus> filteredServlet FilteredServlet suodatettuServlet / suodatettuServlet ... 

Jos käytät servletiä URL-osoitteella / filteredServlet, auditFilter saa halkeaman pyynnöstä ennen servletiä. AuditFilter.doFilter kirjoittaa servlet-säilön lokitiedostoon ja kutsuu chain.doFilter () välittää pyyntö. Servlet-suodattimia ei vaadita soittamiseen chain.doFilter (); jos he eivät, pyyntöä ei välitetä eteenpäin. Voin lisätä lisää suodattimia, jotka kutsutaan siinä järjestyksessä kuin ne on ilmoitettu edellisessä XML-tiedostossa.

Nyt kun olet nähnyt yksinkertaisen suodattimen, katsotaan toinen suodatin, joka muuttaa HTTP-vastausta.

Suodata vastaus Decorator-kuviolla

Toisin kuin edellinen suodatin, joidenkin servlet-suodattimien on muokattava HTTP-pyyntöä tai vastausta. Mielenkiintoista on, että tämä tehtävä liittyy Decorator-kuvioon. Keskustelin Decorator-kuviosta kahdessa edellisessä Java-suunnittelumallit artikkelit: "Hämmästytä kehittäjäystäviäsi suunnittelumalleilla" ja "Koristele Java-koodisi".

Esimerkissä 4 on luettelo suodattimesta, joka suorittaa yksinkertaisen haun ja vaihdon vastauksen rungossa. Tämä suodatin koristaa servlet-vastausta ja välittää sisustajan servletille. Kun servlet pienenee kirjoittamaan koristeltuun vastaukseen, suodatin suorittaa haun ja korvauksen vastauksen sisällössä.

Esimerkki 4. Etsi ja korvaa suodatin

tuo java.io. *; tuo javax.servlet. *; tuo javax.servlet.http. *; public class SearchAndReplaceFilter toteuttaa suodattimen {private FilterConfig config; public void init (FilterConfig config) {this.config = config; } public FilterConfig getFilterConfig () {return config; } public void doFilter (ServletRequest-pyyntö, ServletResponse-vastaus, FilterChain-ketju) heittää java.io.IOException, javax.servlet.ServletException {StringWrapper kääre = uusi StringWrapper((HttpServletResponse) vastaus); chain.doFilter(pyyntö, kääre); Merkkijono responseString = wrapper.toString(); Merkkijono haku = config.getInitParameter ("haku"); Merkkijono korvaa = config.getInitParameter ("korvaa"); if (haku == null || korvaa == null) return; // Parametreja ei ole asetettu oikein int index = responseString.indexOf (haku); if (indeksi! = -1) {String beforeReplace = responseString.substring (0, indeksi); Merkkijono afterReplace = responseString.substring (hakemisto + haku.pituus ()); response.getWriter (). tulosta(beforeReplace + vaihda + afterReplace); }} public void hävitä () {config = null; }} 

Edellinen suodatin etsii nimettyjä suodattimen init-parametreja Hae ja korvata; jos ne on määritelty, suodatin korvaa ensimmäisen esiintymisen Hae parametrin arvo korvata parametrin arvo.

SearchAndReplaceFilter.doFilter () kääri (tai koristaa) vastausobjektin kääreellä (koristelija), joka seisoo vastauksen edessä. Kun SearchAndReplaceFilter.doFilter () puhelut chain.doFilter () välittää pyyntö, se ohittaa kääreen alkuperäisen vastauksen sijaan. Pyyntö välitetään servletille, joka tuottaa vastauksen.

Kun chain.doFilter () palaa, servlet on tehty pyynnön kanssa, joten menen töihin. Ensin tarkistan Hae ja korvata suodatinparametrit; jos on, saan vastauksen kääreeseen liittyvän merkkijonon, joka on vastauksen sisältö. Sitten teen korvauksen ja tulostan sen takaisin vastaukseen.

Esimerkissä 5 luetellaan StringWrapper luokassa.

Esimerkki 5. Sisustaja

tuo java.io. *; tuo javax.servlet. *; tuo javax.servlet.http. *; public class StringWrapper laajentaa HttpServletResponseWrapper {StringWriter-kirjoittaja = new StringWriter (); public StringWrapper (HttpServletResponse response) {super (vastaus); } public PrintWriter getWriter () {palauta uusi PrintWriter (kirjoittaja); } public String toString () {return kirjoittaja.String (); }} 

StringWrapper, joka koristaa HTTP-vastausta esimerkissä 4, on laajennus HttpServletResponseWrapper, mikä säästää meitä siitä, miten luodaan sisustajan perusluokka HTTP-vastausten koristeluun. HttpServletResponseWrapper lopulta toteuttaa Servlet-vastaus käyttöliittymä, joten HttpServletResponseWrapper voidaan siirtää mihin tahansa menetelmään odottaen a Servlet-vastaus esine. Siksi SearchAndReplaceFilter.doFilter () voi soittaa chain.doFilter (pyyntö, kääre) sijasta chain.doFilter (pyyntö, vastaus).

Nyt kun meillä on suodatin ja vastausten kääre, yhdistetään suodatin URL-malliin ja määritetään haku ja korvaavat mallit: