Saturday, May 17, 2014

ADF Mobile Dependent (Cacading) LOV

This blog is to show how we can create depending (or cascading) LOVs using ADF Mobile

Steps are
1. Create client classes to store data that needs to appear in LOVs
2. Create datacontrol class to hold LOV data in a list (array) and provide mechanism to refresh LOV data
3. Create select-one-choice on page using datacontrol
4. Set valuechange listner of LOV1 to refresh dataprovider of LOV2.



In this example I will create a Country/State LOVs

Country LOV will have India, US values. and State LOV will show states of India or US.

==============================

Step 1. Create client classes to store data that needs to appear in LOVs

To represent country and states we need two classes Country, State

Country.java 
package com.san.mobile.client;

public class Country {
    private String countryName;
  
    public Country( String countryName) {
        super();
        this.countryName = countryName;
    }


    public void setCountryName(String countryName) {
        this.countryName = countryName;
    }

    public String getCountryName() {
        return countryName;
    }

}




State.java
package com.san.mobile.client;

public class State {
    private String stateName;
    public State(String stateName) {
        super();
        this.stateName = stateName;
    }

    public void setStateName(String stateName) {
        this.stateName = stateName;
    }

    public String getStateName() {
        return stateName;
    }
}




========================================================

Step 2. Create datacontrol class to hold LOV data in a list (array) and provide mechanism to refresh LOV data 

package com.san.mobile.dc;

import com.san.mobile.client.Country;
import com.san.mobile.client.State;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import oracle.adfmf.framework.api.AdfmfJavaUtilities;
import oracle.adfmf.java.beans.ProviderChangeListener;
import oracle.adfmf.java.beans.ProviderChangeSupport;

public class DependentLOVDC {
    private List countries = new ArrayList();
    private List states = new ArrayList();
  
    protected transient ProviderChangeSupport providerChangeSupport = new ProviderChangeSupport(this);
    public DependentLOVDC() {
        super();
      
        //NOTE: You can call a webservice here to get actual country data.
      
        countries.add(new Country("India"));
        countries.add(new Country("US"));
    }
  
    public void addProviderChangeListener(ProviderChangeListener l) { 
         providerChangeSupport.addProviderChangeListener(l);
       }
       public void removeProviderChangeListener(ProviderChangeListener l) { 
         providerChangeSupport.removeProviderChangeListener(l);
       }
     
     
    public Country[] getCountries(){      
        return (Country[])countries.toArray(new Country[countries.size()]);      
    }
  
  public State[] getStates(){
       
        states = new ArrayList();
            Map pageFlow = (Map)AdfmfJavaUtilities.evaluateELExpression("#{pageFlowScope}");
            String countryName = (String)pageFlow.get("pCountryName");
                      
           
            if(countryName == null || "India".equals(countryName)){
                //NOTE: You can call a webservice here to get actual country data.
                states.add(new State("Delhi"));
                states.add(new State("Karnataka"));
            }
            else  if("US".equals(countryName)){
                //NOTE: You can call a webservice here to get actual country data.
               states.add(new State("California"));
                states.add(new State("Georgia"));
            }
           
           
            return (State[])states.toArray(new State[states.size()]);
           
        }
  
  
        public void refresh(){
            providerChangeSupport.fireProviderRefresh("states");
        }
      
    }


In above class I mainly have two getters, which returns list of countries and states. State getter is returning based on pageFlow variable pCountryName. It means if on selection of country we can set pCountryName in pageFlow. Which is easy.
I have also added provider change support on this class by adding 

protected transient ProviderChangeSupport providerChangeSupport = new ProviderChangeSupport(this); 

and 


  public void addProviderChangeListener(ProviderChangeListener l) { 
         providerChangeSupport.addProviderChangeListener(l);
       }
       public void removeProviderChangeListener(ProviderChangeListener l) { 
         providerChangeSupport.removeProviderChangeListener(l);
       }  



There are two big problems we need to resolve
1. How would we ask framework to execute getStates once we have selected a project.

2. How would we ask framework to refresh UI LOV if getStates returns new set of data. 

Both of this can be done using providerChangeSuuport. You just need to add a refresh method on this class as 


public void refresh(){
            providerChangeSupport.fireProviderRefresh("states");
        }


