Jose Juan MontielJose Juan Montiel

Retrolambda e Hippo Cms

Este proyecto, solo se puede compilar con la JDK 8, ya que hace uso intensivo de sus caracteristicas: lambdas, default methods y otras mas.

Gracias a Retrolambda, muchos proyectos que hacen uso de las nuevas caracteristicas de JDK 8 pueden migrarse a JDK7 (y anteriores) y por tanto tambien a Android, lugar donde se hace principal uso de esta herramienta.

Y con este pluging maven puedes integrarlo en tu buildsystem. Aqui un ejemplo de como usarlo. Y ademas, hay otro para gradle.

Limitaciones

Pero hay unas limitaciones bien conocidas, que hacen que la migracion de los default method de JDK 8 no sean tan faciles, "must to be backported together, with one execution of Retrolambda".

Por eso, por defecto, viene deshabilitada su migracion.

Cuando Retrolmambda migra las Default Methods: Default methods are backported by copying the default methods to a companion class (interface name + "$") as static methods, replacing the default methods in the interface with abstract methods, and by adding the necessary method implementations to all classes which implement that interface.

Retrolambda (e HippoCms)

Tomemos como ejemplo, el siguiente proyecto.

Supongamos que en un primer modulo tenemos esta interfaz con el default method.

public interface IconProvider extends IClusterable {
	default Component getIcon(final String id, IconSize size) {
		return null;
	}
}

Retrolambda lo que hace con esta clase es transformarla en esta otra.

public abstract interface IconProvider2 extends IClusterable {
  public abstract Component getIcon(String paramString, IconSize paramIconSize);
}

E implementa ese metodo (a traves de la "companion class") en las clases que implementen la interfaz, de las clases del mismo modulo.

Por tanto, si no procesa todas las clases hijas de esa interfaz en la pasada del modulo, ya no lo hara, por lo que…​

public class SearchingSectionPlugin extends RenderPlugin implements IBrowserSection

que esta en otro modulo, al intentar ser compilada da este error

[ERROR] /hippo-cms/perspectives/src/main/java/org/hippoecm/frontend/plugins/cms/browse/section/SearchingSectionPlugin.java:[63,7]
	error: SearchingSectionPlugin is not abstract and does not override abstract method getIcon(String,IconSize) in IconProvider

Primeras conclusiones

En un primer momento, sin leer bien el mensaje de error, y sin comprender la jerarquia de clases del proyecto, pense que este pull request podria ayudar

Por si quieres entender mas en detalle, aqui unos apuntes sobre el Lifecycle de de maven. Y aqui sobre el classloading.

Luego, leyendo mas en detalle, pense que el problema era que al tranformar la interfaz a abstracta…​ bueno, que me habia despistado del problema real, y a ese despiste constituyo ver, en este tweet porque las interfaces interfaces son abstractas (por defecto).

Y el propio Esko Luontola nos explica el motivo: "pre-Java 8 JVMs support only abstract methods in interfaces (the <clinit> method being an exception). Retrolambda inserts the default method code to all implementing classes, thus emulating what JVM 8 does."

Aqui una explicacion mas detallada de como funcionan los Default Methods en JDK 8. Y aqui la explicación de oracle sobre "abstract".

Explicacion del problema real

Pero el problema, en este caso, era simplemente la limitacion de la libreria en projectos multi modulo aqui y aqui lo comentan tambien en detalle.

Una manera de resolverlo, haciendo pequeñas modificaciones en el codigo, seria implementando el metodo getIcon(String,IconSize) con el comportamiento por default en las clases del segundo modulo.

diff --git a/perspectives/src/main/java/org/hippoecm/frontend/plugins/cms/browse/section/BrowsingSectionPlugin.java b/perspectives/src/main/java/org/hippoecm/frontend/plugins/cms/browse/section/BrowsingSectionPlugin.java
index 99bdb0e..9b34ce1 100644
--- a/perspectives/src/main/java/org/hippoecm/frontend/plugins/cms/browse/section/BrowsingSectionPlugin.java
+++ b/perspectives/src/main/java/org/hippoecm/frontend/plugins/cms/browse/section/BrowsingSectionPlugin.java
@@ -18,6 +18,7 @@
 import javax.jcr.Node;
 import javax.jcr.RepositoryException;

+import org.apache.wicket.Component;
 import org.apache.wicket.model.IModel;
 import org.apache.wicket.request.resource.ResourceReference;
 import org.hippoecm.frontend.l10n.ResourceBundleModel;
