2011-11-09 25 views
5

Estoy investigando Heroku como una plataforma y estoy tratando de obtener una aplicación de Java básica para ejecutar en ella. La aplicación web ya se compila y funciona con Maven (utilizando Tomcat y el complemento de carga-maven), así que pensé que esto debería ser muy fácil dado que Heroku usa Maven para administrar sus tareas de instalación/implementación.Heroku - ¿Puedo llamar a Maven desde Procfile?

Sin embargo, ese no es el caso, ya que no puedo hacer que la cosa realmente se inicie. Mi Procfile tiene los siguientes en él:

web: sh ./startServer-heroku.sh 

Y el startServer-heroku.sh es simplemente:

mvn clean install cargo:start -Dcargo.maven.wait=true 

Esto funciona bien cuando pruebo localmente utilizando el comando foreman start, tal como se describe en la documentación de tutorial Heroku. Pero cuando lo intento en el servidor real Heroku, consigo los siguientes mensajes de registro:

2011-11-09T02:30:27+00:00 heroku[web.1]: State changed from created to starting 
2011-11-09T02:30:27+00:00 heroku[slugc]: Slug compilation finished 
2011-11-09T02:30:33+00:00 heroku[web.1]: Starting process with command `sh ./startServer-heroku.sh` 
2011-11-09T02:30:33+00:00 app[web.1]: ./startServer-heroku.sh: 1: mvn: not found 
2011-11-09T02:30:33+00:00 heroku[web.1]: Process exited 
2011-11-09T02:30:34+00:00 heroku[web.1]: State changed from starting to crashed 

Parece que mvn está en ninguna parte puede conocer en la PATH del sistema, por lo que el comando está fallando.

¿Es posible invocar mvn desde Heroku Procfile? ¿Y hay algún lugar que tenga una lista definitiva de comandos que están disponibles y no están disponibles en el Procfile?

+0

