Wouldn't that be a hoot? But how do we do that? Simple, we use AOP (Apsect Oriented Programming). Ok, you tried that but couldn't make it work with Liferay. Well, that's what this particular post is all about. I'll show you how in three simple steps.
Ok, so first off your custom entity MUST extend the entity of the service in question.
Here is an example of an entity which extends Layout(Impl, extend the Impl always.. otherwise you will have problems):
package com.ray.portal.model.impl;
import ...
public class VersionLayoutImpl extends LayoutImpl {
// Copy All these methods from *ModelImpl of the entity you are extending
// and adjust as needed for your custom type
public static Layout toModel(LayoutSoap soapModel) {
VersionLayoutImpl model = new VersionLayoutImpl();
model.setPlid(soapModel.getPlid());
...
return model;
}
public Layout toEscapedModel() {
if (isEscapedModel()) {
return (Layout)this;
}
else {
Layout model = new VersionLayoutImpl();
model.setNew(isNew());
...
return model;
}
}
public Object clone() {
VersionLayoutImpl clone = new VersionLayoutImpl();
clone.setPlid(getPlid());
...
return clone;
}
public static VersionLayoutImpl clone(LayoutModelImpl layout) {
VersionLayoutImpl clone = new VersionLayoutImpl();
clone.setPlid(layout.getPlid());
...
return clone;
}
public int compareTo(Object obj) {
...
}
public boolean equals(Object obj) {
...
}
}
We also added a static clone method to make it easier to go from the stock entity to the custom one.
Next, you need to replace the returned types from the existing service, so you need some way to inject your types in their place. To do this write a pretty simple org.aopalliance.intercept.MethodInterceptor implementation like so:
package com.ray.portal.service.aop;
import com.liferay.portal.model.impl.LayoutModelImpl;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import com.ray.portal.model.impl.VersionLayoutImpl;
public class LayoutLocalServiceInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
Object result = invocation.proceed();
if (result instanceof LayoutModelImpl) {
result = wrapLayout(result);
}
else if (result instanceof List) {
result = new WrappedList((List<LayoutModelImpl>)result);
}
return result;
}
protected VersionLayoutImpl wrapLayout(Object object) {
if (object instanceof VersionLayoutImpl) {
return (VersionLayoutImpl)object;
}
return VersionLayoutImpl.clone((LayoutModelImpl)object);
}
public class WrappedList extends ArrayList<LayoutModelImpl> {
public WrappedList(List<? extends LayoutModelImpl> list) {
super(list);
}
public VersionLayoutImpl get(int index) {
return wrapLayout(super.get(index));
}
}
}
Pretty simple right? All we need to do now is wire this interceptor into the IOC container and we're all set.
As usual we make a visit to our friend ext-spring.xml and add:
<aop:config> <aop:pointcut id="versionLayoutOperation" expression="bean(com.liferay.portal.service.LayoutLocalService.impl)" /> <aop:advisor advice-ref="versionLayoutInterceptor" pointcut-ref="versionLayoutOperation" /> </aop:config> <bean id="versionLayoutInterceptor" class="com.ray.portal.service.aop.LayoutLocalServiceInterceptor" />
All done! So, you can add/override methods on your entity and othewise customize away.
Cool eh? Enjoy!

