Why Use PathVariable instead of PathParam? - rest

Before marking this as duplicate, just wanted you guys to know I have checked out the question posted here:
What is the difference between #PathParam and #PathVariable
Thing is, if the usage of PathParam and PathVariable are same (only that one is from the JAX-RS API and one is provided by Spring), why is it that using one gives me null and the other gives me the proper value?
I am using Postman to invoke the service as:
http://localhost:8080/topic/2
(I'm very new to SpringBoot)
Using PathParam :
import javax.websocket.server.PathParam;
import org.apache.tomcat.util.json.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class TopicController {
#Autowired
TopicService topicService;
#RequestMapping(method=RequestMethod.GET,path="/topic/{id}")
public Topic getById(#PathParam("id") long id) throws ParseException {
return topicService.getTopicById(id); //-- here id comes as null (when id is declared as a wrapper type - Long, else it throws an error)
}
}
Using PathVariable:
#RestController
public class TopicController {
#Autowired
TopicService topicService;
#RequestMapping(method=RequestMethod.GET,path="/topic/{id}")
public Topic getById(#PathVariable("id") long id) throws ParseException {
return topicService.getTopicById(id); //-- here id comes as 2
}
}

I think the pathparam in your project is under javax.ws... This one is not what they talked about. It is used in websocket, which means it is not a http annotaiton. JBoss implement pathparam needs additional jars.

Related

Apache Camel + MongoDB documentation incorrect?

I'm reading the instructions on using findOneByQuery using Apache Camel + MongoDBs, and I think I'm doing exactly what it says:
import com.mongodb.client.model.Filters;
import deletion.manager.Constants;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mongodb.MongoDbConstants;
import org.springframework.stereotype.Component;
#Component
public class NotifyClientRoute extends RouteBuilder {
// ...
#Override
public void configure() throws Exception {
from("direct:findOneByQuery")
.setHeader(MongoDbConstants.CRITERIA, Filters.eq("name", "Raul Kripalani"))
.to("mongodb:myDb?database=flights&collection=tickets&operation=findOneByQuery")
.to("mock:resultFindOneByQuery");
I'm getting this compilation error:
Cannot resolve method 'setHeader(java.lang.String, org.bson.conversions.Bson)'
I've tried looking around for other/better examples, but I haven't been able to find any. The options for setHeader are:
#setHeader(String name)
#setHeader(String name, Expression expression)
#setHeader(String name, Supplier<Object>)
I'm pretty sure I'm importing correct classes. I'd rather not create a simple Bean class just so I can #Autowired a MongoTemplate, but that's looking like my only option right now. Any thoughts anyone?

Quarkus could not find writer for content-type multipart/form-data rest client

I'm implementing an API-gateway for my rest microservice using Quarkus.
I want to forward requests to another (Quarkus) rest-api.
I'm trying to forward a POST request with multiform data.
I'm expecting to get a 201 but I'm getting a 500 internal server error.
RESTEASY throws the following error:
RESTEASY002020: Unhandled asynchronous exception, sending back 500: javax.ws.rs.ProcessingException: RESTEASY004655: Unable to invoke request: javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type multipart/form-data type: org.acme.rest.client.multipart.MultipartBody
I've tried upgrading my Quarkus version from 1.4.2 to 1.5.2 because I saw the following issue:
https://github.com/quarkusio/quarkus/issues/8223
Also tried Intellij invalidate cache/restart, re-import maven
Code
MultiPartBody:
package org.acme.rest.client.multipart;
import org.jboss.resteasy.annotations.providers.multipart.PartType;
import javax.ws.rs.FormParam;
import javax.ws.rs.core.MediaType;
public class MultipartBody {
#FormParam("sample_id")
#PartType(MediaType.TEXT_PLAIN)
public Long sampleId;
#FormParam("username")
#PartType(MediaType.TEXT_PLAIN)
public String username;
#FormParam("content")
#PartType(MediaType.TEXT_PLAIN)
public String content;
}
Interface:
package org.acme.rest.client;
import io.smallrye.mutiny.Uni;
import org.acme.rest.client.multipart.MultipartBody;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#RegisterRestClient
#Consumes(MediaType.MULTIPART_FORM_DATA)
public interface SocialService {
#POST
Uni<Response> create(#MultipartForm MultipartBody data);
}
Resource:
package org.acme.rest.client;
import io.smallrye.mutiny.Uni;
import org.acme.rest.client.multipart.MultipartBody;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/comments")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public class SocialResource {
#Inject
#RestClient
SocialService socialService;
#POST
public Uni<Response> create(#MultipartForm MultipartBody data) {
return socialService.create(data);
}
}
Test:
package org.acme.rest.client;
import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.given;
#QuarkusTest
public class SocialResourceTest {
#Test
public void create(){
given().contentType("multipart/form-data")
.multiPart("sample_id", "1")
.multiPart("username", "testuser")
.multiPart("content", "test message")
.when()
.post("/comments")
.then()
.statusCode(201);
}
}
To fix this issue with Resteasy and quarkus, I had to add the MultipartFormDataWriter to resteasy client registry, this is my new code :
Any question click connect to my twitter #AIDeveloper
MultipartFormDataOutput output = new MultipartFormDataOutput();
output.addFormData("file", fileInputStream, MediaType.APPLICATION_OCTET_STREAM_TYPE);
output.addFormData("status", "status1", MediaType.TEXT_PLAIN_TYPE);
Response uploadResponse = newClient()
.target(uploadUri)
.register(MultipartFormDataWriter.class)
.request()
.post(Entity.entity(output
, MediaType.MULTIPART_FORM_DATA_TYPE));
Everything looks fine, but maybe you are missing this dependency
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
</dependency>
That info is available at this quarkus guide
I've had the same issue (could not find writer for content-type multipart/form-data). I solved it by making the MultiPartBody extend MultipartFormDataOutput.
In your case this would be:
...
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput;
...
public class MultipartBody extends MultipartFormDataOutput {
...
}
I found this solution by looking at how Quarkus / Resteasy internally resolves the output writers. This is done in the resolveMessageBodyWriter() method of org.jboss.resteasy.core.providerfactory.ResteasyProviderFactoryImpl. The relevant writer is org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataWriter. There, the isWriteable() method checks if MultipartFormDataOutput is a superclass of your class (MultipartBody).
However, I don't know why it works without extending MultipartFormDataOutput in the Quarkus examples.
My Quarkus version is 1.8.2.Final and I use the io.quarkus.quarkus-resteasy-multipart maven dependency, not org.jboss.resteasy.resteasy-multipart-provider.
The ProcessingException with message RESTEASY003215: could not find writer for content indicates no serializer was bound for the request type. This usually means you're missing the rest client dependency needed for the type.
In your case that might be today (as of Quarkus 2.5.0)
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-multipart</artifactId>
</dependency>
Another possibility for this very same error is you're trying to implement a REST client with the reactive API and missing the reactive client API bindings. You can have both reactive and regular rest-client at the same time in your project.
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-reactive-jackson</artifactId>
</dependency>
It might seems unrelated because it brings the Json writers in that case, but the exception and error message you'll get is exactly the same.

AEM - How to return page information in JSON format

I need to display the title and name of all child and grand child pages from given path in JSON format. Please provide the implementation.
First You have to try something yourself, then you call for help, not the complete solution!anyway there are couple of solution down there.
According to Adobe here you can implement page information in JSON format:
package com.adobe.example;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageInfoProvider;
#Component(metatype = false)
#Properties({
#Property(name="service.description", value="Returns the public URL of a resource.")
})
#Service
public class PageUrlInfoProvider implements PageInfoProvider {
#Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
private com.day.cq.commons.Externalizer externalizer;
private String fetchExternalUrl(ResourceResolver rr, String path) {
return externalizer.publishLink(rr, path);
}
public void updatePageInfo(SlingHttpServletRequest request, JSONObject info, Resource resource)
throws JSONException {
Page page = resource.adaptTo(Page.class);
JSONObject urlinfo = new JSONObject();
urlinfo.put("publishURL", fetchExternalUrl(null,page.getPath()));
info.put("URLs",urlinfo);
}
}
Or you may try this Page for solution
Please refer to below link which may be useful:
http://www.nateyolles.com/blog/2015/12/converting-aem-sling-resources-to-json.
Apart from the above solution you also use recursive level to get the data in the JSON format for example /content/we-retail/language-masters/en.{placeholder}.json
replace the placeholder with the level of nodes you want to print and return back the JSON wherever needed.
To know more about the rendering data in different formats,
Refer: https://sling.apache.org/documentation/bundles/rendering-content-default-get-servlets.html

Flyway Spring Boot Autowired Beans with JPA Dependency

I am using Flyway 5.0.5 and I am unable to create a java (SpringJdbcMigration) with autowired properties... They end up null.
The closest thing I can find is this question: Spring beans are not injected in flyway java based migration
The answer mentions it being fixed in Flyway 5 but the links are dead.
What am I missing?
I struggled with this for a long time due to my JPA dependency. I am going to edit the title of my question slightly to reflect this...
#Autowired beans are instantiated from the ApplicationContext. We can create a different bean that is ApplicationContextAware and use that to "manually wire" our beans for use in migrations.
A quite clean approach can be found here. Unfortunately, this throws an uncaught exception (specifically, ApplicationContext is null) when using JPA. Luckily, we can solve this by using the #DependsOn annotation and force flyway to run after the ApplicationContext has been set.
First we'll need the SpringUtility from avehlies/spring-beans-flyway2 above.
package com.mypackage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class SpringUtility implements ApplicationContextAware {
#Autowired
private static ApplicationContext applicationContext;
public void setApplicationContext(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/*
Get a class bean from the application context
*/
public static <T> T getBean(final Class clazz) {
return (T) applicationContext.getBean(clazz);
}
/*
Return the application context if necessary for anything else
*/
public static ApplicationContext getContext() {
return applicationContext;
}
}
Then, configure a flywayInitializer with a #DependsOn for springUtility. I extended the FlywayAutoConfiguration here hoping to keep the autoconfiguration functionality. This mostly seems to have worked for me, except that turning off flyway in my gradle.build file no longer works, so I had to add the #Profile("!integration") to prevent it from running during my tests. Other than that the autoconfiguration seems to work for me but admittedly I've only run one migration. Hopefully someone will correct me if I am wrong.
package com.mypackage;
import org.flywaydb.core.Flyway;
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer;
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration.FlywayConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.DependsOn;
import com.mypackage.SpringUtility;
#Configuration
#Profile("!integration")
class MyFlywayConfiguration extends FlywayConfiguration {
#Primary
#Bean(name = "flywayInitializer")
#DependsOn("springUtility")
public FlywayMigrationInitializer flywayInitializer(Flyway flyway){
return super.flywayInitializer(flyway);
//return new FlywayMigrationInitializer(flyway, null);
}
}
And just to complete the example, here is a migration:
package db.migration;
import org.flywaydb.core.api.migration.spring.BaseSpringJdbcMigration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import com.mypackage.repository.AccountRepository;
import com.mypackage.domain.Account;
import com.mypackage.SpringUtility;
import java.util.List;
public class V2__account_name_ucase_firstname extends BaseSpringJdbcMigration {
private AccountRepository accountRepository = SpringUtility.getBean(AccountRepository.class);
public void migrate(JdbcTemplate jdbcTemplate) throws Exception {
List<Account> accounts = accountRepository.findAll();
for (Account account : accounts) {
String firstName = account.getFirstName();
account.setFirstName(firstName.substring(0, 1).toUpperCase() + firstName.substring(1));
account = accountRepository.save(account);
}
}
}
Thanks to avehlies on github, Andy Wilkinson on stack overflow and OldIMP on github for helping me along the way.
In case you are using more recent versions of Flyway, then extend BaseJavaMigration instead of BaseSpringJdbcMigration as the later is deprecated. Also, take a look at the below two comments by the user Wim Deblauwe.
The functionality hasn't made it into Flyway yet. It's being tracked by this issue. At the time of writing that issue is open and assigned to the 5.1.0 milestone.
Seems the updated answer provided by #mararn1618 is under documented on the official documentation, so I will provide a working setup here. Thanks to #mararn1618 for guiding in that direction.
Disclaimer, it's written in Kotlin :)
First you need a configuration for loading the migration classes, in Spring Boot (and perhaps Spring) you need either an implementation of FlywayConfigurationCustomizer or a setup of FlywayAutoConfiguration.FlywayConfiguration. Only the first is tested, but both should work
Configuration a, tested
import org.flywaydb.core.api.configuration.FluentConfiguration
import org.flywaydb.core.api.migration.JavaMigration
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer
import org.springframework.context.ApplicationContext
import org.springframework.stereotype.Component
#Component
class MyFlywayConfiguration #Autowired constructor(
val applicationContext: ApplicationContext
) : FlywayConfigurationCustomizer {
override fun customize(configuration: FluentConfiguration?) {
val migrationBeans = applicationContext.getBeansOfType(JavaMigration::class.java)
val migrationBeansAsArray = migrationBeans.values.toTypedArray()
configuration?.javaMigrations(*migrationBeansAsArray)
}
}
Configuration option B, untested, but should also work
import org.flywaydb.core.api.migration.JavaMigration
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
import org.springframework.boot.autoconfigure.flyway.FlywayConfigurationCustomizer
import org.springframework.context.ApplicationContext
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
#Configuration
class MyFlywayConfiguration : FlywayAutoConfiguration.FlywayConfiguration() {
#Bean
fun flywayConfigurationCustomizer(applicationContext: ApplicationContext): FlywayConfigurationCustomizer {
return FlywayConfigurationCustomizer { flyway ->
val p = applicationContext.getBeansOfType(JavaMigration::class.java)
val v = p.values.toTypedArray()
flyway.javaMigrations(*v)
}
}
}
And with that you can just write your migrations as almost any other Spring bean:
import org.flywaydb.core.api.migration.BaseJavaMigration
import org.flywaydb.core.api.migration.Context
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
#Component
class V7_1__MyMigration #Autowired constructor(
) : BaseJavaMigration() {
override fun migrate(context: Context?) {
TODO("go crazy, mate, now you can import beans, but be aware of circular dependencies")
}
}
Side notes:
Be careful of circular dependencies, your migrations can most likely not depend on repositories (also makes sense, you are preparing them, after all)
Make sure your migrations are located where Spring scans for classes. So if you want to place them in the namespace db/migrations, you need to ensure that Spring scans that location
I haven't tested, but it's likely one should be cautious with mixing the path for these migrations and the locations where Flyway scans for migrations
Current flyway 6.5.5 version is released and back from 6.0.0 I believe support for spring beans is provided.
You can directly autowire spring beans into your Java based migrations (using #autowired), But the hunch is your Migration class also should be managed by Spring to resolve dependency.
There is a cool and simple way for it, by overriding default behavior of Flyway, check out https://reflectoring.io/database-migration-spring-boot-flyway/
the article clearly answers your question with code snippets.
If you are using deltaspike you can use BeanProvider to get a reference to your DAO.
Change your DAO code:
public static UserDao getInstance() {
return BeanProvider.getContextualReference(UserDao.class, false, new DaoLiteral());
}
Then in your migration method:
UserDao userdao = UserDao.getInstance();
And there you've got your reference.
(referenced from: Flyway Migration with java)

Create PicasawebService

I try to get the titles and URLs from a public Picasa album. But I am stuck at the very beginning: I cannot create a new PicasawebService.
My Code:
import java.util.List;
import java.io.File;
import java.net.URL;
import com.google.gdata.client.*;
import com.google.gdata.client.photos.*;
import com.google.gdata.data.*;
import com.google.gdata.data.media.*;
import com.google.gdata.data.photos.*;
import java.util.ArrayList;
public class PicasaManager implements PicasaConnector {
public List<Avatar> getPhotoURLs() throws Exception {
PicasawebService myService = new PicasawebService("HI");
myService.setUserCredentials("foo#gmail.com", "mypassword");
ArrayList<Avatar> rl = new ArrayList<Avatar>();
URL feedUrl = new URL("https://picasaweb.google.com/111420671758947023853/EWA2012");
System.out.println("dddddddddddddddddd");
AlbumFeed feed = myService.getFeed(feedUrl, AlbumFeed.class);
for (PhotoEntry photo : feed.getPhotoEntries()) {
Avatar a1 = new Avatar();
a1.setDescription(photo.getTitle().getPlainText());
a1.setUrl(photo.getMediaThumbnails().get(0).getUrl());
rl.add(a1);
}
return (rl);
}
}
The Error Message:
Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.collect.ImmutableSet.copyOf([Ljava/lang/Object;)Lcom/google/common/collect/ImmutableSet;
at com.google.gdata.wireformats.AltFormat$Builder.setAcceptableTypes(AltFormat.java:399)
at com.google.gdata.wireformats.AltFormat$Builder.setAcceptableXmlTypes(AltFormat.java:387)
at com.google.gdata.wireformats.AltFormat.<clinit>(AltFormat.java:49)
at com.google.gdata.client.Service.<clinit>(Service.java:558)
at tuwien.big.mensch.utilities.PicasaManager.getPhotoURLs(PicasaManager.java:27)
at tuwien.big.mensch.utilities.test.main(test.java:29)
test.java is my test class with the public static void main method,
Avatar is a class with two variables: description and url, there are getters and setters for both
in my netbeans IDE line 27 of the PicasaManager.java file is: PicasawebService myService = new PicasawebService("HI");
the implemented interace only defines the getPhotoURLs() method
I have no idea how to solve this problem, i hope somebody here can help me.
Have you included gdata-core-1.0.jar and guava-12.0.jar? Good luck on the rest of Web Engineering UE4 ;-)