Interface | Description |
---|---|
ERAttachmentRequestHandler.Delegate |
The delegate definition for this request handler.
|
Class | Description |
---|---|
ERAttachmentPrincipal |
ERAttachment Framework Principal ('ERAttachment' name is the name of the model object)
|
ERAttachmentRequestHandler |
ERAttachmentRequestHandler is the request handler that is used for loading
any proxied attachment.
|
It is a common requirement to be able to have arbitrary attachments of images, files, or documents throughout your WebObjects application. ERAttachment framework was inspired by the Rails attachment_fu gem to provide a simple mechanism of adding attachments to your application that abstract the actual storage requirements for those attachments. The framework provide a single unified set of components and models that can allow the storage of attachments on your local filesystem, served through your webserver; on your local filesystem served through a custom request handler; in your database, served through a custom request handler; on RackSpace's CloudFiles service; and on Amazon's S3 service, served directly from S3. The intent of the framework is to make it very simple to control how the attachments are stored and served by simple adjusting some configuration properties.
If you use Project Wonder migrations and migrateOnStartup, then the ERAttachment tables will be automatically created for you on the first launch. If you do not, then you can either manually execute the .migration SQL scripts that are in the Resources folder (execute them in numeric order), or you can open the EOModel and generate SQL for your particular database.
You will have to configure a connection dictionary for the ERAttachment model. You can do this either by setting global connection properties for all of your EOModels, or by setting the connection dictionary for just the ERAttachment model.
Each processor has different settings that are required to be able to perform an attachment import. A concept that is carried through all of them, however, is that of a "configuration". It's very likely that a simple application may have multiple attachment columns -- maybe a Person has an avatar, and a Bug has a screenshot. In this case, it is also often desirable to have different configuration sets for each type of attachment. All of the configuration settings can be overridden on a per configuration basis. For instance, if you set the value "er.attachment.s3.bucket" as the default bucket to put attachments into, you could also define a configuration "Person.avatar" (the actual String is unimportant, but a good idea is to make it EntityName.relationshipName to keep things clear) and then also define a property "er.attachment.s3.Person.avatar.bucket" which would override the bucket definition of any "Person.avatar" configuration. All of the ERAttachment components allow you to pass in a configurationName binding which corresponds to this concept.
There are some properties that apply to all attachment types.
While ERAttachment will try and automatically figure out a library for image processing operations such as thumbnailing and resizing, you can configure it yourself using appropriate properties. Here are some properties that apply to image processing libraries configuration.
Several attachment types define filesystem and webserver paths for storing and accessing attachments. A very simple set of template variables are defined for use in the definition of these paths.
The easiest attachment type to use, because it has the least amount of setup required is a database attachment. Database attachments are stored in the database in the ERAttachmentData table. Currently this is not using a method like the C9ResourceStreamer, which could stream in and out of the a database column, rather it is using normal EOF NSData blob columns. Because of the memory characteristics of blob columns in EOF, this attachment type is only suitable for relatively small data sizes. All database attachments are served through the ERAttachmentRequestHandler. Because it is proxied, database attachments provide the capability of security checking access to the attachments from within your WebObjects applications (see the section on security below for more info).
File attachments give a much greater level of flexibility compared to database attachments. In particular, file attachments can be optionally not proxied. A proxied attachment is one that is served through the ERAttachmentRequestHandler, and is streamed to the user by your WebObjects application. A non-proxied attachment is served directly as a static file through Apache (or whatever webserver you are using). To be able to use file attachments, your WebObjects application must have write permission to whatever folders and subfolders you specify as filesystemPath. Additionally, if you are using non-proxied attachments, those folders must be externally visible from your webserver (that is, the filesystemPath must be a subfolder of a document root). Proxied attachments also provide the capability of security checking access to the attachments from within your WebObjecst applications (see the section on security below for more info).
Amazon's S3 service is an incredibly cost-effective way to host large amounts of data, which is ideal if you are offering an attachment system for a public website. For information about signing up for an account, go to Amazon's S3 Page. Because S3 uploading may take a while, the actual upload to S3 is performed in a background queue. As a result, your attachment may be in an "unavailable" state (attachment.available = false) until the upload is complete. The attachment components handle this state with specific views and by disabling attachment links. Note: Deleting an attachment that is in the unavailable state may result in the file not being removed from S3 right now.
RasckSpace's CloudFiles service is an alternative to Amazon's S3. Like the S3 implementation, uploading may take a while, so the actual upload to CloudFiles is performed in a background queue. As a result, your attachment may be in an "unavailable" state (attachment.available = false) until the upload is complete. The attachment components handle this state with specific views and by disabling attachment links. Note: Deleting an attachment that is in the unavailable state may result in the file not being removed from CloudFiles right now.
The ERAttachment framework provides support for tracking the definition of various mime types. You can, however, override and extend the default definitions. The mime type of an attachment determines which viewer will be used to display the attachment.
For each mime type defined in the mimeTypes and additionalMimeTypes properties, there are several additional properties you must specify.
Currently there is no support for automatically thumbnailing of attachments or other attachment variations, however the model was written with the explicit intent of adding this support.
If you are serving S3 or non-proxied file attachments, security is controlled by S3 and your web server respectively. However, if you are serving proxied attachments like database attachments or proxied file attachments, you can provide a delegate to implement security checks on your attachment. Currently the delegate is very simple and does not provide much context for the request, but the delegate features will be extended over time.
To provide a delegate, you must implement the ERAttachmentRequestHandler.Delegate interface, and in your application constructor, you can call:
((ERAttachmentRequestHandler) WOApplication.application().requestHandlerForKey(ERAttachmentRequestHandler.REQUEST_HANDLER_KEY)).setDelegate(yourCustomDelegate);
The delegate interface has one method, which is passed the ERAttachment that the user requested along with the WORequest and WOContext, which you can use to lookup session information as well as authentication and authorization information. You can use the configurationName() property of ERAttachment to determine which security context you are evaluating the attachment in.
To use attachments, you will need to add support for one or more attachment relationships in your EOModel. Let's take the example of a Person with an avatar image. In our Person EO, we would add:
And that's it ... You're done modeling.
Uploading attachments into the system is very easy. You can either use the ERAttachmentUpload component, which provides a very thin layer of features on top of either a WOFileUpload or an AjaxFileUpload component; or you can use your own WOFileUpload and call the attachment import API directly.
Let's look at the case of uploading an avatar attachment for our Person again. We want to upload our avatars to S3 using a non-Ajax file upload in this example.
er.attachment.Person.avatar.storageType=s3
er.attachment.Person.avatar.s3.accessKeyID=xxxxxxxxxx
er.attachment.Person.avatar.s3.secretAccessKey=xxxxxxxxxxxxxxx
er.attachment.Person.avatar.s3.bucket=Avatars
AvatarForm : WOForm {
enctype = "multipart/form-data";
multipleSubmit = true;
}
AvatarUpload : ERAttachmentUpload {
configurationName = "Person.avatar";
editingContext = person.editingContext;
attachment = person.avatar;
}
UploadAction : WOSubmitButton {
action = uploadAvatar;
value = "Upload Avatar!";
}
And that's it. When the file is uploaded, it will be processed and posted to S3. You can override "storageType" on the ERAttachmentUpload, as well as "mimeType" if you are only uploading specific mimeTypes. To better implement security in your request handler delegate, it is also possible to specify an "ownerID" binding on the upload. This ownerID should be a string that corresponds to the ID of the "owner" of this attachment (like "person.primaryKey" in our avatar example). This provides a "loose" foreign key back to the object this attachment is associated with, providing a mechanism to look up the owner during the security checking process to get a better context of the request.
However, you may want to use your own WOFileUpload in a form, or maybe you have an existing upload process. In this case, you will want to call the API directly. Fortunately this is very simple to do:
ERAttachment attachment = ERAttachmentProcessor.processorForType(storageType).process(editingContext, uploadedFile, originalFileName, mimeType, configurationName, ownerID);
In this example, storageType would be "s3", editingContext would be the editing context to create the attachment within, uploadedFile would be a java.io.File reference to the temporary file (which will be deleted after processing), originalFileName is the name the user gave the attachment on upload, mimeType can be null or set explicitly (if null, it will be guessed from the originalFileName extension), configurationName is the name of the configuration properties to use, and ownerID is the optional ID of the object that "owns" this attachment (see the description of the ownerID binding on ERAttachmentUpload for more details).
Note: Once you select the storageType for an avatar, there are currently no tools for changing your mind later. You would need to manually edit the ERAttachment objects if you want to, for instance, move your S3 attachments back onto a local machine and convert them to file attachments. The API provides most of the underlying tools to do the data management, but the grunt work of actually switching everything around is left as an exercise for the reader :)
There are two ways to view an attachment -- linking and embedding. To embed an attachment in the page (assuming it is an embeddable type), you can use the ERAttachmentViewer component. If the attachment type is not one that has a renderer, ERAttachmentViewer will display a default icon instead. If you only want to provide a link, ERAttachmentLink works basically link a WOHyperlink except that you provide a configurationName and attachment reference. Additionally, you can use ERAttachmentIcon to show an icon based on the mime type of the attachment.
Avatar : ERAttachmentViewer {
configurationName = "Person.avatar";
attachment = person.avatar;
width = 50;
}
AvatarLink : ERAttachmentLink {
configurationName = "Person.avatar";
attachment = person.avatar;
}
Viewers for attachments are pluggable. You can create a component that extends AbstractERAttachmentViewer, and set some a configuration property that maps a mime type to your viewer.
As an example, the default Properties for the framework define:
er.attachment.mimeType.image/pdf.viewer=er.attachment.components.viewers.ERAttachmentDefaultViewer
er.attachment.mimeType.image/*.viewer=er.attachment.components.viewers.ERAttachmentImageViewer
er.attachment.mimeType.default.viewer=er.attachment.components.viewers.ERAttachmentDefaultViewer
er.attachment.mimeType.unavailable.viewer=er.attachment.components.viewers.ERAttachmentUnavailableViewer
ERAttachment is written with a modular design. If you want to provide your own storage locations, you can:
Copyright © 2002 – 2024 Project Wonder.