Ordinarily, working with Adobe Content Server (ACS) is more or less tolerable, but recently we have encountered what may be another indication of the quality of this product.

Packaging

Packaging an ebook amounts to posting a signed request to the server, describing the file you’d like to ingest.

You can use UploadTestJar (which may have its own issues), or (if you’re lucky) you can rewrite some sample codes in your chosen language and use that.

You’d think then, that once you’ve got it working you can summarily be on your way, forget about ACS and finish your application.

And you can.

Until the OutOfMemoryExceptions start

While test-driving our application, we naturally wanted to describe what happens with different sizes of files, so we tried some large ones. These would fail with errors about being out of heap space, errors like:

So, it appears the problem may result from usage of the first version.

That paramParsedRequest argument to doPost is of type ParsedRequest, and its data property is a Byte array.

This could be a problem: when submitting a package request with a data node instead of a dataPath node, we’re using the byte array overload.

Where is the error actually coming from?

From the stacktrace it looks as though it is coming from whatever is creating the arguments to supply to Package.doPost.

This is the responsibility of Package‘s supertype: AdeptServlet<RequestParser>. It is this class that is responsible for parsing the http request into one of those ParsedRequest objects, and then supplying that to Package.doPost.

We have some Windows services that need to access network shares, and even though we have net used, those resources are still unavailable. It appears this is because our services are running as LocalSystem.

We have had the case where we’re creating a class that allows clients to block while internally it reads an entire stream asynchronously. This class encapsulates the state required to perform such a task.

While attempting to write unit tests for exceptions, we found that an exception thrown during the asynchronous operation would not be thrown to client. Debugging showed that the exception was being thrown, but no notification was being sent to the parent thread.

No such thing as unhandled exceptions on managed threads

[MSDN] [since .NET Framework v2.0] There is no such thing as an unhandled exception on a thread pool [or finalizer] thread. When a task throws an exception that it does not handle, the runtime prints the exception stack trace to the console and then returns the thread to the thread pool.

Errors raised on a child thread are essentially lost when the thread exits. This means there is some work required to propagate these exceptions.

This requires a blocking wait on the part of the client, and a mechanism for storing the exception so the parent thread can read it.

As an example, we have implemented an AsyncStreamReader which contains a blocking ReadAll method. If an asynchronous read fails with an exception, that exception is exposed internally as a field, and the waiting thread is then signalled. Once the waiting thread wakes up it checks the exception field and throws it if required.

We have blocking Read operation that waits for an async read to complete. The notification mechanism is a ManualResetEvent (WaitHandle).

T1: Invoke ReadAll.

T1: Start async operation (spawns T2).

T1: Wait.

T2: Async operation encounters exception.

T2: Store exception in _error field.

T2: Signals T1.

T2: Returns without triggering any subsequent reads.

T2: Thread exits

T1: Parent thread resumes (still inside ReadAll).

T1: Checks _error field. If it is not null, throw it, otherwise return.

The Future class takes an InputChannel as its ctor argument. This channel is monitored to determine the future’s completion status.

In this instance, the future by !! is configured with the reply channel as supplied to send. In short, the actor has sent itself a message and specified that the future’s channel should be notified when complete. The future then just monitors that channel for the reply.

Note: Actor.send invokes the act method using a Reaction, which spawns threads and runs actors.

Only the actor creating an instance of a Channel may receive from it. This means that the future here must be running on the same thread as the actor that created it.

The send call is instructing the reply to be returned to the channel being monitored by the future.

Note: unless you specify offline_access, your tokens will expire as soon as the user signs out of facebook.

Note: client_secret is not supplied:

[3.5.1. User-Agent Flow] This user-agent flow does not utilize the client secret since the client executables reside on the end user’s computer or device which makes the client secret accessible and exploitable.

secret_type
OPTIONAL. The access token secret type as described by Section 5.3.
If omitted, the authorization server will issue a bearer token
(an access token without a matching secret) as described by Section 5.2.

we have been issued a bearer token.

I think this refers to OAuth 1.0-style authentication using token secret. You’d only need one of those if you were requiring signed requests. This seems to contradict the part above about storing client secret on user agent.

Refreshing tokens

Section 3.5.1 describes that the access token may be delivered with an optional refresh_token fragment. On expiry, this token can be exchanged at the for a new access token. No refresh token is supplied by the Facebook API under User-Agent flow, meaning you’ll have to ask users to sign in again.

Autonomous client flows are used to grant client access to protected resources controlled by the client (i.e. the client is the resource owner). For example, these flows are useful when a service provides both client-specific resources in addition to end user resources.

And more specifially, the Client Credentials Flow is described in section 3.7.1:

The client credentials flow is used when the client acts autonomously without acting on behalf of a separate resource owner. The client secret is assumed to be high-entropy since it is not designed to be memorize by an end user.

Where a client is:

An HTTP client capable of making authenticated requests for protected resources using the OAuth protocol. [This is third-party application that wants to access a resource owner’s Facebook account.]

And a resource owner:

An entity capable of granting access to a protected resource. [This is the user who owns the Facebook account.]

[TBD: So what?]

Tokens, sessions and that

You can see more information about authentication flow by using a bogus redirect_uri, i.e., one that does not match the Connect URL setting in your application, e.g.: