Documente Academic
Documente Profesional
Documente Cultură
Contents
Introduction................................................................................................................................ 6
Welcome.............................................................................................................................. 7
How this course is delivered....................................................................................... 7
Course Schedule......................................................................................................... 7
How Prepared are You?..............................................................................................8
Topics not Covered..................................................................................................... 8
Introductions.................................................................................................................8
Getting Started...........................................................................................................................9
Alfresco SDK..................................................................................................................... 10
Introduction.................................................................................................................10
Downloading and Unpacking the Alfresco SDK........................................................ 10
Importing the Alfresco SDK projects into Eclipse......................................................11
Associating Source Code and Javadocs with the Alfresco Libraries......................... 14
SDK samples............................................................................................................. 16
SVN Repository......................................................................................................... 19
SDK Reference.......................................................................................................... 20
Best Practices....................................................................................................................22
Coding Standards...................................................................................................... 22
Alfresco Module Packages (AMP).............................................................................22
Alfresco Repository Architecture....................................................................................... 24
Out of the Box........................................................................................................... 24
Service & Component Architecture........................................................................... 25
Repository Foundation Services API.........................................................................27
Repository APIs......................................................................................................... 28
Repository Server Protocols...................................................................................... 29
Terminology................................................................................................................30
Developing against the Alfresco Repository........................................................................33
Spring Framework............................................................................................................. 34
Introduction.................................................................................................................34
Bean Factory..............................................................................................................34
Inversion of Control (IoC).......................................................................................... 35
Application Context.................................................................................................... 39
Foundation Services API................................................................................................... 40
Introduction.................................................................................................................40
Access to Repository Foundation Services...............................................................40
FirstFoundationClient walkthrough............................................................................ 40
Other Foundation Services........................................................................................ 48
JCR API............................................................................................................................. 50
Introduction.................................................................................................................50
JCR Compliance Levels............................................................................................ 50
JCR Repository Model...............................................................................................50
FirstJCRClient Walkthrough.......................................................................................51
JCR Transactions...................................................................................................... 53
Web Services API............................................................................................................. 55
Introduction.................................................................................................................55
Available Web Services.............................................................................................55
Access to Web Services in Java...............................................................................55
Web Services Data Types.........................................................................................56
Content Manipulation Language (CML).....................................................................56
FirstWebServiceClient Walkthrough.......................................................................... 57
Separating Concerns using AOP...................................................................................... 61
Public Services and AOP proxies............................................................................. 61
Security Enforcement.................................................................................................61
Transaction Management.......................................................................................... 64
Extending the Alfresco Repository....................................................................................... 69
Repository Policies............................................................................................................ 70
Introduction.................................................................................................................70
Available Policies....................................................................................................... 70
Policy Types...............................................................................................................71
Custom Aspect with Behaviour Howto...................................................................... 71
Repository Actions.............................................................................................................79
Introduction.................................................................................................................79
Repository Action Howto........................................................................................... 79
Repository Action with Parameters Howto................................................................ 85
Content Transformers........................................................................................................91
Introduction.................................................................................................................91
Content Transformer Howto...................................................................................... 91
ContentTransformerRegistry...................................................................................... 95
Further Reading......................................................................................................... 97
Metadata Extractors...........................................................................................................98
Introduction.................................................................................................................98
MetadataExtracterRegistry.........................................................................................98
Metadata Extractor Howto......................................................................................... 99
Further Reading....................................................................................................... 103
Extending the Alfresco Web Client..................................................................................... 105
JavaServer Faces............................................................................................................106
Introduction...............................................................................................................106
Login Page Walkthrough......................................................................................... 106
Actions Framework.......................................................................................................... 116
Introduction...............................................................................................................116
Update UI Action Walkthrough................................................................................ 116
Actions Framework Reference................................................................................ 120
Dialog Framework............................................................................................................123
Introduction...............................................................................................................123
Introduction
Introduction
Introduction
Welcome
Welcome to Alfresco API development. This course has been designed to provide you with the
knowledge and skills necessary to develop against the Alfresco Repository APIs and to develop
extensions for the Alfresco enterprise content management system.
Course objectives
At the conclusion of this course you should be comfortable with all the concepts and tasks
required to competently:
Set up your own development environment and use the Alfresco SDK
Develop against the Alfresco APIs (Foundation Services, JCR, Web Services)
Develop extensions for the Alfresco Repository
Develop extensions for the Alfresco Web Client
Package and deploy extensions
Course Schedule
Day 1
Getting Started
Alfresco SDK
Best Practices
Alfresco Repository Architecture
Developing against the Alfresco Repository
The Spring Framework
Foundation Services API
Java Content Repository (JCR) API
Web Services API
Day 2
Developing against the Alfresco Repository
Separating Concerns using AOP
Extending the Alfresco Repository
Repository Policies
Repository Actions
Introduction
Content Transformers
Metadata Extractors
Day 3
Extending the Web Client
JavaServer Faces
Actions Framework
Dialog Framework
Wizard Framework
Packaging Extensions
Alfresco Module Packages
Introductions
Name
Company
Title, function, job responsibility
Alfresco experience (API development experience)
Courses already taken
Reasons for taking this course
Expectations
Getting Started
Getting Started
Getting Started
Alfresco SDK
Introduction
The Alfresco SDK provides support for developers who wish to extend or customise the Alfresco
platform. It has been designed for the developer to get developing with minimal fuss for the
following development scenarios:
Developing extensions for the Alfresco Repository and Web Client.
Embedding Alfresco into applications via Alfresco's Java Foundation Services API or
standards-compliant JCR API.
Developing applications against a remote Alfresco Repository via Alfresco's Web Services
API.
Typically, the SDK is used stand-alone, but an Alfresco installation is also required if performing
any of the following:
Customising the Alfresco Web Client
Deploying a custom module to a remote Alfresco repository
Testing a custom application that connects to a remote Alfresco repository
The SDK is not designed for re-building Alfresco since it does not provide full build scripts
and artifacts. If you wish to develop bug fixes or extend the core functionality of the
Alfresco platform, you should use the full Alfresco development environment provided in
the Alfresco SVN Repository. For more information, see SVN Repository on page 19.
Getting Started
For a description of the contents of the Alfresco SDK, see SDK Contents on page 20.
b.
c.
In the JDK Compliance panel, set the Compiler compliance level to 5.0 or above:
Getting Started
d.
Click OK
b.
In the Import dialog, select General # Existing Projects into Workspace import
source and click Next >
c.
d.
Navigate to the file system directory where you unpacked the Alfresco SDK and click
OK. The Alfresco SDK projects are now listed under Projects.
Do not navigate to the samples sub-directory, otherwise you will not see the SDK
AlfrescoEmbedded and SDK AlfrescoRemote projects in the list.
Getting Started
In order to run the samples or to develop your own extension modules, you must
import at least the SDK AlfrescoEmbedded and SDK AlfrescoRemote projects. The
other SDK projects are samples for common development scenarios that you can
study and run to learn more about developing Alfresco extension modules.
For more information about the available projects, see SDK Eclipse Projects on page
20.
e.
Once you have selected the projects you wish to import, click Finish.
Getting Started
b.
c.
d.
Getting Started
4. Associating Javadocs
a.
b.
In the Javadoc Location panel, select Javadoc in archive and click Browse...
c.
d.
e.
Click Validate... to validate the Javadoc location, then click either OK to view the
Javadocs in a web browser or Cancel if not.
Getting Started
alfresco-core.jar
alfresco-remote-api.jar
alfresco-web-client.jar
SDK samples
FirstFoundationClient
The SDK FirstFoundationClient, SDK FirstJCRClient and SDK JCRSamples sample projects
demonstrate how to access an embedded Alfresco repository via the Foundation Services API
and the standards-compliant JCR API. These samples can be tested directly from within Eclipse
and will automatically start an Alfresco repository in embedded mode.
Before starting, the embedded repository needs to be configured. By default, the sample projects
are configured to use a MySQL database named alfresco and a data directory with a relative
path of ./alf_data. These parameters are defined in the custom-repository.properties
file in the source/alfresco/extension directory of each project. It is good practice to define
an absolute path for the data directory (dir.root parameter) and to configure all of the SDK
projects to share the same database and the same dir.root.
Example custom-repository.properties file:
dir.root=C:/alf_data
Getting Started
#db.username=alfresco
#db.password=alfresco
#
# MySQL connection (This is default and requires mysql-connector-java-3.1.12bin.jar, which ships with the Alfresco server)
#
#db.driver=org.gjt.mm.mysql.Driver
#db.url=jdbc:mysql://localhost/alfresco
The alfresco MySQL database also needs to be created using the scripts provided in the
extras/databases/mysql directory of the unpacked Alfresco SDK. To create the database from
the command line:
C:\alfresco-enterprise-sdk\extras\databases\mysql>mysql -u root -p <
db_setup.sql
Enter password: ********
The samples can now be tested by running the main Java classes from within Eclipse. In the
following example, the FirstFoundationClient class is run as a Java Application:
The Alfresco repository is automatically started in embedded mode. Because this is the first time
the repository has been started, the initial bootstrap is executed to create the database tables. As
you can see from the console messages below, the embedded repository uses C:\alf_data as
it's data directory (dir.root).
Getting Started
The other embedded repository samples (SDK FirstJCRClient and SDK JCRSamples) can be
run in the same way.
Once the remote repository has been installed and started, the Web Clients samples can be
tested by running the main Java classes from within Eclipse. In the following example, the
FirstWebServiceClient class is run as a Java Application:
Getting Started
modules can be developed and tested using unit tests and an embedded Alfresco repository. The
SDK CustomAspect project has a sample unit test that does this.
An Ant build.xml file is provided for packaging the repository samples. The package target
packages the compiled classes and extension files into a JAR file. To deploy the samples,
copy the JAR file to the WEB-INF/lib folder of an existing Alfresco installation and restart the
application server.
For deployment in a production environment, a custom repository module should be packaged as
an Alfresco Module Package (AMP).
For more information on creating a custom repository action, see the Repository Action Howto
on page 79. For a detailed presentation of the SDK CustomAspect sample, see the Custom
Aspect with Behaviour Howto on page 71.
SVN Repository
The Alfresco Subversion repository gives you access to all of the Alfresco source code and build
artifacts. It provides the latest work-in-progress developments. It should only be used if you wish
to extend the Alfresco core framework or work on Alfresco bug fixes as it allows you to perform
full re-builds of Alfresco itself. For most requirements, it is best to use the Alfresco SDK.
Public read-only access to the Alfresco Subversion repository is available from the Alfresco web
site. To checkout the source code, use the following procedure:
1. Install Subversion and ensure that svn is on the path.
2. Checkout the HEAD of the code stream:
svn co svn://svn.alfresco.com/alfresco/HEAD
or
svn co http://svn.alfresco.com/repos/alfresco-open-mirror/alfresco/HEAD
Getting Started
SDK Reference
SDK Contents
bin
Alfresco libraries required for access to a remote Alfresco repository via web services.
lib/server
License files.
samples
Sample Eclipse projects for common development scenarios (see SDK Eclipse Projects on
page 20).
src
Notices
readme.txt
Project containing all of the Alfresco libraries needed to build a custom module that will be
embedded into the Alfresco repository or Web Client.
SDK AlfrescoRemote
Project containing all of the Alfresco libraries needed to build a custom Web Services client.
SDK Basic AMP
Sample project demonstrating how to build an AMP (Alfresco Module Package) file.
SDK CustomAction
Sample project demonstrating how to develop a custom Action that may be deployed to an
Alfresco repository.
SDK CustomAspect
Sample project demonstrating how to develop a custom Aspect with behaviour that may be
deployed to an Alfresco repository.
Getting Started
SDK CustomDialog
Sample project demonstrating how to develop and configure a custom Dialog for the Alfresco
Web Client.
SDK CustomJSP
Sample project demonstrating how to develop and configure a custom JSP for the Alfresco
Web Client.
SDK CustomLogin
Sample project demonstrating how to override the Login page of the Alfresco Web Client.
SDK CustomWizard
Sample project demonstrating how to develop and configure a custom Wizard for the Alfresco
Web Client.
SDK FirstFoundationClient
Sample project demonstrating how to access an Alfresco (embedded) repository via the
Foundation Services API.
SDK FirstJCRClient
Sample project demonstrating how to access an Alfresco (embedded) repository via the
standards-compliant JCR API.
SDK FirstWebServiceClient
Sample project demonstrating how to access a remote Alfresco repository via the Web
Services API.
SDK JCRSamples
More sample projects demonstrating how to access an Alfresco (embedded) repository via the
standards-compliant JCR API.
SDK TaggingSample
Advanced sample project demonstrating how to develop a custom Action that takes
parameters.
SDK WebServiceSamples
More sample projects demonstrating how to access a remote Alfresco repository via the Web
Services API.
Getting Started
Best Practices
Coding Standards
Coding Standards - Formatting
The core coding standards are the standard Java Code Conventions.
Braces are on new lines.
4 space for tabbing, except for Web Client project that uses 3 spaces.
120 characters on a line is fine.
Import declarations are managed by Eclipse's standard ordering rules (CTRL-SHIFT-O).
This helps prevent code merge conflicts.
XML documents use 3 space tabbing. The Eclipse plug-in, XMLBuddy, is generally used.
Getting Started
Once the contents of the AMP file has been mapped into an Alfresco WAR using the Module
Management Tool, the WAR can be deployed to the application server. When the repository
is next started, the installed module configuration will be detected, and the repository will be
bootstrapped to include the new module functionality and data.
Getting Started
This is typical of a web architecture, where an application server houses the logic for both the
user interface and domain. Storage of data and content is provided by persistent back-ends such
as a database or file system. Any number of web browsers can connect to the application without
prior client installation costs.
In this particular case, the application server houses both the Alfresco Application and the
Alfresco Repository. An Alfresco Application provides a complete solution tailored for a specific
area of Content Management such as Document Management (DM), Web Content Management
(WCM) and Records Management (RM). The Alfresco Repository provides a set of reusable
cross-cutting Content Management services such as content storage, query, versioning and
transformation which may be utilised by one or more applications.
Although this is the default installed deployment, it is only one of many ways of utilising the
capabilities and components of Alfresco. When we first set out to design Alfresco, we wanted to
break away from the mould of typical Content Management architectures which are monolithic
and closed. The result is that Alfresco can neatly fit into existing environments and each of its
Getting Started
components may be used in isolation or together to form the basis of many differing Content
Management solutions.
The remainder of this module explores the anatomy of the Alfresco Repository which will give a
good understanding of the concepts and capabilities and how it achieves openness, scalability
and flexibility.
Getting Started
The public interface point is the Alfresco Repository Foundation Services. Each service is
exposed as a Java Interface to which a Repository client can bind and invoke without knowledge
of its underlying implementation. A Service Registry lists the available services. Behind services
are the implementation black boxes i.e. components. Each service and component is configured
via the Spring framework in XML context files.
The Spring context file public-service-context.xml provides the configuration and
binding of the Alfresco Repository Foundation Services.
The Repository Foundation Services are the lowest level of public interface providing access
to all Repository capabilities. Binding to this interface is possible via the Repository Service
Registry, or via Spring dependency injection if the client is also Spring aware. Access to
Foundation Services is limited to Repository clients who reside in the same process as the
Repository. That is, the Foundation Services are an excellent API for clients who wish to embed
the Repository.
An important point to note is that the Foundation Services are where transaction and security
policies are enforced. The policies themselves are declaratively specified and enforced via the
injection of a transaction and security implementation into each service. Every service of Alfresco
is transactional and secure.
Other forms of API are provided too, however, all public entry points eventually go through this
layer.
Alfresco supports a common scheme for making extensions to the Repository i.e. configuring
a component, adding a new component or service, or removing capabilities. Extensions are
Getting Started
encapsulated outside of the core Repository and plugged-in automatically. This means the core
Repository can be upgraded to a newer version and extensions remain intact.
Getting Started
Apart from the strong Object/Relational mapping that Hibernate provides, it also brings pluggable
caching support and SQL dialects. The first allows for tuning of the Alfresco meta-data store to
provide optimum read and write performance in both single and clustered environments. The
second allows for nearly any SQL database back-end by configuring just two properties; the
Alfresco community has already confirmed working support for MySQL, Oracle, DB2, Sybase,
SQL Server.
By externalising the indexing of meta-data and content and using the Lucene engine as a basis,
it is possible to perform complex queries which combine property, location, classification and
full-text predicates in a single query against any content type. Multiple query languages are
supported including Lucene's native language as well as XPath and a SQL-like language in the
future. To ensure reliable operation, transactional support has been added to both Lucene and
the content file store providing ACID operations across the complete store. Security is woven into
each of the service layer ensuring illegal modifications are not permissible and hidden meta-data
and content are not returned.
Nearly all other Foundation services and clients rely upon these three core building blocks.
Repository APIs
The Alfresco Repository actually provides three APIs. We've already seen one - the Repository
Foundation Services - a set of local Java Interfaces covering all capabilities which are ideal for
clients who wish to embed the Repository.
The two other APIs are:
JCR
Web Services
JCR (Content Repository API for Java Technologies) is a standard Java API (as defined by
JSR-170) for accessing Content Repositories. Alfresco provides support for level 1 and level 2
giving standardised read and write access. Supporting this API provides the following benefits:
No risk: The Alfresco Repository can be trialled and developed against, but swapped out
with another JCR Repository if it does not fit requirements.
Familiarity: Developers who know JCR, know Alfresco.
Tools: Tools, Clients and other 3rd Party JCR solutions are immediately available to the
Alfresco community.
Alfresco JCR is implemented as a light facade on top of the Repository Foundation Services.
So, although a familiar API is provided, it sits upon a fully transactional, secure and scalable
Repository which supports many deployment options. Alfresco will continue investment in JCR by
both broadening the compliance of the full specification as well driving forward JSR-283, the next
version of the JCR.
Web Services is the final API provided by the Alfresco Repository. This API supports remote
access and bindings to any client environment, not just Java. For example, the Alfresco
community is already using PHP, Ruby and Microsoft .NET. Numerous standards and integration
efforts are focused around Web Services - SOA is now recognised as a way forward for
integrating disparate systems including Content Management and building new enterprise-wide
solutions. BPEL plays an important role in orchestrating all of these services. Alfresco fits neatly
into this way of thinking.
Getting Started
Once again, the Repository Foundation Services serve as the base. Both the JCR and Web
Services API eventually go through this layer meaning that all encapsulated content model logic
and rules are honoured.
Getting Started
CIFS protocol having brought on board the engineers who spent 7 years developing such a
capability.
Like every other feature of the Repository, the protocol components are Spring configured and
as with all other components may or may not be included in a deployment. Typically, they are
enabled when the Repository is deployed as a server to provide access points for remote clients.
The various Repository deployment options are explored later in this document.
Protocol components are implemented against the Repository Foundation Services. This is
important to note, as each protocol will honour the behaviour and content logic encapsulated
behind the Foundation Services. In fact, it cannot be bypassed.
Terminology
Store Reference (StoreRef)
A StoreRef is made up of a store protocol and a store id.
Getting Started
The standard store used by the Web Client has the following StoreRef:
workspace://SpacesStore
Getting Started
or
cm:auditable
Node Browser
The Node Browser is your friend!
Spring Framework
Introduction
The Spring Framework is a full-stack Java/JEE application framework. Spring's main aim is to
make J2EE easier to use and promote good programming practise. It does this by enabling a
POJO-based programming model remaining faithful to the fundamental ideas of Expert One-onOne J2EE Design and Development. Spring is portable between application servers.
Bean Factory
A Spring BeanFactory is a generic factory that enables objects to be retrieved by name, and
which can manage relationships between objects. Bean factories support two modes of object:
Singleton: in this case, there's one shared instance of the object with a particular name.
Prototype or non-singleton: in this case, each retrieval will result in the creation of an
independent object.
Beans are defined in an XML bean definition file that is loaded when a new Bean Factory is
created.
b.
We retrieve bean1 from the Bean Factory using the getBean() method:
Bean1 bean1a = (Bean1) factory.getBean("bean1");
System.out.println("Retrieved Bean1: " + bean1a.toString());
Bean1 bean1b = (Bean1) factory.getBean("bean1");
System.out.println("Retrieved Bean1: " + bean1b.toString());
Each call retrieves the same bean (there is only one instance):
Retrieved Bean1: ex01_simplebean.Bean1@471e30
Retrieved Bean1: ex01_simplebean.Bean1@471e30
c.
Dependency Injection
Dependency Injection is a form of IoC that removes explicit dependencies on container APIs.
Ordinary Java methods are used to inject dependencies such as collaborating objects or
configuration values into application object instances. The two major flavors of Dependency
Injection are:
Setter Injection (injection via JavaBean setters)
Constructor Injection (injection via constructor arguments).
Spring provides sophisticated support for both, and even allows you to mix the two when
configuring the one object.
b.
c.
respectively:
<beans>
<bean id="bean1" class="ex04_dependency.Bean1">
<constructor-arg index="0">
<ref bean="bean2"/>
</constructor-arg>
<property name="bean3">
<ref bean="bean3"/>
</property>
</bean>
<bean id="bean2" class="ex04_dependency.Bean2"/>
<bean id="bean3" class="ex04_dependency.Bean3"/>
</beans>
Application Context
To start using Spring you either instantiate a Spring BeanFactory or an ApplicationContext.
ApplicationContext is derived from BeanFactory and provides all of the BeanFactory
funtionality and more including:
MessageSource, providing access to messages in, i18n-style
Access to resources, such as URLs and files
Event propagation to beans implementing the ApplicationListener interface
Loading of multiple (hierarchical) contexts, allowing each to be focused on one particular
layer, for example the web layer of an application
Spring contextConfigLocation
Use the contextConfigLocation <context-param> to set which context files to load:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:alfresco/web-client-application-context.xml
classpath:web-services-application-context.xml
classpath:alfresco/application-context.xml
</param-value>
<description>Spring config file locations</description>
</context-param>
walkthrough
Before getting started, you should be familiar with the Introduction on page 34.
The sample uses several of the key foundation services, including the ServiceRegistry,
TransactionService, AuthenticationService, SearchService, NodeService and
ContentService. After initialising the Spring Application Context and starting the repository in
embedded mode, we will use the Spring getBean() method to access the ServiceRegistry.
We will then use the ServiceRegistry to access the other foundation services.
After authenticating to the repository using the AuthenticationService, we will search for
the Company Home node using the SearchService. We will then create a new node with
properties and add an aspect using the NodeService. Finally, we will write some content to the
new node using the ContentService. The sample will be wrapped in a single user transaction
with the help of the TransactionService.
1. Getting the ServiceRegistry
The Service Registry maintains a list of available foundation services and some meta-data
about each. In particular, the Service Registry provides access to each service interface.
The registry is a service itself and is therefore accessed using either Spring IoC or the
Spring getBean() method. The static variable SERVICE_REGISTRY found on the interface
org.alfresco.service.ServiceRegistry provides the Spring Bean name to lookup by.
The FirstFoundationClient sample uses the getBean() method on the Spring
Application Context to retrieve the ServiceRegistry:
ApplicationContext ctx =
ApplicationContextHelper.getApplicationContext();
final ServiceRegistry serviceRegistry = (ServiceRegistry)
ctx.getBean(ServiceRegistry.SERVICE_REGISTRY);
For clarity, not all of the available methods are shown. For a complete description,
please consult the Javadocs: Interface TransactionService
The TransactionService and RetryingTransactionHelper are presented in more
detail in the section on Transactions.
The following example runs the example work in a user transaction via the
RetryingTransactionHelper:
TransactionService transactionService =
serviceRegistry.getTransactionService();
RetryingTransactionCallback<Object> exampleWork =
new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
doExample(serviceRegistry);
return null;
}
};
transactionService.
getRetryingTransactionHelper().doInTransaction(exampleWork);
Before making any call to the repository through the public Foundation Services API,
a user must first be authenticated. This is done via the AuthenticationService by
using the authenticate() method and providing a username and password. Once
authenticated, a ticket can be requested using either the getNewTicket() or the
getCurrentTicket() method. The ticket can then be used to re-validate the user using
the validate() method.
The AuthenticationService defines the API for managing authentication information
against a username.
For clarity, not all of the available methods are shown. For a complete description,
please consult the Javadocs: Interface AuthenticationService
In the example, the authenticate() method is used to authenticate as the admin user.
The password must be passed as a character array.
AuthenticationService authenticationService =
serviceRegistry.getAuthenticationService();
authenticationService.authenticate("admin", "admin".toCharArray());
For clarity, not all of the available methods are shown. For a complete description,
please consult the Javadocs: Interface SearchService
The following example runs a Lucene query using the PATH syntax to locate the
Company Home by it's absolute path. The getNodeRef(0) call is used to retrieve the first
NodeRef from the ResultSet. In theory, the query should only return one result.
SearchService searchService = serviceRegistry.getSearchService();
StoreRef storeRef = new StoreRef(StoreRef.PROTOCOL_WORKSPACE,
"SpacesStore");
ResultSet resultSet = searchService.query(
storeRef,
SearchService.LANGUAGE_LUCENE,
"PATH:\"/app:company_home\"");
NodeRef companyHome = resultSet.getNodeRef(0);
For more information on using the SearchService and on query string syntax, see the
Search page on the Alfresco Wiki.
5. Using the NodeService to create a node
The NodeService provides methods for operations on nodes and stores.
Stores are created with createStore().
Nodes are created with createNode() and deleted with deleteNode(). Properties
are set with either setProperty() or setProperties(). Properties are removed
with removeProperty(). Aspects are applied with addAspect() and removed with
removeAspect().
Associations are created and removed with createAssociation() and
removeAssociation(). Associations can be navigated with getSourceAssocs() or
getTargetAssocs(). Child associations can be navigated with getChildAssocs() and
getParentAssoc().
Almost all NodeService methods take a NodeRef as an argument. A NodeRef is obtained
either by navigation or from the results of a search. Otherwise a new NodeRef object can
be created from the node's unique UUID.
For clarity, not all of the available methods are shown. For a complete description,
please consult the Javadocs: Interface NodeService
a.
Setting properties
Properties are set on nodes using either the setProperty() or setProperties()
methods. setProperty() allows a single property to be set, whilst setProperties()
takes a Map of properties and sets all of the node's properties at once. Each property
is identified by it's QName and it's value must be Serializable.
public void setProperty(
NodeRef nodeRef,
QName qname,
Serializable value)
public void setProperties(
NodeRef nodeRef,
Map<QName, Serializable> properties)
nodeRef
NodeRef of the node to set the property on.
qname
QName of the property to set.
value
The property QNames are usually defined as a static constants on the dictionary
model interfaces. For example, the cm:name property is defined by the static constant
ContentModel.PROP_NAME.
The following example creates a Map containing the cm:name property that will be
used in the next step:
String name = "Foundation API sample (" + System.currentTimeMillis() +
")";
Map<QName, Serializable> contentProps = new HashMap<QName,
Serializable>();
contentProps.put(ContentModel.PROP_NAME, name);
b.
Creating a node
Nodes are created using the createNode() method. A node is created as a child
of a parent node. The child association name and child association type have to be
supplied as QName objects, as well as the QName of the type of node to create and
optionally a Map of properties to set on the newly created node.
public ChildAssociationRef createNode(
NodeRef parentRef,
QName assocTypeQName,
QName assocQName,
QName nodeTypeQName,
Map<QName, Serializable> properties)
parentRef
NodeRef of the parent node. The created node will be one of it's children.
assocTypeQName
QName of the type of association to create. This is used for verification against the
data dictionary.
assocQName
QName of the association.
nodeTypeQName
QName of the node type.
properties
The following example creates a new node of type cm:content, using the standard
cm:contains child association. The Map created in the previous step sets the
cm:name property on the newly created node.
NodeService nodeService = serviceRegistry.getNodeService();
ChildAssociationRef association = nodeService.createNode(companyHome,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_PREFIX,
name),
ContentModel.TYPE_CONTENT,
contentProps);
NodeRef content = association.getChildRef();
c.
Adding an aspect
Aspects are applied to nodes using the addAspect() method. The node is identified
by it's NodeRef and the aspect by it's QName. The property values are provided as a
Map.
public void addAspect(
NodeRef nodeRef,
QName aspectTypeQName,
Map<QName, Serializable> aspectProperties)
nodeRef
NodeRef of the node to apply the aspect to.
aspectTypeQName
The aspect QNames are usually defined as a static constants on the dictionary model
interfaces. For example, the cm:titled aspect is defined by the static constant
ContentModel.ASPECT_TITLED.
The following example applies the cm:titled aspect and sets the cm:title and
cm:description properties:
Map<QName, Serializable> titledProps = new HashMap<QName,
Serializable>();
titledProps.put(ContentModel.PROP_TITLE, name);
titledProps.put(ContentModel.PROP_DESCRIPTION, name);
nodeService.addAspect(content, ContentModel.ASPECT_TITLED,
titledProps);
For clarity, not all of the available methods are shown. For a complete description,
please consult the Javadocs:
Interface ContentService
Interface ContentReader
Interface ContentWriter
Interface ContentAccessor
The following example gets a ContentWriter to the newly created node. The property
to be updated is defined by the QName ContentModel.PROP_CONTENT. The boolean true
value is to request that the content is updated atomically when the content write stream
is closed. The content mime type and encoding are set before writing the content with the
putContent() method.
ContentService contentService = serviceRegistry.getContentService();
ContentWriter writer = contentService.getWriter(content,
ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
writer.setEncoding("UTF-8");
String text = "The quick brown fox jumps over the lazy dog";
writer.putContent(text);
Once completed, the newly created node may be viewed via the Web client.
The web client will need to be re-started after executing the sample to see the changes in
effect.
For clarity, not all of the available methods are shown. For a complete description, please
consult the Javadocs:
Interface FileFolderService
Interface FileInfo
JCR API
Introduction
The JCR API (Java Content Repository) specifies a standard, implementation independent API to
access content repositories in Java. It is defined by the Java Specification Request (JSR) 170 as
part of the Java Community Process (JCP). The official JSR-170 Specification can be found on
the JCP web site at the following address: http://jcp.org/en/jsr/detail?id=170
Alfresco implements the JCR API against its own scalable repository and is actively contributing
to the next version of JCR defined by the Java Specification Request (JSR) 283.
Any item in the tree hierarchy can be identified by either an absolute or a relative path using
a Unix style path syntax. The path / refers to the root node of a workspace and the path /
app:company_home refers to the Company Home space in the standard Alfresco SpacesStore
workspace. The special paths . and .. (meaning respectively, this and parent) are also
supported.
FirstJCRClient
Walkthrough
Before getting started, you should be familiar with the Introduction on page 34.
The sample uses several of the basic JCR APIs, including Repository, Session and Node. After
initialising the Spring Application Context and starting the repository in embedded mode, we will
use the Spring getBean() method to access the JCR.Repository.
After logging into the JCR repository, we will navigate to the Company Home node. We will then
create a new node with properties, add an aspect and write some content. Finally, we will mix the
JCR API calls with the Alfresco Foundation Services API calls to set the content mime type. The
sample is wrapped by an implicit JCR transaction.
1. Getting the JCR.Repository
The JCR repository as a whole is represented by a Repository object. JSR-170 does not
dictate how to obtain the Repository object. In Alfresco, the JSR repository is a Spring
bean called JCR.Repository.
The JCR.Repository bean is defined in jcr-api-context.xml. It has one configuration
parameter; the default workspace name. This is used when a JCR client performs a login
without specifying the workspace. The default workspace is SpacesStore, the same one
used by the Alfresco Web Client.
<bean id="JCR.Repository"
class="org.alfresco.jcr.repository.RepositoryImpl"
init-method="init">
<property name="serviceRegistry">
<ref bean="ServiceRegistry"/>
</property>
<property name="importerComponent">
<ref bean="importerComponent"/>
</property>
<property name="defaultWorkspace">
<value>SpacesStore</value>
</property>
</bean>
The following example uses the Spring getBean() method to access the JCR Repository:
ApplicationContext context = new
ClassPathXmlApplicationContext("classpath:alfresco/applicationcontext.xml");
Repository repository = (Repository)context.getBean("JCR.Repository");
within the session will not be committed until Session.save() is called. For more
information, see JCR Transactions on page 53.
Session session = repository.login(new SimpleCredentials("admin",
"admin".toCharArray()));
5. Adding an aspect
Aspects are added to nodes using the Node.addMixin() method. The aspect properties
are set using the Node.setProperty() as above.
The following example adds the cm:titled aspect:
content.addMixin("cm:titled");
content.setProperty("cm:title", name);
content.setProperty("cm:description", name);
possible to mix JCR API calls with Alfresco Foundation Services API calls and have them
controlled in the same transaction.
In the FirstJCRClient sample, the mime type is set using the Alfresco NodeService:
ServiceRegistry serviceRegistry = (ServiceRegistry)
context.getBean(ServiceRegistry.SERVICE_REGISTRY);
NodeService nodeService = serviceRegistry.getNodeService();
NodeRef nodeRef = JCRNodeRef.getNodeRef(node);
ContentData content = (ContentData)nodeService.getProperty(nodeRef,
ContentModel.PROP_CONTENT);
content = ContentData.setMimetype(content, mimeType);
nodeService.setProperty(nodeRef, ContentModel.PROP_CONTENT, content);
JCR Transactions
Alfresco provides Transaction capabilities across all of its persistence based services. The JCR
API is built upon these services and as such Alfresco provides a fully transactional JCR interface.
Implicit Transactions
By default, an Alfresco JCR Session is backed by a transaction. Work performed within the
session will not be committed until Session.save() is called.
A typical interaction sequence is as follows...
Login and establish a session (this starts a transaction):
Session session = repository.login(credentials);
Logout from the session (this rolls back the transaction - unsaved items are lost):
session.logout();
It is not just the JCR calls that are bound to the transaction. Calls to the native Alfresco
Foundation Services API are also included. So, if you happen to mix JCR and Alfresco
calls between Session.login() and Session.logout(), they are all bound to the same
transaction and controlled by the JCR session.
Explicit Transactions
It is also possible to explicitly control transaction boundaries using Alfresco's
TransactionService. Using this technique, you can explicitly control when JCR updates are
committed.
An example of explicit transaction management follows...
Get a user transaction from the Alfresco TransactionService and start the transaction:
UserTransaction trx =
serviceRegistry.getTransactionService().getUserTransaction;
trx.begin();
Login and establish a session (the explicit transaction started above is inherited):
Session session = repository.login(credentials);
Perform work on the session and save the session (do not commit just yet):
session.save();
Perform more work on the session and save again (still no commit):
session.save();
For more information on Alfresco user transactions, see Alfresco User Transactions on page
65.
Static attributes on the WebServiceFactory define the default endpoint address, the location
of the Web Services configuration property file and the name of the property that defines the
repository location:
private static final String DEFAULT_ENDPOINT_ADDRESS = "http://localhost:8080/
alfresco/api";
private static final String PROPERTY_FILE_NAME = "alfresco/
webserviceclient.properties";
private static final String REPO_LOCATION = "repository.location";
The AuthenticationUtils and Utils classes provide common utility methods useful when
using the Web Services API.
FirstWebServiceClient
Walkthrough
try
{
...
}
catch(Throwable e)
{
System.out.println(e.toString());
}
finally
{
// End the session
AuthenticationUtils.endSession();
System.exit(0);
}
}
Assign "1" as a local id, so we can refer to it in subsequent CML statements within
the same CML block.
4. Adding an aspect
The addAspect CML statement is used to add an aspect.
Security Enforcement
When any call is made to the repository through the public foundation services API, the caller
must first be authenticated. This can be done by logging in using a username and password
or using a ticket. A ticket can be requested after logging in and can be used, under certain
conditions, to re-validate a user.
Once authenticated, access control allows or denies a user from calling public service
methods on a particular object by checking if the authenticated user, or any of the authorities
granted to that user, has a particular permission or permission group. For example, to call the
readProperties() method on the NodeService, the authenticated user must have read access
to the properties of the node. On the SearchService, the results from queries are restricted to
return only the nodes for which a user has read permission. The public services are the only
services to have access restrictions.
Security is enforced around every public service method call and can be based on:
the method called,
the objects provided as arguments to the call,
the objects returned by the call.
Since the Web Client, JCR API, Web services API, CIFS, WebDav, FTP etc. all use public
services behind the scenes, the same security enforcement always applies.
ACL_NODE.#.*
Access control is restricted to users who have the the specified permission for the node on the
identified argument. If the argument is a NodeRef it will be used; if it is a StoreRef then the
root node for the store will be used; if it is a ChildAssociationRef then the child node will be
used.
ACL_PARENT.#.*
Access control is restricted to users who have the the specified permission for the parent of
the node on the identified argument. If the argument is a NodeRef the parent of the node will
be used; if it is a ChildAssociationRef then the parent node will be used.
ROLE_...
Check for an Acegi authority starting with ROLE_
GROUP_...
Check for an Acegi authority starting with GROUP_
If more than one ACL_NODE.#.* or ACL_PARENT.#.* entry is present then all the permissions
must be available for the method to execute. ACL_ALLOW can be used to give access to all.
ROLE_ADMINISTRATOR can be used to grant access to administrator related methods.
Post-conditions take the forms:
AFTER_ACL_NODE.*
Similar to ACL_NODE.#.* but the restriction applies to the return argument.
AFTER_ACL_PARENT.*
Similar to ACL_PARENT.#.* but the restriction applies to the return argument. The supported
return types are ChildAssociationRef, FileInfo, NodeRef, StoreRef, ResultSet;
Collections and arrays of StoreRef, NodeRef, ChildAssociationRef, and FileInfo.
The post-conditions will create access denied exceptions for return types like NodeRef,
StoreRef, ChildAssociationRef. For collections and arrays, the members will be filtered based
on the access conditions.
NodeService
Security Examples
The following examples are taken from the NodeService_security method security interceptor
bean.
Only administrators can create stores:
NodeService.createStore=ACL_METHOD.ROLE_ADMINISTRATOR
Read permission is required on the node specified as the first argument to the exist() method
call:
NodeService.exists=ACL_NODE.0.sys:base.Read
Creating a new node requires the create children permission on the parent node:
NodeService.createNode=ACL_NODE.0.sys:base.CreateChildren
Reading child associations requires the read children permission on the parent node and the read
permission on the children nodes:
NodeService.getChildAssocs=ACL_NODE.0.sys:base.ReadChildren,
AFTER_ACL_NODE.sys:base.Read
Transaction Management
All repository foundation services are transactional. Transactions are controlled either:
implicitly via Spring declarative transaction demarcation or
explicitly via Alfresco user transactions.
Transaction Attributes
The transaction attributes are defined on the TransactionInterceptor as Properties. The
transaction attributes are defined on a per method basis. For each property, the method name is
the key and the transaction attribute descriptor is the value.
For the NodeService, the transaction attributes are as follows:
<property name="transactionAttributes">
<props>
<prop key="exist*">${server.transaction.mode.readOnly}</prop>
<prop key="get*">${server.transaction.mode.readOnly}</prop>
<prop key="has*">${server.transaction.mode.readOnly}</prop>
<prop key="*">${server.transaction.mode.default}</prop>
</props>
</property>
In this example, the method names contain the * wildcard character, to match all methods starting
with a specific keyword.
The tokens can be in any order. Only the propagation code is required. The propagation and
isolation codes must use the names of the constants defined in the TransactionDefinition class
(see related link below). Timeout values are in seconds. If no timeout is specified, the transaction
manager will apply a default timeout specific to the particular transaction manager. A + before
an exception name substring indicates that transactions should commit even if this exception is
thrown; a - that they should roll back.
Propagation and isolation codes used in Alfresco:
PROPAGATION_REQUIRED
Support a current transaction; create a new one if none exists.
PROPAGATION_REQUIRES_NEW
Create a new transaction, suspending the current transaction if one exists.
PROPAGATION_NOT_SUPPORTED
Do not support a current transaction; rather always execute non-transactionally.
ISOLATION_DEFAULT
Use the default isolation level of the underlying datastore.
TIMEOUT_DEFAULT
Use the default timeout of the underlying transaction system, or none if timeouts are not
supported.
Transaction Manager
The TransactionInterceptor delegates the actual transaction handling to a
PlatformTransactionManager instance, defined by the transactionManager property. In the
case of Alfresco, the transaction manager is a HibernateTransactionManager.
The transactionManager bean is defined in the hibernate-context.xml file:
<!-- create a transaction manager -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="transactionSynchronizationName">
<value>SYNCHRONIZATION_ALWAYS</value>
</property>
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
Spring configuration files and not in your Java code. In most cases, declarative transactions are
preferred to user transactions since they are less invasive. There are situations, however, when
user transactions do need to be used explicitly in your code.
An Alfresco user transaction is accessed via the TransactionService. An example using the
ServiceRegistry is as follows:
UserTransaction trx =
serviceRegistry.getTransactionService().getUserTransaction();
With a UserTransaction in hand, it is possible to mark the beginning and end of a transaction.
Any service calls within the begin and end are thus forced to be included in the same transaction.
For example, the following two NodeService calls are wrapped in the same transaction. Without
the user transaction, the default behaviour would be for each NodeService call to be in its own
transaction.
NodeService nodeService = serviceRegistry.getNodeService();
try
{
trx.begin()
nodeService.createNode(...);
nodeService.createNode(...);
trx.commit();
}
catch(Throwable e)
{
if (trx.getStatus() == Status.ACTIVE)
{
try
{
trx.rollback();
}
catch(Throwable ee)
{
e.printStackTrace();
}
}
}
Although the example shows the usage of one service, any mixture of Alfresco's public services
can be pulled into the same transaction. It is important to note that a UserTransaction cannot be
re-used. That is, once a commit or rollback has been issued, a new UserTransaction has to be
retrieved (via getUserTransaction()) to begin another.
TransactionService
As we have already seen, an Alfresco user transaction is accessed via the
TransactionService.
For clarity, not all of the available methods are shown. For a complete description, please
consult the Javadocs: Interface TransactionService
The interface defines four methods for accessing a user transaction:
getUserTransaction();
This method retrieves a UserTransaction with the PROPAGATION_REQUIRED attribute that
supports transaction propagation.
getUserTransaction(boolean readOnly);
Used to request a read only transaction that supports transaction propagation.
getNonPropagatingUserTransaction();
This method retrieves a UserTransaction with the PROPAGATION_REQUIRES_NEW attribute that
ensures a new transaction is created. Any enclosing transaction is not propagated. When the
transaction is started, the current transaction will be suspended and a new one started.
getNonPropagatingUserTransaction(boolean readOnly);
Used to request a read only transaction, also ensuring that a new transaction is created.
RetryingTransactionHelper
The RetryingTransactionHelper is a helper that runs a unit of work inside a
UserTransaction. If the unit of work fails due to an optimistic locking failure, or a deadlock loser
failure, it will transparently retry the unit of work until it succeeds, or until a maximum number of
retries have been attempted.
The exceptions that trigger retries are:
ConcurrencyFailureException
DeadlockLoserDataAccessException
StaleObjectStateException
LockAcquisitionException
BatchUpdateException
The RetryingTransactionHelper is retrieved from the TransactionService via the
getRetryingTransactionHelper() method.
Repository Policies
Introduction
Repository policies are similar to events. Each service defines its own set of policies. For
example the Content Service defines two policies:
OnContentUpdatePolicy is fired when the content is updated on a node,
OnContentReadPolicy is fired when the content is read on a node.
A custom method or behaviour can be registered against a policy and will be called
automatically when the policy is fired. This enables tasks such as maintaining the last modified
date on a node or creating a new version and incrementing the version number on a node to be
automated.
The event-driven processing paradigm is similar to that used in traditional user interfaces.
Available Policies
The available policies are defined as interfaces for each service. The Node Service defines more
than twenty policies, including BeforeCreateNodePolicy, OnUpdatePropertiesPolicy and
OnAddAspectPolicy, the other services each define their own specific policies. The policies are
usually defined on an interface named after the service. For example, the Node Service policies
are defined on the NodeServicePolicies interface and the Content Service policies are defined
on the ContentServicePolicies interface:
Classes interested in certain policies must implement the corresponding interfaces and methods.
Each policy method (or behaviour) defines its own specific list of arguments. For example:
A behaviour registered against the BeforeCreateNodePolicy is called before a node
is created. The beforeCreateNode() method receives the NodeRef of the parent of the
node being created, the QName of the association type (usually cm:contains), the QName of
the new association and the node type of the node being created.
A behaviour registered against the OnCreateNodePolicy is called when a node is created.
The onCreateNode() method receives the ChildAssociationRef to the node created.
Policy Types
Each policy extends one of three policy types:
Class policy
Property policy
Association policy
Class policies are for events related to content types or aspects, Property policies are for events
related to properties and Association policies for events related to associations. Most policies
extend the Class policy interface, a few policies extend the Association policy interface and, at
the time of writing, none extend the Property policy.
<properties>
<property name="ch:countStartedDate">
<type>d:date</type>
<mandatory>true</mandatory>
</property>
<property name="ch:updateCount">
<type>d:int</type>
<default>0</default>
</property>
<property name="ch:readCount">
<type>d:int</type>
<default>0</default>
</property>
</properties>
</aspect>
The ch:countStartedDate will be set when the aspect is added to a node. The
ch:updateCount and ch:readCount properties will be incremented for each content
update or read respectively.
2. Implementing the aspect behaviour class
This ContentHitsAspect class contains the behaviour behind the ch:contentHits
aspect.
a.
b.
c.
When creating a new JavaBehaviour instance, you must provide the following:
public JavaBehaviour(Object instance,
String method,
NotificationFrequency frequency)
instance
the object instance holding the method
method
the method name
frequency
one of three possible values defining when the behaviour should be notified:
EVERY_EVENT, FIRST_EVENT or TRANSACTION_COMMIT
The supplied method implements the behaviour logic and may have dependencies on
Foundation Services that can be resolved using Spring dependency injection.
A behaviour is bound to a policy using the bindClassBehaviour() method on the
Policy Component:
bindClassBehaviour(QName policy,
QName classRef,
Behaviour behaviour);
policy
the policy name
classRef
QName of type or aspect concerned by the policy
behaviour
a Behaviour object (instance of JavaBehaviour or ScriptBehaviour)
A behaviour is bound to a specific content type or aspect using the classRef
argument.
The Policy Component is also used by services to register policies and to invoke
policy behaviours.
In the Content Hits example, the Spring initialise() method is used to bind the
behaviours to policies:
public void initialise()
{
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI,
"onAddAspect"),
ASPECT_CONTENT_HITS,
new JavaBehaviour(this, "onAddAspect",
NotificationFrequency.FIRST_EVENT));
this.policyComponent.bindClassBehaviour(
ContentServicePolicies.ON_CONTENT_READ,
ASPECT_CONTENT_HITS,
new JavaBehaviour(this, "onContentRead",
NotificationFrequency.TRANSACTION_COMMIT));
this.policyComponent.bindClassBehaviour(
ContentServicePolicies.ON_CONTENT_UPDATE,
ASPECT_CONTENT_HITS,
new JavaBehaviour(this, "onContentUpdate",
NotificationFrequency.TRANSACTION_COMMIT));
}
d.
e.
f.
g.
The afterCommit() method retrieves the list of nodes stored as a resource on the
transaction by the onContentRead() behaviour:
public void afterCommit()
{
The Content Hits read count (ch:readCount) is incremented for each node in the list.
The afterCommit() method retrieves the list of nodes stored as a resource on the
transaction by the onContentUpdate() behaviour:
public void afterCommit()
{
....
Set<NodeRef> writeNodeRefs = (Set<NodeRef>)
AlfrescoTransactionSupport.getResource(KEY_CONTENT_HITS_WRITES);
if (writeNodeRefs != null)
{
for (NodeRef nodeRef : readNodeRefs)
{
Runnable runnable = new
ContentHitsWriteCountIncrementer(nodeRef);
threadExecuter.execute(runnable);
}
}
}
The Content Hits update count (ch:updateCount) is incremented for each node in the
list.
h.
PolicyComponent policyComponent;
BehaviourFilter policyFilter;
NodeService nodeService;
TransactionService transactionService;
ThreadPoolExecutor threadExecuter;
The Content Hits model is registered with the following Spring bean definition taken from
contents-hits-context.xml:
<bean id="contentHits.dictionaryBootstrap"
parent="dictionaryModelBootstrap"
depends-on="dictionaryBootstrap">
<property name="models">
<list>
<value>org/alfresco/sample/contentHitsModel.xml</value>
</list>
</property>
</bean>
The initialise() method will be called once the bean has been instantiated and the
properties set.
5. Configuring the property sheet
In order to see the Content Hits aspect properties in the Web Client, the property sheet
has to be configured as in the example web-client-config-custom.xml:
<config evaluator="aspect-name" condition="ch:contentHits">
<property-sheet>
<show-property name="ch:countStartedDate"
read-only="true"
show-in-edit-mode="false"/>
<show-property name="ch:updateCount"
read-only="true"
show-in-edit-mode="false"/>
<show-property name="ch:readCount"
read-only="true"
show-in-edit-mode="false" />
</property-sheet>
</config>
the Web Client custom configuration file containing the aspect property sheet
definition (web-client-config-custom.xml).
The compiled class needs to be exported to a JAR file. The files then need to be deployed
to the following directories in the Alfresco repository:
the JAR file to the WEB-INF/lib directory;
all other files to the WEB-INF/classes/alfresco/extension directory.
For deployment in a production environment, the files should be packaged and deployed
as an Alfresco Module Package (AMP). For more details, see Introduction on page 143.
Repository Actions
Introduction
An action is a unit of work that is performed against a node. For example, moving a node,
copying a node, checking a node in, transforming the contents of a node, etc. Many actions
already exist, however it is possible to add you own custom actions in a few easy steps.
An action has to implement the org.alfresco.repo.action.executer.ActionExecuter
interface. The ActionExecuter interface can be implemented directly, however it is best to
extend the abstract class ActionExecuterAbstractBase that has been written to provide
basic services for action executer implementations. Only two methods need implementing when
deriving from the abstract superclass. ActionExecuterAbstractBase is presented in detail in
the Repository Action Howto on page 79.
For clarity, not all of the available methods are shown. For a complete description, please
consult the Javadocs:
Interface ActionExecuter
Class ActionExecuterAbstractBase
The name of the action executer is defined by the static NAME attribute. In our case the
action executer name is tag.
public class TagActionExecuter extends ActionExecuterAbstractBase
{
public static final String NAME = "tag";
...
}
b.
In the first part of the tutorial, the action does not take any parameters. The action
executer, however, still has to implement the addParameterDefinitions() method,
even if it is empty.
protected void addParameterDefinitions(List<ParameterDefinition>
paramList)
{
// there are no parameters
}
c.
d.
where <action-name> is the name of the action defined by the static NAME attribute on the
action executer class.
In the example, the I18N messages are defined in the tag-actionmessages.properties file.
# Action title and description
The bean must define the action-executer bean as its parent bean.
The action-executer parent bean is defined in the action-services-context.xml
Spring configuration file:
<bean id="action-executer" abstract="true" init-method="init">
<property name="runtimeActionService">
<ref bean="actionService" />
</property>
</bean>
</property>
</bean>
The action will automatically add the taggable aspect, however the tag values will have to be
added manually via the property sheet:
b.
where <action-name> is the name of the action defined by the static NAME attribute
on the action executer class and <param-name> is the parameter value passed to the
getParamDisplayLabel() method.
In the example, the I18N display label for the param_tags parameter has been added to
the tag-action-messages.properties file:
# Action title and description
tag.title=Add tags to item
tag.description=This action adds tags to the matched item
# Action parameter display labels
tag.param_tags.display-label=Tags
b.
c.
The prepareForSave() method places the tags the user entered into the repository
properties map passed in.
public static final String PROP_TAGS = "tags";
public void prepareForSave(Map<String, Serializable> actionProps,
Map<String, Serializable> repoProps)
{
repoProps.put(TagActionExecuter.PARAM_TAGS,
(String)actionProps.get(PROP_TAGS));
}
d.
e.
The summary page show the tags that will automatically be added:
When the action is run against a content item, the taggable aspect will be added and
automatically initialised with the default tag values defined on the action:
Content Transformers
Introduction
A content transformer is a Java class that can transform content from one mime type to another.
Many content transformers already exist, however it is possible to add you own custom content
transformer in a few easy steps.
A content transformer has to implement the
org.alfresco.repo.content.transform.ContentTransformer interface.
ContentTransformer extends the org.alfresco.repo.content.ContentWorker interface that
is a common marker interface for specific worker interfaces such as content transformers and
metadata extractors.
An abstract base class called AbstractContentTransformer has been written to provides basic
services for ContentTransformer implementations. Only two methods need implementing when
deriving from the abstract superclass. AbstractContentTransformer is presented in detail in
the tutorial Content Transformer Howto on page 91.
For clarity, not all of the available methods are shown. For a complete description, please
consult the Javadocs:
Interface ContentTransformer
Class AbstractContentTransformer
b.
c.
There is no need to handle any exceptions generated during the transformation, either
runtime or otherwise, the AbstractContentTransformer superclass will handle and
report these as required.
AbstractContentTransformer also calculates and maintains the average time taken
The bean may also define a list of explicit transformations that the content transformer
can perform regardless of what it returns via the getReliability() check. The
explicit transformations are defined on the explicitTransformations property as
a list of ContentTransformerRegistry.TransformationKey objects. The source
and target mime types for the explicit transformation are passed as arguments to the
TransformationKey constructor.
The baseContentTransformer parent bean is defined in content-servicecontext.xml:
<!-- Abstract bean definition defining base definition for all
transformers -->
<bean id="baseContentTransformer"
class="org.alfresco.repo.content.transform.AbstractContentTransformer"
abstract="true"
init-method="register">
<property name="mimetypeService">
<ref bean="mimetypeService" />
</property>
<property name="registry">
<ref bean="contentTransformerRegistry" />
</property>
</bean>
The register() method is defined as a Spring bean initialisation method on the parent
bean and is called automatically by Spring once the content transformer has been
instantiated. It registers the content transformer with the ContentTransformerRegistry
for each explicit transformation provided.
For clarity, not all of the available methods are shown. For a complete description, please
consult the Javadocs: Class ContentTransformerRegistryr
The getTransformer() method is used by clients to get the best transformer available for a
given transformation. If two transformers perform the same transformation, the most reliable one
will always be chosen. If two or more transformers exist with the same reliability, then they will
be cycled until the fastest one is determined. The timing code is automatically provided by the
AbstractContentTransformer superclass.
Further Reading
Working with Temporary Files
If it is necessary to work against physical files during the transformation, use the
org.alfresco.util.TempFileProvider#createTempFile to ensure that all temporary files will
be cleaned up appropriately. Failure to do this will mean that temporary files are not cleaned up
while the system is running. Do not use deleteOnExit - the Alfresco repository is designed to
run under load indefinitely, i.e. until the next upgrade. Give your temp files meaningful prefixes as
it will help during debugging.
Example taken from
org.alfresco.repo.content.transform.OpenOfficeContentTransformer:
String sourceMimetype = getMimetype(reader);
String targetMimetype = getMimetype(writer);
MimetypeService mimetypeService = getMimetypeService();
String sourceExtension = mimetypeService.getExtension(sourceMimetype);
String targetExtension = mimetypeService.getExtension(targetMimetype);
// create temporary files to convert from and to
File tempFromFile = TempFileProvider.createTempFile(
"OpenOfficeContentTransformer-source-",
"." + sourceExtension);
File tempToFile = TempFileProvider.createTempFile(
"OpenOfficeContentTransformer-target-",
"." + targetExtension);
Metadata Extractors
Introduction
A metadata extractor is a Java class that can extract metadata from content of a particular mime
type. Many metadata extractors already exist, however it is possible to add you own custom
metadata extractor in a few easy steps.
A metadata extractor has to implement the
org.alfresco.repo.content.metadata.MetadataExtracter interface. MetadataExtracter
extends the org.alfresco.repo.content.ContentWorker interface that is a common marker
interface for specific worker interfaces such as metadata extractors and content transformers.
An abstract class called AbstractMappingMetadataExtracter has been written to provides
basic services for MetadataExtracter implementations. Only one method needs implementing
when deriving from the abstract superclass. AbstractMappingMetadataExtracter is presented
in detail in the tutorial Metadata Extractor Howto on page 99.
For clarity, not all of the available methods are shown. For a complete description, please
consult the Javadocs:
Interface MetadataExtracter
Class AbstractMappingMetadataExtracter
MetadataExtracterRegistry
The MetadataExtracterRegistry holds a list of available metadata extractors and provides the
most appropriate extractor for a particular mime type extraction request.
Upon initialisation, metadata extractors register themselves with the
MetadataExtracterRegistry via the register() method.
For clarity, not all of the available methods are shown. For a complete description, please
consult the Javadocs: Class MetadataExtracterRegistry
The getExtracter() method is used by clients to get the most appropriate metadata extractor
for a particular mime type.
The MetadataExtracterRegistry bean is defined in the content-service-context.xml
Spring configuration file:
<!-- Metadata Extraction Regisitry -->
<bean id="metadataExtracterRegistry"
class="org.alfresco.repo.content.metadata.MetadataExtracterRegistry" />
b.
c.
Extracted metadata values are added to the rawProperties map using the
putRawValue() method on the abstract superclass. Keys are the same as those
used in the default mapping properties file and the value has to be Serializable.
putRawValue() will only add a value if it is non-trivial (not null, not an empty String
or Collection or Array).
private static final String KEY_AUTHOR = "author";
private static final String KEY_TITLE = "title";
private static final String KEY_SUBJECT = "subject";
putRawValue(KEY_AUTHOR, docInfo.getAuthor(), rawProperties);
putRawValue(KEY_TITLE, docInfo.getTitle(), rawProperties);
putRawValue(KEY_SUBJECT, docInfo.getSubject(), rawProperties);
extractRaw() need only be concerned with performing the extraction. There is no
need to handle any exceptions generated during the extraction, either runtime or
otherwise, the abstract superclass will handle and report these as required.
The reader will be closed after the extraction completes, however when accessing the
content via a stream, failure to close the stream, regardless of success of failure, will
result in an error message being generated to the log output and the stream will be
held open indefinitely.
Example from PdfBoxMetadataExtracter. The newRawMap() and putRawValue()
methods on the abstract superclass are used to initialise and populate the result Map
with the extracted metadata. The source InputStream and PDDocument are closed
once the extraction is complete. The method may throw an exception.
public Map<String, Serializable> extractRaw(ContentReader reader)
throws Throwable
{
Map<String, Serializable> rawProperties = newRawMap();
PDDocument pdf = null;
InputStream is = null;
try
{
is = reader.getContentInputStream();
// stream the document in
pdf = PDDocument.load(is);
if (!pdf.isEncrypted())
{
// Scoop out the metadata
PDDocumentInformation docInfo =
pdf.getDocumentInformation();
putRawValue(KEY_AUTHOR, docInfo.getAuthor(),
rawProperties);
putRawValue(KEY_TITLE, docInfo.getTitle(), rawProperties);
putRawValue(KEY_SUBJECT, docInfo.getSubject(),
rawProperties);
Calendar created = docInfo.getCreationDate();
if (created != null)
{
putRawValue(KEY_CREATED, created.getTime(),
rawProperties);
}
}
}
finally
{
if (is != null)
{
try { is.close(); } catch (IOException e) {}
}
if (pdf != null)
{
try { pdf.close(); } catch (Throwable e)
{ e.printStackTrace(); }
}
}
// Done
return rawProperties;
}
The register() method is defined as a Spring bean initialisation method and is called
automatically by Spring once the metadata extractor has been instantiated. It registers the
metadata extractor with the MetadataExtracterRegistry.
The standard metadata extractor class names and Spring bean names are all written
er (extracter), they are not written with the correct or spelling (extractor)!
PdfBoxMetadataExtracter example taken from content-services-context.xml.
<!-- Content Metadata Extracters -->
<bean id="extracter.PDFBox"
class="org.alfresco.repo.content.metadata.PdfBoxMetadataExtracter"
parent="baseMetadataExtracter" />
Further Reading
Metadata Mapping
The extracted metadata returned by extractRaw() is mapped to node properties using a
mapping. The default mapping is supplied as a properties file in a format similar to the following:
# Namespaces
namespace.prefix.cm=http://www.alfresco.org/model/content/1.0
namespace.prefix.my=http://www.mycompany.com/model/mymodel/1.0
# Mappings
editor=cm:author, my:editor
title=cm:title
summary=cm:summary
subject=cm:description
Namespaces used in the mapping have to be declared. The property keys are the same as those
used in the raw properties map returned by the extractRaw() method. The property value is a
valid node property with a shorthand namespace prefix. A single raw property can be mapped to
several node properties provided as a comma separated list.
The raw property values are converted to the corresponding node property types using the
org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter default
converter.
The default mapping is returned by the getDefaultMapping() method. The default
implementation looks for a properties file with the same name and in the same location as
the metadata extractor class. For example, the PdfBoxMetadataExtracter default mapping
properties file is called PdfBoxMetadataExtracter.properties and is located in the
org.alfresco.repo.content.metadata package or in the org/alfresco/repo/content/
metadata directory on the classpath.
The default implementation can be specialised by overriding the getDefaultMapping() method
in the subclass. If the default mapping is defined in a properties file other than the one named
after the class, then the readMappingProperties(String propertiesUrl) method can be
used to read the mapping from an alternate location:
protected Map<<String, Set<QName>> getDefaultMapping()
{
return readMappingProperties(PROPERTIES_URL);
}
Implementations can also dynamically modify the default mapping using either the setMapping()
method, if the mapping is supplied as a Map, or the setMappingProperties() method, if the
mapping is supplied as a Properties object. By default, the supplied mapping will replace the
default mapping returned by getDefaultMapping(). If the supplied mapping should augment the
default mapping, the setInheritDefaultMapping(boolean inheritDefaultMapping) method
should first be called with a value of true. Finally, the init() method needs to be called to reinitialise the extractor.
Overwrite Policies
The overwrite policy determines whether extracted values are written to the destination property
map or not. Three overwrite policies have been defined:
EAGER
If the extracted value is not null, always write the extracted value to the destination property
map.
PRAGMATIC
JavaServer Faces
Introduction
JavaServer Faces (JSF) is a user interface framework for Java web applications. JSF defines an
event-driven, component-based model for web application development, similar to the model that
has been used successfully for standalone GUI applications for years. JSFs core architecture is
designed to be independent of specific protocols and markup. However it is also aimed directly at
solving many of the common problems encountered when writing applications for HTML clients
that communicate via HTTP to a Java application server that supports servlets and JavaServer
Pages (JSP) based applications.
JSF is a specification (JSR-252) that has been developed as part of the Java Community
Process. It is a standard, vendor independent specification. Several implementations exist,
including the GlassFish JavaServer Faces reference implementation and Apache MyFaces.
Alfresco uses the Apache Myfaces implementation.
The specification defines a set of standard user interface components and an API for extending
the standard components or developing new ones.
As well as UI components, JSF also defines artifacts like converters, validators, events listeners,
and renderers:
converters perform type conversion between server-side Java objects and their
representation in the user interface. A good example is a date;
validators can be associated with a JSF component to perform input validation checks on
local values before they are processed;
event listeners are registered against events that are triggered when a user clicks a button
or a link, changes a value in a field, or makes a selection in a list. The outcome of the
event processing controls which page is displayed next;
renderers generate the actual markup for the user interface. JSF is not limited to HTML or
any other markup language and the same JSF component can be coupled with different
renderers to produce different output, for example, either HTML or WML;
When used with JSP as a presentation layer, the JSF components are represented by JSP
custom actions (or custom tags) on the JSP page.
Some examples of JSF components on the login page are as follows...
Enter Login details: text label:
<h:outputText value="#{msg.login_details}" />:
The <h:form> custom tag represents an HTML form component. It is a container for input
components that hold values that should be processed together. All JSF input components
must be nested within a form component.
4. Output Text and Message bundles
The first component we will look at in detail is represented by the <h:outputText> custom
tag.
The following tags output text to the browser:
<h:outputText value="#{msg.login_details}" />:
<h:outputText value="#{msg.username}"/>:
<h:outputText value="#{msg.password}"/>:
<h:outputText value="#{msg.language}"/>:
Attribute values #{ ... } are written using a Unified Expression Language (Unified EL,
or just EL). In the above example, the values beginning with msg correspond to properties
defined in a message bundle.
A message bundle is loaded using the <f:loadBundle> custom tag:
<%-- load a bundle of properties I18N strings here --%>
<f:loadBundle basename="alfresco.messages.webclient" var="msg"/>
The above example loads the default Web Client message bundle
webclient.properties from the alfresco/messages directory on the classpath.
All of the Web Client I18N strings are stored in the webclient.properties default
message bundle. Message translations can be provided in separate files, named
with an extra suffix corresponding to the standard ISO language and country codes.
For example, the French translations of the Web Client messages are found in the
webclient_fr_FR.properties file and the French Canadian translations are found in the
webclient_fr_CA.properties file. The messages in the base bundle (without a suffix)
are in the en_US locale.
Login page messages from the default webclient.properties bundle:
login_details=Enter Login details
username=User Name
password=Password
language=Language
login=Login
The same login page messages from the French webclient_fr_FR.properties bundle:
login_details=Entrez les informations de connexion
username=Nom d'utilisateur
password=Mot de passe
language=Langue
login=Connexion
Custom message bundles can be loaded using the <f:loadBundle> custom tag.
In the following example, a custom webclient.properties message bundle is loaded
from the alfresco/extension directory and stored under the mymsg key:
<f:loadBundle basename="alfresco.extension.webclient" var="mymsg"/>
Custom messages can then be displayed using the <h:outputText> custom tag:
<h:outputText value="#{mymsg.lost_password}" />:
managed by the JSF framework. Value binding expressions are bound to getters and
setters on managed beans that are automatically called by the JSF framework to retrieve
values to display in the UI (via getter methods) and to update values on the managed bean
(via setter methods) with new values selected or input by the user.
The items to display in the drop-down language selection menu on the login page are
retrieved by calling the getLanguages() method on the UserPreferencesBean:
public SelectItem[] getLanguages()
{
SelectItem[] items = getLanguageItems();
// Change the current language
if (this.language == null)
{
// first try to get the language that the current user is using
Locale lastLocale =
Application.getLanguage(FacesContext.getCurrentInstance());
if (lastLocale != null)
{
this.language = lastLocale.toString();
}
...
}
return items;
}
The default language for the current user is retrieved by calling the getLanguage()
method on the UserPreferencesBean:
public String getLanguage()
{
return this.language;
}
and the setLanguage() method will be called on the UserPreferencesBean to update the
current language:
public void setLanguage(String language)
{
this.language = language;
Application.setLanguage(FacesContext.getCurrentInstance(), language);
...
}
6. FacesContext
During request processing for a JSF page, a FacesContext object is used to represent
request specific information as well as provide access to services for the application.
The static FacesContext.getCurrentInstance() method is used to obtain the current
FacesContext instance:
FacesContext context = FacesContext.getCurrentInstance();
The values of the text fields are bound to the managed bean LoginBean via value binding
expressions.
Both components have an optional validator associated with them to perform input
validation checks on the user name and password values as part of the login process.
JSF calls the validateUsername() method on the LoginBean, to validate the value of the
user name:
public void validateUsername(FacesContext context, UIComponent component,
Object value)
throws ValidatorException
{
...
if (name.length() < minUsernameLength || name.length() > 256)
{
...
throw new ValidatorException(new FacesMessage(err));
}
}
actions to methods on managed beans that are automatically called by the JSF framework
when an action event is triggered.
When the Login button is clicked, JSF will call the login() method on the LoginBean:
public String login()
{
String outcome = null;
if (this.username != null && this.username.length() != 0 &&
this.password != null && this.password.length() != 0)
{
...
this.authenticationService.authenticate(this.username,
this.password.toCharArray());
...
if
(NavigationBean.LOCATION_MYALFRESCO.equals(this.preferences.getStartLocation()))
{
return "myalfresco";
}
else
{
// generally this will navigate to the generic browse screen
return "success";
}
...
}
return outcome;
}
<description>
The decision rule used by the NavigationHandler to
determine which view must be displayed after the
current view, login.jsp is processed.
</description>
<from-view-id>/jsp/login.jsp</from-view-id>
<navigation-case>
<description>
Indicates to the NavigationHandler that the browse.jsp
view must be displayed if the Action referenced by a
UICommand component on the login.jsp view returns
the outcome "success".
</description>
<from-outcome>success</from-outcome>
<to-view-id>/jsp/browse/browse.jsp</to-view-id>
</navigation-case>
</navigation-rule>
An outcome of success from the login page will navigate to the standard browse page
(browse.jsp).
<navigation-rule>
<from-view-id>/jsp/*</from-view-id>
<navigation-case>
<from-outcome>browse</from-outcome>
<to-view-id>/jsp/browse/browse.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>myalfresco</from-outcome>
<to-view-id>/jsp/dashboards/container.jsp</to-view-id>
</navigation-case>
<navigation-case>
<from-outcome>about</from-outcome>
<to-view-id>/jsp/dialog/about.jsp</to-view-id>
</navigation-case>
</navigation-rule>
An outcome of myalfresco from any page will navigate to the dashboard container page
(dashboards/container.jsp).
The standard JSF Navigation Handler can be extended and overridden to customise
navigation handling. A custom navigation handler is defined using a <navigationhandler/> element in a <faces-config/> file.
In Alfresco, the custom navigation handler is defined in the WEB-INF/faces-configapp.xml file:
<application>
<navigation-handler>
org.alfresco.web.app.AlfrescoNavigationHandler
</navigation-handler>
...
</application>
The managed bean name is not always the same as the implementing class name.
When required, JSF automatically instantiates and initialises managed beans. The
managed beans are then stored in the scope defined by the value of the <managed-beanscope/> element. The managed bean scope takes one of the following values:
none
Does not store the managed bean in any scope.
request
The managed bean is stored and kept for a single request.
session
The managed bean is stored and kept for a single user session.
application
The managed bean is stored and shared between all users.
The properties defined by <managed-property/> elements are automatically set on
the managed beans once they have been instantiated via setter methods. The #{...}
property values are resolved by one or more variable resolvers. Custom variable resolvers
can extend the standard JSF VariableResolver.
In Alfresco, a custom variable resolver is defined in the WEB-INF/faces-config-app.xml
file:
<application>
...
<variable-resolver>
org.alfresco.web.app.AlfrescoVariableResolver
</variable-resolver>
...
</application>
Actions Framework
Introduction
The Alfresco Web Client UI actions are configured using the Actions Framework. Actions such
as
Edit,
View Details,
Update,
Copy are all examples of UI Actions. Actions are
grouped into action groups. Individual actions can be reused between groups and the action
groups reused across pages. Action Groups define an ordered list of actions that are displayed
together, either as a serial list (for example, as a strip of icons) or grouped together in a dropdown menu.
The More Actions menu is an example of an action group.
UI actions and actions groups are configured in XML. The standard UI actions and action groups
are defined in the web-client-config-actions.xml configuration file. You can define your own
custom UI actions and action groups in a Web Client configuration extension file. You can also
extend existing action groups to add your own custom UI actions to existing menus in the Web
Client.
to define an inline action specific to the current action group. The order of the child
<action/> elements defines the order in which the actions will be displayed on the page.
All <action/> and <action-group/> elements must be contained within an <actions/>
element.
Custom actions and action groups should be defined in the standard web-clientconfig-custom.xml extension file. Defining an action or an action group with the same ID
as an existing one effectively overrides the original definitions. In this way, action groups
may be overridden to add custom actions to existing action groups or to redefine display
attributes for a consistent look and feel. Existing actions can also be hidden in an action
group by using the hide attribute in conjunction with idref.
Below is the Update action definition from the web-client-config-actions.xml
configuration file:
<action id="update_doc">
<permissions>
<permission allow="true">Write</permission>
</permissions>
<evaluator>
org.alfresco.web.action.evaluator.UpdateDocEvaluator
</evaluator>
<label-id>update</label-id>
<image>/images/icons/update.gif</image>
<action-listener>
#{CheckinCheckoutBean.setupContentAction}
</action-listener>
<action>dialog:updateFile</action>
<params>
<param name="id">#{actionContext.id}</param>
</params>
</action>
The document_browse_menu action group corresponds to the More Actions drop down
menu on documents on the main browse page:
Most standard UI actions and action groups are defined in a global <config/> section (i.e.
there is no evaluator or condition). Actions and action groups can, however, be configured
by node type. It is thus possible to add new actions or hide actions for a specific folder or
document type.
The following action group configuration hides the Cut and Copy actions from WCM
WebProject type folders:
<config evaluator="node-type" condition="wca:webfolder">
<actions>
<action-group id="space_browse">
<show-link>false</show-link>
<action idref="cut_node" hide="true" />
<action idref="copy_node" hide="true" />
</action-group>
</actions>
</config>
The UpdateDocEvaluator.evaluate(Node) method returns true if the node is a subtype of cm:content and the node is a Working Copy owned by the current user or the
node is not locked and the node is not a Working Copy:
public class UpdateDocEvaluator implements ActionEvaluator
{
public boolean evaluate(Node node)
{
DictionaryService dd = Repository.getServiceRegistry(
FacesContext.getCurrentInstance()).getDictionaryService();
return dd.isSubClass(node.getType(), ContentModel.TYPE_CONTENT) &&
((node.isWorkingCopyOwner() == true ||
(node.isLocked() == false &&
node.hasAspect(ContentModel.ASPECT_WORKING_COPY) ==
false)));
}
}
The id parameter is defined as the id of the actionContext object (id of the Node).
The setupContentAction(ActionEvent) method retrieves the node id from the list of
parameters:
public void setupContentAction(ActionEvent event)
{
UIActionLink link = (UIActionLink)event.getComponent();
Map<String, String> params = link.getParameterMap();
String id = params.get("id");
if (id != null && id.length() != 0)
{
setupContentDocument(id);
}
...
}
4. Action Outcome
The <action/> element defines a JSF navigation outcome to use when a user selects the
action.
The dialog:updateFile outcome will be interpreted by the Alfresco Navigation Handler
to call the updateFile dialog:
<!-- Update document -->
<action id="update_doc">
...
<action>dialog:updateFile</action>
...
</action>
The mandatory context attribute defines the object to use as the actionContext.
In the above example, r is the current node in the list.
label-id
The id of an I18N string to be used as the label for the action.
label
A literal string to be used in place of label-id if you do not want an I18N string.
tooltip-id
The id of an I18N string to be used as the tooltip for the action.
tooltip
A literal string to be used in place of tooltip-id if you do not want an I18N string.
show-link
A presentation attribute. Valid values are true or false. If set to true, the action will be
displayed as an icon and a textual link, if set to false, the action will only be displayed as an
icon with the label as the tooltip for the icon image.
style
A presentation attribute. The CSS style to apply to the action.
style-class
A presentation attribute. The CSS class to apply to the action.
image
The icon image to display.
action-listener
The JSF action listener method to call upon user selection of the action.
action
The JSF action navigation outcome to execute upon user selection of the action.
script
The Alfresco JavaScript file to execute upon user selection of the action. The JavaScript file is
specified by either Path or Node Reference.
href
The href to navigate to upon user selection of the action.
target
The associated href target.
onclick
The JavaScript onclick handler to execute upon user selection of the action.
params
Contains 1 or more <param/> elements.
param
Contained within the <params/> element. Defines the JSF <f:param/> custom tags to be
generated as children of the action component. Each <param/> element has a mandatory
name attribute as the name of the parameter and the mandatory value of the element contains
the value to passed as the parameter. Parameters are available from the UIActionLink
component passed to the JSF action listener method upon user selection of the action.
Parameters are also passed directly as href and script URL arguments.
<action-group/> element
Each <action-group/> element defines a single action group. The mandatory id attribute is
used to identify the action group in a JSF <r:actions/> custom tag. Defining another action
group with the same ID as an existing action group effectively overrides the original action group
definition. All other elements must be contained within the <action-group/> element.
action
References or defines an action definition for the group. The order of the attributes defines
the order in which the actions are displayed. Action elements may be defined using an idref
attribute to reference an existing action definition or inline to define an action specific to the
action group. Existing actions can also be hidden by using the hide attribute in conjunction
with idref.
show-link
Overrides any similarly named attribute set on individual action definitions.
style
Overrides any similarly named attribute set on individual action definitions.
style-class
Overrides any similarly named attribute set on individual action definitions.
Dialog Framework
Introduction
The dialog framework manages the user interface dialogs in the Alfresco Web Client. Most of the
standard Web Client dialog are managed by the framework and it is possible to create your own
custom dialogs using the framework.
Each dialog has three main components:
A <dialog/> configuration element in a web-client-config.xml file.
A dialog JSP that only contains the HTML and JSF components to define the body of the
dialog.
A JSF managed bean that is used as a backing bean for the dialog.
At the centre of the dialog framework is the Dialog Manager. When a dialog is opened, the
Alfresco Navigation Handler looks up the <dialog/> configuration then calls the Dialog Manager
to initialise the dialog and instantiate the managed bean before navigating to the dialog JSP
page.
See The Dialog Manager on page 130 for more details.
name
Defines the unique name (or id) of the dialog. The above dialog can be referenced in an
action definition as dialog:addAspect.
page
Defines the path to the JSP page to be used for the dialog.
managed-bean
Defines the name of the JSF managed bean to be used as the backing bean for the
dialog.
See <dialog/> attributes on page 130 for a complete description of all dialog attributes.
2. Defining an action
The custom UI action definition from the Configuring a Custom UI Action tutorial can now
be updated to call the custom addAspect dialog.
Example taken from web-client-config-custom.xml:
<!-- Launch Add Aspect Dialog -->
<action id="add_aspect">
<label>Add Aspect</label>
<image>/images/icons/add.gif</image>
<action>dialog:addAspect</action>
<action-listener>#{BrowseBean.setupSpaceAction}</action-listener>
<params>
<param name="id">#{actionContext.id}</param>
</params>
</action>
<!-- Add action to more actions menu for each space -->
<action-group id="space_browse_menu">
<action idref="add_aspect" />
</action-group>
The add-aspect.jsp example below displays a simple label and a drop-down list of
aspects for the user to choose from.
<%@
<%@
<%@
<%@
taglib
taglib
taglib
taglib
The first 4 lines simply include the JSF and Alfresco tag libraries.
The <h:outputText/> element will output some text, in this example, the string to display
is read from the webclient.properties file.
The <h:selectOneMenu/> element will display the drop-down list of aspects. The list of
aspects (<f:selectItems/>) is supplied by the getTestableAspects() method on the
RunActionWizard class.
Once the user has selected an aspect and clicked on the OK button, the setAspect()
method will be called on the dialog's JSF managed bean to set the value of the selected
aspect. The expression DialogManager.bean refers to the JSF managed bean being
used by the current dialog and configured using the managed-bean attribute in the dialog's
configuration. Because an explicit bean is not being referenced, the same JSP page could
be re-used for multiple dialogs, each dialog defining it's own specific bean implementation.
b.
Implementing the getter and setter methods for the aspect property
The aspect property stores the value of the aspect selected by the user from the
drop-down list. Once the user has selected an aspect and clicked on the OK button,
the setAspect() method will be called to set the value of the selected aspect. If
the user returns to the dialog, the value of the aspect property will be read using
the getAspect() method to initialise the drop-down list to the aspect that the user
selected the last time.
protected String aspect;
public String getAspect()
{
return aspect;
}
public void setAspect(String aspect)
{
this.aspect = aspect;
}
c.
d.
Once the user has selected an aspect from the dialog JSP and clicked on the OK
button, JSF will automatically call the setAspect() method on the AddAspectDialog
bean passing the value of the aspect selected by the user. The setAspect() method
stores the selection in the this.aspect field. The finish() method on the
BaseDialogBean will then be called which will in turn call the finishImpl() method
on the AddAspectDialog bean. The finishImpl() will do the actual work of the
dialog and add the selected aspect to the Space.
6. Registering the JSF Managed Bean
The new AddAspectDialog managed bean now has to be registered with JSF in a facesconfig.xml file. The faces-config.xml file will be packaged in the META-INF directory
for deployment.
Example taken from META-INF/faces-config.xml:
<managed-bean>
<managed-bean-name>AddAspectDialog</managed-bean-name>
<managed-bean-class>
org.alfresco.sample.AddAspectDialog
</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>nodeService</property-name>
<value>#{NodeService}</value>
</managed-property>
<managed-property>
<property-name>browseBean</property-name>
<value>#{BrowseBean}</value>
</managed-property>
</managed-bean>
The AddAspectDialog bean uses the nodeService and browseBean. JSF will inject
references to the public NodeService and BrowseBean defined as managed properties
in the above example. The setter methods are implemented by the BaseDialogBean
superclass.
See Introduction on page 106 for more information on registering managed beans.
7. Packaging and deploying
In order to deploy a custom dialog to the Alfresco Web Client, the following files need to be
packaged:
the web-client-config-custom.xml file containing the dialog and action
configuration;
the dialog JSP containing the HTML and JSF components to define the body of
the dialog;
the custom webclient.properties file containing custom dialog messages;
the compiled JSF managed bean class;
the custom JSF faces-config.xml file.
The compiled class and META-INF/faces-config.xml file needs to be exported to a JAR
file. The files then need to be deployed to the following directories in the Alfresco Web
Client:
the JAR file to the WEB-INF/lib directory;
the dialog JSP to the jsp/extension directory;
the web-client-config-custom.xml and webclient.properties to the WEBINF/classes/alfresco/extension directory.
For deployment in a production environment, the files should be packaged and deployed
as an Alfresco Module Package (AMP). For more details, see Introduction on page 143.
After deployment, the custom Add Aspect action is available from the More Actions menu on
Spaces.
Once selected, the custom Add Aspect dialog is displayed as in the screenshot below:
Further Information
Dialog Beans
Dialog beans are JSF managed beans referenced by the managed-bean attribute in the <dialog/
> configuration. Dialog beans must implement the IDialogBean interface.
override the title and description provided by configuration. This in essence allows the bean to
provide titles and descriptions that are not known until runtime, for example if the title needs to
include the title of the node it is acting upon.
The getAdditionalButtons() method returns a list of DialogButtonConfig objects
representing additional buttons to render. This method essentially allows bean implementations
to dynamically add buttons at runtime based on the context of the dialog, see Dialog Buttons on
page 129 for more details.
Dialog Buttons
The getAdditionalButtons() method returns a list of DialogButtonConfig objects
representing additional buttons to render. This method essentially allows bean implementations
to dynamically add buttons at runtime based on the context of the dialog. The DialogManager
combines the button definitions returned by the method and those configured at design time to
build a list of extra buttons to render. As an example, this technique is used to render the buttons
required for each transition defined in a workflow task.
The internals of the DialogButtonConfig class are shown below.
public class DialogButtonConfig
{
private String id;
private String label;
private String labelId;
private String action;
private String disabled;
private String onclick;
}
buttons.add(new DialogButtonConfig(
"my-button",
"My Dialog Button",
null,
"#{DialogManager.bean.myButtonSelected}",
"false",
"javascript:method()"));
return buttons;
}
attributes
name
The unique name (id) of the dialog.
page
The path to the JSP page to be used for the dialog.
managed-bean
The name of the JSF managed bean to be used as the backing bean for the dialog.
icon
The path to the icon displayed in the header area of the dialog.
title-id
The id of an I18N string to be used as the title of the dialog.
title
A literal string to be used in place of title-id if you do not want an I18N string.
description-id
The id of an I18N string to be used as the description of the dialog.
description
A literal string to be used in place of description-id if you do not want an I18N string.
error-message-id
The id of an I18N string to be used in the case of an error.
actions-config-id
The id of a configured action group. The actions are displayed in the header area of the
dialog.
show-ok-button
Flag to determine whether the OK button should be displayed. This allows dialogs to display
just the Cancel button.
Dialog<button/> attributes
id
The unique id of the button.
label-id
The id of a string to be used as the label of the button.
label
A label attribute can be used in place of label-id if you want to use a literal string instead of
an I18N string.
action
The action method binding to call when the button is clicked.
disabled
Flag to determine whether the button should be rendered as disabled, this can be a JSF
binding expression.
onclick
The JavaScript onclick handler code to execute when the button is clicked.
Wizard Framework
Introduction
The wizard framework manages the user interface wizards in the Alfresco Web Client. Most of the
standard Web Client wizards are managed by the framework and it is possible to create your own
custom wizards using the framework.
Each wizard has three main components:
A <wizard/> configuration element in a web-client-config.xml file.performed in exactly
the same
A JSP for each step in the wizard that only contains the HTML and JSF components to
define the body of the step.
A JSF managed bean that is used as a backing bean for the wizard.
At the centre of the wizard framework is the Wizard Manager. When a wizard is opened, the
Alfresco Navigation Handler looks up the <wizard/> configuration then calls the Wizard Manager
to initialise the wizard and instantiate the managed bean before navigating to the JSP page for
the first step.
See The Wizard Manager on page 138 for more details.
</step>
...
</wizard>
</wizards>
name
Defines the unique name (or id) of the wizard. The above wizard can be referenced in
an action definition as wizard:createContent.
managed-bean
Defines the name of the JSF managed bean to be used as the backing bean for the
wizard.
path
Defines the path to the JSP page to be used for each step.
See <wizard/> attributes, <step/> attributes on page 140 and <page/> attributes on page
140 for a complete description of all wizard attributes.
2. Defining an action
The createContent wizard is already called from the standard Create Content action. The
action is part of the browse_create_menu action group (Create menu on the Web Client
browse page):
The select-aspect.jsp example below displays a simple label and a drop-down list of
aspects for the user to choose from.
<%@
<%@
<%@
<%@
taglib
taglib
taglib
taglib
The first 4 lines simply include the JSF and Alfresco tag libraries.
The <h:outputText/> element will output some text, in this example, the string to display
is read from the webclient.properties file.
The <h:selectOneMenu/> element will display the drop-down list of aspects. The list of
aspects (<f:selectItems/>) is supplied by the getAspects() method on the wizard's
JSF managed bean.
Once the user has selected an aspect and clicked on the Next button, the setAspect()
method will be called on the wizard's JSF managed bean to set the value of the selected
aspect. The expression WizardManager.bean refers to the JSF managed bean being
used by the current wizard and configured using the managed-bean attribute in the
wizard's configuration. Because an explicit bean is not being referenced, the same JSP
page could be re-used for multiple wizards, each wizard defining it's own specific bean
implementation.
The JSF value binding expressions #{WizardManager.bean.aspect}
and #{WizardManager.bean.aspects} will actually
call the WizardManager.getBean().setAspect() and
WizardManager.getBean().getAspects() methods respectively.
4. Localising wizard messages
All of the Web Client I18N strings are stored in a default message bundle called
webclient.properties located in the alfresco/messages directory. Message
translations can be provided in separate files, named with an extra suffix corresponding to
the standard ISO language and country codes. For example, the French translations of the
Web Client messages are found in the webclient_fr_FR.properties file and the French
Canadian translations are found in the webclient_fr_CA.properties file. The messages
in the base bundle (without a suffix) are in the en_US locale.
Custom messages need to be provided in a custom message bundle in the alfresco/
extension directory. The base extension file is also named webclient.properties.
Translations can be provided in separate files in the usual manner.
The custom Create Content wizard messages are stored in a webclient.properties
extension file:
custom_create_content_wizard_title=Custom Create Content Wizard
select_aspect=Select Aspect
create_content_step3_title=Step Three - Select Aspect
create_content_step3_desc=Select the aspect to apply to the content.
b.
c.
Implementing the getter and setter methods for the aspect property
The aspect property stores the value of the aspect selected by the user from the
drop-down list. Once the user has selected an aspect and clicked on the Next button,
the setAspect() method will be called to set the value of the selected aspect. If the
user returns to the same step, the value of the aspect property will be read using the
API Development Course 135
getAspect() method to initialise the drop-down list to the aspect that the user has
already selected.
protected String aspect;
public String getAspect()
{
return aspect;
}
public void setAspect(String aspect)
{
this.aspect = aspect;
}
d.
In order to deploy a custom wizard to the Alfresco Web Client, the following files need to
be packaged:
the web-client-config-custom.xml file containing the wizard and action
configuration;
the wizard JSPs containing the HTML and JSF components to define the body of
each wizard step;
the custom webclient.properties file containing custom wizard messages;
the compiled JSF managed bean class;
the custom JSF faces-config.xml file.
The compiled class and META-INF/faces-config.xml file needs to be exported to a JAR
file. The files then need to be deployed to the following directories in the Alfresco Web
Client:
the JAR file to the WEB-INF/lib directory;
the wizard JSPs to the jsp/extension directory;
the web-client-config-custom.xml and webclient.properties to the WEBINF/classes/alfresco/extension directory.
For deployment in a production environment, the files should be packaged and deployed
as an Alfresco Module Package (AMP). For more details, see Introduction on page 143.
After deployment, the Create Content wizard now has en extra Select Aspect step:
Further Reading
Wizard Beans
Wizard beans are JSF managed beans referenced by the managed-bean attribute in the
<wizard/> configuration. Wizard beans must implement the IWizardBean interface that extends
the IDialogBean interface from the Dialog Framework.
When the Alfresco navigation handler gets a request to open a wizard it examines the current
page. If it detects that it is a wizard, the state of the wizard is retrieved and stored on the view
stack. If the current page is not a wizard the current page is added to the view stack.
The navigation handler then looks up the configuration for the wizard and passes the
configuration object to the WizardManager via the setCurrentWizard() method. The
WizardManager then proceeds to instantiate the wizard's managed-bean and set up any
parameters passed into the wizard via the setupParameters() action listener method. It
processes the list of steps the wizard has, and determines what the current page is.
The navigation handler then navigates to the wizard container page. The wizard container page
also uses the #{WizardManager...} syntax to resolve the title, description and icon for the
header area, the list of steps on the left and the page to show as the body for the current step of
the wizard.
The WizardManager either returns the requested information from the wizard config object it was
passed, or passes the request on to the underlying managed bean. For example, when the user
presses the Finish button, the WizardManager calls finish() on the managed bean.
When a user presses the Next or Back button, the WizardManager increases or decreases the
current step counter and determines from the wizard config what page should be shown.
When the container page renders the buttons, the Next and Finish buttons disabled state is
controlled via the getNextButtonDisabled() and getFinishButtonDisabled() methods.
These methods can be used to enable or disable the buttons appropriately, for example, if the
wizard already has enough state to complete successfully.
When a wizard:close request is received, the navigation handler examines the view stack to
see what the item at the top of the stack is. If it is a wizard, the state object is retrieved from the
stack and restored, and the wizard container page is then navigated to. If the top of the stack
is a normal page, the page is retrieved and navigated to. If the close request is overridden by
an outcome of wizard:close:browse, the view stack is emptied and the overridden outcome
processed.
attributes
name
The unique name (id) of the wizard.
managed-bean
The name of the JSF managed bean to be used as the backing bean for the wizard.
icon
The path to the icon displayed in the header area of the wizard.
title-id
The id of an I18N string to be used as the title of the wizard.
title
A literal string to be used in place of title-id if you do not want an I18N string.
description-id
The id of an I18N string to be used as the description of the wizard.
description
A literal string to be used in place of description-id if you do not want an I18N string.
error-message-id
The id of an I18N string to be used in the case of an error.
actions-config-id
The id of a configured action group. The actions are displayed in the header area of the
wizard.
<step/>
attributes
name
The unique name (within the wizard) of the step.
title-id
The id of an I18N string to be used as the title of the step (appears in the list of steps on the
left hand side).
title
A literal string to be used in place of title-id if you do not want an I18N string.
description-id
The id of an I18N string to be used as the description of the step (the tooltip for the step on
the left hand side).
description
A literal string to be used in place of description-id if you do not want an I18N string.
<page/>
attributes
path
The path to the JSP page to be used for the wizard step.
title-id
The id of an I18N string to be used as the title of the page (appears on the first line of the inner
panel).
title
A literal string to be used in place of title-id if you do not want an I18N string.
description-id
The id of an I18N string to be used as the description of the page (appears on the second line
of the inner panel).
description
A literal string to be used in place of description-id if you do not want an I18N string.
instruction-id
The id of an I18N string to be used as the instruction text for the page (appears on the last line
of the inner panel).
instruction
A literal string to be used in place of instruction-id if you do not want an I18N string.
<condition/>
element
if
A value binding expression that must resolve to true or false.
A <step/> can have any number of <condition/> elements. The first condition to return true is
selected as the active page. A page without a condition will be treated as the default page if none
of the conditions within the step match.
Packaging Extensions
Packaging Extensions
Packaging Extensions
Packaging Extensions
build/
The module id specified in this file will act as a unique identifier for this module. It
is important that the module id is globally unique so that it never clashes with other
modules when it is installed. For example: org.alfresco.module.RecordsManagement. It
is possible to rename a module using the alias mechanism. Module IDs may contain az, A-Z, 0-9, space, minus and underscore.
module.version
The module version number specifies the current version of the module. This is taken
into consideration when installing the module into a WAR. It will determine whether it is
a new install or an update to an existing installation of a previous version. The version
number must be made up of numeric values separated by dots. For example '2.1.56' is
a valid version number, '2.3.4a' is not.
module.title
When a module gets renamed, it is necessary to add the original name to the list of
aliases. This ensures that the module tool and repository startup can correctly match
the new name to one of the old names.
Packaging Extensions
module.depends.* (optional)
The Demo class prints a message to the console when it is initialised by Spring:
public class Demo
{
public void init()
{
System.out.println("SDK Demo AMP class has been loaded");
}
}
Packaging Extensions
The DemoComponent is an example component that will be executed just once during
module initialisation:
public class DemoComponent extends AbstractModuleComponent
{
@Override
protected void executeInternal() throws Throwable
{
System.out.println("DemoComponent has been executed");
}
}
/config
/lib
/licenses
/web
|
|-- /jsp
|-- /css
|-- /images
|-- /scripts
|
|-- module.properties
|
|-- file-mapping.properties
config/
Contains any module specific JAR files. The contents are mapped into the /WEBINF/lib directory in the WAR file.
licenses/
Contains any custom or modified JSPs, CSS files, images and JavaScript. The
contents are mapped to the equivalent directories in the WAR file. All sub-directory
structures are preserved. If a file already exists it is overridden in the WAR and a
recoverable backup is taken.
module.properties
The module.properties file is required. It contains meta-data about the module,
Packaging Extensions
b.
The package-jar target packages the compiled class files into the destination JAR
file:
<target name="package-jar" depends="compile">
<jar destfile="${jar.file}" >
<fileset dir="${build.dir}/classes"
excludes="**/custom*,**/*Test*"
includes="**/*.class" />
</jar>
</target>
The package-amp target packages the JAR files and configuration files into the
destination AMP file:
<target name="package-amp" depends="mkdirs, package-jar"
description="Package the Module" >
<zip destfile="${amp.file}" >
<fileset dir="${project.dir}/build"
includes="lib/*.jar" />
<fileset dir="${project.dir}"
includes="config/**/*.*"
excludes="**/module.properties" />
<fileset dir="${project.dir}/config/alfresco/module/sdkDemoAmp"
includes="module.properties" />
</zip>
</target>
The update-war target uses the Module Management Tool to install the AMP into an
existing WAR file:
<target name="update-war" depends="package-amp"
description="Update the WAR file. Set -Dwar.file=..." >
<echo>Installing SDK Demo AMP into WAR</echo>
Packaging Extensions
<java dir="."
fork="true"
classname="org.alfresco.repo.module.tool.ModuleManagementTool">
<classpath refid="class.path" />
<arg line="install ${amp.file} ${war.file} -force -verbose"/>
</java>
</target>
setting the value of war.file to the correct path to the Alfresco WAR file to update.
Example Ant build from within Eclipse:
Alternatively, the MMT install command can be run directly from the command line. The
following command will do a dry-run (preview) installation:
java -jar alfresco-mmt-2.1.jar install alfresco-sdk-custom-service.amp
alfresco.war -preview
The following example will install the AMP file in verbose mode:
java -jar alfresco-mmt-2.1.jar install alfresco-sdk-custom-service.amp
alfresco.war -verbose
The modified Alfresco WAR can then be re-deployed back into your application server.
On re-starting the application server, the console will show that the custom class has been
initialised during startup:
Packaging Extensions
Further Reading
Customising the structure of an AMP file
In order to customise the structure of your AMP file, a file-mapping.properties file must be
provided which describes how the structure of your AMP file will be mapped to the Alfresco WAR
when the AMP file is installed by the Module Management Tool.
If no file-mapping.properties file is provided the default mapping will be used.
The structure of file-mapping.properties file is that of a standard Java property file, with the
key of each entry being the directory in the AMP file structure and the value being the location
that the contents for that directory should be copied to in the WAR file.
If the source directory does not exist in the AMP file, then the mapping will be ignored; however,
if the destination directory in the WAR file does not exist then a runtime exception will be raised
when the Module Management Tool tries to install the AMP.
If a mapping provided in the file-mapping.properties file overrides one of the default
mappings, then this will take precedence when the installation into the WAR takes place. If a
mapping is declared that has a folder as the source, then the folder will be recursively copied into
the WAR file.
Below is an example of a file-mapping.properties file:
# Custom AMP to WAR location mappings
#
# The following property can be used to include the standard set of mappings.
# The contents of this file will override any defaults. The default is
# 'true', i.e. the default mappings will be augmented or modified by values in
# this file.
#
include.default=false
#
# Custom mappings.
set.
#
/WEB-INF=/WEB-INF
/web=/
Packaging Extensions
<property name="bootstrapViews">
<list>
<props>
<prop key="path">
/${spaces.company_home.childname}
</prop>
<prop key="location">
alfresco/module/myModule-123/myACP.acp
</prop>
</props>
</list>
</property>
</bean>
The XML and/or ACP files to be imported and the destination location in the repository where the
data should be imported are supplied as a list to the bootstrapViews property.
Packaging Extensions
Install
usage: install <AMPFileLocation> <WARFileLocation> [options]
valid options:
-verbose
: enable verbose output
-directory : indicates that the amp file location specified is a
directory.
All amp files found in the directory and its sub directories
are installed.
-force
: forces installation of AMP regardless of currently installed
module version
-preview
: previews installation of AMP without modifying WAR file
-nobackup : indicates that no backup should be made of the WAR
Installs the files found in the AMP file into the Alfresco WAR, updates if an older version
is already installed. This is done using the standard mapping, unless a custom mapping is
provided.
If the module represented by the AMP is already installed and the installing AMP is of a higher
release version, then the files relating to the older version will be removed from the WAR and
replaced with the newer files.
It is the responsibility of the module developer to provide the appropriate Module components
to bootstrap or patch any data as required when updated WAR is run.
If the installing module version is less than or equal to the version already installed in the WAR
then installation will be aborted unless the -force option is specified. In this case the installing
AMP will always replace the currently installed version. This option is especially useful when
developing an AMP.
Before an AMP is installed into a WAR a copy of the original WAR is taken an placed in the
same directory. Specifying the -nobackup option prevent this from occurring.
It is considered good practice to do a -preview install prior to doing the install for real. This
reports the modifications that will occur on the WAR without making any physical changes.
The changes that are of most importance to note are those that are going to update existing
files.
As a general rule it is considered bad practice to overwrite an existing file in an AMP, however
it is sometimes necessary. In these situations when the AMP is installed a backup of the
updated file is taken and stored in the WAR.
When an update of the module occurs and the old files are removed, this backup will be
restored prior to the installation of the new files. Problems may occur if multiple installed
modules modify the same existing file. In these cases a manual restore may be necessary if
recovery to an existing state is required.
list
usage: list <warFile>
Lists the details about all the modules currently installed in the WAR file specified. The output
is directed to the console.