Ebean #Transactional annotation not working on Heroku - heroku

I have a Play Framework 2.1.1 app running on Heroku.
I've written a bunch of new code in my local dev environment, and it works fine, but when I deploy it to Heroku the app crashes and I get this in the logs:
Configuration error: Configuration error[Cannot register class [models.Article] in Ebean server]
Oops, cannot start the server.
...
Caused by: java.lang.NoClassDefFoundError: com/avaje/ebean/bean/EnhancedTransactional
...
Caused by: java.lang.ClassNotFoundException: com.avaje.ebean.bean.EnhancedTransactional
...
Process exited with status 255
State changed from starting to crashed
When I remove the #Transactional annotations from my models, I no longer get the errors.
Here is an example of how I'm using #Transactional in one of my models:
#Transactional
public static Product find(Long id) {
return find.byId(String.valueOf(id));
}
Has anyone experienced this or have any idea why it works fine locally, but not in Heroku?
Per request, here is my build.scala
import sbt._
import Keys._
import play.Project._
object ApplicationBuild extends Build {
val appName = "myapp"
val appVersion = "1.0-SNAPSHOT"
val appDependencies = Seq(
// Add your project dependencies here,
"postgresql" % "postgresql" % "8.4-702.jdbc4",
"com.typesafe" %% "play-plugins-mailer" % "2.1.0",
"com.stripe" % "stripe-java" % "1.15.1",
"com.amazonaws" % "aws-java-sdk" % "1.3.11",
javaCore,
javaJdbc,
javaEbean
)
val main = play.Project(appName, appVersion, appDependencies).settings(
// Add your own project settings here
)
}

Related

How to use SpringBoot actuator over JMX

I am having existing Spring Boot application and I want to do monitoring the application through actuator.I tried with http endpoints and it is working fine for me. Instead of http end points I need JMX end points for my existing running application.
If you add spring-boot-starter-actuatordependency in your build.gradle or pom.xml file you will have JMX bean enabled by default as well as HTTP Endpoints.
You can use JConsole in order to view your JMX exposed beans. You'll find more info about this here.
More details about how to access JMX endpoints here.
Assuming you're using a Docker image where the entry point is the Spring Boot app using java in which case the PID is "1" and so would the Attach API's Virtual Machine ID. You can implement a health probe as follows.
import com.sun.tools.attach.spi.AttachProvider;
import java.util.Map;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
public class HealthProbe {
public static void main(String[] args) throws Exception {
final var attachProvider = AttachProvider.providers().get(0);
final var virtualMachine = attachProvider.attachVirtualMachine("1");
final var jmxServiceUrl = virtualMachine.startLocalManagementAgent();
try (final var jmxConnection = JMXConnectorFactory.connect(new JMXServiceURL(jmxServiceUrl))) {
final MBeanServerConnection serverConnection = jmxConnection.getMBeanServerConnection();
#SuppressWarnings("unchecked")
final var healthResult =
(Map<String, ?>)
serverConnection.invoke(
new ObjectName("org.springframework.boot:type=Endpoint,name=Health"),
"health",
new Object[0],
new String[0]);
if ("UP".equals(healthResult.get("status"))) {
System.exit(0);
} else {
System.exit(1);
}
}
}
}
This will use the Attach API and make the original process start a local management agent.
The org.springframework.boot:type=Endpoint,name=Health object instance would have it's health method invoked which will provide a Map version of the /actuator/health output. From there the value of status should be UP if things are ok.
Then exit with 0 if ok, or 1 otherwise.
This can be embedded in an existing Spring Boot app so long as loader.main is set. The following is the HEALTHCHECK probe I used
HEALTHCHECK --interval=5s --start-period=60s \
CMD ["java", \
"-Dloader.main=net.trajano.swarm.gateway.healthcheck.HealthProbe", \
"org.springframework.boot.loader.PropertiesLauncher" ]
This is the technique I used in distroless Docker Image.
Side note: Don't try to put this in a CommandLineRunner interface because it will try to pull the configuration from the main app and you likely won't need the whole web stack.

Microservice with Spring Boot

