Sunday, January 1, 2012

ADF: MDS power user layer and "How to control MDS customizations at runtime"

Tested with: JDeveloper/ADF 11.1.1.2
Happy new year 2012 to all of my readers! First post this year is a follow up on "ADF: Define default UI for MDS user customization".
http://multikoop.blogspot.com/2010/01/adf-11g-define-default-ui-for-mds-user.html
Introduction
In that previous post I introduced a feature to be able to define a default UI layout at runtime by a specific user. That particular UI customization acts as the initial default for any other logged in users. That is great and simplifies developers life as the default layouting (which columns to display, in which order arrange, splitter posisions and so on) can be done by a business user.
Although those great capabilities there is still a lack of control regarding applied MDS customizations of specific users. In User Support situations it is often helpful to reset the user customizations to factory defaults in order to have the "same view on the UI".
So here is our solution how to be able to reset MDS Customizations at runtime for ALL users, or for specific users with regards keeping the power user defaults.
First recap that we have defined a special layer "DefaultUserCC"
0000@2752_2752-41266a4f4bf6e5d5
With this Customization class configured it is now possible to login as the user "layout" and define the default UI for all other users. It just means that the DefaultUserCC customizations are applied before the UserCC. (So the order of the CCs in the above screen is important!)
(You can test the behaviour with attached sample application based on ADF11.1.1.2. See download link at the end of this post)
Sample application
0001@2752_2752-41266a4f51401846
Two test users bob and alice and the power layout user "layout".
1. Login as layout
2. Remove all rows except Firstname and Lastname, Email.
0005@2752_0004@2752_2752-41266a4f52fc9630
3. Logout layout
4. Login as bob
0006@2752_0005@2752_0004@2752_2752-41266a4f52fc9630
[Notice that bob has inherited the ui settings from user layout]
5. Now Remove the email column
0007@2752_0002@2752_2752-41266a4f523ef500
6. Logout bob
[Imagine: You get a support call from user bob. He says he is not able to lookup the email adress. To simplify further investigations you should be able to get his user layout back to default.]

7. Login as layout (or in general as an MDS Admin)
8. Enter username: bob
0008@2752_2752-41266a4f5894ab6d
9.Click "Reset to layout user defaults"
As feedback you will get
0009@2752_2752-41266a4f58efb11d
[Imagine: In Support case you would now advise the user to login again. From that moment you can be sure user bob has same ui layout as defined by the user layout.]
Wow. This is a really powerful feature I think. Is there any other web app framework which can do this? ;)
How we do it: Delete MDS documents at runtime
To implement the mentioned features there is not really much to do (beyond the basic MDS configuration which I will not describe here. See references for details.)
1. Implement and configure custom Customization Class (Here: DefaultUserCC.java)
2. Use Oracle MDS API to delete MDS documents. To find how this actually can be done I followed to approaches. a) Reverse engineeer from deleteMetadata WLST command. b) Google.
Option a guided me through
oracle.mds.internal.lcm.mbean.AppRuntimeMBeanImpl
   public Map deleteMetadata(String[] docs, String[] custNames, boolean excludeAllCust, boolean excludeBaseDocs, boolean excludeExtendedMD, boolean cancelOnException)

oracle.mds.internal.lcm.MDSLCMManager
   Map deleteMetadata(String[] docs, String[] custNames, boolean excludeAllCust, boolean excludeBaseDocs, boolean excludeExtendedMD, boolean cancelOnException, Locale locale)
      ...
      doDelete(...)

  List doDelete(MetadataStore store, String[] docs, boolean excludeAllCust, boolean excludeBaseDocs, boolean excludeExtendedMD, boolean cancelOnException, TypeConfig typeConfig, CustConfig custConfig, List custLayerFilterList, DependencyConfig dConfig)
       ...
       TransferUnitList transferUnitList = TransferUnitList.create(mdsInstance, docsList, transferOptions, !excludeBaseDocs, true);
       List resultList = MDSTransfer.getInstance(mdsInstance).delete(transferUnitList, cancelOnException);
       ...
