I have a jsf 2.0 application without Spring and I have implemented a cache as application scope which should be accessed from a Rest service. Now I would like to call the rest webservice which should check the cache, but when I want access it, it is always null.
I tried already Accessing FacesContext from Web Service and this one https://www.mkyong.com/jsf2/how-to-get-servletcontext-in-jsf-2/ , but it doesn't work for me.
#ManagedBean(eager=true)
#ApplicationScoped
public class CacheController implements Serializable{
private static final long serialVersionUID = 123L;
private Map<String, Cache> map = new HashMap<String, Cache>();
public Map<String, Cache> getMap() {
return map;
}
public void setMap(Map<String, Cache> map) {
this.map = map;
}
}
#Path("/service")
public class RestService {
#POST
#Path("anlieferung/kennzahlen")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String getValueFromCache(String item) throws JSONException, ParseException {
//is always null
CacheController cacheController= (CacheController) getServletContext().getAttribute("cacheController");
//is always null
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
CacheController cacheBean = application.evaluateExpressionGet(context, "#{cacheController}", CacheController.class);
//doSomeStuff and check if the item is in the Cache (CacheController.getMap())
}
}
I have initialised the cache before over the jsf application and it works. Now I would expected that I get the Cache Object through the FacesContent or ServletContext, but it is always null. Do I need to create something like a ServletListener? Can somebody give me an example? Thank you
Related
I have a REST API (using wildfly 20 with microprofile-jwt) so I would like to audit changes with Hibernate Envers. Unfortunately I can't get my Principal object : the javax.ws.rs.core.SecurityContext is null.
So my question is : how can I inject the SecurityContext in my RevisionListener and get the Principal ?
import java.security.Principal;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.SecurityContext;
import org.hibernate.envers.RevisionListener;
public class CustomRevisionListener implements RevisionListener {
#Context
private SecurityContext context;
#Override
public void newRevision(Object o) {
CustomRevEntity e = (CustomRevEntity) o;
e.setLogin(getUser());
}
private String getUser() {
if(context == null) return "anonymous no context";
Principal principal = context.getUserPrincipal();
return principal == null ? "anonymous" : principal.getName();
}
}
That will not work since the creation of the bean is done outside of the container. If you want to use envers, you would need a way to communicate the principle from beans that are managed by the container to the RevisionListener-bean that is created and managed by the envers-extension.
A way could be to use public static ThreadLocal<Principal> threadlocalPrincipal = new ThreadLocal<>();.
In a bean using JPA or Hibernate, #Context SecurityContext context should work. Before calling any Dao or any other Hibernate-Using method fill the public static Variable using threadlocalPrincipal.set(...). Inside the listener use <Classname of Bean>threadlocalPrincipal.get() to access the current variable set in the current thread.
Make sure to clear the Threadlocal variable afterwards using threadLocalPrincipal.remove() inside a finally block, to avoid memory leaks.
I received the answer from hibernate forum : https://discourse.hibernate.org/t/how-to-get-username-envers-api-rest/5592
To be able to inject the SecurityContext into the RevisionListner (with #Inject), you need this filter :
#Provider
public class SecurityRequestFilter implements ContainerRequestFilter, ContainerResponseFilter {
private static final ThreadLocal<SecurityContext> THREAD_LOCAL = new ThreadLocal<>();
#RequestScoped
#Produces
public SecurityContext getSecurityContext() {
return THREAD_LOCAL.get();
}
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
THREAD_LOCAL.remove();
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
THREAD_LOCAL.set(requestContext.getSecurityContext());
}
}
I am currently trying to setup a custom ConfigSource reading config values from our DB2. As the ConfigSources are loaded via ServiceLoader it looks like there is no way to access the database via JPA as the ServiceLoader is scanning for custom ConfigSources very early.
Any ideas?
You can anotate your ConfigSource as a singleton session bean and mark it for eager initialization during the application startup sequence.
Also you need to define a static member variable holding your config values.
With this setup you can lazy load your properties values from an injected JPA source or also from any other CDI or EJB.
See the following example Code
#Startup
#Singleton
public class MyConfigSource implements ConfigSource {
public static final String NAME = "MyConfigSource";
public static Map<String, String> properties = null; // note to use static here!
#PersistenceContext(unitName = ".....")
private EntityManager manager;
#PostConstruct
void init() {
// load your data from teh JPA source or EJB
....
}
#Override
public int getOrdinal() {
return 890;
}
#Override
public String getValue(String key) {
if (properties != null) {
return properties.get(key);
} else {
return null;
}
}
#Override
public String getName() {
return NAME;
}
#Override
public Map<String, String> getProperties() {
return properties;
}
}
ConfigSources are POJO’s because if a CDI bean expected config to be injected into it at startup based on a ConfigSource that had dependencies on CDI’s you could get into startup looping issues.
For this reason the example CongigSoruce is constructed twice - once at the beginning from the Config-API and later from the CDI implemenation on #PostConstruct. With the static variable 'properties' we overload the values from the already constructed ConfigSource. Of course you can also separate the code in two classes if you like.
I've created a Rest service with four methods, GET,POST,UPDATE and DELETE.
These methods make connections to a Database to retrieve and store data.
Now I want to test each method. I've used the Jersey Test Framework for this. And it is working as long as I remove the code what actually makes the call to the database. When I leave the code that makes the call to the database it throws an exception that it could not connect to the database.
EDIT: I have done some research and used dependancy injection. The db calls are moved to a separate class but I'm still doing something wrong.
DatabaseResults. In this class the call to the DB is made.
public class DatabaseResults {
private final String getQuery = "SELECT * FROM movies";
private Connection connection = null;
private PreparedStatement pstmt = null;
private final ArrayList<Movie> jsonList = new ArrayList<>();
public JSONObject getAllMovies() throws SQLException {
try {
ComboPooledDataSource dataSource = DatabaseUtility.getDataSource();
connection = dataSource.getConnection();
pstmt = connection.prepareStatement(getQuery);
ResultSet rs = pstmt.executeQuery();
while (rs.next()) {
jsonList.add(new Movie(rs.getString(1), rs.getString(2), rs.getString(4), rs.getString(3)));
}
} catch (SQLException ex) {
System.out.println(ex);
System.out.println("Could not retrieve a connection");
connection.rollback();
} finally {
connection.close();
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("movies", jsonList);
return jsonObject;
}
}
MoviesResource that contains the REST methods
#Path("movies")
public class MoviesResource {
....
private DatabaseResults dbResults = null;
public MoviesResource() {
this(new DatabaseResults());
}
MoviesResource(DatabaseResults dbr){
this.dbResults = dbr;
}
....
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllMovies() throws JSONException, SQLException {
return Response.status(200).entity(dbResults.getAllMovies().toString()).build();
}
The Test class
#RunWith(MockitoJUnit44Runner.class)
public class MovieResourceTest extends JerseyTest {
JSONObject jsonObject = new JSONObject();
#Mock
DatabaseResults dbr;
#Before
public void setup() throws SQLException{
jsonObject.put("id", "hello");
when(dbr.getAllMovies()).thenReturn(jsonObject);
}
Client client = ClientBuilder.newClient();
WebTarget target = client
.target("http://localhost:9998/RestServiceMovies/resources");
#Override
protected Application configure() {
return new ResourceConfig(MoviesResource.class);
}
#Test
public void getAllMoviesTest() throws SQLException {
String responseGetAllMovies = target("/movies").request().get(String.class);
Assert.assertTrue("hello".equals(responseGetAllMovies));
}
At this moment I can run the tests but still when I test the getAllMovies() method it makes a call to the real database instead of returning the jsonObject.
I have the feeling that a connection is missing between the mock object and the constructor from the MovieResource class?
When you register your resource as a class
new ResourceConfig(MoviesResource.class)
you are telling Jersey to create the instance. If you don't have any DI configured, it will just call the no-arg constructor. In your no-arg constructor, you are just creating the service yourself. It knows nothing about your mock.
What you should do instead is register the resource class as an instance. That way you can pass the mock to the constructor.
MockitoAnnotations.initMocks(this);
return new ResourceConfig()
.register(new MoviesResource(dbr));
Don't use the Mockito runner. Instead use the MockitoAnnotations.initMocks method. That way you control when the #Mocks are injected. If you use the runner, the injection will not happen in time, as the the configure method is called by the framework before the Mockito injection happens.
I'm new to Jersey 2 and JAX-RS, so probably I'm missing something.
What I'm trying to do is a test program to define a coding style in rest services developing.
The test was written in JAVA and uses JERSEY 2.22.2, JDK 1.8.31, MOXY AS JSON Provider.
I defined a Resource with GET methods to support LIST/DETAIL. Due to the size of my POJO, I used some filters and everything was fine.
// 1) First of all I defined the annotation.
#Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#EntityFiltering
public #interface MyDetailView {
public static class Factory extends AnnotationLiteral<MyDetailView>
implements MyDetailView {
private Factory() {
}
public static MyDetailView get() {
return new Factory();
}
}
// 2) Once defined the annotation, I used to
// programmaticaly exclude the list of subItems in the response...
#XmlRootElement
public class MyPojo {
...
//*** THIS SHOULD BE FILTERED IF THE ANNOTATION IS NOT SPECIFIED IN THE RESPONSE ***
#MyDetailView
private List<SubItem> subItems = new ArrayList<SubItem>();
public List<SubItem> getSubItems() {
return subItems;
}
public void setSubItems(List<SubItem> subItems) {
this.subItems = subItems;
}
}
// 3) I registered the EntityFilteringFeature
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
}
// 4) Finally, I wrote the code to include/exclude the subItems
/*
The Resource class has getCollection() and getItem() methods...
getCollection() adds the annotation only if filterStyle="detail"
getItem() always add the annotation
*/
#Path(....)
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class MyResource extends SecuredResource {
//filterStyle -> "detail" means MyDetailAnnotation
#GET
public Response getCollection(
#QueryParam("filterStyle") String filterStyle,
#Context UriInfo uriInfo) {
//THIS CODE AFFECTS THE RESPONSE
boolean detailedResponse = "detail".equals(filterStyle);
Annotation[] responseAnnotations = detailedResponse
? new Annotation[0]
: new Annotation[]{MyDetailView.Factory.get()};
//pojo collection...
MyPagedCollection myCollection = new MyPagedCollection();
//.....
ResponseBuilder builder = Response.ok();
return builder.entity(myCollection, responseAnnotations).build();
}
#GET
#Path("/{id}")
public Response getItem(#PathParam("{id}") String idS, #Context UriInfo uriInfo) {
MyPOJO pojo = ...
Annotation[] responseAnnotations = new Annotation[]{MyDetailView.Factory.get()};
return Response.ok().entity(pojo, responseAnnotations).build();
}
}
After the first test, I tried to use the SelectableEntityFilteringFeature to allow the client to ask for specific fields in the detail, so I changed the ApplicationConfig
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
register(SelectableEntityFilteringFeature.class);
property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
and I've add the "fields" QueryParam to the Resource getItem() method...
#GET
#Path("/{id}")
public Response getDetail(#PathParam({id}) String id,
#QueryParam("fields") String fields,
#Context UriInfo uriInfo) {
....
But as long as I registered the SelectableEntityFilteringFeature class, the EntityFilteringFeature class stopped working. I tried to add "fields" parameter to one of the Resource methods, it worked perfectly. But the MyDetailAnnotation was completely useless.
I tried to register it using a DynamicFeature
public class MyDynamicFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if ("MyResource".equals(resourceInfo.getResourceClass().getSimpleName())
&& "getItem".equals(resourceInfo.getResourceMethod().getName())) {
//*** IS THE CORRECT WAY TO BIND A FEATURE TO A METHOD? ***
//
context.register(SelectableEntityFilteringFeature.class);
context.property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
}
Now the questions:
1) Why registering both the SelectableEntityFilteringFeature feature breaks the EntityFilteringFeature?
2) What is the correct way to bind a feature to a method with the DynamicFeature interface?
Thanks in advance.
This is my first post to Stack Overflow, I hope it was written complaining the rules.
Short answer: you can't. It appears to be a bug as of 2.25.1 and up to 2.26(that I tested with). https://github.com/jersey/jersey/issues/3523
SelectableEntityFilteringFeature implictily registers EntityFilteringFeature (As mentioned here). So I don't see a need to add this.
Since you need Annotation based filtering, you can exclude registering SelectableEntityFilteringFeature.
You can just do,
// Set entity-filtering scope via configuration.
.property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {MyDetailView.Factory.get()})
// Register the EntityFilteringFeature.
.register(EntityFilteringFeature.class)
// Further configuration of ResourceConfig.
You can refer to this example for usage and this example for registering the filter.
So you can remove SelectableEntityFilteringFeature and try just the above mentioned way to register it.
I have a Library application which is already implemented in spring MVC.
I need to use ReST web services for the same application using spring 3.
I have a Controller class I want is to be as a RestFul webService
#Controller #SessionAttributes("category")
public class CategoryController {
private static final Log log = LogFactory.getLog(CategoryController.class);
#Autowired
private CategoryService categoryService;
#Autowired
private ItemService itemService;
#RequestMapping("/category/categoryList.htm")
public ModelAndView list(HttpServletRequest request,
HttpServletResponse response) throws Exception {
List<Category> list = categoryService.getAllMainCategories();
Map map = new HashMap();
map.put("categoryList", list);
map.put("category", new Category());
return new ModelAndView("categoryList", map);
}
#RequestMapping(method = RequestMethod.POST, value = "/category/save.htm")
public String save(HttpServletRequest request,
HttpServletResponse response, Category command) throws Exception {
log.debug("save method called" + command);
Category category = (Category) command;
System.out.println(category);
categoryService.saveCategory(category);
return "redirect:/category/categoryList.htm";
}
#RequestMapping("/category/edit.htm")
public String edit(#RequestParam String id, ModelMap model)
throws Exception {
log.debug("edit method called :" + id);
log.debug(Long.parseLong(id));
Category cat = categoryService.getCategory(Long.parseLong(id));
model.put("categoryList", categoryService.getAllMainCategories());
model.put("category", cat);
return "categoryList";
}
#RequestMapping("/category/delete.htm")
public String remove(#RequestParam String id, ModelMap model)
throws Exception {
log.debug("remove method called " + id);
categoryService.deleteCategory(Long.parseLong(id));
return "redirect:/category/categoryList.htm";
}
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Category.class,
new PropertyEditorSupport() {
#Override
public void setAsText(String text) {
setValue(categoryService.getCategory(Long.valueOf(text)));
}
});
}
}
it is CategoryController class which add delete or update a category
ItemService and CategoryService are data sources
Category is a domain object having properties like id,name,description etc..,
How do I write a REST web service for this?
There's a simple example showing how in Barebones Spring. Check it out.