Expose this class as a datacontrol. For this right click on
DependentLOVDC and select 'Create data control'.
You will wee following structure in Data Controls. 



 











===================================================================
Step 3. Create select-one-choice on page using datacontrol

 Drag and drop countries/states on your page and create select-one-choice as shown below.




















Select OK on next popup.

You will get following code in your amx

    <amx:selectOneChoice value="#{bindings.countries.inputValue}" label="#{bindings.countries.label}" id="soc1">
      <amx:selectItems value="#{bindings.countries.items}" id="si1"/>
    </amx:selectOneChoice>
    <amx:selectOneChoice value="#{bindings.states.inputValue}" label="#{bindings.states.label}" id="soc2">
      <amx:selectItems value="#{bindings.states.items}" id="si2"/>
    </amx:selectOneChoice>


AS we know in ADF when you select a choice list value and try to use valueChangeEvent.getNewValue(), it returns index but not the actual value. To avoid using valueChangeEvent.getNewValue() we can create an attribute binding in pageDef as 



================================================================
4. Set valuechange listner of LOV1 to refresh dataprovider of LOV2.


Create method action binding for refresh method of DependentLOVDC in pageDef.























Create a bean and register it as usual. Write valuechangelistener on country LOV as

    public void handleCountrySelection(ValueChangeEvent valueChangeEvent) {
        // Add event code here...
       
        Map pageFlow = (Map)AdfmfJavaUtilities.evaluateELExpression("#{pageFlowScope}");
        String countryName = (String)valueChangeEvent.getNewValue();
       
        pageFlow.put("pCountryName", countryName);
       
        MethodExpression me = AdfmfJavaUtilities.getMethodExpression("#{bindings.refresh.execute}", Object.class, new Class[] {});
        me.invoke(AdfmfJavaUtilities.getAdfELContext(), new Object[]{});
    }


======================================================

Thats it.

Code flow is like

1. System create both lovs using datacontrol data provider
2. On selection of country, valuechange listener runs. Listener executes a bean method, which sets pageFlow variable pCountryName and calls refresh method of binding. Binding refresh points to refresh method of datacontrol.
3. Refresh method asks providerChangeSupport to refresh "states" dataprovider, which is nothing but getStates method of DependentLOVDC. Once data is changed, providerchangesupport will refresh on UI components showing data from that dataprovider.

Disclaimer: Any views or opinions presented in this blog are solely those of the author and do not necessarily represent those of the company.

Sunday, March 2, 2014

Adding Custom search results in WebCenter Search

WebCenter provides a Search task-flow, which can be used to search in all or specific webcenter services. Common services supported by WebCenter are Announcement, Discussion, Events, Related Resources, Documents, Page, People Connection, Tags, Blogs etc. 

Webcenter global serach can by default perform search in above mentioned services.


Our aim here is to perform webcenter search in other places as well. For example we can have data in our custom table and we want that also to appear in webcenter search. For this example I will try to show data from Employee table in webcenter search.

 Here are the steps
1. Have employee table, populate data in that table.
2. Have ADF Model (EO/VO/AM) which can query Employee table.
3. Add bindings to a page
4. Register your service
5. Implement QueryManager and QueryExecuter classes.
6. Create a page EmployeeDetailPg.jspx which should show the detail of an employee.
7. Use webcenter provided Search task-flow to perform global search

============================================================
1. Have Employee table and populate data in that table: For this demo I am using HR.EMPLOYEES table.

2. Have ADF Model (EO/VO/AM) which can query employee table:
 
   a. VO should have a view criteria as 'SearchEmployee', which can search employee data based on first_name, middle_name, last_name, email, phone_number as




   b. Add this VO in AM as View Instance.


  c. Create EmployeeResourceRow class which represents every employee row in search.

    package com.san.model.search.employee;

import java.io.Serializable;

public class EmployeeResourceRow implements Serializable{
    private static final long serialVersionUID = 1090909090909L;
    private String resourceId;
    private String title;
    private String creator;
    private String lastModified;
    private int size;
    /**Constructor to create an instance of ResourceRow.
     * @param resourceId
     * @param title
     * @param creator
     * @param lastModified
     * @param size
     */
    public EmployeeResourceRow(String resourceId, String title,    String creator, String lastModified,    int size){
        this.resourceId = resourceId;
        this.title = title;
        this.creator = creator;
        this.lastModified = lastModified;
        this.size = size;
    }

