Hi,
I'm making a few modifications to the Url class hierarchy from Squeak. The HttpUrl subclass has a method to retrieve whatever the url points to. Since it might need a username and password, it has a method for asking the user for it, in case it gets an authentication error. I'd like to change this behavior. In some cases, I'd like it not to open any window (asking for the username and password) and just log an error somewhere. What do you recommend? Having a flag inside HttpUrl that indicates the behavior in case of an authentication error, or having 2 separate classes? BTW, where should I log the error? O:-) Thanks |
Fernando,
> BTW, where should I log the error? O:-) Notification signal: 'a message' is a reasonable choice. In a development image the message gets written to the Transcript, in a deployed image (by default) it is written to the Windows debug stream where it can be read with tools like DebugView from www.sysinternals.com (highly recommended). > What do you recommend? Having a flag inside HttpUrl that indicates the > behavior in case of an authentication error, or having 2 separate > classes? Or you could pass a block to #connect, something like: anUrl connectAuthenticatingUsing: [:realm | Prompter promp: ... ] where the block would be evaluated if authentication was required and not otherwise. There's another possibility that you might like. You can make #connect throw a /resumable/ exception. The rough idea is that client code can catch that exception, prompt the user for the password, and then resume it /if/ it wants to. The advantage is that the place where the user is prompted for a password can be separated by several layers of code from the place where the connection takes place. It doesn't have to be an exception hander immediately around #connect, but could be at the very top-level of the GUI where a command that might ultimately use '#connect was issued. This allows you to decouple the low-level networking code from the high-level GUI code, without having to pass a load of extra data through all the intervening layers of software. (Unlike the case of using a block as above, or a flag or separate classes.) Create your own subclass of Exception, HttpAuthenticationRequiredException, add an override of #isResumable that answers true, and add a few instvars to hold the domain/URL/realm/whatever for which authentication is required, and user name and password that client code may fill in if it wishes. In the place where the "error" is discovered you can raise the exception with HttpAuthenticationRequiredException signal or similar. [ex := (HttpAuthenticationRequiredException new) realm: ...whatever...; url: ... whatever...; yourself. ex signal: 'Authentication required'] whileTrue: [...re-try using info in ex..]. If that is handled by the client code (below) then #signal: should answer either true or false. If true then you can extract the supplied user-name and password from the exception (that the client code has filled in) and attempt to connect again. If it answers false then the client code has handled the exception but the user (or whatever) has decided that he/she/it does not want to attempt authentication, so you can bomb out (without logging anything, since it's all working according to how the user wants it to). If the client code does /not/ handle the exception, then (because it a subclass of Exception rather than Notification) the normal default exception handling will kick in. Unless you change it, the default is to abort the calling process and log an error. In the client code, if it wishes to be able to supply a username/password then it should use an exception handler, something like: [connection := anURL connect] on: HttpAuthenticationRequiredException do: [:ex | ... handler... ]. In the handler code you can do three things. You can prompt the user for username/password (using the information supplied in 'ex'), if the user supplies some data then you can stuff that into the exception and then do ex resume: true. Which will return true to the place where the exception was raised. That code can then extract the username/password from the exception and use it to attempt to make the connection. Alternatively you could do ex resume: false. which would return false to the place where the exception was raised. Presumably you'd do that if the user didn't want to supply a password. Or you could decide that the exception should be treated /as/ an exception, aborting the current process and creating a log record. To do that you can do ex pass which, in effect, re-signals it for any containing signal handler to see, or for its default behaviour to kick in if there is no such handler. Of course, if you never want to handle such exceptions in one particular place where you use #connect, then you wouldn't bother setting up an exception handler at all. Of course, there are other options... -- chris P.S. Having just written all that, I've just noticed/remembered that Steve Waring's 'SW HTTPClient' package uses a similar exception-based technique; you might like to take a look at it. |
In reply to this post by Fernando Rodriguez
Fernando,
> I'm making a few modifications to the Url class hierarchy from Squeak. > The HttpUrl subclass has a method to retrieve whatever the url points > to. > > Since it might need a username and password, it has a method for > asking the user for it, in case it gets an authentication error. > > I'd like to change this behavior. In some cases, I'd like it not to > open any window (asking for the username and password) and just log an > error somewhere. FWIW, you are on the right track. Inappropriate GUI interaction is a problem in Squeak. It is improving over time, but you are wise to correct it in your port. I will also second Chris' plug for Steve's HTTP client. I have not actually used it, but it appears to be well worth a look. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
Free forum by Nabble | Edit this page |