We want to implement a REST-Service on a Jetty, that receives a Heartbeat from multiple Clients and these Heartbeats are stored in a Hashmap to work with them in further functionallities.
Thats how the code looks like at the moment:
The class that should be injected.
public class SharedMemory {
private SharedMemory sharedMemory = null;
private HashMap<String, HeartbeatModel> memory = null;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
public SharedMemory(){
memory = new HashMap<String, HeartbeatModel>();
}
...
}
The binder class:
public class SharedMemoryBinder extends AbstractBinder{
#Override
protected void configure() {
bind(new SharedMemory()).to(SharedMemory.class);
}
}
The feature class:
public class SharedMemoryFeature implements Feature{
#Override
public boolean configure(FeatureContext context) {
context.register(new SharedMemoryBinder());
return true;
}
}
The service class
#Path("/HeartbeatService")
public class GCEHeartbeatService {
#Inject
private SharedMemory sharedMemory;
#POST
#Path("/postHeartbeat")
#Consumes(MediaType.APPLICATION_JSON)
public Response postHeartbeat(Heartbeat heartbeat){
sharedMemory.storeToMemory(heartbeat);
return Response.ok().build();
}
...
}
The application class:
#ApplicationPath("/")
public class Application extends javax.ws.rs.core.Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(GCEHeartbeatService.class);
classes.add(SharedMemoryFeature.class);
return classes;
}
}
These are the used dependencies:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.17</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.17</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4.1</version>
</dependency>
<dependency>
<groupId>de.hszg</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>jaxrs-ri</artifactId>
<version>2.13</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.5.3</version>
</dependency>
</dependencies>
So with this configuration, i always get a 415 unsupported media type exception when i post a heartbeat to the postHeartbeat service, even with another media type.
"...i always get a 415 unsupported media type exception when i post"
This doesn't seem like a problem related to your DI configuration. It looks like you just need to register the JSON provider. With this particular provider
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.5.3</version>
</dependency>
If you override the registration of resources/provider through classpath scanning (which you have done, simply by overriding getClasses() and returning a non-empty set, then the provider will not be registered.
You can simply register it yourself.
classes.add(JacksonJsonProvider.class);
-- or --
classes.add(JacksonJaxbJsonProvider.class);
If you look in the jar, you will also see an ExceptionMapper you may want to register.
Alternatively, you can use this dependency
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.17</version>
</dependency>
which wraps the above dependency in a auto-discoverable feature, and registers, the provider and the ExceptionMapper. With this dependency, you won't need to register anything. If for some weird reason it isn't register, you can just add it yourself
classes.add(JacksonFeature.class);
As an aside, if portability is not an issue, I would use the ResourceConfig class (which extends Application) instead. It's much easier to work with.
#ApplicationPath("/")
public class AppConfig extends ResourceConfig {
public AppConfig() {
packages("scan.packages.for.resource.and.provider");
register(ResourceOrProvider.class);
register(new MyAppBinder());
}
}
See the ResourceConfig API
Related
I'm looking for a unit test for Controllers using Mockito and PowerMock. Every controller has only private void methods with a single argument and a #Autowired service dependency:
public class ProjectController {
private ProjectServiceImpl service;
#Autowired
public void setInjectedBean(ProjectServiceImpl service) {
this.service = service;
}
private void createProject(String someString) {
// do stuff by calling service.doSomething(someString)
}
}
ProjectServiceImpl is annotated with #Service and has only a public void method with a String as argument. As it is too simple, I didn't bother providing the code.
My last attempt to accomplish that test looks like this:
#RunWith(PowerMockRunner.class)
#ExtendWith(MockitoExtension.class)
#PrepareForTest({ProjectController.class, ProjectServiceImpl.class})
class ProjectControllerTest {
#InjectMocks
private ProjectController controller;
#Mock
private ProjectServiceImplservice;
#Test
void createProject() throws Exception {
controller = PowerMockito.spy(new ProjectController());
PowerMockito.doNothing().when(controller, "createProject", new String(""));
}
}
Question 1: is it possible to make PowerMock work aside of Mockito?
Question 2: using PowerMock, what's the most appropriate way of calling for a private void method with argument?
IMPORTANT: Before marking this post as a duplicate, I personally challenge you finding any content how to do so. It seems like there is just no content showing not only how to make both work together, but make PowerMockito calls for a private void method with one or more arguments.
dependencies used for PowerMock:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
I am trying to use a MongoRepository, but Spring complains, that no instance of the MonoRepository-Interface is in the context.
#SpringBootApplication
public class BackendApplication {
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
}
#Document(collection = "tableItems")
class TableItem {
#Id
public String id;
public int roundNbr;
}
interface TableItemsRepository extends MongoRepository<TableItem, String> {
}
#Service
class TableItemsService {
#Autowired
public TableItemsService(TableItemsRepository tableItemsRepository) {
}
}
The server complains:
Parameter 0 of constructor in backend.TableItemsService required a bean of type 'backend.TableItemsRepository' that could not be found.
Action:
Consider defining a bean of type 'backend.TableItemsRepository' in your configuration.
my maven pom.xml:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>3.4.0</version>
</dependency>
How can I get a bean instance of my MongoRepository?
The dependency is incorrect.
spring-data-mongodb only makes it compile:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-mongodb</artifactId>
<version>3.4.0</version>
</dependency>
But you need additional dependencies, to make it run. For convenience you can import spring-boot-starter-data-mongodb, which will import all required artifacts as transitive dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
I am developing a backend using springboot. I need to implement a soap client within my springboot application but I am facing an issue that I cannot understand why it is raised and how.
#WebServiceClient(name = "WSCryptDecrypt", targetNamespace = "example", wsdlLocation = "Example")
public class WSCryptDecrypt extends Service {
private final static QName WSCRYPTDECRYPT_QNAME = new QName("Example", "WSCryptDecrypt");
public WSCryptDecrypt(URL wsdlLocation) {
super(wsdlLocation, WSCRYPTDECRYPT_QNAME);
}
}
I instantiate this class like this:
WSCryptDecrypt wsCryptDecrypt = new WSCryptDecrypt(new URL("<WSDL-URL>"));
But I get this error:
Caused by: java.lang.NullPointerException: null
at javax.xml.ws.Service.<init>(Service.java:112) ~[jaxws-api-2.3.1.jar:na]
at com.mybackend.model.WSCryptDecrypt.<init>(WSCryptDecrypt.java:43) ~[classes/:na]
I don't get why and how this error is thrown. The url of the wsdl, that I pass as parameter, is correct for sure. I tried the code outside the springboot environmente and it works well. Springboot instead is complaining throwing this error.
UPDATE:
As #Laurens suggested I tried this approach:
Annotate WSCryptDecrypt class with #Component and then into WsCryptDecryptService class I do like that
#Autowired
WSCryptDecrypt wsCryptDecrypt;
In addition I annotated WsCryptDecryptService class with #Service
UPDATE 2:
javax.xml.ws.service class when instantiated it calls this.getClass().
Maybe this is the error, Spring did not create the Service object yet, so this is null. But I don't know how may I fix that.
UPDATE 3:
New fully updated code:
#Component
#WebServiceClient(name = "WSCryptDecrypt", targetNamespace = "example", wsdlLocation = "Example")
public class WSCryptDecrypt extends Service {
private final static QName WSCRYPTDECRYPT_QNAME = new QName("Example", "WSCryptDecrypt");
public WSCryptDecrypt(URL wsdlLocation) {
// Here it throws the error
super(wsdlLocation, WSCRYPTDECRYPT_QNAME);
}
}
Service class
#Service
public class WsCryptDecryptService {
//this is the soap service
private WSCryptDecryptSoap wsCryptDecryptSoap;
#Autowired
WSCryptDecrypt wsCryptDecrypt;
/**
* Creates the service pointing to TEST Environment.
* #throws MalformedURLException
*/
public WsCryptDecryptService() {
wsCryptDecryptSoap = wsCryptDecrypt.getWSCryptDecryptSoap();
}
}
UPDATE 4
I thought that maybe it is a problem of dependency. Those are the dependency that I put in my pom for javax.xml.ws (They are more than needed just because I wanted to check that I load all the possible libraries)
<dependency>
<groupId>javax.jws</groupId>
<artifactId>javax.jws-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.jws</groupId>
<artifactId>jsr181</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>javax.jws</groupId>
<artifactId>com.springsource.javax.jws</artifactId>
</dependency>
<dependency>
<groupId>javax.jws.jsr181-api</groupId>
<artifactId>jsr181-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.servicemix.bundles</groupId>
<artifactId>org.apache.servicemix.bundles.jaxws-api-2.0</artifactId>
<version>4.0-m1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>rt</artifactId>
<version>2.3.1</version>
</dependency>
Don't instantiate your services, let Spring take care of this using #Autowired. I wouldn't recommend using a constructor as this is bad practise, just call the method when you need to request your WSCryptDecryptSoap
#Service
public class WsCryptDecryptService {
#Autowired
private WSCryptDecrypt wsCryptDecrypt;
public WSCryptDecryptSoap getWSCryptDecryptSoap() {
return wsCryptDecrypt.getWSCryptDecryptSoap();
}
}
I downgrade my application from spring 4.x to 3.x and now when I fire simple test in spock which using autowired bean, this bean is null.
#ContextConfiguration(classes = Configuration.class)
class SomeTestClass extends Specification {
#Autowired
SomeService someService
def "someService"(){
expect:
someService.returnHelloWorld() == "Hello World" // (<- NullPointer)
}
}
My pom.xml file:
<dependencies>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.12</version>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.1-groovy-2.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<version>1.1-groovy-2.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.1.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
After when I downgrade also spock-core/spring to 0.6-groovy-1.8 and groovy-all to 1.8 and fire my test it throws this exception:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'someAnotherBean' defined in file
../SomeBean.class: Instantiation of bean failed; nested exception
isorg.springframework.beans.BeanInstantiationException: Could
notinstantiate bean class [..SomeBean.class]: No default
constructorfound; nested exception is
java.lang.NoSuchMethodException:..SomeBean.()
This bean contains contructor which i used to intizialize final fieds in class:
#Component
#PropertySource("classpath:someproperties.properties")
public class HeaderFactory {
private final SomeObject someObject;
public HeaderFactory(#Value("${someProperty1}") String someProperty1, #Value("${someProperty2}") String someProperty2) {
SomeObject someObject = new SomeObject(someProperty1,someProperty2);
this.someObject = someObject;
}
}
Everything worked pretty well before I dowgraded spring version. Any ideas?
You could create a configuration class and define the problematic beans there:
#Configuration
class MyConfig {
#Value("${someProperty1}") String prop1;
#Value("${someProperty2}") String prop2
#Bean
public SomeBean someBean() {
SomeBean bean = new SomeBean(prop1, prop2);
return bean;
}
}
Documentation here: https://docs.spring.io/spring/docs/3.2.0.RELEASE/spring-framework-reference/html/new-in-3.0.html#new-feature-java-config
I resolved my issue by this way:
#Component
public class UrlBuilder {
private final String host;
private final String port;
private final String protocol;
#Autowired
public UrlBuilder(Environment env) {
this.protocol = env.getRequiredProperty("app.server.protocol").toLowercase();
this.serverHost = env.getRequiredProperty("app.server.host");
this.serverPort = env.getRequiredProperty("app.server.port", Integer.class);
}
}
Source
I am new to the concept of unit testing with Spring controllers. I'm following some examples I found online and trying to implement their testing strategy. This is my basic controller:
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting(#RequestParam(value = "name2", required = false, defaultValue = "World2") String name2,
Model model) {
model.addAttribute("name", name2);
return "greeting";
}
}
This is my unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class ControllerGreetingTest {
private MockMvc mockMvc;
#Autowired
GreetingController greetingController;
#Test
public void shouldReturnSomething() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greeting"));
}
}
Seems pretty straight forward but I get the following error:
java.lang.IllegalStateException: Neither GenericXmlWebContextLoader
nor AnnotationConfigWebContextLoader was able to detect defaults, and
no ApplicationContextInitializers were declared for context
configuration [ContextConfigurationAttributes#1698539 declaringClass =
'com.practice.demo.ControllerGreetingTest', locations = '{}', classes
= '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass =
'org.springframework.test.context.ContextLoader']
I'm assuming I have to add a parameter to the #ContextConfiguration annotation but not sure what to include in there.
EDIT = This is what I have so far:
public class ControllerGreetingTest {
private MockMvc mockMvc;
#Before
public void setup(){
this.mockMvc = standaloneSetup(new GreetingController()).build();
}
#Test
public void shouldReturnDefaultString() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greetings"))
.andExpect(model().attribute("name","World2"));
}
}
It does the job but it doesn't use any of the Spring annotations like I tried to do before.. this approach is not good so trying to figure out why I keep gettings errors whenever I include the annotations in my test file.
My POM:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.5.7.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.7.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
You should use spring-boot-starter-test dependency. It has almost everything for testing.
And for testing controller part of a spring application, you should use #WebMvcTest annotation for your test class. With this annotation spring will load context just for controller part. Plus you don't need to setup method if you use this annotation. You can simply autowire mockMvc. Your test class should be like this:
#RunWith(SpringRunner.class)
#WebMvcTest(GreetingController.class)
public class ControllerGreetingTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private SomeServiceClass someServiceClass;
#Test
public void shouldReturnDefaultString() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greetings"))
.andExpect(model().attribute("name","World2"));
}
}
Note: Your controller does not have any autowired fields. In cases that controller has some autowired objects like service or repository objects. you can simply mock them with annotation #MockBean as you can see above code.
See this link for other test slice annotations spring provided
For projects with org.springframework.boot:spring-boot-starter-test can use
#RunWith(SpringRunner.class)
#SpringBootTest(classes = App.class)
public class ControllerGreetingTest {
...
}
Where App.class is you main application class annotated with #SpringBootApplication. But you better read the documentation. And if you don't want to include (classes = App.class) part you also can change folder structure
For simple controllers it is possible to perform simple standalone tests
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ControllerGreeting.class)
public class ControllerGreetingTest {
#Autowired
private MockMvc mockMvc;
...
}
Just add the #ContextConfiguration annotation and refer one or more XML configuration file locations or one or more configuration classes. Otherwise Spring cannot autowire your controller, which should be tested.
Example: You want to test a controller, which uses MyService via #Autowired:
MyControllerTest: Injects the controller, which should be tested using the MyTestConfig configuration class.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {MyTestConfig.class})
#WebAppConfiguration
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
private MyController controller;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
// Tests
}
MyTestConfig: Returns all beans, which are required for the test. Use Mockito to mock the depedencies of your controller, because we want to test only the controller and not the service layer.
#Configuration
public class MyTestConfig {
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
#Bean
public MyController myController() {
return new MyController();
}
}