    public void setResourceId(String resourceId) {
        this.resourceId = resourceId;
    }

    public String getResourceId() {
        return resourceId;
    }


   .... other getters/sertters

}

  d. Generate AMImpl class and add a method getSearchResult, which should query employeeVO and create EmployeeResourceRow based on each vo row.


    public List getSearchResult(String keyword){
       
      
        ViewObjectImpl vo = this.getEmployeeVO();
        if(keyword != null){
            vo.appendViewCriteria(vo.getViewCriteria("SearchEmployee"));
            vo.setNamedWhereClauseParam("bind_searchKeyword", keyword);
        }
        vo.executeQuery();

        List searchResultList = new ArrayList();
        while (vo.hasNext()) {
            Row resRow = vo.next();
            String resourceId = (String)resRow.getAttribute("EmployeeId").toString();
            String title = (String)resRow.getAttribute("FirstName");
            searchResultList.add(new EmployeeResourceRow(resourceId, title, null, null, 0));
        }
        return searchResultList;
    }


3. Add bindings (Drop AM method getSearchResult on any page's  page-definition file): We are going to use this page def files binding-container to execute AM method. For that purpose you can create a test page and add getSearchResult method on its Page-Def file. Page-Def will look something like

 <bindings>
    <methodAction id="getSearchResult"
                  InstanceName="HRAppModuleDataControl.dataProvider"
                  DataControl="HRAppModuleDataControl"
                  RequiresUpdateModel="true" Action="invokeMethod"
                  MethodName="getSearchResult" IsViewObjectMethod="false"
                  ReturnName="data.HRAppModuleDataControl.methodResults.getSearchResult_HRAppModuleDataControl_dataProvider_getSearchResult_result">
      <NamedData NDName="keyword" NDType="java.lang.String"/>
    </methodAction>
  </bindings>

Open databinding.cpx file and note down page-def entry.


 <pageMap>
        <page path="/oracle/webcenter/portalapp/pages/PortalSearchPg.jspx"
                    usageId="com_san_ui_PortalSearchPgPageDef"/>

</pageMap>
 <pageDefinitionUsages>
         <page id="com_san_ui_PortalSearchPgPageDef"
          path="oracle.webcenter.portalapp.pages.PortalSearchPgPageDef"/>

</pageDefinitionUsages>

usageid com_san_ui_PortalSearchPgPageDef will be used to execute AM method programatically.


4. Register your service:
To register a service in webcenter you need to add following entry in service-definition.xml file. This file is located under <application-folder>/.adf/META-INF
 <?xml version="1.0" encoding="UTF-8" ?>
<definitions xmlns="http://xmlns.oracle.com/webcenter/framework/service">

 <service-definition id="com.san.searchapp.employee" version="11.1.1.0.0">
   <resource-view urlRewriterClass="com.san.portal.search.employee.EmployeeDetailURL">
        <parameters>
            <parameter name="resourceId" value="{0}"/>
        </parameters>
    </resource-view>
        <!--resource-bundle-class>mycompany.myproduct.myapp.mycomponent.resource.ComponentMessageBundle</resource-bundle-class-->
    <name-key>Employee Search</name-key>
    <description-key>Employee Search</description-key>
    <!--icon>/mycompany/myproduct/myapp/mycomponent/component.png</icon-->
    <search-definition xmlns="http://xmlns.oracle.com/webcenter/search"
                       id="com.san.searchapp.employee.query" version="11.1.1.0.0">
      <query-manager-class>com.san.portal.search.employee.EmployeeQueryManager</query-manager-class>
    </search-definition>
  </service-definition>
</definitions> 

NOTE down we have mentioned a query-manager class com.san.portal.search.employee.EmployeeQueryManager. 
We need to implement this class and few other class to make it working. Whenever user performs a search, webcenter will look for all services registered in service-definition.xml file and if any query-manager-class is specified, it will use that class to start search operation for that service. 


5. Implement QueryManager and QueryExecuter classes: 
   a. Write a query manager class: Query manager class is a class which is specified while registering a service. This class should implement oracle.webcenter.search.QueryManager. QueryManager will force you to implement a method createRowQueryHandler. This is the method, which will be executed by webcenter framework when a user performs search.
      Here is the code.

package com.san.portal.search.employee;

import java.util.List;

import oracle.webcenter.search.QName;
import oracle.webcenter.search.Query;
import oracle.webcenter.search.QueryExecutor;
import oracle.webcenter.search.QueryManager;
import oracle.webcenter.search.Row;

public class EmployeeQueryManager  implements QueryManager{
    public EmployeeQueryManager() {
        super();
    }
   
    public QueryExecutor<Row> createRowQueryHandler(Query query,
    List<QName> columns)
    {
        return new EmployeeQueryExecutor(query, columns);
       
    }
}

b.  Query  Executor class:
QueryManager (EmployeeQueryManager) returns a QueryExecutor. We need to create a class EmployeeQueryExecutor which implements QueryExecutor. Class should look as

package com.san.portal.search.employee;
import com.san.model.search.employee.EmployeeResourceRow;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import java.util.Map;
import java.util.logging.Level;
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;

import oracle.adf.share.logging.ADFLogger;
import oracle.jbo.uicli.binding.JUCtrlActionBinding;
import oracle.webcenter.framework.resource.ResourceRow;
import oracle.webcenter.search.ComplexPredicate;
import oracle.webcenter.search.QName;
import oracle.webcenter.search.Query;
import oracle.webcenter.search.QueryExecutor;
import oracle.webcenter.search.QueryHandler;
import oracle.webcenter.search.QueryResult;
import oracle.webcenter.search.Row;
import oracle.webcenter.search.TextPredicate;

public class EmployeeQueryExecutor implements QueryExecutor<Row>{
   private static final int PRESET_ESTIMATED_RESULT_COUNT = 20;
    private static final int PRESET_VALUES_BATCH_SIZE = 5;
    private static final int MAX_RESULT_LIMIT = 100;
    private int m_resultStart;
    private int m_resultLimit;
    private Query m_query;
    private List<QName> m_columns;
    private Map<String, String> m_properties;
    private static ADFLogger _logger =   ADFLogger.createADFLogger(EmployeeQueryExecutor.class);
   
    public EmployeeQueryExecutor(Query query, List<QName> columns)
    {
        m_query = query;
        m_columns = columns;
        m_properties = new HashMap<String, String>();
        m_properties.put(QueryHandler.TITLE, "Sample");
    }
   
   
  
    private void createRows(List<Row> rows, int batchRepeat)
    {
        Map param = new HashMap();        

        ComplexPredicate cp = (ComplexPredicate)m_query.getPredicate();
        TextPredicate tp = (TextPredicate)cp.getChildren().get(0);
        String searchQuery = tp.getTextQueryString();
        if(searchQuery == null)
            return;
        param.put("keyword",searchQuery);       
   
        BindingContext bc = BindingContext.getCurrent();
                DCBindingContainer dc =  bc.findBindingContainer("com_san_ui_PortalSearchPgPageDef");
                JUCtrlActionBinding lBinding =(JUCtrlActionBinding)dc.findCtrlBinding("getSearchResult");
                lBinding.getParamsMap().putAll(param);
                lBinding.invoke();
                List<EmployeeResourceRow> allResources = (List<EmployeeResourceRow>)lBinding.getResult();

               
       
        
        Iterator it = allResources.iterator();
       
        int i = 1;
        while(it.hasNext()){
            if(i > m_resultStart){
                EmployeeResourceRow resRow = (EmployeeResourceRow) it.next();
                String resourceId = (String)resRow.getResourceId();
                String title = (String)resRow.getTitle();
                String creator = (String)resRow.getCreator();
                String lastModifiedOn = (String)resRow.getLastModified();
                rows.add(new EmployeeRow(resourceId,title,creator,lastModifiedOn,null));
              
            }
            else{
                it.next();
            }
           
            i++;
              
           
        }   
    }
   
   
    public QueryResult<Row> execute()    {
        List<Row> rows = new ArrayList<Row>();
        int realLimit = Math.min(MAX_RESULT_LIMIT, m_resultLimit);
        
        createRows(rows, (realLimit / PRESET_VALUES_BATCH_SIZE));
        // Create the QueryResult
        QueryResult<Row> ret = new EmployeeQueryResult(rows.iterator());
        ret.getProperties().put(QueryResult.ESTIMATED_RESULT_COUNT,
                                new Integer(rows.size()).toString());
        return ret;
    }
   
    /**
    * Remember the result start.
    */
    public void setResultStart(int resultStart){
      m_resultStart = resultStart;
    }
    

    public void setResultLimit(int resultLimit){
        m_resultLimit = resultLimit;
    }
   
    public Map<String, String> getProperties(){
        return m_properties;
    }
}

Highlighted text shows how to execute a binding method using java code. I have shown executing a binding because this way we can execute anything for which we can create a binding. It might be AM method, a webservice call, a java method call etc etc.

c. Create EmployeeRow class which should implement Row.

package com.san.portal.search.employee;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import static oracle.webcenter.search.AttributeConstants.DCMI_CREATOR;
import static oracle.webcenter.search.AttributeConstants.DCMI_EXTENT;
import static oracle.webcenter.search.AttributeConstants.DCMI_IDENTIFIER;
import static oracle.webcenter.search.AttributeConstants.DCMI_MODIFIED;
import static oracle.webcenter.search.AttributeConstants.DCMI_TITLE;
import static oracle.webcenter.search.AttributeConstants.WPSAPI_ICON_PATH;

import oracle.adf.share.logging.ADFLogger;
import oracle.webcenter.search.QName;
import oracle.webcenter.search.Row;

public class EmployeeRow implements Row{
    private Map<QName, Object> m_storage = new HashMap<QName, Object>();
    private DateFormat m_dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
    private static ADFLogger _logger =   ADFLogger.createADFLogger(EmployeeRow.class);
   
    EmployeeRow(String id, String title,    String creator, String lastModified,    Long size)
    {
               m_storage.put(DCMI_IDENTIFIER, id);
        // Only if you have an external URL to go to
        // m_storage.put(DCMI_URI, "http://www.externalurl.com");
        m_storage.put(DCMI_TITLE, title);
        m_storage.put(DCMI_CREATOR, creator);
        // The below path points to a path accessible in the classpath to an icon
        m_storage.put(WPSAPI_ICON_PATH, "/adf/webcenter/search_qualifier.png"); //we can set human icon here
        m_storage.put(DCMI_EXTENT, size);
        try
        { 
            if(lastModified != null){
                Date date = m_dateFormat.parse(lastModified);
                Calendar cal = Calendar.getInstance();
                cal.setTime(date);
                m_storage.put(DCMI_MODIFIED, cal);
            }
          
        }
        catch (ParseException e)
        {
            e.printStackTrace();
           
        }
       
       
      
    }
    public Object getObject(QName columnName)
    {
            return m_storage.get(columnName);
    }
    public Iterator<QName> getColumns()
    {
            return m_storage.keySet().iterator();
    }
}

d. Create EmployeeQueryResult class which should extend WrapperQueryResult

package com.san.portal.search.employee;

import java.util.Iterator;

import oracle.webcenter.search.Row;
import oracle.webcenter.search.util.WrapperQueryResult;

public class EmployeeQueryResult extends WrapperQueryResult<Row> {
    public EmployeeQueryResult(Iterator<Row> rows)
    {    super(rows);
    }
}





e. Create a class EmployeeDetailURL which should implement ResourceUrlRewriter

package com.san.portal.search.employee;

import oracle.webcenter.framework.resource.ResourceUrlRewriter;

public class EmployeeDetailURL implements ResourceUrlRewriter{
    private String resourceId;
    private String resourceType;
    private String resourceTitle;
    public EmployeeDetailURL() {
        super();
    }

    public String rewriteUrl(String url) {
        return "/faces/oracle/webcenter/portalapp/pages/EmployeeDetailPg.jspx" + "?resourceId=" + url ;
    }

    public void setResourceId(String resourceId) {
        this.resourceId = resourceId;
    }

    public String getResourceId() {
        return resourceId;
    }

    public void setResourceType(String resourceType) {
        this.resourceType = resourceType;
    }

    public String getResourceType() {
        return resourceType;
    }

    public void setResourceTitle(String resourceTitle) {
        this.resourceTitle = resourceTitle;
    }

    public String getResourceTitle() {
        return resourceTitle;
    }
}



6. Create a page EmployeeDetailPg.jspx which should show the detail of an employee:
 When user performs a search, he will see a list of employees. From these employees user can select one to see more details. We have already specified our page EmployeeDetailPg.jspx  in class
EmployeeDetailURL.java. Now we need to create this jspx page. Its just a normal page. To get employee-id which it needs to show you can use expression #{param.resourceId}. There could be multiple ways to implement this page. 
   a. Develop a task-flow which takes employee-id as input and show details of that employee. Drop such task flow on this page and pass it #{param.resourceId} as employee-id
   b. Directly create a form on this page which shows detail of an employee from a VO. Add first binding executable as setCurrentRowWithKeyValue and pass value as #{param.resourceId}



7. User Search task-flow to perform global search
Add webcenter provided search task-flow on your page (or template) where you want to show query box.

Page entry:
<af:region value="#{bindings.searchtoolbar1.regionModel}" id="r1"/>
PageDef entry:
<taskFlow id="searchtoolbar1"
              taskFlowId="/oracle/webcenter/search/controller/taskflows/localToolbarSearch.xml#search-toolbar"
              activation="deferred"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="serviceIds" value="com.san.searchapp.employee"/>
      </parameters>
    </taskFlow>





NOTE: I have not tested above implementation with webcenter secured enterprise search enabled. If your implementation is not usig SES you need to make extra configuration in adf-config.xml file.

Comment out following line

 <!--crawl-properties fullCrawlInterval="P5D" enableWcServicesCrawl="true"
                      enableWcDiscussionsCrawl="true" enableWcUcmCrawl="true"/-->



Other Issues faced:


Error: 
<SearchServiceAPI> <execute> oracle.webcenter.concurrent.TimeoutException occurred during Search result transfer of service with identifier "com.san.searchapp.employee" with message "Execution timedout
            <SearchResultsBean> <collectResults> Execution of the search query did not complete within the time limit for executor com.san.searchapp.employee.

  Solution: 
Increase timeout <execution-properties timeoutMs="300000" prepareTimeoutMs="100000"/> in adf-config.xml



Disclaimer: Any views or opinions presented in this blog are solely those of the author and do not necessarily represent those of the company.



Wednesday, February 19, 2014

ADF/WebCenter (Fusion Middleware) Errors and their solutions

  This page is to list down all issues that I face while working with ADF/Webcenter (Fusion Middleware products)

 

Issue 1:                                                                                           

ADF - Error in Jdev as shown below

Caused by: oracle.jbo.PersistenceException: JBO-34000: Definition class name missing in XML file of type taskFlow
     at oracle.adf.model.binding.DCDefBase.createAndLoadFromXML(DCDefBase.java:402)
     at oracle.adf.model.binding.DCBindingContainerDef.loadExecutables(DCBindingContainerDef.java:1405)
     at oracle.adf.model.binding.DCBindingContainerDef.loadChildrenFromXML(DCBindingContainerDef.java:1201)
     at oracle.adf.model.binding.DCDefBase.loadFromXML(DCDefBase.java:325)

Solution: Add following entries in DataBindings.cpx

  <definitionFactories>
      <dtfactory className="oracle.adf.controller.internal.dtrt.binding.BindingDTObjectFactory"/>
      <factory nameSpace="http://xmlns.oracle.com/adfm/dvt"
               className="oracle.adfinternal.view.faces.dvt.model.binding.FacesBindingFactory"/>
      <factory nameSpace="http://xmlns.oracle.com/adf/controller/binding"
               className="oracle.adf.controller.internal.binding.TaskFlowBindingDefFactoryImpl"/>
      <dtfactory className="oracle.adf.controller.internal.dtrt.binding.BindingDTObjectFactory"/>
   </definitionFactories>



=================================================================

Issue 2: http://server/webcenter/faces/oracle/webcenter/sitestructure/render.jspx?datasource=UCM%23dDocName%3mydoc shows blank page.

Solution: Check if you have navigation-renderer.jspx and its pagedef file in project. At times we delete them by mistake. These files are needed to render content which does not have a dedicated page.

===========================================================================


Issue 3: Weblogic throws 'Too many open files'
Solution: ulimit -n 4096