@@ -144,4 +145,10 @@
         return null;
     }

+    @Override
+    public Component getIcon(String id, IconSize size) {
+    	return null;
+    }
+
+
 }
diff --git a/perspectives/src/main/java/org/hippoecm/frontend/plugins/cms/browse/section/SearchingSectionPlugin.java b/perspectives/src/main/java/org/hippoecm/frontend/plugins/cms/browse/section/SearchingSectionPlugin.java
index 450712b..9d09210 100644
--- a/perspectives/src/main/java/org/hippoecm/frontend/plugins/cms/browse/section/SearchingSectionPlugin.java
+++ b/perspectives/src/main/java/org/hippoecm/frontend/plugins/cms/browse/section/SearchingSectionPlugin.java
@@ -56,6 +56,7 @@
 import org.hippoecm.frontend.service.IconSize;
 import org.hippoecm.frontend.service.render.RenderPlugin;
 import org.hippoecm.frontend.skin.Icon;
+import org.hippoecm.frontend.skin.IconProvider;
 import org.hippoecm.repository.api.HippoNodeType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -337,6 +338,11 @@
     public ResourceReference getIcon(IconSize type) {
         return null;
     }
+
+    @Override
+    public Component getIcon(String id, IconSize size) {
+    	return null;
+    }

     private boolean hasSearchResult() {
         return collection.getType() == DocumentCollectionType.SEARCHRESULT;

¿Proximos pasos?

Teras y Orfjackal, ¿porque el PR 101 no modifica estas clases para hacer ese cambio? ¿Quizas no lo hace porque ya existe un metodo con ese nombre, aunque no con el mismo numero de paramtros?

¿Y que pasa con la compilacion retrolambda de hippocms?

Aun nos quedaba algun error por resolver…​ metodo estaticos…​

[ERROR] hippo-cms/workflow/frontend/src/main/java/org/hippoecm/frontend/plugins/reviewedactions/list/ReviewedActionsListColumnProviderPlugin.java:[72,38] error: cannot find symbol
[ERROR] symbol:   method of(Calendar)
[ERROR] location: interface DateTimePrinter
[ERROR] hippo-cms/workflow/frontend/src/main/java/org/hippoecm/frontend/plugins/reviewedactions/list/ReviewedActionsListColumnProviderPlugin.java:[79,38] error: cannot find symbol
[ERROR] symbol:   method of(Calendar)
[ERROR] location: interface DateTimePrinter

Static methods on interfaces are backported by moving the static methods to a companion class (interface name + "$"), and by changing all methods calls to call the new method location.[1]

En este caso la solucion temporal es cambiar los import en las clases del segundo modulo, a la clase companion.

diff --git a/workflow/frontend/src/main/java/org/hippoecm/frontend/plugins/reviewedactions/RequestsView.java b/workflow/frontend/src/main/java/org/hippoecm/frontend/plugins/reviewedactions/RequestsView.java
index 5a97066..ab938b7 100644
--- a/workflow/frontend/src/main/java/org/hippoecm/frontend/plugins/reviewedactions/RequestsView.java
+++ b/workflow/frontend/src/main/java/org/hippoecm/frontend/plugins/reviewedactions/RequestsView.java
@@ -41,7 +41,7 @@
 import org.hippoecm.frontend.plugin.IPluginContext;
 import org.hippoecm.frontend.plugins.reviewedactions.model.Request;
 import org.hippoecm.frontend.plugins.reviewedactions.model.RequestModel;
-import org.hippoecm.frontend.plugins.standards.datetime.DateTimePrinter;
+import org.hippoecm.frontend.plugins.standards.datetime.DateTimePrinter$;
 import org.hippoecm.frontend.plugins.standards.icon.HippoIcon;
 import org.hippoecm.frontend.session.UserSession;
 import org.hippoecm.frontend.skin.Icon;
@@ -110,7 +110,7 @@
                 String state = request.getState();

                 final String parameter = schedule != null ?
-                        DateTimePrinter.of(schedule).appendDST().print(FormatStyle.FULL) : "??";
+                        DateTimePrinter$.of(schedule).appendDST().print(FormatStyle.FULL) : "??";
                 return new StringResourceModel("state-" + state, this, null, "unknown", parameter);
             }

Notas finales

Si lo que has usado en tu proyecto con JDK 8 son las APIs de streaming, puedes usar esto.

Aqui un poco de historia.