Querying data about your environment

You can use the AdminPortletModel interface to find out more information about your portal installation, things like what all Web applications are installed on your portal, the PortletDefintions means all the portlets installed on your portal. I built this sample portlet to demonstrate that


package com.wpcertification.spi;

import java.io.IOException;
import java.util.Iterator;
import java.util.Locale;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ibm.portal.ModelException;
import com.ibm.portal.model.PortletModelHome;
import com.ibm.portal.portletmodel.PortletDefinition;
import com.ibm.portal.portletmodel.WebApplication;
import com.ibm.portal.portletmodel.admin.AdminPortletModel;
import com.ibm.portal.portletmodel.admin.PortletDefinitionList;
import com.ibm.portal.portletmodel.admin.WebApplicationList;

public class PortletDeflistPortlet extends GenericPortlet{

PortletModelHome portletModelHome;

public void init() throws PortletException {
System.out.println("Entering PortalPOCPortlet.init()");
try {
InitialContext context = new InitialContext();
portletModelHome =(PortletModelHome) context.lookup("portal:service/model/PortletModel");
System.out.println("PortletModelHome " + portletModelHome);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
System.out.println("Entering PortalPOCPortlet.init()");
}

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
response.getWriter().println("**************** Portlet Web Applications ************
");
printPortletApplicationList(request,response);
response.getWriter().println("**************** Portlet Definitions ************
");

printPortletDefinitionList(request,response);
response.getWriter().println("**********************************************************************
");
}

public void printPortletApplicationList(RenderRequest request, RenderResponse response){
System.out.println("Entering PortalPOCPortlet.getObjectIdOfPortlet()");
try {
AdminPortletModel adminModel = portletModelHome.getPortletModelProvider().getAdminPortletModel((HttpServletRequest)request, (HttpServletResponse)response);
WebApplicationList webApplicationList = adminModel.getWebApplicationList();

Iterator webAppIt = webApplicationList.iterator();
while(webAppIt.hasNext()){
WebApplication webApplication = (WebApplication)webAppIt.next();
response.getWriter().println(webApplication.getObjectID() +" " +webApplication.getContextRoot() +"
");
}
} catch (ModelException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Exiting PortalPOCPortlet.getObjectIdOfPortlet()");
}

public void printPortletDefinitionList(RenderRequest request, RenderResponse response){
System.out.println("Entering PortalPOCPortlet.getObjectIdOfPortlet()");
try {
AdminPortletModel adminModel = portletModelHome.getPortletModelProvider().getAdminPortletModel((HttpServletRequest)request, (HttpServletResponse)response);
PortletDefinitionList portletDefinitionList = adminModel.getPortletDefinitionList();

Iterator portletDefinitionIt = portletDefinitionList.iterator();
while(portletDefinitionIt.hasNext()){
PortletDefinition portletDefinition = portletDefinitionIt.next();
response.getWriter().println(portletDefinition.getObjectID().toString() +" " + portletDefinition.getObjectID().getUniqueName() +" " + portletDefinition.getTitle(new Locale("en","US"))+" " + portletDefinition.getDescription(new Locale("en","US")) +"
");
}
} catch (ModelException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Exiting PortalPOCPortlet.getObjectIdOfPortlet()");
}

}

Finding uniqueName from ObjectId of the portlet and other way round

When your working with Portal Model then you might need a way to figure out unique name of the portlet from its ObjectId and other way. I built this POC to do that. In


import java.io.IOException;
import java.util.Iterator;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletRequest;
import javax.portlet.PortletResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ibm.portal.ModelException;
import com.ibm.portal.ObjectID;
import com.ibm.portal.model.PortletModelHome;
import com.ibm.portal.portletmodel.PortletDefinition;
import com.ibm.portal.portletmodel.admin.AdminPortletModel;
import com.ibm.portal.portletmodel.admin.PortletDefinitionList;

public class PortletUniqueNamePortlet extends GenericPortlet{
PortletModelHome portletModelHome;

public void init() throws PortletException {
System.out.println("Entering PortalPOCPortlet.init()");
try {
InitialContext context = new InitialContext();
portletModelHome =(PortletModelHome) context.lookup("portal:service/model/PortletModel");
System.out.println("PortletModelHome " + portletModelHome);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
System.out.println("Entering PortalPOCPortlet.init()");
}

protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
response.getWriter().println("Object Id of the sitemap portlet " + getObjectIdOfPortlet(request, response, "wps.p.Sitemap"));
response.getWriter().println("
Unique Name of the sitemap portlet " + getUniqueNameOfPortlet(request, response, getObjectIDStr(getObjectIdOfPortlet(request, response, "wps.p.Sitemap"))));

}

public static String getObjectIDStr(ObjectID objectID){
String temp = objectID.toString();
int firstInd = temp.indexOf("'");
String result = temp.substring(firstInd+1, temp.indexOf("'", firstInd+1 ));
return result;
}

public ObjectID getObjectIdOfPortlet(PortletRequest request, PortletResponse response, String portletUniqueName){
System.out.println("Entering PortalPOCPortlet.getObjectIdOfPortlet()");
try {
AdminPortletModel adminModel = portletModelHome.getPortletModelProvider().getAdminPortletModel((HttpServletRequest)request, (HttpServletResponse)response);
PortletDefinition portletDef = adminModel.getPortletDefinitionList().getLocator().findByUniqueName(portletUniqueName);
return portletDef.getObjectID();
} catch (ModelException e) {
e.printStackTrace();
}
System.out.println("Exiting PortalPOCPortlet.getObjectIdOfPortlet()");
return null;
}

public String getUniqueNameOfPortlet(PortletRequest request, PortletResponse response, String portletObjectId){
System.out.println("Entering PortalPOCPortlet.getUniqueNameOfPortlet()");
try {
AdminPortletModel adminModel = portletModelHome.getPortletModelProvider().getAdminPortletModel((HttpServletRequest)request, (HttpServletResponse)response);
PortletDefinitionList portletDefinitionList = adminModel.getPortletDefinitionList();
Iterator portletDefinitionIterator = portletDefinitionList.iterator();
while(portletDefinitionIterator.hasNext()){
PortletDefinition portletDefinition = portletDefinitionIterator.next();
String currentObjectIdStr = getObjectIDStr(portletDefinition.getObjectID());
if(currentObjectIdStr.equals(portletObjectId)){
return portletDefinition.getObjectID().getUniqueName();
}
}
} catch (ModelException e) {
e.printStackTrace();
}
System.out.println("Not able to find portlet for given portletObjectId");
return null;
}
}

Redirecting user on login

One of the reader posted a comment, asking how do i redirect user as soon as they login based on some condition, so i changed my SampleExplicitLoginFilter like this


public class SampleExplicitLoginFilter implements ExplicitLoginFilter{

public void login(HttpServletRequest request, HttpServletResponse response,
String userId, char[] password, FilterChainContext portalLoginContext, Subject subject,
String realm, ExplicitLoginFilterChain chain) throws LoginException,
WSSecurityException, PasswordInvalidException,
UserIDInvalidException, AuthenticationFailedException,
AuthenticationException, SystemLoginException,
com.ibm.portal.auth.exceptions.LoginException {
System.out.println("Entering SamplExplicitLoginFilter.login()");

System.out.println("User Id " + userId);
System.out.println("Password " + String.valueOf(password));
System.out.println("Realm" + realm);

chain.login(request, response, userId, password, portalLoginContext, subject, realm);
if(request.getRemoteUser().equals("wasadmin"))
portalLoginContext.setRedirectURL("/wps/myportal/Administration");
System.out.println("Exiting SamplExplicitLoginFilter.login()");
}
public void destroy() {
}
public void init(SecurityFilterConfig arg0)
throws SecurityFilterInitException {
}

}


I am checking if the remote user is wasdmin if yes i am redirecting him to /wps/myportal/Administration page.

Creating loginfilter for WebSphere Portal

The portal authentication filters are a set of plug-in points. You can use them to intercept or extend the portal login, logout, session timeout, and request processing by custom code, for example to redirect users to a specific URL.

The New Security API in WebSphere Portal talks about various ways to extend the login process. I wanted to play with the LoginFilters so i followed simple steps to build this solution

First i did create SampleExplicityLoginFilter java class like this

public class SampleExplicitLoginFilter implements ExplicitLoginFilter{

public void login(HttpServletRequest request, HttpServletResponse response,
String userId, char[] password, FilterChainContext portalLoginContext, Subject subject,
String realm, ExplicitLoginFilterChain chain) throws LoginException,
WSSecurityException, PasswordInvalidException,
UserIDInvalidException, AuthenticationFailedException,
AuthenticationException, SystemLoginException,
com.ibm.portal.auth.exceptions.LoginException {
System.out.println("Entering SamplExplicitLoginFilter.login()");

System.out.println("User Id " + userId);
System.out.println("Password " + String.valueOf(password));
System.out.println("Realm" + realm);

chain.login(request, response, userId, password, portalLoginContext, subject, realm);
System.out.println("Exiting SamplExplicitLoginFilter.login()");
}
public void destroy() {
}
public void init(SecurityFilterConfig arg0)
throws SecurityFilterInitException {
}

}

This class only reads the userId and password and prints it in the System.out and lets control go to next step.

Similarly i did create a Sample Filter for each of the other interfaces and you can download the sample application from here

Then i built that project and copied it into the PortalServer/shared/app directory. I went to WAS Admin Console and configured all my sample login filters like this.



After that i had to restart my server but after restart when i tried login into portal i could see that i was able to get control in the LoginFilter and write userId and password used by user while login in to System.out like this


[12/17/09 10:52:51:198 PST] 0000002e SystemOut O Entering SamplExplicitLoginFilter.login()
[12/17/09 10:52:51:198 PST] 0000002e SystemOut O User Id wasadmin
[12/17/09 10:52:51:198 PST] 0000002e SystemOut O Password password
[12/17/09 10:52:51:198 PST] 0000002e SystemOut O Realmnull
[12/17/09 10:52:51:245 PST] 0000002e SystemOut O Exiting SamplExplicitLoginFilter.login()

WebSphere Portal 6.1.5 ships with Dojo 1.3.2

Starting from version 6.1.5 WebSphere Portal ships with Dojo 1.3.2 in addition to Dojo version 1.1.1. Starting from portal 6.1 dojo version 1.1.1 is shiped as part of the wps.ear, to be precise it is in wp_profile\installedApps\sunpa\wps.ear\wps.war\themes\dojo\portal_dojo folder, IBM kept it as it is.

In order to include Dojo 1.3.2 they create a Dojo_Resources.ear file which has 1.3.2 version of the dojo



The Dojo_Resources.ear has new version of dojo and few additional dojo classes created by IBM, these classes implement some of the IBM's client side logic. This enterprise application does not have any java classes so its used only for making dojo resources accessible.



As you can see the Dojo_Resources.war is available at /portal_dojo path. The dojo client side theme loads dojo from this path



As you can see the value of baseUrl property in the djConfig is /portal_dojo/dojo/, that means dojo is loaded from this location. You can see that even the tundra.css or other dijit related resources are loaded from Dojo_Resources.war.



You can verify the dojo version by looking at dojo.version properties. As you can see we are using 1.3.2 version

Implementation of GenericPortlet

My first impression of specification document is that it will be very complicated and only very few people or someone who is developing portlet engine will be able to understand it but surprisingly both Portlet Specification 2.0 and Portlet Specification 1.0 are very well written document.

What i like to do is download the portlet specification, which has a .pdf document describing specification and a src.zip file. This file has source code for javax.portlet files, or the Java classes/interfaces defined by the specification. Reading through these documents gives us a very good understanding of the specification.

Take a look at GenericPortlet.java that is part of src.zip which give us very good understanding of default portal implementation, Ex. doDispatch(), how the resource serving or action processing work

package javax.portlet;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.xml.namespace.QName;
public abstract class GenericPortlet implements Portlet, PortletConfig, EventPortlet, ResourceServingPortlet {

private transient PortletConfig config;

private transient Map processActionHandlingMethodsMap = new HashMap();
private transient Map processEventHandlingMethodsMap = new HashMap();
private transient Map renderModeHandlingMethodsMap = new HashMap();
public GenericPortlet() {
}
public void init(PortletConfig config) throws PortletException {
this.config = config;
cacheAnnotatedMethods();
this.init();
}
public void init() throws PortletException {
}

public void processAction(ActionRequest request, ActionResponse response) throws PortletException,
java.io.IOException {
String action = request.getParameter(ActionRequest.ACTION_NAME);

try {
// check if action is cached
Method actionMethod = processActionHandlingMethodsMap.get(action);
if (actionMethod != null) {
actionMethod.invoke(this, request, response);
return;
}
} catch (Exception e) {
throw new PortletException(e);
}

// if no action processing method was found throw exc
throw new PortletException("processAction method not implemented");
}
public void render(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException {
Object renderPartAttrValue = request.getAttribute(RenderRequest.RENDER_PART);
if (renderPartAttrValue != null) {
// streaming portal calling
if (renderPartAttrValue.equals(RenderRequest.RENDER_HEADERS)) {
doHeaders(request, response);
Collection nextModes = getNextPossiblePortletModes(request);
if (nextModes != null)
response.setNextPossiblePortletModes(nextModes);
response.setTitle(getTitle(request));
} else if (renderPartAttrValue.equals(RenderRequest.RENDER_MARKUP)) {
doDispatch(request, response);
} else {
throw new PortletException("Unknown value of the 'javax.portlet.render_part' request attribute");
}
} else {
// buffered portal calling
doHeaders(request, response);
Collection nextModes = getNextPossiblePortletModes(request);
if (nextModes != null)
response.setNextPossiblePortletModes(nextModes);
response.setTitle(getTitle(request));
doDispatch(request, response);
}
}

protected java.lang.String getTitle(RenderRequest request) {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getResourceBundle(request.getLocale()).getString("javax.portlet.title");
}


protected void doDispatch(RenderRequest request, RenderResponse response) throws PortletException,
java.io.IOException {
WindowState state = request.getWindowState();

if (!state.equals(WindowState.MINIMIZED)) {
PortletMode mode = request.getPortletMode();
// first look if there are methods annotated for
// handling the rendering of this mode
try {
// check if mode is cached
Method renderMethod = renderModeHandlingMethodsMap.get(mode.toString());
if (renderMethod != null) {
renderMethod.invoke(this, request, response);
return;
}
} catch (Exception e) {
throw new PortletException(e);
}

// if not, try the default doXYZ methods
if (mode.equals(PortletMode.VIEW)) {
doView(request, response);
} else if (mode.equals(PortletMode.EDIT)) {
doEdit(request, response);
} else if (mode.equals(PortletMode.HELP)) {
doHelp(request, response);
} else {
throw new PortletException("unknown portlet mode: " + mode);
}
}
}


protected void doView(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException {
throw new PortletException("doView method not implemented");
}


protected void doEdit(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException {
throw new PortletException("doEdit method not implemented");
}


protected void doHelp(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException {
throw new PortletException("doHelp method not implemented");
}


public PortletConfig getPortletConfig() {
return config;
}

public void destroy() {
// do nothing
}
public String getPortletName() {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getPortletName();
}
public PortletContext getPortletContext() {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getPortletContext();
}

public java.util.ResourceBundle getResourceBundle(java.util.Locale locale) {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getResourceBundle(locale);
}


public String getInitParameter(java.lang.String name) {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getInitParameter(name);
}


public java.util.Enumeration getInitParameterNames() {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getInitParameterNames();
}

public Enumeration getProcessingEventQNames() {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getProcessingEventQNames();
}

public Enumeration getPublishingEventQNames() {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getPublishingEventQNames();
}

public Enumeration getSupportedLocales() {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getSupportedLocales();
}
public Map getContainerRuntimeOptions() {
return config.getContainerRuntimeOptions();
}

public void serveResource(ResourceRequest request, ResourceResponse response) throws PortletException, IOException {
if (request.getResourceID() != null) {
PortletRequestDispatcher rd = getPortletConfig().getPortletContext().getRequestDispatcher(
request.getResourceID());
if (rd != null)
rd.forward(request, response);
}
}

public void processEvent(EventRequest request, EventResponse response) throws PortletException, IOException {
String eventName = request.getEvent().getQName().toString();

try {
// check for exact match
Method eventMethod = processEventHandlingMethodsMap.get(eventName);
if (eventMethod != null) {
eventMethod.invoke(this, request, response);
return;
} else {
// Search for the longest possible matching wildcard annotation
int endPos = eventName.indexOf('}');
int dotPos = eventName.lastIndexOf('.');
while (dotPos > endPos) {
String wildcardLookup = eventName.substring(0, dotPos + 1);
eventMethod = processEventHandlingMethodsMap.get(wildcardLookup);
if (eventMethod != null) {
eventMethod.invoke(this, request, response);
return;
}
if (dotPos == 0) {
break;
}
dotPos = eventName.lastIndexOf('.', dotPos - 1);
}
}
} catch (Exception e) {
throw new PortletException(e);
}

// if no event processing method was found just keep render params
response.setRenderParameters(request);
}

protected void doHeaders(RenderRequest request, RenderResponse response) {
return;
}


protected java.util.Collection getNextPossiblePortletModes(RenderRequest request) {
return null;
}

public Enumeration getPublicRenderParameterNames() {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getPublicRenderParameterNames();
}


public String getDefaultNamespace() {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

return config.getDefaultNamespace();
}

private void cacheAnnotatedMethods() {
// cache all annotated and visible public methods
for (Method method : this.getClass().getMethods()) {
Annotation[] annotations = method.getAnnotations();
if (annotations != null) {
for (Annotation annotation : annotations) {
Class annotationType = annotation.annotationType();
if (ProcessAction.class.equals(annotationType)) {
String name = ((ProcessAction) annotation).name();
if (name != null && name.length() > 0)
processActionHandlingMethodsMap.put(name, method);
} else if (ProcessEvent.class.equals(annotationType)) {
String qname = ((ProcessEvent) annotation).qname();
if (qname == null || qname.length() <= 0) {
if (config == null)
throw new java.lang.IllegalStateException(
"Config is null, please ensure that your init(config) method calls super.init(config)");

String name = ((ProcessEvent) annotation).name();
if (name != null && name.length() > 0) {
qname = new QName(config.getDefaultNamespace(), name).toString();
processEventHandlingMethodsMap.put(qname, method);
}
} else
processEventHandlingMethodsMap.put(qname, method);
} else if (RenderMode.class.equals(annotationType)) {
String name = ((RenderMode) annotation).name();
if (name != null && name.length() > 0)
renderModeHandlingMethodsMap.put(name.toLowerCase(), method);
}
}
}
}
}
}

Spring Web Application, loading classes from classpath

Recently i had to break my big Spring Portlet Application into smaller pieces and move some of the code to shared library so that it can be used by multiple web application. Actually in my case Portlet Skin, which is part of wps.war needed access to same beans as that of my portlet.

What i did to solve this problem is i created a new Java Project and moved my DAO classes along with the Spring configuration for DAO classes to that java project, the jar file built by that java project would go in shared lib. Since the spring configuration files were copied to root of the shared library my Skin could load those classes by using ClassPathXmlApplicationContext like this


new ClassPathXmlApplicationContext(new String[]{"dao.xml","dao1.xml","dao2.xml"})


But how do i include the dao.xmls while creating WebApplicationContext. In Spring Portlet MVC framework the org.springframework.web.context.ContextLoaderListener is used for reading value of contextConfigLocation Web Application Context parameter and then reads all those Spring configuration files into Web Application COntext. Normally WebApplication Context looks for the configuration files in WEB-INF directory. In my case dao.xml is not part of web application so it was throwing file not found error.

I was able to solve that problem by appending classpath: instead of file name. When i do that the Spring framework starts using ClasspathResourceLoader instead of WebApplicationResourceLoader for loading configuration file. And ClasspathResourceLoader is capable of reading files from the classpath, which is shared lib

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:dao.xml,classpath:dao1.xml,classpath:dao2.xml</param-value>
</context-param>

Sharing ApplicationContext in Spring MVC and Non Spring MVC Portlet

In my project i had this interesting problem that i have one Portlet Application, which has 4 portlets now 3 of the portlets are using Spring MVC framework but the fourth portlet does not use Spring Portlet MVC framework instead it extends GenericPortlet. All my 4 portlets use same DAO and other classes and i am using Spring for wiring my DAOs

Problem was that how do i obtain ApplicationContext for the portlet that is not Spring Portlet MVC or how do i get access to ApplicationContext object.


PortletApplicationContextUtils.getWebApplicationContext(getPortletContext())


You can use PortletApplicationContextUtils.getWebApplicationContext() method, which is static method anywhere in your web application code to get access to ApplicationContext created by the ContextLoaderListener in your class.

Improved version of enable-develop-mode-startup-performance

WebSphere Portal has concept of Development mode for some time, basic idea is to improve the startup time of portal by delaying the startup of application. The application should be started when it is accessed for first time instead of starting it at the server startup time. As per Marshal Lamb there are close to 75 portlets for administrating portal and some of the companies dont use Portal Admin Console in certain environments such as Production.

When working in Portlet developer role i dont use Portal Admin Console as much, most of my work revolves around updating the portlet application which i can do easily using RAD and RAD makes use of xmlaccess and wsadmin script so i dont need any of the Admin portlets.

WebSphere Portal provides enable-develop-mode-startup-performance to enable development mode and disable-develop-mode-startup-performance task to disable development mode. This part is same as that of WPS 6.1, what has changed is that now this task works more gracefully, before it use to disable Portal Help, Portlet palette, Personalization. As part of WPS 6.1.5, IBM did test this feature to make sure that everything works properly.

I tried using development mode on WPS 6.1.5 and now it works really well. I havent run into any problems so far.

Assiging UniqueName to Skin

I had this business requirement in which i had to assign a unique name to my skin. In order to do that i followed these steps

  1. First copy the skin folder say Test Folder into your wp_profile\installedApps\sunpa\wps.ear\wps.war\skins\html folder.

  2. Next define the Skin using Themes and Skins Admin portlet

  3. Export full portal and find out the Skin element for test, you will notice that portal has assigned a objectid to skin. Copy that element in separate file and add uniqueName attribute to it and import it into portal using xmlaccess

    <?xml version="1.0" encoding="UTF-8"?>
    <request xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" build="wp6103_201_01" type="update" version="6.1.0.3" xsi:noNamespaceSchemaLocation="PortalConfig_6.1.0.2.xsd">
    <portal action="locate">
    <skin action="update" active="true" default="false" domain="rel" objectid="K_8000CB1A08N4C0IKUIQTT60000" resourceroot="Test" type="default" uniquename="wps.skin.TestSkin">
    <localedata locale="en">
    <title>Test</title>
    </localedata>
    </skin>
    </portal>
    </request>