2012-07-13 3 views
5

Sto cercando di capire i concetti di I/O reattivo del framework Play 2.0. Per capire meglio fin dall'inizio, ho deciso di saltare gli helper del framework per costruire iterate di diverso tipo e di scrivere uno Iteratee personalizzato da utilizzare da un BodyParser per analizzare il corpo di una richiesta.Perché rende l'errore di chiamata o fatto in un Iteratee di BodyParser la richiesta si blocca in Play Framework 2.0?

partire con le informazioni disponibili in Iteratees e ScalaBodyParser documenti e due presentazioni su gioco I reattiva/O questo è ciò che mi si avvicinò con:

import play.api.mvc._ 
import play.api.mvc.Results._ 
import play.api.libs.iteratee.{Iteratee, Input} 
import play.api.libs.concurrent.Promise 
import play.api.libs.iteratee.Input.{El, EOF, Empty} 

01 object Upload extends Controller { 
02 def send = Action(BodyParser(rh => new SomeIteratee)) { request => 
03  Ok("Done") 
04 } 
05 } 
06 
07 case class SomeIteratee(state: Symbol = 'Cont, input: Input[Array[Byte]] = Empty, received: Int = 0) extends Iteratee[Array[Byte], Either[Result, Int]] { 
08 println(state + " " + input + " " + received) 
09 
10 def fold[B](
11  done: (Either[Result, Int], Input[Array[Byte]]) => Promise[B], 
12  cont: (Input[Array[Byte]] => Iteratee[Array[Byte], Either[Result, Int]]) => Promise[B], 
13  error: (String, Input[Array[Byte]]) => Promise[B] 
14 ): Promise[B] = state match { 
15  case 'Done => { println("Done"); done(Right(received), Input.Empty) } 
16  case 'Cont => cont(in => in match { 
17  case in: El[Array[Byte]] => copy(input = in, received = received + in.e.length) 
18  case Empty => copy(input = in) 
19  case EOF => copy(state = 'Done, input = in) 
20  case _ => copy(state = 'Error, input = in) 
21  }) 
22  case _ => { println("Error"); error("Some error.", input) } 
23 } 
24 } 

(Nota: Tutti queste cose sono nuove per me , per favore perdonami se qualcosa su questo è una schifezza totale.) L'Iteratee è piuttosto stupido, legge solo tutti i blocchi, somma il numero di byte ricevuti e stampa alcuni messaggi. Tutto funziona come previsto quando chiamo l'azione del controller con alcuni dati: posso osservare che tutti i blocchi vengono ricevuti dall'Iteratee e quando tutti i dati vengono letti, passa allo stato completato e la richiesta termina.

Ora ho iniziato a giocare con il codice perché volevo vedere il comportamento di questi due casi:

  • commutazione in errore stato precedente tutti gli input viene letto.
  • Passaggio allo stato eseguito prima che tutti gli input vengano letti e restituendo un Result anziché lo Int.

La mia comprensione della documentazione di cui sopra è che entrambi dovrebbero essere possibili ma in realtà non sono in grado di comprendere il comportamento osservato. Per testare il primo caso ho cambiato linea 17 del codice precedente:

17  case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Error else 'Cont, input = in, received = received + in.e.length) 

Così ho aggiunto un condizione di passare nello stato di errore se sono state ricevute più di 10000 byte. L'output che ottengo è questo:

'Cont Empty 0 
'Cont El([[email protected]) 8192 
'Error El([[email protected]) 16384 
Error 
Error 
Error 
Error 
Error 
Error 
Error 
Error 
Error 
Error 
Error 

Quindi la richiesta si blocca per sempre e non finisce mai. La mia aspettativa dai documenti sopra citati era che quando chiamo la funzione error all'interno di fold di un Iteratee l'elaborazione dovrebbe essere interrotta. Quello che sta accadendo qui è che il metodo fold di Iteratee viene chiamato più volte dopo che è stato chiamato error - beh e quindi la richiesta si blocca.

Quando si passa allo stato di fatto prima di leggere tutti gli input, il comportamento è abbastanza simile. Modificando la riga 15 a:

15 case 'Done => { println("Done with " + input); done(if (input == EOF) Right(received) else Left(BadRequest), Input.Empty) } 

e la linea 17 a:

17  case in: El[Array[Byte]] => copy(state = if(received + in.e.length > 10000) 'Done else 'Cont, input = in, received = received + in.e.length) 

produce il seguente output:

'Cont Empty 0 
'Cont El([[email protected]) 8192 
'Done El([[email protected]) 16384 
Done with El([[email protected]) 
Done with El([[email protected]) 
Done with El([[email protected]) 
Done with El([[email protected]) 

e nuovamente la richiesta si blocca sempre.

La mia domanda principale è perché la richiesta è sospesa nei casi sopra citati. Se qualcuno potesse far luce su questo, lo apprezzerei molto!

risposta

0

Le cose devono essere cambiate con Play 2.1 - Promise non è più parametrico e questo esempio non compila più.