1 Reply Latest reply on Dec 18, 2008 4:07 PM by johnnythehun

    Howto: insert Portlet via your layout.jsp

    johnnythehun

      What I was looking for:
      - I am going to create a dozen pages (programmaticly) with the same layout. - On every page I want to have the same portlets on one region.
      - I can add portlets by hand or programmaticly, but! if I want to change the portlets that need to get displayed on every page, I have to go through all pages again

      So I was looking for a solution to add portlets in the layout code. This way I only have to change the layout code if I want different portlets to get displayed on every layout (for example I ads a brand new portlet I want to get displayed on all pages)

      Unfortunatly JBoss doesn't support this.

      But I have found a (half) solution on how to insert portlets via your layout:

      1. Get JBoss sources

      2. Copy the code from org.jboss.portal.core.aspects.controller.PageCustomizerInterceptor and paste it into your own class (swapping the class name and package for your own)

      3. Add some new class variables:

      LayoutService ls=null;
      Logger log = Logger().getLogger(getClass());

      4. Add the following code snippet into the invoke method after it injects the navigation code:

      List<String> toInjectListStr=new ArrayList<String>();
       if(ls==null) try {
       log.info("Getting layoutservice");
       MBeanServer mbeanServer = MBeanServerLocator.locateJBoss();
       ls = (LayoutService) MBeanProxy.get(LayoutService.class, new ObjectName("portal:service=LayoutService"), mbeanServer);
       } catch(Exception e) {
       log.error("Unable to get layout stuff",e);
       }
      
       String layoutId = rpc.getPage().getProperty("layout.id");
       PortalLayout pl = ls.getLayoutById(layoutId);
       log.info("LayoutService: "+ls+", layout for "+layoutId +" = "+pl);
       if(pl!=null) {
       List<String> regionNames = (List<String>)pl.getLayoutInfo().getRegionNames();
       for (String regionName : regionNames) {
       log.debug("Found region: "+regionName+" on layout "+pl);
       if(regionName.startsWith("inject")) toInjectListStr.add(regionName);
       }
       }
       log.info("Injectregions: "+toInjectListStr);
      
      for(String fullName : toInjectListStr) {
       //String fullName = region.getId();
       int ind = fullName.indexOf(':');
       String regionName = fullName.substring(0, ind);
       String allPortletsStr = fullName.substring(ind+1);
       String[] portletInstanceIds = allPortletsStr.split("\\|");
      
       for(String portletInstanceId : portletInstanceIds) {
       log.info("Injecting "+portletInstanceId+" at tergion: "+regionName);
       String portletCode = injectPortlet(rpc, portletInstanceId);
       if(portletCode==null) {
       log.warn("No such portlet: " + portletInstanceId);
       continue;
       }
       //log.info("Code generated: " + portletCode);
       Map windowProps = new HashMap();
       windowProps.put(ThemeConstants.PORTAL_PROP_WINDOW_RENDERER, "divRenderer");
       windowProps.put(ThemeConstants.PORTAL_PROP_DECORATION_RENDERER, "emptyRenderer");
       windowProps.put(ThemeConstants.PORTAL_PROP_PORTLET_RENDERER, "emptyRenderer");
       WindowResult res = new WindowResult("", portletCode, Collections.EMPTY_MAP, windowProps, null, WindowState.NORMAL, Mode.VIEW);
       WindowContext blah = new WindowContext("BLAH" + regionName, regionName, "0", res);
       rendition.getPageResult().addWindowContext(blah);
       //System.out.println("Injecting custom region "+mvhregion.regionName+" at at "+rpc.getPage().getName());
       //
       Region renditionRegion = rendition.getPageResult().getRegion2(regionName);
       DynaRenderOptions.NO_AJAX.setOptions(renditionRegion.getProperties());
       }
       }
      
      


      5. Create the following method:

      public String injectPortlet(PageCommand rpc, String instanceId) throws Exception {
       ControllerContext context = rpc.getControllerContext();
       InstanceContainer container = context.getController().getInstanceContainer();
       Instance instance = container.getDefinition(instanceId);
       if(instance==null) return null;
       PortletContextFactory pcf1 = new PortletContextFactory(context);
       InvokePortletCommandFactory pcf2 = new InvokePortletInstanceCommandFactory(instanceId);
       StateString navigationalState = WindowNavigationalState.create().getContentState();
       RenderInvocation render = PortletInvocationFactory.createRender(context, Mode.VIEW, WindowState.MAXIMIZED, navigationalState, pcf1, pcf2);
       PortletInvocationResponse response = instance.invoke(render);
      
      
       // For now let the controller handle non fragment response
       String content = "";
       List<Element> headElements = null;
       if (response instanceof FragmentResponse) {
       FragmentResponse fragment = (FragmentResponse) response;
       content = fragment.getContent();
       ResponseProperties properties = fragment.getProperties();
       if (properties != null) {
       // header handling
       //MultiValuedPropertyMap<Element> headers = properties.getMarkupHeaders();
       //headElements = headers.getValues(MimeResponse.MARKUP_HEAD_ELEMENT);
       }
       }
      return content;
      }
      



      6. Replace PageCustomizerInterceptor with your class in
      server\default\deploy\jboss-portal.sar\META-INF\jboss-service.xml


      7. Create a jar out of your classes in
      build/web/web-inf/classes
      and put the jar in server/default/lib

      8. restart JBoss


      9. To specify what portlets to inject into which region you have to:

      A. in your layout code:
      create a region starting with "inject". Example: "inject_left"

      B. In portal-layouts.xml create the layout:
      an example to illustrate what to write:
      <layout>
       <name>my_layout_inject</name>
       <uri>/path_to_layouts/layout_inject.jsp</uri>
       <uri state="maximized">/path_to_layouts/layout_inject_maximized.jsp</uri>
       <regions>
       <region name="inject3:MyPortletInstance|IdentityUserPortletInstance">
       </region>
       <region name="inject2:CurrentUsersPortletInstance">
       </region>
       </regions>
       </layout>
      



      the PageCustomizerInterceptor uses the region name to determine what to inject (I couldn't find a better solution, didn't find any other property I could use):

      "inject3:MyPortletInstance|IdentityUserPortletInstance"
      the name takes the following convetion:
      <region name>:<portlet instance id>[|<portlet instance id>]*


      The following code doesn't take into account what kind of renderers you set for the page, it uses the div and empty renderers. If you want you can change that in the code, or update to code to satisfy your needs. If you create a new and better version, please post it here, it took me ages to find a solution like this.

      If anyone wants to use this without coding around, I can make a jar out of it.

        • 1. Re: Howto: insert Portlet via your layout.jsp
          johnnythehun

          Java doesn't like special characteris in the portlet names: ':' and '|'
          so I changed the above characters to the strings:
          "-" and "--" respectivly.

          The new naming convention in portal-layouts.xml
          <region name>-<portlet instance id>[--<portlet instance id>]*

          and you need to change the PageCustomizerInterceptor code too