EclipseLink error: Entity class has no primary key specified. It should define either an @Id, @EmbeddedId or an @IdClass

I had an entity working perfectly until I added a ‘custom’ getter, to return an object array as a string array.

This entity had the @Id attribute properly set, but when deploying, it threw me this nasty error

Entity class [class …] has no primary key specified

I discovered after some time wondering what had just happened, that if you use EclipseLink < 2.6.0 and deploy to Glassfish (very common if you use Netbeans IDE), if you add lambda expressions on entities it just goes wrong. bug

This is my entity class and the seemingly innocent culprit method

@Entity
public class Carpeta implements Serializable {

    @Id
    @NotNull
    @Column(name = "ID")
    private String id;

    ... other fields

    @OneToMany(mappedBy = "acumulada")
    @JsonIgnore
    private Collection<Carpeta> carpetasAcumuladas;

    public Carpeta() {
    }

    ... other getter and setters

    public Collection<Carpeta> getCarpetasAcumuladas() {
        return carpetasAcumuladas;
    }

    /** custom getter using lambda expression **/
    public List<String> getNucsAcumulados(){
        return getCarpetasAcumuladas().stream().map(carpeta -> {
            return carpeta.getNuc();
        }).collect(Collectors.toList());
    }

}

to solve the issue, just remove lambda expressions from your entities if you're using EclipseLink < 2.6.0 on Glassfish.

In our case

    public List<String> getNucsAcumulados(){
        List<String> nucs = new ArrayList<>();
        for (Carpeta carpeta : getCarpetasAcumuladas()) {
            nucs.add(carpeta.getNuc());
        }
        return nucs;
    }

we can cache the array instead of returning a new
array each time

    private List<String> nucsAcumulados = null;

    public List<String> getNucsAcumulados(){
        if (nucsAcumulados == null) {
            nucsAcumulados = new ArrayList<>();
            for (Carpeta carpeta : getCarpetasAcumuladas()) {
                nucsAcumulados.add(carpeta.getNuc());
            }
        }
        return nucsAcumulados;
    }

sources:
https://stackoverflow.com/a/35623026/961652
https://bugs.eclipse.org/bugs/show_bug.cgi?id=429992

Anuncios

GWT – Casting Overlay Types Arrays

Para la implementación de un sistema de mensajes utilicé los llamados overlay types de GWT. Declaré una clase de la siguiente manera

    public class SystemMessage extends JavaScriptObject {</p><pre><code>    protected SystemMessage() {
    }

    public final native String getId()/*-{
        return this.id;
    }-*/;

    public final native String getHtml()/*-{
        return this.html;
    }-*/;
}
</code></pre><p>

Agregué a esta clase un método para obtener los mensajes del sistema desde un servicio REST

    public static void getMessages(final AsyncCallback<SystemMessage[]> callback) {
        try {
            RequestBuilder rb = new RequestBuilder(RequestBuilder.GET,
                    URLRESTSYSTEMMESSAGES);
            rb.sendRequest(null, new RequestCallback() {</p><pre><code>            @Override
            public void onResponseReceived(Request request,
                    Response response) {
                if (response.getStatusCode() == 200) {
                    JavaScriptObject jsResponse = JSON.decode(response
                            .getText());
                    DSResponse dsResponse = new DSResponse(JSOHelper
                            .getAttributeAsJavaScriptObject(jsResponse,
                                    "response"));
                    if (dsResponse.getStatus() == 0) {
                        JavaScriptObject[] array = JSOHelper
                            .getAttributeAsJavaScriptObjectArray(dsResponse.getJsObj(),
                                        "data");
                        SystemMessage[] messages = (SystemMessage[]) array;
                        callback.onSuccess(messages);
                    }
                } else {
                    callback.onFailure(new Exception(response.getText()));
                }
            }

            @Override
            public void onError(Request request, Throwable exception) {
                callback.onFailure(exception);
            }
        });
    } catch (Exception e) {
        callback.onFailure(e);
    }
}
</code></pre><p>

