Follow Us on Twitter

The Struggles of Personalization in ADF

by Mike Heeren on July 10, 2018 · 26 comments

A Mike Heeren & Richard Olrichs co-production

ADF comes with the out-of-the-box features of personalization. This means that whenever you configure personalisation, users can persist changes they make to the application across sessions and personalize their experience with the application. We have seen that this feature can also confuse some of our users, so it is not always wise to use this. It depends on the use case you have. However, when recently implementing personalization on an ADF 12.2.1.2 application, we had a couple of issues regarding persisting these personalizations to the MDS.

We felt that most of the blogs we came across while implementing these features, share the joyful out-of-the-box configuration. Just select some of the checkboxes in the properties and you are done, ready to enjoy your beer and have your designers and product owners cheer for you.
Sometimes however, real life applications at customers do not match the out-of-the-box configuration and things can become a little bit more tricky than you might expect.

Let’s start at the top, and go through some of the steps you will always need within your application for personalization to work. You need to have authentication and authorization set up. If you also want to follow our struggles, we have posted a sample application at the end of this blog to show both the problems as well as the solutions.

Getting started with customizations

The basic configuration of customization in ADF is pretty simple. We start with a simple ADF application (with authentication already configured), and select the Project Properties > ADF View. Here we check Enable user customization and Across sessions using MDS, as seen below:

When enabling user customizations, the following files are edited:

  • In the adf-config.xml file the following lines are added:
<adf-faces-config xmlns="http://xmlns.oracle.com/adf/faces/config">
	<persistent-change-manager>
		<persistent-change-manager-class>oracle.adf.view.rich.change.MDSDocumentChangeManager</persistent-change-manager-class>
	</persistent-change-manager>
</adf-faces-config>
  • In the web.xml file the javax.faces.FACELETS_RESOURCE_RESOLVER context-param is changed from oracle.adfinternal.view.faces.facelets.rich.AdfFaceletsResourceResolver to oracle.adfinternal.view.faces.facelets.rich.MDSFaceletsResourceResolver, and the following blocks are added:
<servlet>
	<servlet-name>adflibResources</servlet-name>
	<servlet-class>oracle.adf.library.webapp.ResourceServlet</servlet-class>
