Jetspeed Proposal: Profiler Service LAST MODIFIED: $Date$ AUTHOR: david@bluesunrise.com (David S. Taylor) STATUS: approved, work in progress for 1.3a2 release ============================ Jetspeed Profiler Proposal ============================ The ProfilerService is a service to abstract the management and mapping of profile resources, also known now as Portal Markup Resources (PSML). The Profiler maps a request for a profile to a profile resource. Since each implementation of a profiler will be different, we are providing the interfaces to the service, and we will also provide a default implementation. The default implementation has a profiling policy defined in this proposal based on resource-specific URLs, Mime-Types and language preferences. More complex implementations will need to use other inputs in mapping to resources such as Cookies, IP Address Ranges, Statistical Resource Usage Analysis, Business Rules inside of servlets or EJBs,... Each ProfilerService will store its profiles in whatever datastore best fits the needs of its application, perhaps in LDAP servers, WebDAV, relational or object databases. The default service persists to the file system, using the existing implementation of PSML files with some changes to the directory layout. The default implementation is meant to be useful for many installations. It also provides base functionality which can be reused or extended by other implementations. The ProfilerService is a Turbine service. =================================== The Default Implementation =================================== The default service uses Turbine Security to control access to user, group and role resources. It makes use of the new Turbine security. The Rundata now has an ACL member, which is used to check for users having access to groups and roles. Optionally, this security checking can be configured to be turned off. The basic mapping provided by the default provider: Resource URL Profile Path ---------------> Request Language Preferences -----------> Profiler ----------> Actual Profile Resource Request Mime Types -------------------> A resource is identified in a request by a Resource URL. This URL is made up of the base part of the URL which addresses the server, along with a Profile Path portion. The profiler looks at the path, which provides addressing for the user, group, role, media type, and localization which is used to map to the actual resource. ---------------------- Capability Map ---------------------- The media type is determined by the CapabilityMap service. The CapabilityMap determines the media type from the HTTP headers. This media type is then passed to the Profiler to be used in mapping the request to a particular PSML resource. The CapabilityMap is planned to eventually support W3 CC/PP RDF descriptions - see http://www.w3.org/TR/NOTE-CCPP/. ---------------------- Users, Groups and Roles ---------------------- Groups allow for resources to be shared by users. A typical use-case is a common portal page for Human Resources. Instead of designing a HR markup for every employee in the company, they only design a markup for the organizations (groups) in the company i.e. Finance, Accounting, Marketing. Secure access to groups and roles is controlled by the ProfilerService. Since Jetspeed does not yet have a security service in place, it is up to the ProfilerService to make use of a security service. The Profiler service queries the security service to verify that a user has access to the group and role resources. The default implementation uses Turbine security. Like groups, roles allow for resources to be shared by users. We are providing support for both roles and groups since some implementations may use group-based security, while others may use role-based security. Groups and role resources may be specified in the resource URL. Alternatively, they can be automatically used when the group or role parameter is not specified. The Profiler will make use of the first matching group or role resource that a user has read access to. ----------------- Resource Names ----------------- The Profiler expects to have a default resource for every user. To use the default resource, you don't need to specify a resource name in the resource URL path. The default resource name is set in the Profiler configuration. In order to specify a resource other than the default markup for a user, resource names are specified in the Resource URL Path. To specify a non-default resource, a resource name must be specified in the path. Resource paths are specified using Turbine parameter pairs. Paths specified in this format are not order specific, but they do expect that every parameter is provided in a name/value consecutive pair in the URL path. To tell Jetspeed that the request is for a specific portal markup resource, use the 'profile' keyword as the name of the parameter, and then the actual resource name is specified in the value portion of the pair. This resource name simply identifies the resource that the user is requesting. An example of a 'BenefitsPage' resource would be: http://host/servlet/jetspeed/page/BenefitsPage When no name or path is specified, the default resource is used for the current user. The resource name can also have an extension. If it is omitted, the default is to append a configurable extension (PSML). ----------------- Parameters ----------------- Here are the supported parameters in a resource path url: /page/ - Specifies the name of the profiling resource. /role/ - Specifies that the request can only be satisfied by a common role resource . An example would be /page/FinancePage/role/Accountant/, meaning that we are requesting the FinancePage resource, and it is the Finance resource for the Accountant role. It will be up to the Profiler implementation to determine if the user is authorized to assume this role. The Security service, whether it is Turbine Security or other, should hold the controlling access information. /group/ - Specifies that the request can only be satisfied by a common group resource. An example would be /group/Finance/page/BenefitsPage, meaning that we are requesting the BenefitsPage, and it is the Benefits page for the group 'Finance'. It will be up to the Profiler implementation to determine if the user is a member of this group. The Security service, whether it be Turbine Security or other, should hold this mapping. /media-type/ - To specifically request a mediatype in the request. /user/ - Specifies that the request can only be satisfied by a user resource. An example would be /page/BenefitsPage/user/, meaning that we are requesting the BenefitsPage, but only for the current user. Specifying the name of the user is not allowed, only the authenticated username is applied. This format breaks the rules of Turbine name/value pairs in parameters. Thus, the 'user' keyword must be specified last in the resource path. When no user/group/role parameter is specified, the resource is resolved by first looking for a user resource, then a group resource, and finally a role resource. Only groups and roles that the user has read acess priveleges will be considered. ------------------------------- Localized Resources -------------------------------- Resources can optionally be localized. Resources are localized by placing them in sub-directories based on language and country code abbreviations. The language-code sub-directory contains one or more country-code subdirectories. The language-code directory name is specified with an ISO-639 standard two-character language abbreviation. The country-code subdirectory is optional, and is specified with an IS0-3166 standard two-character country code abbreviation. An example: example: user |-- david |-- html |-- fr // french language language-code |-- FR // France country-code |-- BE // Belgium country-code NOTE: The country codes must be in upper-case For a given locale of fr_FR, the search order for the default accounting resource would be: groups/accounting/html/fr/FR/default.psml groups/accounting/html/fr/default.psml groups/accounting/html/default.psml groups/accounting/fr/FR/default.psml groups/accounting/fr/default.psml groups/accounting/default.psml groups/FR/fr/default.psml groups/fr/default.psml groups/default.psml fr/FR/default.psml fr/default.psml default.psml // finally, look in the psml root The profiler looks at the "Content Language" HTML header for locale specific settings. If there are multiple settings, all settings will be searched until the first resource is found. (This is currently not supported) Specifying a resource in a resource path by language-code or country-code is currently not supported. To turn on/off all localization checking: services.Profiler.language=true For a complete list of ISO-639 standard language abbreviations, see: http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt For a complete list of ISO-3166 standard country code abbreviations, see: http://userpage.chemie.fu-berlin.de/diverse/doc/ISO_3166.html ------------------------------- Device Explicit Resource Names -------------------------------- Optionally, names can include the media type explicitly. http://host/servlet/jetspeed/media-type/html In this case, the 'media-type' parameter must be specified, and then the actual media type as the parameter value. The above example explicitly asks for the html resource for the current user. First the profiler will look for the media type in the resource name. If it is not specified there, it will then query the CapabilityMap. ------------------------------- Default Media-Type and Fallback -------------------------------- If a request is for a given media type, and the resource is not found for the given media type, then the profiler will fallback to a default resource if available. This default resource is assumed to work for all media types without a specific resource. The default media type resource should be placed in the root directory for a given user, role or group. When searching for default resources, the profiler will search back up language trees at each node in the fallback path. ------------------------------- Anonymous Access and Profiles -------------------------------- The resources shown to anonymous logons are stored in the /psml/anon directory tree. The /psml/anon directory can be configured for default resources and specific resouces based on media type and language, just like in any other directory. The actual resource displayed is then dependent on media type, language, and the default resource markup filename. ------------------------------- Creation of New Users -------------------------------- The profiler has a hook into the creation of new users. The profiler will search the fallback path starting at the /psml/user directory. You can set your default resources anywhere in the fallback path, such as: /psml/user/default.psml /psml/user/html/fr/default.psml // careful - a user may also be 'html' /psml/html/fr/default.psml // fallback to root and up to language /psml/html/default.psml /psml/default.psml As the examples show above, when searching for the default resource for a new user, the profiler will search down the fallback path, and at each level, it will attempt to search backup for media type, and then for languages specific resources. ------------------------------- Authorized Profiles -------------------------------- When a user first signs up, they could be given a choice of profiles available authorized users. This services can be configured as: profiler.authorized.profiles=/role/engineer/ profiler.authorized.profiles=/group/accounting/ NOTE: this feature is not implemented. -------------------------------- Cookie-based Anonymous Sessions -------------------------------- Cookies are used to automatically remember an unauthorized user's last profile. The cookie would remember which 'anon' role it used last. This provides access for unauthorized users to other resources besides the default. NOTE: this feature is not implemented. -------------------------------- Cookie-based Authorized Sessions -------------------------------- Cookies are used to automatically logon an authorized user and remember the default profile for the authorized user. NOTE: this feature is not implemented. -------- Profile -------- PSML resource are materialised as a 'Profile' object. It would be interesting if the Profile's contents (Controls, Controllers, Portlets) were also first-class persistence-capable objects, so that Customizers could store changes to one portlet ref instead of re-writing the entire psml tree. At this point in time, the Profile will simply be a wrapper around the existing implementation along with some new accessors. ----------------------------------- Jetspeed Resources Configuration ----------------------------------- # The Profile Service is implemented as a Turbine service. services.Profiler.classname=org.apache.jetspeed.services.profiler.JetspeedProfilerService # The webapp relative path to the root profiling directory services.Profiler.root=/WEB-INF/psml # The default resource filename services.Profiler.resource.default=default # The default resource filename extension services.Profiler.resource.ext=.psml # Use security? # if you aren't securing your psml files, turn this off services.Profiler.security=false # Use language? # turn off language profiling if you have a one-language site services.Profiler.language=true # Use NameCache? # the name cache speeds up profiling, but if you want to # add psml resources without taking the server down, the # cache currently doesn't support it services.Profiler.namecache=false # Enable? # If your psml is from version 1.3a1 or before, set to false services.Profiler.enable=true ---------------- Directory Layout ----------------- All resources are defined as relative to the root (profiler.base.url). Markup files are organized in directories under the root. There are three subpaths under the root: Root | --- / user | --- / role | --- / group | --- / anon Under the /user subtree, are the directories for all users and likewise for roles and groups: user | --- / raul | --- / steve | --- / luis role | --- / engineer | --- / accountant | --- / supervisor group | --- / finance | --- / marketing | --- / accounting anon | --- / html | --- / en | --- / US | --- / wml | --- / fr | --- / FR Under each of these subtrees, there is one directory for each media type. Media types are capable of supporting one or more mime types. For instance, both "text/html" and "text/dhtml" could map to the the same media type. But they also may not. This mapping is controlled in the Portlet Registry (JetspeedConfig.jcfg) with the CapabilityMapRegistry. In the example below, both mime types map to the same media type. text/html text/dhtml Media types are defined in the media type registry. HTML ---------- Examples ---------- Here is an example tree: {root} users {default} default.psml // fallback: can be used as crossdevice media html en US default.psml news.psml UK default.psml news.psml raul html default.psml page1.psml wml default.psml steve default.psml // fallback: can be used as crossdevice media html default.psml groups accounting html en default.psml page1.psml fr default.psml page1.psml wml default.psml marketing html fr default.psml anon default.psml // default anon user: can be used as crossdevice An incoming request from an html browser of: http://server/servlet/jetspeed would map to the resource path: {root}/user/{current-user}/html/default.psml An incoming request from an html browser with a language-code of 'fr' and country code of 'FR': http://server/servlet/jetspeed would map to the resource path: {root}/user/{current-user}/html/fr/Fr/default.psml An incoming request from an html browser of: http://server/servlet/jetspeed/group/sales/page/SalesCenter would map to the resource path: {root}/group/sales/html/SalesCenter.psml An incoming request from a wml browser of: http://server/servlet/jetspeed/page/JavaCenter/role/programmer/ would map to the resource path: {root}/role/programmer/wml/JavaCenter.psml An incoming request from a wml browser of for user 'raul': http://server/servlet/jetspeed/page/MySportsPortal would map to the resource path: {root}/user/raul/wml/MySportsPage.psml and if that failed, it would try again for the user's groups and roles. it may find a page such as: {root}/role/footballfan/wml/MySportsPage.psml ------------------------- Modifications to the Code ------------------------- The JetspeedProfilerService will be implemented in the org.apache.jetspeed.services.profiler package. The ProfilerService interface will be placed in the org.apache.jetspeed.services.profiler package. The Profiler static accessor class will be placed in the org.apache.jetspeed.services package. The JetspeedProfile class will be implemented and placed in the org.apache.jetspeed.om.profiler package The interception of requests will only occur in org.apache.jetspeed.turbine.screens.Home.java Home.java will need to be modified. This is under the assumption that there is only one entry point into Jetspeed markup generation. ------------------------------ Modifications to Configuration ------------------------------ The Profile Registry in the jcfg file would be removed. A new directory structure as described above is added under the 'WEB-INF/psml' directory. The JetspeedResources.properties will be modified to add the configuration settings described above. ==================== Interfaces ==================== All profiler services must implement the ProfilerService interface. The Profile interface defines a profile instance. A Profile is the object representation of a Jetspeed PSML resource. public interface ProfilerService extends Service { /** The name of this service */ public String SERVICE_NAME = "Profiler"; /** * get the Profile object using the Rundata state and capability map * this is the mapping functionality of the profiler * * @param rundata the rundata object for the current request * @return a Profile object if found by the service or null */ public Profile getProfile(RunData rundata, CapabilityMap cm) throws ProfileException; /** * sets a profile as the current profile for this session * * @param rundata the rundata object for the current request * @param profile the profile object to set in the session context */ public void setSessionProfile(RunData rundata, Profile profile); /** * get the Profile object using the Rundata state and capability map * this is the mapping functionality of the profiler * * @param rundata the rundata object for the current request * @return a new Profile object */ public Profile getProfile(RunData rundata) throws ProfileException; /** * get the Profile object using the Rundata state and capability map * this is the mapping functionality of the profiler * * @param rundata the rundata object for the current request * @return a new Profile object */ public Profile getProfile(RunData data, MimeType mt) throws ProfileException; /** * gets the current profile for this session * * @param rundata the rundata object for the current request * @param profile the profile object to set in the session context */ public Profile getSessionProfile(RunData rundata) throws ProfileException; /** * create a user profile and its resources (psml) for a new user * * @param rundata the rundata object for the current request * @param userName the new user to create * @return a new Profile object */ public Profile createUserProfile(RunData rundata, String userName) throws IOException, ProfileException; /** * stores a cookie in response to the current resource * * @param rundata the rundata object for the current request */ public DynamicURI makeDynamicURI( RunData data ) throws ProfileException; /** * stores a cookie in response to the current resource * * @param rundata the rundata object for the current request */ public void storeCookie(RunData rundata); /** * turn on/off security checking when accessing profile resources * * @param flag indicates to turn security on with true, off with false. */ public void setSecurity( boolean flag ); /** * get status of security checking when accessing profile resources * * @return the security-checking status: true in on, false is off */ public boolean getSecurity(); /** * turn on/off NLS (language support) when accessing profile resources * * @param flag indicates to turn NLS on with true, off with false. */ public void setLanguageProfiling( boolean flag ); /** * get status of NLS (language support) when accessing profile resources * * @return the language profiling status: true in on, false is off */ public boolean getLanguageProfiling(); /** * turn on/off caching of resource names * * @param flag indicates to turn caching on with true, off with false. */ public void setResourceNameCaching( boolean flag ); /** * get status of caching of resource names * * @return the resource caching status: true in on, false is off */ public boolean getResourceNameCaching(); } public interface Profile extends Initable { /** Gets the root set of portlets for this profile object. @return The root portlet set for this profile. */ public PortletSet getRootSet(RunData rundata); /** Gets the PortletSetFactory for this profile @return The portlet set factory for this profile. */ public PortletSetFactory getFactory(); /** Gets the URL for this profile, a fully qualified local path and resource name @return The URL of the profile */ public String getURL(); public void setURL(String url); /** stores the resource by merging and rewriting the psml file @throws ProfileException if an error occurs storing the profile */ public void store() throws ProfileException; } ============================== Future Directions ============================== - Mapping IP address ranges to Groups. This would require a table, either in the database or a simple text file. - Dynamic profiling based on usage statistics - The Profile's content generation based on runtime criteria, not static psml files - Search Multiple languages supplied in device header - Request a resource by language or country code - Authorized Profiles - Cookie-based sessions