El servidor nos regresa una respuesta en JSON parecida a la siguiente

    {"response":{ "status": 0, "data": [{"id":"1", "html": "Mensaje 1 
"},{"id":"2", "html": "Mensaje 2 
"}]}}

El problema es que este código no marca errores, incluso funciona correctamente cuando lo ejecuto en DevMode. Pero cuando hice el deploy en un servidor JBoss e intenté ejecutarlo, simplemente no me mostraba los mensajes pero tampoco registraba algún error en la consola. Después de muchísimas líneas de log.info() llegué a la conclusión que el problema era el casting del array. Intenté hacer un ‘cast’ de un array de tipo JavascriptObject[] a uno de tipo SystemMessage[]

    SystemMessage[] messages = (SystemMessage[]) array;

Para hacer el casting de un array en Java tenemos que hacer el casting elemento por elemento.

    SystemMessage[] messages = new SystemMessage[array.length];
    for(int i=0; i<array.length; i++){
        messages[i] = (SystemMessage) array[i];
    }

Nota: intenté utilizar el método Arrays.copyOf() pero éste no ha sido implementado en GWT. Si intentas utilizarlo te arrojará una error parecido al siguiente

The method copyOf(JavaScriptObject[], int, Class) is undefined for the type Arrays

Oracle Application Server – Deployed application throwing an error – NoClassDefFoundError

Desarrollé una aplicación con servicios web mediante el framework Apache CXF. Localmente funcionaba bien, lo probé mediante maven y el jetty-maven-plugin, con la ayuda de SoapUI para invocar a los servicios.

Pues cuando hice el deploy en un viejo Application Server de Oracle que utiliza OC4J, me arrojaba varios errores de que no encontraba ciertas clases (NoClassDefFoundException). El problema es que cuando ejecutas una aplicación en un contenedor, esta hereda el classpath de este. Así que,si OC4J cargó una versión vieja de log4J -por ejemplo- y utilizas una función disponible sólo en versiones más recientes, tendrás estos problemas.

Normalmente deberás haber incluído las dependencias en la carpeta WEB-INF/lib (ya sea mediante maven o manualmente). Para indicarle al contenedor que deseamos utilizar las librerías que hemos incluído en esta carpeta -desde la consola Enterprise Manager- al hacer el deploy desactiva la opción “Inherit parent application’s shared library imports”. Ó puedes crear un EAR que contenga tu WAR y agrega un archivo META-INF/orion-application.xml con el siguiente contenido

</pre>
<?xml version="1.0" encoding="UTF-8"?>
<orion-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://xmlns.oracle.com/oracleas/schema/orion-application-10_0.xsd"
deployment-version="10.1.3.4.0" default-data-source="jdbc/OracleDS"
component-classification="external" schema-major-version="10"
schema-minor-version="0">

<imported-shared-libraries>
<remove-inherited name="*"></remove-inherited>
</imported-shared-libraries>

</orion-application>
<pre>

Fuentes:
logging – Oracle application server deployed application throwing an error – NoClassDefFoundError – Stack Overflow
.
http://docs.oracle.com/html/E13979_01/classload.htm#CIHJHADG

BIRT – Publicar tus reportes en JBoss

Para publicar los reportes creados con BIRT en un servidor de aplicaciones no hay nada más fácil que utilizar el Report Viewer. En JBoss AS7 es tan sencillo como copiar el archivo birt.war -que viene con el BIRT Runtime– en el directorio {$as7home}/standalone/deployments, abrir el archivo birt.war y agregar el reporte que deseas.

Una vez hecho esto, abres el navegador y accedes a http://localhost:8080/birt/frameset?__report=rptSentenciasPendientes.rptdesign
Obviamente tienes que cambiar el parámetro “__report” por el nombre de tu reporte. Listo, ya deberías ver tu reporte!

En mi caso me arrojaba el error

org.eclipse.birt.report.engine.api.EngineException: Can not load the report query: 104. Errors occurred when generating the report document for the report element with ID 104. (Element ID:104)
    at org.eclipse.birt.report.engine.data.dte.DataPresentationEngine.doExecuteQuery(DataPresentationEngine.java:160)
    at org.eclipse.birt.report.engine.data.dte.AbstractDataEngine.execute(AbstractDataEngine.java:267)
    at org.eclipse.birt.report.engine.executor.ExecutionContext.executeQuery(ExecutionContext.java:1905)

Supe entonces que había un problema al ejecutar una consulta. Recordé que los datos provienen de una base de datos de Oracle y por lo tanto necesitaba colocar el controlador de la base de datos, ojdbc6.jar, en el directorio WEB-INF/lib.

Con ello los errores desaparecieron y el reporte se generó correctamente.

GWT+RestEasy en JBoss AS7

Recientemente instalé JBoss AS7 y quede impresionado con su velocidad y rendimiento. Rápidamente quise migrar un proyecto a este servidor pero desafortunadamente no funcionó. Esto debido a que en la aplicación utilizo servicios REST con Jersey y esto ocasionó un error. Fue entonces que decidí utilizar RestEasy en vez de Jersey.

Lo primero que necesitamos es crear una aplicación de GWT con soporte para Maven. Una vez hecho esto, abrimos el archivo pom.xml y agregamos el repositorio de JBoss donde se encuentran las librerías de RestEasy

    <repositories>
        <repository>
            <id>jboss-public-repository-group</id>
            <name>JBoss Public Maven Repository Group</name>
            <url>http://repository.jboss.org/nexus/content/groups/public-jboss/</url>
            <layout>default</layout>
        </repository>
    </repositories>

luego agregamos la dependencia a RestEasy

<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.2.1.GA</version>
</dependency>
</dependencies>

Por último tendríamos que configurar el archivo web.xml. La configuración va a depender de si vamos a depurar/ejecutar la aplicación en “Web Development Mode” (Debug As->Web Application) o si vamos a instalar la aplicación en el servidor JBoss AS7.
Para la primera opción necesitamos agregar lo siguiente

<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/api</param-value>
</context-param>
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>resteasy-servlet</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>resteasy-servlet</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>

Y si vamos a generar el archivo war y colocarlo en JBoss AS7 (Run As -> Maven build…   goals: clean package) tenemos que sustituir las líneas anteriores por las siguientes:

<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>

Fuentes:
http://www.mkyong.com/ (resteasy-hello-world-example)
RestEasy – User Guide
JBoss AS7 Documentation – JAX-RS

Eclipse – Deploy App Engine lanza UnknownHostException

Tengo instalado el Google Plugin for Eclipse. Hace poco intenté desplegar (deploy) una aplicación en el Google App Engine con el botón “Deploy App Engine Project” pero después de un rato de espera me arrojaba el error:

Could not sign in: java.net.UnknownHostException: http://www.google.com

Verifiqué que tuviera acceso a internet abriendo el sitio indicado en mi navegador, Después volví a intentar pero sucedió lo mismo. Lo que noté es que cuando ponía esa url en el navegador me redireccionaba al sitio de mi país: http://www.google.com.mx
Para que me mostrara el sitio tal cual, tuve que dar clic en un enlace en la parte de abajo donde dice Google.com in English.
Eclipse tiene su propio navegador, así que intente abrir el sitio en él (Window -> Show View-> Other… Internal Web Browser) y sucedió lo mismo la primera vez, me redireccionó. Pero una vez que di clic en el enlace, ya me mostró el sitio en inglés. Después de esto intente nuevamente el deploy y ya fuincionó correctamente.

Eclipse – Resolver dependencias con Ant en proyectos de GWT

Uno de mis dolores de cabeza con Eclipse, es la resolucion de las dependencias. En Visual Studio cuando agregamos una dependencia a una libreria o proyecto, sus respectivas dll’s son copiadas al directorio de salida -donde queda el ejecutable de la aplicacion o al directorio bin en el caso de aplicaciones web- al momento de compilar. En Eclipse esto no pasa , en realidad, no se como ni si sea posible. Para resolver las dependencias, Eclipse usa el classpath y no tiene problemas a la hora de ejecutar o depurar la aplicacion desde el IDE. Pero cuando intentas copiar (deploy) la aplicacion a un servidor vienen los problemas ya que no se encuentran los JAR’s dependientes en el direcotrio WEB-INF/lib. Para resolverlo basta con exportar cada uno de los JAR’s de los proyectos dependientes en esa carpeta de forma manual. Este procedimiento se vuelve muy tedioso conforme aumenta el numero de dependencias o actualizas los proyectos dependientes frecuentemente.

Como la mayoria de proyectos que genero son de GWT, he creado un script de Ant que copia el directorio war a una carpeta llamada “deployme” y luego resuelve las dependencias recusrivamente generando y copiando los JAR’s necesarios a la carpeta “deployme/WEB-INF/lib”. Una vez hecho esto, con las dependencias resueltas, podemos copiar la carpeta “deployme” al servidor 😀
Para ejecutar el script necesitas primero instalar ant4eclipse y ant-contrib, asi como instalar el soporte de scripts de javascript.
La seccion con las etiquetas de “jdtClassPathLibrary” son para que ant4eclipse pueda resolver las librerias de usuario, si no tuvieras ninguna, solo necesitas indicarle como resolver la de “com.google.gwt.eclipse.core.GWT_CONTAINER”

<?xml version="1.0" encoding="UTF-8"?>
<project name="gwt-dependency-build" default="build" basedir="." 
	xmlns:a4e="antlib:org.ant4eclipse" 
	xmlns:ac="antlib:net.sf.antcontrib">

	<property name="web-project" value="MI_PROYECTO_GWT" />
	<property name="workspace" value="${basedir}/.." />

	<a4e:jdtClassPathLibrary name="org.eclipse.jdt.USER_LIBRARY/SmartGwt">
		<fileset dir="/home/vladimir/Projects/Librerias/smartgwt-2.2/" includes="*.jar" />
	</a4e:jdtClassPathLibrary>
	<a4e:jdtClassPathLibrary name="com.google.gwt.eclipse.core.GWT_CONTAINER">
		<fileset dir="/opt/eclipse-helios/plugins/com.google.gwt.eclipse.sdkbundle.2.0.4_2.0.4.v201006301309/gwt-2.0.4/" includes="gwt-user.jar, gwt-dev.jar, gwt-servlet.jar" />
	</a4e:jdtClassPathLibrary>
	<a4e:jdtClassPathLibrary name="org.eclipse.jdt.USER_LIBRARY/GwtCKEditor">
	  <fileset dir="/home/vladimir/Projects/Librerias/ckeditor-gwt" includes="gwt-ckeditor.jar"/>
	</a4e:jdtClassPathLibrary >
	<a4e:jdtClassPathLibrary name="org.eclipse.jdt.USER_LIBRARY/Gson">
	  <fileset dir="/home/vladimir/Projects/Librerias/google-gson-1.4" includes="gson-1.4.jar" />
	</a4e:jdtClassPathLibrary >
	
	<ac:var name="dependencyList" value="" />

	<target name="build">
		<list-projects project="${web-project}" />
		<script language="javascript">
		  <![CDATA[
			importPackage(java.lang, java.util);
			
		    var jars = project.getProperty("dependencyList").split(",");
			var tmp=new ArrayList();
			for(i=0;jars[i]!=null;i++){
				if(!tmp.contains(jars[i])){
					tmp.add(jars[i]);
				}
			}
			var dependencies=tmp.toString();
		    project.setProperty("dependencies", dependencies.substring(1,dependencies.length()-1));
		  ]]>
		  </script> 
		<echo>Dependencies found: ${dependencies}</echo>
		<echo>Copying "war" directory to "deployme"</echo>
		<copy todir="deployme">
			<fileset dir="war" />
		</copy>
		<a4e:getJdtOutputPath workspaceDirectory="${workspace}" projectName="${web-project}" property="web-project-classes-dir" />
		<ac:for list="${dependencies}" param="dependency" trim="true">
			<sequential>
				<a4e:getProjectDirectory workspaceDirectory="${workspace}" projectName="@{dependency}" property="dependency-dir" />
				<a4e:getJdtOutputPath workspaceDirectory="${workspace}" projectName="@{dependency}" property="dependency-classes-dir" />
				<ac:if>
					<not>
						<equals arg1="@{dependency}" arg2="${web-project}" />
					</not>
					<ac:then>
						<echo>Compiling ${dependency-classes-dir} into deployme/WEB-INF/lib/@{dependency}.jar</echo>
						<jar destfile="deployme/WEB-INF/lib/@{dependency}.jar" basedir="${dependency-classes-dir}" />
					</ac:then>
				</ac:if>
				
			</sequential>
		</ac:for>
	</target>

	<macrodef name="list-projects">
		<attribute name="project" />
		<attribute name="prefix" default="recurse" />
		<sequential>
			<a4e:getUsedProjects property="@{prefix}.usedProjects" workspaceDirectory="${basedir}/.." projectName="@{project}" separator="," />
			
			<ac:for param="usedProject" list="${@{prefix}.usedProjects}" delimiter=",">
				<sequential>
					<list-projects project="@@{usedProject}" />
				</sequential>
			</ac:for>
			<ac:if>
				<equals arg1="" arg2="${dependencyList}"/>
				<then><ac:var name="dependencyList" value="@{project}" /></then>
				<else><ac:var name="dependencyList" value="${dependencyList},@{project}" /></else>
			</ac:if>
		</sequential>
	</macrodef>
</project>