</servlet>
...
<servlet-mapping>
	<servlet-name>adflibResources</servlet-name>
	<url-pattern>/adflib/*</url-pattern>
</servlet-mapping>
...
<filter>
	<filter-name>ADFLibraryFilter</filter-name>
	<filter-class>oracle.adf.library.webapp.LibraryFilter</filter-class>
</filter>
...
<filter-mapping>
	<filter-name>ADFLibraryFilter</filter-name>
	<url-pattern>/*</url-pattern>
	<dispatcher>FORWARD</dispatcher>
	<dispatcher>REQUEST</dispatcher>
</filter-mapping>
...
<context-param>
	<param-name>oracle.adf.jsp.provider.0</param-name>
	<param-value>oracle.mds.jsp.MDSJSPProviderHelper</param-value>
</context-param>
<context-param>
	<param-name>org.apache.myfaces.trinidad.CHANGE_PERSISTENCE</param-name>
	<param-value>oracle.adf.view.rich.change.FilteredPersistenceChangeManager</param-value>
</context-param>
  • Finally, the following block will be added to the project .jpr file:
<hash n="oracle.adfdtinternal.view.rich.setting.ADFViewSettings">
	<value n="ENABLE_ADF_WEBAPP_LIB_SUPPORT" v="true"/>
	<value n="KEY_ENABLE_USER_CUSTOMIZATIONS" v="2"/>
</hash>

After this we need to configure the adf-config.xml file and add the oracle.adf.share.config.UserCC Customization Class on the MDS tab. This is a default customization class that is shipped with ADF. You can also write your own, but that is more of a use case for customization than it is for personalization. In our case we’ll just configure the application using the UserCC:

Now you need to configure the components that you want the end user to be able to personalize. This can be done in the View tab. Select the ADF Faces Components Tag Library, because we want to personalize the table and column components, these are default component from ADF Faces. By default all attributes will be persisted, you can uncheck them if you do not wish to persist these:

After the above steps are configured, you can give a fancy demo on your demo application, and everybody is happy. However, in our production application, there were still a few issues to tackle, we struggled with some of these.    

Struggle 1: Task flows from libraries combined with a file based MDS on a Windows machine.

Our application was not a simple MVC application, but like many application out there, we used ADF libraries to include taskflows from library projects and included them in a bigger main application. When we turned on personalisation on components from task flows which come from libraries instead of directly from the application, they were not correctly persisted to the file based MDS on Windows machines.

When running the application via JDeveloper (in our example application by right clicking default.jsf in the ViewController project, and selecting Run), we see that the task flow that comes directly from the ViewController project (the table on the left of the screen), behaves as expected. However, when personalizing the table from the task flow that comes from the imported library (so from the ViewControllerLibrary project), we see the following warning in the log files.

<Apr 5, 2018, 10:44:26,157 AM CEST> <Warning> <oracle.adf.view.rich.change.MDSDocumentChangeManager> <BEA-000000> <Attempt to persist a DocumentChange failed : MDS-02401: The operation ModifyAttribute on the column node is not allowed.    

oracle.mds.exception.MDSRuntimeException: java.net.MalformedURLException: no !/ in spec    

java.lang.NullPointerException: no !/ in spec>

We can verify that the preferences were persisted to the MDS for the left table, but not for the right table, by opening the application (with the same user) in another browser:

Unfortunately, this issue occurs when using task flows from libraries in combination with using a file based MDS on a Windows machine. Weblogic is not able to create a file path, which contains !/ (which is used to indicate that the resource is part of a library).

Luckily, in our case all other DTAP environments don’t use a file based MDS, but a database MDS. In the database MDS, we don’t have the file path issues, so there we won’t see this issue. So on all environments except the (local) Integrated WLS, we don’t see these warning logs, and we will see both table personalizations being persisted. It might take you some time to realise this, if you do not want to deploy to Dev or Test without having the Personalization working on your local machine.

Struggle 2: Deploying the application as EAR instead of via JDeveloper

Deploying both from JDeveloper as well as creating an EAR file seems to work. However, there was still some struggle there as well. The personalisation was working when deploying via JDeveloper, but when we would build in EAR from the application via JDeveloper, and deploy it manually to the Integrated WLS via the console, none of the settings were persisted to the MDS, and we saw the following warning in the log files:

<Apr 5, 2018, 2:58:12,545 PM CEST> <Warning> <oracle.adf.view.rich.change.DocumentUtils> <BEA-000000> <ADFv: Trouble getting the mutable document from MDS. MDS-01273: The operation on the resource /WEB-INF/view-from-library.jsff.xml failed because source metadata store mapped to the namespace / DEFAULT is read only..>

By opening the application in different browsers again, we can also confirm that neither of customizations the tables is persisted in the MDS now:

When Personalization across sessions with the MDS is configured, JDeveloper always creates a metadata store usages in the adf-config file at deployment time. This configuration is named MAR_TargetRepos.

It does not matter that we have configured a different metadata store usages within the adf-config. If the MAR_TargetRepos is not present while creating the EAR file, it will be added to the adf-config file. The only solution that we found to this, is by naming our metadata store usages to match the expected default, then it will not override or add anything.

We added the following snippet to the adf-config/adf-mds-config/mds-config tag in adf-config.xml:

<persistence-config>
	<metadata-namespaces>
		<namespace path="/persdef" metadata-store-usage="MAR_TargetRepos"/>
	</metadata-namespaces>
	<metadata-store-usages>
		<metadata-store-usage id="MAR_TargetRepos" default-cust-store="true">
			<metadata-store class-name="oracle.mds.persistence.stores.file.FileMetadataStore">
				<property name="metadata-path" value="${TEMP}"/>
				<property name="partition-name" value="PersDef"/>
			</metadata-store>
		</metadata-store-usage>
	</metadata-store-usages>
</persistence-config>

Note that we did not set deploy-target to true, because if we do this, our custom MAR_TargetRepos will be overridden with the default implementation again, when building the EAR file. This default implementation does not contain the FileMetadataStore implementation:

<metadata-store-usage id="MAR_TargetRepos" deploy-target="true" default-cust-store="true"/>

Because we want to override the default MAR_TargetRepos, so the personalization will also work when deploying the EAR instead of deploying via JDeveloper, we do not set the deploy-target to true so the default (false) will be used. In this case the FileMetadataStore configuration will be preserved in the EAR file.

If we deploy the new EAR file, we see the same behaviour as we did when deploying it via JDeveloper. Also, when we use the personalization functions on the screen, we will see files being created within the PersDef folder within the %TEMP% environment variable.

This FileMetadataStore configuration is changed (back) to a DBMetaDataStore configuration by an ANT build script, before deploying on the different DTAP environments instead of the Integrated WLS environment. An example of such ANT script can be found below.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="antlib:org.apache.tools.ant">
	<property environment="env"/>
	<property name="ear.location" location="/path/to/your/deployment.ear"/>
	<!-- Classpath -->
	<path id="wlst.classpath">
		<fileset file="${env.WLS_HOME}/modules/features/wlst.wls.classpath.jar"/>
	</path>
	<!-- WLST task definition -->
	<taskdef name="wlst" classname="weblogic.ant.taskdefs.management.WLSTTask" classpathref="wlst.classpath"/>
	<!-- Target to replace persDef repository -->
	<target name="replace-persdef-repo">
		<echo message="Updating MDS config for [${ear.location}]..."/>
		<wlst failonerror="true" debug="false" classpathref="wlst.classpath">
			<arg file="${ear.location}"/>
			<script>
			archive = getMDSArchiveConfig(fromLocation = sys.argv[0])
			archive.setAppSharedMetadataRepository(
				namespace='/persdef',
				repository='mds-owsm',
				partition='PersDef',
				type='DB',
				jndi='jdbc/mds/owsm'
			)
			archive.save()
			</script>
		</wlst>
		<echo message="Updating MDS config done!"/>
	</target>
</project>

Be sure that the WLS_HOME environment variable is set in the system environment variables, and the ear.location property is replaced in the ANT file when you want to use the above example.

Struggle 3: Suddenly our application does persisting during the session.

We have configured the adf-config to persist only certain components and attributes across the session. This works very nice and clear, however, suddenly all the other components also persist their state, just not across the session, but during the session.

It is possible that this is not what you want, it certainly was not what we expected or had in mind for our application, but there is nothing much we can do about it. It would have made more sense to turn this off for all the components and only persist those that were configured to be persisted.

Luckily the ADF components have an attribute persist and dontPersist on them. When reading the documentation on these attributes, it sounds exactly like what we need for our application! Before adding the dontPersist attribute to the hundreds of components we have in the application, we decide to test it on a couple. What we found out was very unpleasing, basically these attributes could be used for documentation purpose or for fun, but it certainly did nothing concerning persistence.

We decided to create our own custom class to adjust the framework and get this working. To achieve this, the context-param org.apache.myfaces.trinidad.CHANGE_PERSISTENCE can be adjusted to a custom class.

At first we tried to extend the oracle.adf.view.rich.change.FilteredPersistenceChangeManager class, which is the class ADF uses by default. However, unfortunately this class is declared final. We decided to create a class that extends the org.apache.myfaces.trinidad.change.SessionChangeManager class, and use the FilteredPersistenceChangeManager as an instance variable. This may not be the prettiest solution, but it serves our purpose:

package nl.whitehorses.personalization.changemanager;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

import oracle.adf.view.rich.change.FilteredPersistenceChangeManager;

import org.apache.myfaces.trinidad.change.AttributeComponentChange;
import org.apache.myfaces.trinidad.change.ChangeManager;
import org.apache.myfaces.trinidad.change.ComponentChange;
import org.apache.myfaces.trinidad.change.DocumentChange;
import org.apache.myfaces.trinidad.change.SessionChangeManager;

public class CustomChangeManager extends SessionChangeManager {

	private final FilteredPersistenceChangeManager fpcmInstance = new FilteredPersistenceChangeManager();

	@Override
	public void addComponentChange(final FacesContext context, final UIComponent component, final ComponentChange change) {
		if (component == null || component.getAttributes() == null) {
			return;
		}
		final String[] persistArray = (String[]) component.getAttributes().get("persist");
		if (persistArray == null) {
			return;
		}
		for (final String persistVal : persistArray) {
			if (persistVal != null && change instanceof AttributeComponentChange && ("ALL".equals(persistVal) || ((AttributeComponentChange) change).getAttributeName().equals(persistVal))) {
				fpcmInstance.addComponentChange(context, component, change);
			}
		}
	}

	@Override
	public void addDocumentChange(final FacesContext context, final UIComponent component, final DocumentChange change) {
		fpcmInstance.addDocumentChange(context, component, change);
	}

	@Override
	public boolean supportsDocumentPersistence(final FacesContext context) {
		return fpcmInstance.supportsDocumentPersistence(context);
	}

	@Override
	public ChangeManager.ChangeOutcome addDocumentChangeWithOutcome(final FacesContext context, final UIComponent component, final DocumentChange change) {
		return fpcmInstance.addDocumentChangeWithOutcome(context, component, change);
	}

}

As you can see, the logic for the persist attribute has been implemented in the addComponentChange method. The addComponentChange method from the FilteredPersistenceChangeManager instance, will only be called when the component contains the persist attribute which is set to ALL. Besides the addComponentChange method, all other (public) methods from the FilteredPersistenceChangeManager have been implemented to use the instance variable as well.

Conclusion

After some struggles and adjustments to the implementation and configuration of the application, we got personalization to work in our real world application used by customers. We overcame the struggles, but this was not as easy as the blogs on the internet made us believe beforehand. We hope that sharing this experience, might save you for some of the troubles we had.

To give you some more insight in the code and the struggles, we have created a (simple) demo application AdfPersonalization, to reproduce the issues we had, and which we used to solve them.

This application consists of the default Model and ViewController projects. Also, we added a ViewControllerLibrary project. This ViewControllerLibrary project is imported as a library by the ViewController project.

The AdfPersonalization application can be deployed to Weblogic in multiple ways:

  • Using the ‘Run’ button in JDeveloper.
  • Using Application > Deploy > … to IntegratedWebLogicServer in JDeveloper. This can be done to verify that struggle 2 is no longer an issue.
  • Using Application > Deploy > … to EAR, followed by running the replace-persdef-repo ANT target from the build.xml file. Afterwards the EAR can be deployed to a Weblogic servers, which is capable using a database based MDS. This can be done to verify struggle 1 is no longer an issue.

The source of this project can be downloaded via AdfPersonalization.zip.

Resources

The Struggles of Personalization in ADF, 5.0 out of 5 based on 1 rating
Ratings:
VN:D [1.9.22_1171]
Rating: 5.0/5 (1 vote cast)

{ 26 comments… read them below or add one }

abu Kahar November 12, 2018 at 8:34 pm

It is possible that this is not what you want, it certainly was not what we expected

Reply

Legitimate Essay Writer Services November 26, 2019 at 9:32 am

If handling your academic obligations seems hard consider Essay Writing Help Services from professionals for your Custom Essay Writing Services and Legitimate Custom Writing Services.

Reply

Apple Error 3194 January 8, 2020 at 10:53 pm

I read this article and knew the new things. this is very interesting seriously. I want to know more about this topic from this website. I would like to share it with all my friends and hope they will like it too.

Reply

Jothibasu February 4, 2020 at 9:45 am

Thanks for Sharing this impressive and Valuable Article. It has so much information in it. Thanks for providing such excellent information to us. The post will be helpful to everyone.

Thanks and Regards,
Jothibasu
FilingPoint.Com Team

Reply

Mens Red Leather Jacket February 26, 2020 at 12:28 pm

the blog is very informative who interested in coding many people want to read these type of blogs for taking extra information.

Reply

Bobmovies February 27, 2020 at 11:32 am

Awesome identify to visit to experience the natural life.

Reply

made in abyss season 2 February 27, 2020 at 11:36 am

Thank you to communion more mileposts in the time to come.

Reply

Black Widow Bomber Jacket March 4, 2020 at 8:14 am

The blog is very helpful for me because I am a designer and always search these types of posts for taking information about designing and development.

Reply

Cyberpunk 2077 Jacket March 6, 2020 at 7:43 am

The post is full of information about the coding I am a web designer at the best jacket company and also search these types of blogs for taking information.

Reply

Cyberpunk 2077 Jacket March 9, 2020 at 9:42 am

The post is very helpful for who those want to read this type of post many people want to read this type of post I am also part of them and after the job, my first choice is reading these types of posts.

Reply

Mac Error Code 50 April 9, 2020 at 8:56 pm

I have been through the whole content of this article which is very informative and knowledgeable stuff, I would like to visit again.
Mac Error Code 50

Reply

Business2community April 23, 2020 at 9:49 am

Business 2 Community established itself as one of the most popular commercial enterprise blogs on the web.
Business2Community targets to provide a balanced view of the modern commercial enterprise landscape based on enterprise information and trends, as well as the real-existence experiences.

Reply

EssaysMate.com April 23, 2020 at 10:35 pm

I don’t know any other writing company besides Essaysmate.com that would work so hard on polishing papers.

Reply

Lesa Cote June 20, 2020 at 1:45 pm

Thank you for the amazing blog on personalization in adf.

Reply

Law Research Writing Services June 23, 2020 at 2:50 pm

Online law research paper help services are very common nowadays since there are very many students seeking Law Research Writing Services and law essay writing services.

Reply

Psychology Research Writing Services July 1, 2020 at 2:52 pm

Psychology coursework writing services are essential and they have become very popular for those seeking psychology research writing help services since most of them seek Psychology Research Writing Services.

Reply

Health and Safety Writing Services July 8, 2020 at 7:39 am

Professional Health and Safety Writing Services is very popular for students in search of healthcare and safety research paper writing services and health and safety essay writing services.

Reply

The Last Of Us Part 2 Ellie Backpack July 17, 2020 at 11:30 am

This is the first time that I visit here. I found so many exciting matters in this particular blog, One thing I would like to request you that pls keep posting such type of informative blog.

Reply

The Last Of Us Part 2 Ellie Backpack July 17, 2020 at 11:31 am

I appreciate this blog your blog is vert help full for me i really enjoyed this stuff dude.

Reply

sms bomber July 18, 2020 at 1:56 pm

good post thanks for share article

Reply

Imaster July 20, 2020 at 1:15 pm

Thanks for sharing the valuable article.

Reply

Michael Miller July 25, 2020 at 10:49 am

Like science fiction, diverse worlds of the future?.. like gloomy cyberpunk, neo-modern utopia, then take a look at this blog – SCI-fi arts. There you will find many new beautiful science fiction artworks from the most famous authors.

Reply

USTVNow.com/Roku July 27, 2020 at 4:23 pm

How to Activate the USTVNow on Roku?
On the US TV Now channel on the Roku device, on the Roku channel hub you need to get the channel app. After installing the channel app you need to complete the activation steps by providing the TV provider user id and the password. After this, you can start to stream the twenty-eight channels on the US TV Now streaming service.
For more update on the USTVNow.com/Roku, you can get guidance from our expert team by dialing the Toll free Number +1-805-436-1200

Reply

thetechtrending August 2, 2020 at 1:49 pm

I came here for the first time and I am glad that I visit your page. Thank you for sharing your knowledge with.

Reply

smconsultant August 7, 2020 at 11:17 am

the blog is very informative who is interested in coding many people want to read these type of blogs for taking extra information.

Reply

Musicdel September 23, 2020 at 2:07 pm

many thanks a good deal this amazing site can be conventional in addition to relaxed.

Reply

Leave a Comment

 

Previous post:

About Whitehorses
Company profile
Services
Technology

Whitehorses website

Home page
Whitebooks
Jobs

Follow us
Blog post RSS
Comment RSS
Twitter