I have implemented a REST API using Spring Boot (version 1.3.6.RELEASE), which is working as expected. However I would like to add JSON API support to my application.
The problem is that I get a 404 when trying to execute a GET on a Katharsis resource.
The dependencies in pom.xml look as follows (I use Spring Security too):
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<exclusion>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
<version>4.4.1</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>${hsqldb.version}</version>
</dependency>
<!--SpringFox dependencies -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox-version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-tools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>dumbster</groupId>
<artifactId>dumbster</artifactId>
<version>1.6</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.katharsis</groupId>
<artifactId>katharsis-spring</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.9</version>
</dependency>
</dependencies>
My application initialisation class is as follows:
#SpringBootApplication
#Configuration
#EnableAutoConfiguration(exclude = WebMvcAutoConfiguration.class)
#EnableSwagger2
#ComponentScan(basePackages = "com.myapp")
#Import({KatharsisConfigV2.class})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
The resource class:
#ApiModel(description = "")
#JsonApiResource(type = "foo-data")
public class FooData extends DomainRepresentation {
#JsonApiId
private String id = null;
#JsonProperty
private String name = null;
// .... getters & setters
}
ResourceRepository class:
#JsonApiResourceRepository(FooData.class)
#Component
public class FooDataKatApi extends MyApi {
#Autowired
private FooDataService fooDataService;
#JsonApiFindOne
public FooData foodataIdGet(String id, QueryParams requestParams) {
FooData result = fooDataService.getFooDataById(id);
return result;
}
}
If I add Spring #ResourceMappings than I get data back but without Katharsis additions like "link", etc. It's as if the API call is not being picked up by Katharsis, but it's handled by Spring.
Any help would be appreciated.
-- EDITTED --
The FooDataService is as follows:
#Service
public class FooDataService extends MyService {
#Autowired
private FooDataRepository fooDataRepository;
public FooData getFooDataById(String id) {
FooDataEnt fooDataEnt = fooDataRepository.findByFooDataId(id);
if (fooDataEnt != null) {
return convertFooDataEntToFooData.apply(fooDataEnt);
}
throw new NotFoundException("Foo not found");
}
}
The FooDataRepository extends Spring's PagingAndSortingRepository.
Katharsis config in application.yaml:
katharsis:
resourcePackage: com.my.package
domainName: http://localhost:8080
pathPrefix: /api/kat
The request and response from PostMan
-- UPDATE 1 --
Apparently the configured value for katharsis.pathPrefix was not correct. Web application's context-root is "/api" and the value of katharsis.pathPrefix should be only "/kat".
My next problem is that a RepositoryNotFoundException is being throw while handling the request, while the Resource class is found already.
I don't see your foo data service implementation anywhere. However, I am 99% sure you're not passing the json api accept type.
Accept: application/vnd.api+json
I solved the riddle by debugging the code and finding out that my Katharsis config was not correct. Katharsis only picked up the resource classes but not the resource repository classes. When I changed the katharsis.resourcePackage to a higher level where the resource repositories resided, the problem got solved and Katharsis picked up all related annotated classes.
Related
I need to test different client against each other. for that i need to be able to open different html pages with spring boot.
i tried to do this:
Multiple index.html in spring boot
but it does only return a string "thick_client" on the webpage.
my controller looks like this:
#RestController
public class ApiController {
#Autowired
private StockRepository stockRepository;
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index() {
return "thick_client";
}
#RequestMapping(value = "/anotherIndex", method = RequestMethod.GET)
public String anotherIndex() {
return "thin_client";
}
application.properties:
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.html
My Folder Structure looks like this:
pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
cheers!
Try returning the string without the extension.
For e.g return "thin_client";
I'm facing a problem with Spring boot validation. Currently, I'm using Oracle JDK 11.0.12 and Spring boot 2.5.4 to build my project. I added constraints to validate the fields but it does not work. My code here:
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
#Data
public class LoginFormDTO implements Serializable {
private static final long serialVersionUID = 1L;
#NotBlank(message = "Login must not be blank")
private String login;
#NotNull(message = "Password must be provided")
private String password;
private boolean rememberMe = false;
}
#RestController
#RequestMapping("/api")
public class AccountResource {
// Logger and autowired components
#PostMapping("/authenticate")
#ResponseBody
public ResponseEntity<JWTToken> authorize(#Valid #RequestBody LoginFormDTO account) {
// Some code lines
return ResponseEntity.ok().body(new JWTToken(jwt));
}
}
Dependencies in pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.libphonenumber</groupId>
<artifactId>libphonenumber</artifactId>
<version>8.12.31</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
I expect when I send POST request with body
{
"login": null, // or empty string "", or blank string " "
"password": "string",
"rememberMe": true
}
then the server should validate fields then throw exceptions or errors (because of null constraint violation) before executing code in my authorize(#Valid #RequestBody LoginFormDTO account) function, but it does not. So what's wrong with spring validation or am I missing something?
You can simplify your Controller. You have way too many annotations doing the same thing and even some weird stuff (you return ResponseEntity but then you tell Spring that the return of the method should be the Response Body).
This may not fix the #Valid issue but for sure will make it simpler:
#RestController
#RequestMapping("/api")
public class AccountResource {
// Logger and autowired components
#PostMapping("/authenticate")
#ResponseStatus(HttpStatus.OK)
public JWTToken authorize(#Valid #RequestBody LoginFormDTO account) {
// Some code lines
return new JWTToken(jwt);
}
}
#RestController is itself annotated with #Controller and #ResponseBody, which means that #RequestMapping methods assume #ResponseBody semantics by default, so you don't need to explicitly add it.
I found out that my SwaggerConfig class should use BeanValidatorPluginsConfiguration.class instead of SpringValidatorAdapter.class in #Import({}). Now my validator works normally. Thank João Dias for supporting me in figuring it out.
I am writing a Java Spring REST server using Spring Boot.
This is the dependency list:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
</dependencies>
This is a part of my a controller class:
#RequestMapping(path = "/", method = RequestMethod.GET)
public GenericResponse<List<Trip>> getUserTrips(Pageable pageable , #RequestParam("owner") String owner) {
List<Trip> trips = tripRepository.findByOwner(owner , pageable);
for (Trip trip : trips) {
Link link = linkTo(methodOn(TripController.class).getTripInfo(trip.getTripId())).withSelfRel();
trip.add(link);
}
return new GenericResponse<List<Trip>>(200, "Success", trips);
}
When compiled, I got the following error:
method getUserTrips in class com... cannot be applied to given types
required org.sptringframework.data.domain.Pageable, java.lang.String
found: java.lang.String
reason: actual and formal argument lists differ in length
I don't understand. I thought Pageable automatically parse the ?page=x&pageSize=y to the pageable var.
Do I forget to add any dependencies? What is wrong?
I do not use WebMv (it's not in my dependency list) Is it why this does not work?
I have a piece of code that uses Spring dependency injection with ApplicationContext
public abstract class BigDataParent {
private static final Logger log = LoggerFactory.getLogger(BigDataOperation.class);
protected static ApplicationContext context =
new ClassPathXmlApplicationContext("file.xml");
...
}
And the child:
public class BigBadChild extends BigDataParent {
private Something query;
#Override
public void doSomething() {
query = (Something) context.getBean("beanName");
....
}
}
I want to change the injection to use annotations instead of context, so I have just changed the child class to
public class BigBadChild extends BigDataParent {
#Resource(name = "beanName")
private Something query;
#Override
public void doSomething() {
// Removed query = (Something) context.getBean("beanName");
....
}
}
I have also changed my xml configuration file and have added
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
But now I'm getting that query is null. I bet that I am missing something basic, but what is it? Thank you.
EDIT:
Something is an interface that has several implementations, I'm choosing the implementation in the xml file file.xml. When I'm doing so using context.getBean(), it works and I have the right implementation, but when using #Reousrce (javax.annotation.Resource) I'm getting null.
What is the difference in flow when using context.getBean() and #Resource? Is there something different to how and when the values are initialized that might cause this?
EDIT #2: My list of dependencies:
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.1</version>
</dependency>
<dependency>
<groupId>commons-configuration</groupId>
<artifactId>commons-configuration</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.2</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.4.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-c3p0</artifactId>
<version>4.3.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>4.3.4.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.175</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
By specifying
<context:annotation-config />
Spring registers a number of BeanPostProcessor beans to perform bean injection. One of these is CommonAnnotationBeanPostProcessor which handles #Resource annotated members.
This BeanPostProcessor is only registered if javax.annotation.Resource is on the classpath when the program runs.
You must be running the application without providing it to the classpath.
Note that you must be providing it at compilation time, otherwise any references to it would fail. This is not the same as providing it at run time.
I am working in spring and spark for a OLAP, then I found the spring 3.12 used cglib 2.2.2, and spark used 3.0, so how can I execute them in the same time?
Some configuration could be done with maven ?
<dependency>
<groupId>com.commons.algorithm</groupId>
<artifactId>simulator</artifactId>
<version>1.0</version>
</dependency>
<dependency> spring works with this
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
<!--dependency> spark requires this
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.0</version>
</dependency-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.9.3</artifactId>
<version>0.8.0-incubating</version>
<type>jar</type>
</dependency>
code:
#SparkParameter(taskJarLocation = {"analysis/1.0/analysis-1.0.jar"})
#SparkCassandraParameter (keySpace="commons10", port = 9160)
#RunWith(value = Parameterized.class)
public class PerformanceExecutor {
private Simulator simulator;
private Analysis analysis;
private Algorithm algorithm;
public PerformanceExecutor(Simulator simulator, Analysis analysis, Algorithm algorithm){
this.simulator = simulator;
this.analysis = analysis;
this.algorithm = algorithm;
}
#Parameters
public static Collection<Object[]> data() throws ClassNotFoundException, IOException {
ApplicationContext ctx = new AnnotationConfigApplicationContext(TestConfiguration.class);
CommonDao commonDao = ctx.getBean(CommonDao.class);
OpinionDao opinionDao = ctx.getBean(OpinionDao.class);
DataBaseAdaptor adaptor = new DataBaseAdaptorImpl(opinionDao, commonDao, 1);
SparkRddHelper helper = new SparkRddHelper();
Object[][] data = new Object[][]{{new RegisterSimulation(), new CommonRelationAnalysisTask(helper), new CommonRenewAlgorithm(adaptor)}};
return Arrays.asList(data);
}
#Test
public void pushTest() throws Exception {
simulator.start("src/main/resources/userCreated/users2.txt", 10); // <-- spring required
analysis.runAnalysis(); // <-- spark required
algorithm.process();
}
}
If cglib 3 is backwards compatible with 2, you can exclude Spring's dependency on cglib using the following:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</exclusion>
</exclusions>
</dependency>
So, your pom.xml will look like this:
<dependency>
<groupId>com.commons.algorithm</groupId>
<artifactId>simulator</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>3.1.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.spark</groupId>
<artifactId>spark-core_2.9.3</artifactId>
<version>0.8.0-incubating</version>
<type>jar</type>
</dependency>