oracle.mds.internal.transfer.InternalMDSTransfer
    delete ...
    execute....

Option b guided me to a some really great resources. 1. http://oraclemw.blogspot.com/2011/04/working-with-oracle-mds-repository.html 2. http://idmclub.wikidot.com/metadata-store where I found some basic information on the usage of the MDS API.
Finally I came up with a MDSUtils.java and MDSAdminBean.java which does the needed work.
Here are the most important code snippets
MDSUtils.java
public static void deleteDocuments(final MDSInstance mdsInstance,
final List listOfFiles) throws MDSException {

MDSTransfer transferInstance = MDSTransfer.getInstance(mdsInstance);

TransferUnitList transferUnitList =
TransferUnitList.create(mdsInstance, listOfFiles, null, true,
true);
transferInstance.delete(transferUnitList, false /* cancelOnException */);
transferInstance.release(mdsInstance);
}

public static List queryAllFiles() {
final MDSSession mdsSession = MDSUtils.getCurrentMDSSession();

//String packagePath, String documentName, boolean recursive,
final NameQueryImpl nameQuery =
new NameQueryImpl(mdsSession, ConditionFactory.createNameCondition("/",
"%",
true));
final Iterator result = nameQuery.execute();
final ArrayList listOfFiles = new ArrayList();
while (result.hasNext()) {
final QueryResult qr = result.next();
final String absoluteName = qr.getAbsoluteName();
listOfFiles.add(absoluteName);
}

return listOfFiles;
}


   
MDSAdminBean.java


Nothing special here. Just use the "hard worked out";-) MDSUtils in action listern methods, e.g.


List listOfFiles =
MDSUtils.queryFilesMatch("/mdssys/cust/user/" + username);
try {
System.out.println("MDS Admin: Deleting " + listOfFiles);
MDSUtils.deleteDocuments(listOfFiles);
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_INFO,
null,
"Deleted the following MDS-Documents for user " +
getUsername() +
": " +
listOfFiles));
} catch (MDSException e) {
e.printStackTrace();
}


Download the application and checkout the customization possibilities with bob, alice and layout!

http://www.box.com/s/kvjc43my8mbvoyi9lh3v (JDev 11112 Workspace)


References


User Customization with and without MDS

http://blogs.oracle.com/raghuyadav/entry/user_customization_with_and_wi


Programmatic access to Oracle Metadata Store (MDS)

http://idmclub.wikidot.com/metadata-store


Working with Oracle MDS Repository (MetaData Services)

http://oraclemw.blogspot.com/2011/04/working-with-oracle-mds-repository.html


ADF: Define default UI for MDS user customization

http://multikoop.blogspot.com/2010/01/adf-11g-define-default-ui-for-mds-user.html

4 comments:

  1. hello,
    I follow this steps but not working.
    I have an application authorize users from database (not from jazn-data file) is there any change I should make?
    Thanks

    ReplyDelete
  2. Hello Sameh,

    well, I do not think that it depends on the authorization provider. Have you configured MDS for personalization (http://biemond.blogspot.com/2008/07/customize-and-personalize-your-jsf.html)? Is default MDS personalization working?

    Do you see any exceptions?

    Andreas.

    ReplyDelete
  3. Is there a way to retrieve and restore the MDS metadata for a particular page and persist it myself. I want to be able to save multiple instances that correspond to each saved search query so that the users column settings (visible, width, sort order etc) settings are restored along with the saved query. MDS seems to only allow saving a single set of settings per user.

    ReplyDelete
    Replies
    1. Hi,

      sorry for that late response. I don't think it is possible out of the box as the components attribute changes are saved per user/view id.

      Maybe a custom PersistenceChangeManager could do the trick.

      Andreas

      Delete