How to override super interface #PreAuthorize expression - spring-boot

I have a generic service with annotations, this is extended by every service interface. How can I override this annotation from the service interface?
I put the #PreAuthorize annotation on the service interface but it did not work, however, when I put this on the service implementation it did.
My generic service interface looks like this:
public interface GenericService<T, ID> {
#PreAuthorize(/*Generic condition*/)
T get(ID id);
}
My entity's service interface (where I want this to work)
public interface EntityService extends GenericService<Entity, Integer> {
#PreAuthorize(/*Specific condition*/)
Entity get(Integer id);
}
My entity's service implementation (where it does work)
#Service
public class EntityServiceImpl extends AbstractServiceImpl<Entity, Integer> implements EntityService {
#Override
#PreAuthorize(/*Specific condition*/)
public Entity get(Integer id) {
return super.get(id);
}
}
Slice of my abstract service implementation class:
public abstract class AbstractServiceImpl<T extends AbstractEntity<ID>, ID> implements GenericService<T, ID> {
#Override
public T get(ID id) {
return repository.findById(id)
.orElseThrow(() -> new EntityNotFoundException(id));
}
}
I don't want to put this on the implementation because this is not specific to it. It must be the same condition across every posible implementation of the service.

Related

How to handle field injection to Couchbase repository

In order to add functionality to the couchbase repository I implemented the below class.
Inside this class is another #componenet name extensionHelper. Since this class is not standard spring bean I can't find a way to inject this field in order to use it in save()
Any idea how to inject such a class?
public class SimpleCustomRepository<T, ID> extends SimpleCouchbaseRepository<T, ID> {
#Autowired
ExtensionHelper extensionHelper;
public SimpleCustomRepository(CouchbaseEntityInformation<T, String> entityInformation,
CouchbaseOperations couchbaseOperations, Class<?> repositoryInterface) {
super(entityInformation, couchbaseOperations, repositoryInterface);
}
#Override
public <S extends T> S save(S entity) {
extensionHelper.applyExtensions(entity);
return super.save(entity);
}
#Override
public void delete(T entity) {
super.delete(entity);
}
}

Spring Data Rest - Base Repository using Generics not working

I want to disable export of DELETE operation using Spring Data rest using generics. Any repository which extends from BaseResourceRepository should not export DELETE verb. I am using groovy
#NoRepositoryBean
interface BaseResourceRepository<T extends BaseEntity, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
#Override
#RestResource(exported = false)
void delete(T t)
}
#RepositoryRestResource(collectionResourceRel = "contacts", path = "contacts")
interface ContactRepository extends BaseResourceRepository<Contact, Long> {
}
I want to disable the DELETE operation for /contacts endpoint
This configuration is still allowing me to DELETE the contact resource
Any help is appreciated.
Thanks in advance
See the official documentation regarding hiding CRUD methods:
https://docs.spring.io/spring-data/rest/docs/current/reference/html/#customizing-sdr.hiding-repository-crud-methods
As mentioned there you need to hide both methods.
Should look like this (may contain errors due to no IDE present ;))
#NoRepositoryBean
interface BaseResourceRepository<T extends BaseEntity, ID extends Serializable> extends PagingAndSortingRepository<T, ID> {
#Override
#RestResource(exported = false)
void delete(T t)
#Override
#RestResource(exported = false)
void delete(ID id);
}
Edit
An approach to give a try to
The base repository
package com.stackoverflow.generics.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;
#NoRepositoryBean
public interface NoDeleteRepository <T, ID> extends Repository<T, ID> {
T findOne(ID id);
Iterable<T> findAll();
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
// Define other necessary methods
}
The repository for an concrete entity
package com.stackoverflow.generics.repository;
import com.stackoverflow.generics.repository.SomeEntity;
public interface SomeNoDeleteRepository extends NoDeleteRepository<SomeEntity, Long> {
}
The repository does not expose the delete method therefore I assume it wont be exposed as REST endpoint. Can't test it as my spring does not expose all repositories as rest resource. Some misconfiguration I think.
public class SomeService {
private final SomeNoDeleteRepository repository;
public void deleteSome(Long id) {
// cannot resolve method delete
repository.delete(id);
}
}

Creating custom functions in Spring Boot services