I'm working in Windows 7. I've Spring CLI v1.5.3.RELEASE installed. In a working directory, using command
spring init --build maven --groupId com.redhat.examples
--version 1.0 --java-version 1.8 --dependencies web
--name hola-springboot hola-springboot
I created holo-springboot app. Then navigated to hola-springboot directory,ran
$ mvn spring-boot:run
The application run. Going to http://localhost:8080, I do see Whitelabel error page. Whereafter, I tried to add helloworld fuctionality. That is, in the app, in the packeage com.example, I included the following java class.
#RestController
#RequestMapping("/api")
public class HolaRestController {
#RequestMapping(method = RequestMethod.GET, value = "/hola",
produces = "text/plain")
public String hola() throws UnknownHostException {
String hostname = null;
try {
hostname = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
hostname = "unknown";
}
return "Hola Spring Boot de " + hostname;
}
}
Re-built from hola-springboot dircetory,
mvn clean package
I get build failure as at
https://pastebin.com/77Ru0w52
I'm unable to figure out. Could somebody help?
I'm following the book Microservices for Java Developers by Christian Posta, Chapter 2, available free at developers Redhat.
Looks like you are missing a dependency on spring boot starter web in your maven pom.xml file https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web/1.5.3.RELEASE.
Or you are not importing the classes correctly.
You are accessing http://localhost:8080 but you have defined a mapping in your rest controller "/hola". So you will have to access the url http://localhost:8080/hola as you do not have any default method in your rest controller.
BuildFailure shows that you have not given import statements in you Class. statements missing are the below
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import java.net.InetAddress;
import java.net.UnknownHostException;
include these and you will be fine.

Running cucumber-groovy features against a spring boot api

I've been attempting to get cucumber-groovy working with spring-boot, but it's not been going well. I get the error org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8080/applicants": Connection refused; nested exception is java.net.ConnectException: Connection refused which seems to indicate that it's hitting the endpoint, but that the service isn't running.
I've read that I need to have a cucumber.xml file, but my project is not using any xml config, it's all annotations, so instead I've got this:
package support
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = "com.base.package")
public class CucumberConfiguration {}
I've added it to the World, but this seems to be the wrong way of doing things (i.e. I don't know how to add an annotation on groovy step defs).
package support
import com.thing.app.Application
import org.junit.runner.RunWith
import org.springframework.boot.test.SpringApplicationContextLoader
import org.springframework.boot.test.WebIntegrationTest
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import org.springframework.test.context.web.WebAppConfiguration
import static cucumber.api.groovy.Hooks.*
//#RunWith(SpringJUnit4ClassRunner)
//#ContextConfiguration(classes = Application, loader = SpringApplicationContextLoader)
//#WebAppConfiguration
//#WebIntegrationTest
#ContextConfiguration(classes = CucumberConfiguration)
public class AbstractTest {
}
World() {
new AbstractTest()
}
Before() {}
After() {}
I left in my other annotations to kind of show what I've done so far. None of it has worked.
I've also tried setting up an AbstractDefs class as seen here https://github.com/jakehschwartz/spring-boot-cucumber-example/tree/master/src/test/java/demo, but that also hasn't worked, mostly because I'm not using the cucumber-java style of things, but instead the cucumber-groovy style, which doesn't use step definition classes.
Edit: Just discovered I was doing things wrong by having an env.groovy, I'm used to the ruby cucumber, so I'm having trouble finding all the little problems. Still am having the same issue though, I don't know how to execute in a Spring context.
You can instantiate Spring test context with io.cucumber.spring.SpringFactory and register adapter in World to allow groovy script has access to Spring beans:
env.groovy:
#ContextConfiguration(classes = TestConfiguration, loader = SpringBootContextLoader)
class CucumberContextConfiguration {
}
//adapter bypassing World properties to Spring context
class SpringFactoryWorldAdapter {
private final SpringFactory factory;
SpringFactoryWorldAdapter(SpringFactory factory) {
this.factory = factory;
}
#Override
Object getProperty(String s) {
return factory.testContextManager.getContext().getBean(s);
}
}
def factory; //Keep state to prevent repeated context initialization
World { args ->
if (factory == null) {
factory = new SpringFactory()
factory.addClass(CucumberContextConfiguration)
factory.start()
}
new SpringFactoryWorldAdapter(factory)
}

ShrinkWrap Maven resolver can't find war artifact in local repo