eche un vistazo a [jcabi-heroku-maven-plugin] (http://www.jcabi.com/jcabi-heroku-maven-plugin/index.html), que automatiza todo el proceso de implementación en Heroku. tal vez te ayude en tu caso. – yegor256

Respuesta

2

La respuesta de James proporciona buenas instrucciones para hacer que Jetty funcione en Heroku, y su comentario incluye un enlace a una buena referencia sobre el uso de Tomcat integrado. Pero también es posible ejecutar la versión estándar e independiente de Tomcat en Heroku. Así es como yo era capaz de conseguir que funcione:

En primer lugar, configurar el POM para instalar y configurar Tomcat como parte de su construcción, y también para implementar la aplicación a la instancia de Tomcat instalada:

 <plugin> 
      <groupId>org.codehaus.cargo</groupId> 
      <artifactId>cargo-maven2-plugin</artifactId> 
      <configuration> 
       <container> 
        <containerId>tomcat6x</containerId> 
        <zipUrlInstaller> 
         <url>http://archive.apache.org/dist/tomcat/tomcat-6/v6.0.18/bin/apache-tomcat-6.0.18.zip</url> 
        </zipUrlInstaller> 
        <dependencies> 
         <dependency> 
          <groupId>javax.activation</groupId> 
          <artifactId>activation</artifactId> 
         </dependency> 
         <dependency> 
          <groupId>javax.mail</groupId> 
          <artifactId>mail</artifactId> 
         </dependency> 
        </dependencies> 
       </container> 
       <configuration> 
        <type>standalone</type> 
        <deployables> 
         <deployable> 
          <groupId>com.yourcompany.name</groupId> 
          <artifactId>yourArtifact</artifactId> 
          <type>war</type> 
          <properties> 
           <context>ROOT</context> 
          </properties> 
         </deployable> 
        </deployables> 
       </configuration> 
      </configuration> 
      <executions> 
       <execution> 
        <phase>package</phase> 
        <goals> 
         <goal>install</goal> 
         <goal>configure</goal> 
         <goal>deploy</goal> 
         <goal>package</goal> 
        </goals> 
       </execution> 
      </executions> 
     </plugin> 

a continuación, crear un archivo de server.xml recortado hacia abajo que funcionará en Heroku:

<?xml version='1.0' encoding='utf-8'?> 
<Server port="-1"> 
    <Listener className="org.apache.catalina.core.JasperListener" /> 
    <Service name="Catalina"> 
     <Connector port="${http.port}" protocol="HTTP/1.1" connectionTimeout="20000"/> 
     <Engine name="Catalina" defaultHost="localhost"> 
      <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"/> 
     </Engine> 
    </Service> 
</Server> 

... esto es necesario debido a que su aplicación Heroku sólo se le permite unirse a un solo puerto (que cambia cada vez que una nueva instancia se crea y se especifica en elVariable de entorno). Intentar enlazar a cualquier otro puerto bloqueará su aplicación. Como el puerto es dinámico, se debe pasar al server.xml a través de la propiedad del sistema http.port, pero lo abordaremos más adelante.

Mientras estás en ello, también crea un archivo persistence.xml que trabajará con Heroku:

<?xml version="1.0" encoding="UTF-8"?> 
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
    http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> 
    <persistence-unit name="quiz_devel"> 
    <provider>org.hibernate.ejb.HibernatePersistence</provider> 
    <exclude-unlisted-classes>false</exclude-unlisted-classes> 
    <properties> 
     <property name="hibernate.archive.autodetection" value="class"/> 
     <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/> 
     <property name="hibernate.hbm2ddl.auto" value="update"/> 
     <property name="hibernate.show.sql" value="true"/> 
     <property name="hibernate.c3p0.acquire_increment" value="1"/> 
     <property name="hibernate.c3p0.idle_test_period" value="10"/> 
     <property name="hibernate.c3p0.max_size" value="20"/> 
     <property name="hibernate.c3p0.max_statements" value="40"/> 
     <property name="hibernate.c3p0.min_size" value="1"/> 
     <property name="hibernate.c3p0.timeout" value="30"/> 
    </properties> 
    </persistence-unit> 
</persistence> 

Tenga en cuenta que no hay hibernate.connection.url especificado aquí. Esto se debe a que Heroku especifica la URL de la base de datos que su aplicación debe usar en la variable de entorno $DATABASE_URL.

Ahora es el momento para crear un script sencillo que configura el medio ambiente y pone todo de modo que Tomcat en realidad puede funcionar:

#point to the correct configuration and webapp 
CATALINA_BASE=`pwd`/target/cargo/configurations/tomcat6x 
export CATALINA_BASE 

#copy over the Heroku config files 
cp ./server-heroku.xml ./target/cargo/configurations/tomcat6x/conf/server.xml 
cp ./persistence-heroku.xml ./target/cargo/configurations/tomcat6x/webapps/ROOT/WEB-INF/classes/META-INF/persistence.xml 

#make the Tomcat scripts executable 
chmod a+x ./target/cargo/installs/apache-tomcat-6.0.18/apache-tomcat-6.0.18/bin/*.sh 

#set the correct port and database settings 
JAVA_OPTS="$JAVA_OPTS -Dhttp.port=$PORT -Dhibernate.connection.url=$DATABASE_URL" 
export JAVA_OPTS 

#start Tomcat 
./target/cargo/installs/apache-tomcat-6.0.18/apache-tomcat-6.0.18/bin/catalina.sh run 

esto está haciendo una serie de cosas:

  1. Se le dice a Tomcat que use los artefactos de configuración e implementación que cargó el paquete como parte de su compilación, configurando CATALINE_BASE para que apunte al lugar correcto.
  2. Sobrescribe los archivos predeterminados server.xml y persistence.xml con sus variantes específicas de heroku.
  3. Marca todas las secuencias de comandos de inicio en la instancia de Tomcat que la carga instalada como parte de la construcción como ejecutable.
  4. Especifica los valores para http.port y hibernate.connection.url en función de las variables de entorno proporcionadas por la plataforma Heroku.
  5. Finalmente, ejecuta Tomcat. Tenga en cuenta que no puede usar startup.sh para hacer esto, ya que startup.sh lanzará Tomcat en un nuevo proceso y luego finalizará. Heroku no entiende esto, y cree que la terminación de startup.sh es la finalización del proceso de Tomcat.

Finalmente, el último paso es configurar su Procfile para llamar a su script de inicio, algo a lo largo de las líneas de:

web: sh startServer-heroku.sh 

Con este enfoque puede tener un proyecto que sea compatible con Heroku, a la vez que conserva su capacidad de ejecutar de forma independiente como una aplicación web estándar de Java.

7

Maven no está en la bala que se despliega en dynos. Solo está disponible en tiempo de compilación. Una de las opciones para hacer frente a esto es utilizar el embalaje appassembler-maven-plugin y jar para generar una secuencia de comandos de inicio:

 <plugin> 
      <groupId>org.codehaus.mojo</groupId> 
      <artifactId>appassembler-maven-plugin</artifactId> 
      <version>1.1.1</version> 
      <configuration> 
       <assembleDirectory>target</assembleDirectory> 
       <programs> 
        <program> 
         <mainClass>foo.Main</mainClass> 
         <name>webapp</name> 
        </program> 
       </programs> 
      </configuration> 
      <executions> 
       <execution> 
        <phase>package</phase> 
        <goals> 
         <goal>assemble</goal> 
        </goals> 
       </execution> 
      </executions> 
     </plugin> 

Entonces el Procfile sería:

web: sh target/bin/webapp 

Otra opción es la maven-dependency-plugin y war embalaje:

 <plugin> 
      <groupId>org.apache.maven.plugins</groupId> 
      <artifactId>maven-dependency-plugin</artifactId> 
      <version>2.3</version> 
      <executions> 
       <execution> 
        <phase>package</phase> 
        <goals> 
         <goal>copy</goal> 
        </goals> 
        <configuration> 
         <artifactItems> 
          <artifactItem> 
           <groupId>org.mortbay.jetty</groupId> 
           <artifactId>jetty-runner</artifactId> 
           <version>7.5.3.v20111011</version> 
           <destFileName>jetty-runner.jar</destFileName> 
          </artifactItem> 
         </artifactItems> 
        </configuration> 
       </execution> 
      </executions> 
     </plugin> 

Con un Procfile de:

web: java $JAVA_OPTS -jar target/dependency/jetty-runner.jar --port $PORT target/*.war 
+0

Gracias, eso tiene sentido, aunque su ejemplo está usando Jetty. ¿Sabes si es posible hacer algo similar con Tomcat? – aroth

+1

Tomcat no tiene el equivalente de embarcadero-corredor, pero todavía puede implementarlo con Tomcat de varias maneras. Uno está usando Tomcat integrado. Consulte este artículo que acaba de publicarse: http://devcenter.heroku.com/articles/create-a-java-web-application-using-embedded-tomcat –

0

Sí, puede llamar a maven desde su archivo de registro.

Para hacerlo es necesario incluir experto en su babosa haciendo una pequeña modificación en el guión 'compilación' en el paquete java build como se describe en el README.md file from the "heroku-buildpack-java" project:

Por ejemplo, si usted quiere tener Maven disponibles para su uso en tiempo de ejecución en su aplicación, simplemente tienes que copiar desde el directorio de caché para el directorio de construcción mediante la adición de las siguientes líneas al script de compilación:

for DIR in ".m2" ".maven" ; do 
cp -r $CACHE_DIR/$DIR $BUILD_DIR/$DIR 
done 

I added these lines r ight después de la descarga de maven, y antes de cambiar a BUILD_DIR.

En su Procfile a continuación, puede llamar .maven/bin/mvn

Si usted está construyendo un artefacto (guerra/frasco) que se puede ejecutar desde Java o en un contenedor de servlets, entonces probablemente debería hacer algo en el camino de las otras respuestas, pero si realmente necesitas ejecutar Maven, entonces este es el camino.