I am writing a simple Spring Boot Application.I'm creating a repository,then its service and then its implementation
The code works fine if I use inbuilt functions of the JPA repository.
However it throws error if I try to make a function in the service interface.
If I make that function in the repository it doesn't throw that error
Here is the code:
Repository:
#Transactional
public interface Local_Repository extends JpaRepository<LocalModel,Long> {
}
Service:
public interface Local_Service {
public List<LocalModel> findAll();
public LocalModel findById(Long id);
public LocalModel findBymo(String mo);//this is the function I added
}
Implementation:
#Service
public class Local_Impl implements Local_Service {
#Autowired
private Local_Repository repository;
#Override
public List<LocalModel> findAll() {
List<LocalModel> cities = (List<LocalModel>) repository.findAll();
return cities;
}
#Override
public LocalModel findById(Long id) {
LocalModel city = repository.findOne(id);
return city;
}
#Override //this throws error
public LocalModel findBymo(String mo) {
LocalModel city=repository.findBymo(mo);
return null;
}
}
In the service you are calling a function that doesn't exist in the repository layer. There is no method called findBymo in the repository. If LocalModel has a field called mo, you can just add a function in the repository interface like this
LocalModel findByMo(String mo);
and it will be implemented automatically.
If LocalModel doesn't have such field you should implement the query yourself in the repository like this
#Query("select ... query here")
LocalModel findByMo(String mo);

Best practise when using Querydsl with Spring Data