I've an integration test where I'm deploying 2 web services in Wildfly using Arquillian and ShrinkWrap resolver. Both services are independent such that neither is dependent on the other in any Maven dependency sense. Service 2 makes a HTTP call to service 1. This is purely a B2B scenario where one component calls out to another.
That said, here's my test. Problem is, Arquillian fails to deploy the service 1. Since ShrinkWrap error messages are just useless (there're actually no messages), I'm trying to figure out what am I doing wrong. I've verified that the artifact for service 1 does exist in my local Maven repo.
#Deployment(name = AVAILABILITY_SERVICE_NAME, order = 1)
public static WebArchive createAvailabilityServiceDeployment() {
WebArchive availabilityService = Maven.configureResolver()
.workOffline().withMavenCentralRepo(false)
.withClassPathResolution(true)
.resolve(AVAILABILITY_SERVICE_MVN_COORD).withoutTransitivity()
.asSingle(WebArchive.class);
System.out.println(availabilityService.toString(true));
return availabilityService;
}
#Deployment(name = APPOINTMENT_SERVICE_NAME, order = 2)
public static WebArchive createAppointmentServiceDeployment()
throws FileNotFoundException {
WebArchive appointmentService = create(WebArchive.class,
APPOINTMENT_SERVICE_NAME + ".war").addPackages(true,
Filters.exclude(".*Test.*"), AppointmentApp.class.getPackage())
.addAsWebInfResource(EmptyAsset.INSTANCE,
ArchivePaths.create("beans.xml"));
System.out.println(appointmentService.toString(true));
return appointmentService;
}
java.lang.RuntimeException: Could not invoke deployment method: public static org.jboss.shrinkwrap.api.spec.WebArchive name.abhijitsarkar.microservices.appointment.AppointmentResourceIT.createAvailabilityServiceDeployment()
at org.jboss.shrinkwrap.resolver.spi.format.FormatProcessors.find(FormatProcessors.java:53)
at org.jboss.shrinkwrap.resolver.impl.maven.MavenFormatStageImpl.as(MavenFormatStageImpl.java:82)
at org.jboss.shrinkwrap.resolver.impl.maven.MavenFormatStageImpl.asSingle(MavenFormatStageImpl.java:100)
at name.abhijitsarkar.microservices.appointment.AppointmentResourceIT.createAvailabilityServiceDeployment(AppointmentResourceIT.java:50)
Apparently, it needs to be done in 2 steps. Hope it helps someone else.
private static WebArchive createDependentServiceDeployment(String name) {
String mvnCoordinate = join(":", DEPENDENT_SERVICE_GROUP, name,
DEPENDENT_SERVICE_PACKAGING, DEPENDENT_SERVICE_VERSION);
File service = Maven.configureResolver().workOffline()
.withMavenCentralRepo(false).withClassPathResolution(true)
.resolve(mvnCoordinate).withoutTransitivity().asSingleFile();
return ShrinkWrap.create(ZipImporter.class,
join(".", name, DEPENDENT_SERVICE_PACKAGING))
.importFrom(service).as(WebArchive.class);
}

Invoking remote service using JMS

I have two projects one the service project another one consumer project,
Consumer project consumes the services of other project and the call should be async using JMS
I installed jms plugin in both of the projects
I have defined the JMSConnectionFactory in both of the project as below in resources.groovy
import org.springframework.jms.connection.SingleConnectionFactory
import org.apache.activemq.ActiveMQConnectionFactory
beans = {
jmsConnectionFactory(org.apache.activemq.ActiveMQConnectionFactory) { brokerURL = 'vm://localhost' }
}
Note: Both of the project are for now on same machine (i.e. localhost)
Now from consumer's controller I am making call to service from ServiceProvider project
jmsService.send(service:'serviceProvider', params.body)
In ServiceProvider the service is defined as follow
import grails.plugin.jms.*
class ServiceProviderService {
def jmsService
static transactional = true
static exposes = ['jms1']
def createMessage(msg) {
print "Called1"
sleep(2000) // slow it down
return null
}
}
now when controller submits the call to service it gets submitted successfully but doesn't reach to the actual service
I also tried
jmsService.send(app: "ServiceProvider", service: "serviceProvider", method: "createMessage", msg, "standard", null)
Update
Now I have installed activeMQ plugin to service provider to make it embedded broker (jms is already there)
and created a service
package serviceprovider
class HelloService {
boolean transactional = false
static exposes = ['jms']
static destination = "queue.notification"
def onMessage(it){
println "GOT MESSAGE: $it"
}
def sayHello(String message){
println "hello"+message
}
}
resources.groovy in both of the project is now
import org.springframework.jms.connection.SingleConnectionFactory
import org.apache.activemq.ActiveMQConnectionFactory
beans = {
jmsConnectionFactory(org.apache.activemq.ActiveMQConnectionFactory) { brokerURL = 'tcp://127.0.0.1:61616' }
}
from consumer's controller I am calling this service like below
jmsService.send(app:'queue.notification',service:'hello',method: 'sayHello', params.body)
call to method gets submitted but actually it is not getting called!
The in vm activemq config (vm://localhost) works only within a single VM. If your 2 projects run in separate VMs try setting up an external AMQ broker.
if you are using separate processes, then you need to use a different transport than VM (its for a single VM only), also, is one of your processes starting a broker? If not, then one of them should embed the broker (or run it externally) and expose it over a transport (like TCP)...

Resources