Using Spring Data nad Querydsl we can just declare repository interface and skip the implementation class. Some methods with a specific name or using #Query annotation and that's all.
But sometimes I'd like to use JPAQuery and define method's body by myself, let's say
#Repository
public class MyRepositoryImpl implements MyRepository {
#PersistenceContext
private EntityManager em;
#Override
public List<Tuple> someMethod(String arg) {
JPAQuery query = new JPAQuery(em);
...
}
but this way I would have to implement other MyRepository interface methods, which ruins all Spring Data's advantages!
I can see two options:
Declare another interface per each repository and then normally implement it (which doubles number of interfaces)
Inject EntityManager into #Service class and implement my custom methods there
I like option #2 more, but as far I as know, in #Service class we should only call repository methods, so it's not a perfect solution as well.
So how does programmers deal with it?
You should not implement the actual Spring Data repository, instead you have to declare another custom interface where you can put your custom methods.
Let's say you have a MyRepository, defined as
#Repository
public interface MyRepository extends JpaRepository<Tuple, Long> {}
Now you want to add your custom findTuplesByMyArg(), for a sake of purpose you need to create custom repository interface
public interface MyRepositoryCustom {
List<Tuple> findTuplesByMyArg(String myArg);
}
Afterwards comes the implementation of custom interface
public class MyRepositoryImpl implements MyRepositoryCustom {
#PersistenceContext
private EntityManager em;
#Override
public List<Tuple> findTuplesByMyArg(String myArg) {
JPAQuery query = new JPAQuery(em);
...
}
}
And we need to change MyRepository declaration, so it extends custom repository, so that
#Repository
public interface MyRepository extends JpaRepository<Tuple, Long>, MyRepositoryCustom {}
And you can easily access your findTuplesByMyArg() by injecting MyRepository, e.g.
#Service
public class MyService {
#Autowired
private MyRepository myRepository;
public List<Tuple> retrieveTuples(String myArg) {
return myRepository.findTuplesByMyArg(myArg);
}
}
Pay attention that names are important here (you need to have Impl postfix by default configs in repo implementation).
You can find all needed information here
I would suggest a minor rectification to the answer above, which tries to use JPAQueryFactory. It is good to make use of the provided factory class.
public class MyRepositoryImpl implements MyRepositoryCustom {
#Autowired
private JPAQueryFactory factory;
#Override
public List<Tuple> findTuplesByMyArg(String myArg) {
JPAQuery query = factory.query();
...
}}
#Configuration
public class Config {
#Autowired
private EntityManager em;
#Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}

requestfactory complain about find method

I have a spring (3.1) application with a service and dao layer.
I try to use requestfactory (gwt 2.4) withi this spring layer.
Here some of my class
My domain class
public class Account {
Long id;
String username;
// get, set
}
The bridge between spring and gwt
public class SpringServiceLocator implements ServiceLocator {
#Override
public Object getInstance(Class<?> clazz) {
HttpServletRequest request = RequestFactoryServlet.getThreadLocalRequest();
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
return context.getBean(clazz);
}
}
My account proxy
#ProxyFor(value=Account.class, locator = AccountLocator.class)
public interface AccountProxy extends EntityProxy{
public Long getId();
public String getUsername();
public void setUsername(String userName);
public void setId(Long id);
}
RequestContext class
#Service(locator = SpringServiceLocator.class, value =AccountService.class)
public interface AccountRequest extends RequestContext {
Request<List<AccountProxy>> loadAllAccounts();
}
My requestFactory class
public interface AccountRequestFactory extends RequestFactory {
AccountRequest accountRequest();
}
My spring service
public interface AccountService {
public List<Account> loadAllAccounts();
}
#Service
public class AccountServiceImpl implements AccountService{
#Autowired
private AccountDAO accountDAO;
}
Account locator to avoid to put method in the entity
public class AccountLocator extends Locator<Account, Long> {
#Autowired
private AccountDAO accountDAO;
#Override
public Account create(Class<? extends Account> clazz) {
return new Account();
}
}
applicationContext.xml
<context:annotation-config />
<context:component-scan base-package="com.calibra" />
<bean id="accountService" class="org.calibra.server.service.AccountServiceImpl"/>
<bean id="accountDAO" class="org.calibra.server.dao.AccountDAOImpl"/>
The demo work but i get this error:
com.google.web.bindery.requestfactory.server.UnexpectedException: Could not find static method with a single parameter of a key type
Also on my AccountProxy i get this complain (a warning)
The domain type org.calibra.domain.Account has no Account findAccount(java.lang.Long) method. Attempting to send a AccountProxy to the server will result in a server error.
I don't want to add a find methond in my domain class.
I tried to put this method in my spring service, but i get the same warning.
Edit with the Locator that work fine
Just strange i need to put bean in the applicationContext, context:annotation and context:component-scan seem useless
Any idea?
The domain type org.calibra.domain.Account has no Account findAccount(java.lang.Long) method.
If you don't provide a find method of some kind, RequestFactory has no way of reconstituting objects when they get to the server - it can only create brand new ones, which prevents it from merging with existing data. Take this away, and you might as well have RPC again.
If you don't want static methods, provide a Locator instance which is able to find objects. From https://developers.google.com/web-toolkit/doc/latest/DevGuideRequestFactory#locators:
What if you don't want to implement persistence code in an entity itself? To implement the required entity locator methods, create an entity locator class that extends Locator:
public class EmployeeLocator extends Locator<Employee, Long> {
#Override
public Employee create(Class<? extends Employee> clazz)
{
return new Employee();
}
...
}
Then associate it with the entity in the #ProxyFor annotation:
#ProxyFor(value = Employee.class, locator = EmployeeLocator.class)
public interface EmployeeProxy extends EntityProxy {
...
}
You'll need to implement all of the methods, not just create - and the main one you are interested in is find(Class, Long). It may be possible to use one single Locator type for all proxies - as of 2.4.0 and 2.5.0-rc1 it is safe to fail to implement getDomainType(), and all of the other methods that need to know the exact type are provided with it as an argument.
Here is an example of what this can look like with JPA and Guice, but I think the idea is clear enough that it can be implemented with Spring and whatever persistence mechanism you are using. Here, all entities are expected to implement HasVersionAndId, allowing the locator to generalize on how to invoke getVersion and getId - you might have your own base class for all persisted entities.
(from https://github.com/niloc132/tvguide-sample-parent/blob/master/tvguide-client/src/main/java/com/acme/gwt/server/InjectingLocator.java)
public class InjectingLocator<T extends HasVersionAndId> extends Locator<T, Long> {
#Inject
Provider<EntityManager> data;
#Inject
Injector injector;
#Override
public T create(Class<? extends T> clazz) {
return injector.getInstance(clazz);
}
#Override
public T find(Class<? extends T> clazz, Long id) {
return data.get().find(clazz, id);
}
#Override
public Class<T> getDomainType() {
throw new UnsupportedOperationException();//unused
}
#Override
public Long getId(T domainObject) {
return domainObject.getId();
}
#Override
public Class<Long> getIdType() {
return Long.class;
}
#Override
public Object getVersion(T domainObject) {
return domainObject.getVersion();
}
}

Resources