I am currently trying to use jOOQ in my springboot project to insert certain data given by user from a html form into a MySQL db. This is my MainController responsible for getting data from the form and executing the query through a VisitorRepository: I'm using both add and insertVisitor methods but neither of them are working.
#Controller
public class MainController {
private final VisitorRepository visitorRepository;
#Autowired
public MainController (VisitorRepository visitorRepository) {
this.visitorRepository = visitorRepository;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String main() {
return "main";
}
#ResponseBody
#PostMapping("/")
public Phonenum firstSubmit(Phonenum phonenum){
System.out.println(phonenum.getPhonenumber());
visitorRepository.insertVisitor(phonenum.getPhonenumber(),"now", phonenum.getName());
return visitorRepository.add(phonenum);
}
}
VisitorRepository interface:
public interface VisitorRepository {
public Phonenum add(Phonenum visitor);
public List<Phonenum> findAll();
public void insertVisitor(String phoneNumber, String submitTime, String name);
}
And the repository implementation:
#Repository
#Transactional
public class VisitorRepo implements VisitorRepository{
private final DSLContext dslContext;
public VisitorRepo(DSLContext dslContext){
this.dslContext = dslContext;
}
public void insertVisitor(String phoneNumber, String submitTime, String name){
this.dslContext
.insertInto(Phonenum.PHONENUM)
.columns(Phonenum.PHONENUM.PHONENUMBER, Phonenum.PHONENUM.SUBMITTIME, Phonenum.PHONENUM.NAME)
.values(phoneNumber, submitTime, name);
}
public com.demo.visitorlog.model.Phonenum add(com.demo.visitorlog.model.Phonenum visitor) {
this.dslContext
.insertInto(Phonenum.PHONENUM)
.columns(Phonenum.PHONENUM.PHONENUMBER, Phonenum.PHONENUM.SUBMITTIME, Phonenum.PHONENUM.NAME)
.values(visitor.getPhonenumber(), String.valueOf("submit time"), visitor.getName());
System.out.println("ran");
return visitor;
}
I'm not entirely sure how to setup the config class but here is what I have:
#Configuration
public class mainConfig{
#Autowired
private DataSource dataSource;
#Bean
public DataSourceConnectionProvider connectionProvider() {
return new DataSourceConnectionProvider
(new TransactionAwareDataSourceProxy(dataSource));
}
#Bean
public DefaultDSLContext dsl() {
return new DefaultDSLContext(configuration());
}
public DefaultConfiguration configuration() {
DefaultConfiguration jooqConfiguration = new DefaultConfiguration();
jooqConfiguration.set(connectionProvider());
return jooqConfiguration;
}
So I get the System.out.println() after the post request but when I look into my db there isn't anything inserted. Am I missing something? Any sort of help would be greatly appreciated :)
You have to call .execute() to execute the query
this.dslContext
.insertInto(Phonenum.PHONENUM)
.columns(Phonenum.PHONENUM.PHONENUMBER, Phonenum.PHONENUM.SUBMITTIME, Phonenum.PHONENUM.NAME)
.values(visitor.getPhonenumber(), String.valueOf("submit time"), visitor.getName())
.execute();
The problem was with Spring Security automatically enabling csrf Protection which I had to manual disable in the Security Config class using:
#Override
protected void configure(HttpSecurity http) throws Exception{
http.cors().and().csrf().disable();
}
Related
I have a #Controller that write a query result into the Model attributes.
When a GETquery arrives, it may be rewritten by a QuerydslBinderCustomizer to translate the query to the database fields accordingly:
#Controller
public class MyController {
#Autowired
private MyEntityRepository dao;
#GetMapping("/test")
public String findAll(Model model,
#QuerydslPredicate(root = MyEntity.class) Predicate predicate) {
model.addAttribute("page", dao.findAll(predicate, Pageable.of(5));
return "/test";
}
}
public interface MyEntityRepository extends
JpaRepository<MyEntity, Long>,
QuerydslPredicateExecutor<MyEntity>,
QuerydslBinderCustomizer<QMyEntity> {
#Override
default void customize(final QuerydslBindings bindings, final QMyEntity entity) {
bindings.bind(entity.somevalue).first((path, value) -> //... create a custom binding of it );
}
}
Question: how could I (unit)-test the QuerydslBinderCustomizer? Because: it is only rendered when a request arrives through the spring MVC layer.
In general, I could test the servlet as follows, but I'm then unable to read the return results from Model object:
#AutoConfigureMockMvc
public class MyTest {
#Test
public void test(#Autowired WebTestClient webTestClient) {
webTestClient.get()
.uri("/test?somevalue=junit")
.exchange()
.expectStatus().isOk();
//TODO how to access the 'Model.page' for assertion?
}
}
I am working within an environment that changes credentials every several minutes. In order for beans that implement clients who depend on these credentials to work, the beans need to be refreshed. I decided that a good approach for that would be implementing a custom scope for it.
After looking around a bit on the documentation I found that the main method for a scope to be implemented is the get method:
public class CyberArkScope implements Scope {
private Map<String, Pair<LocalDateTime, Object>> scopedObjects = new ConcurrentHashMap<>();
private Map<String, Runnable> destructionCallbacks = new ConcurrentHashMap<>();
private Integer scopeRefresh;
public CyberArkScope(Integer scopeRefresh) {
this.scopeRefresh = scopeRefresh;
}
#Override
public Object get(String name, ObjectFactory<?> objectFactory) {
if (!scopedObjects.containsKey(name) || scopedObjects.get(name).getKey()
.isBefore(LocalDateTime.now().minusMinutes(scopeRefresh))) {
scopedObjects.put(name, Pair.of(LocalDateTime.now(), objectFactory.getObject()));
}
return scopedObjects.get(name).getValue();
}
#Override
public Object remove(String name) {
destructionCallbacks.remove(name);
return scopedObjects.remove(name);
}
#Override
public void registerDestructionCallback(String name, Runnable runnable) {
destructionCallbacks.put(name, runnable);
}
#Override
public Object resolveContextualObject(String name) {
return null;
}
#Override
public String getConversationId() {
return "CyberArk";
}
}
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk")
public String dateString(){
return LocalDateTime.now().toString();
}
}
#RestController
public class HelloWorld {
#Autowired
private String dateString;
#RequestMapping("/")
public String index() {
return dateString;
}
}
When I debug this implemetation with a simple String scope autowired in a controller I see that the get method is only called once in the startup and never again. So this means that the bean is never again refreshed. Is there something wrong in this behaviour or is that how the get method is supposed to work?
It seems you need to also define the proxyMode which injects an AOP proxy instead of a static reference to a string. Note that the bean class cant be final. This solved it:
#Configuration
#Import(CyberArkScopeConfig.class)
public class TestConfig {
#Bean
#Scope(scopeName = "CyberArk", proxyMode=ScopedProxyMode.TARGET_CLASS)
public NonFinalString dateString(){
return new NonFinalString(LocalDateTime.now());
}
}
I am trying out a simple application using Springboot.
Here is my Application.java class
#SpringBootApplication
public class Application {
#Autowired(required=true)
private Listener listener;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#PostConstruct
public void initialize() {
SQS sqs = new SQS();
sqs.init();
sqs.createQueue("myQueue");
listener.run();
}
}
And here is my UserController.java
#Controller
public class UserController {
#Autowired
private UserDao userDao;
#RequestMapping(value = "/findall", method = RequestMethod.GET)
public ModelAndView getAll(#RequestParam(value = "status", required=false) String status, Model model) {
Iterable<User> users = userDao.findAll();
ModelAndView mav = new ModelAndView("findall");
model.addAttribute("userobj",new User());
mav.addObject("users", users);
mav.addObject("status",status);
return mav;
}
#RequestMapping (value = "edit", method = RequestMethod.POST, params="action=ADD")
{
public String saveUser(#ModelAttribute UserDto userDto) {
try {
if(userDto.getId() != null) {
throw new UserExists();
}
SQS.sendMessage(userDto);
return "redirect:findall?status=success";
} catch (Exception e) {
System.err.println("Error Saving User");
e.printStackTrace();
return "redirect:findall?status=failure";
}
}
Now the issue is that my Controller does not get registered(i.e when I try hitting the URL I get page not found)when I use #PostConstruct on the initialize method.
When I try removing the #PostConstruct it works fine. But I need the initialize() to run() the listener (Another runnable class that I have written for reading message from the Queue)
I tried tweaking the Application class where I would not require #PostConstruct but it did not work.
Here was what I tried to do with Application.java class:
#SpringBootApplication
public class Application {
#Autowired(required=true)
private static Listener listener;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
initialize();
}
public static void initialize() {
SQS sqs = new SQS();
sqs.init();
sqs.createQueue("myQueue");
listener.run();
}
}
In this case the #Controller works fine. But the #Autowiring of private static Listener listener; fails and throws NullPointerException.
So could anyone tell me a workaround this so that both of the below work.
#Autowired(required=true)
private Listener listener; --> gets autowired so I will be able to run() it
My Controller API's are registered so that I can hit the URL's
You need to use Spring Cloud AWS library and register SQS endpoint listener with the following annotation:
#SqsListener("${cloud.aws.sqs.queue.name}")
I recommend to create a separate #Component class for this SQS listener, the main Application class is not proper place for such thing.
Here is Spring Cloud AWS guide
I am using scheduled task to update my database like this:
public interface UserRatingManager {
public void updateAllUsers();
}
#Service
public class DefaultUserRatingManager implements UserRatingManager {
#Autowired
UserRatingDAO userRatingDAO;
#Override
#Transactional("txName")
public void updateAllUsers() {
List<String> userIds = userRatingDAO.getAllUserIds();
for (String userId : userIds) {
updateUserRating(userId);
}
}
}
public interface UserRatingDAO extends GenericDAO<UserRating, String> {
public void deleteAll();
public List<String> getAllUserIds();
}
#Repository
public class HibernateUserRatingDAO extends BaseDAO<UserRating, String> implements UserRatingDAO {
#Override
public List<String> getAllUserIds() {
List<String> result = new ArrayList<String>();
Query q1 = getSession().createQuery("Select userId from UserRating");
}
}
I configured the persistence like this:
#Configuration
#ComponentScan({ "com.estartup" })
#PropertySource("classpath:jdbc.properties")
#EnableTransactionManagement
#EnableScheduling
public class PersistenceConfig {
#Autowired
Environment env;
#Scheduled(fixedRate = 5000)
public void run() {
userRatingManager().updateAllUsers();
}
#Bean
public DataSource dataSource() {
DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource(env.getProperty("connection.url"), env.getProperty("connection.username"), env.getProperty("connection.password"));
driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
return driverManagerDataSource;
}
public PersistenceConfig() {
super();
}
#Bean
public UserRatingUpdate userRatingUpdate() {
return new UserRatingUpdate();
}
#Bean
public UserRatingManager userRatingManager() {
return new DefaultUserRatingManager();
}
#Bean
public LocalSessionFactoryBean runnableSessionFactory() {
LocalSessionFactoryBean factoryBean = null;
try {
factoryBean = createBaseSessionFactory();
} catch (Exception e) {
e.printStackTrace();
}
return factoryBean;
}
private LocalSessionFactoryBean createBaseSessionFactory() throws IOException {
LocalSessionFactoryBean factoryBean;
factoryBean = new LocalSessionFactoryBean();
Properties pp = new Properties();
pp.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
pp.setProperty("hibernate.max_fetch_depth", "3");
pp.setProperty("hibernate.show_sql", "false");
factoryBean.setDataSource(dataSource());
factoryBean.setPackagesToScan(new String[] { "com.estartup.*" });
factoryBean.setHibernateProperties(pp);
factoryBean.afterPropertiesSet();
return factoryBean;
}
#Bean(name = "txName")
public HibernateTransactionManager runnableTransactionManager() {
HibernateTransactionManager htm = new HibernateTransactionManager(runnableSessionFactory().getObject());
return htm;
}
}
However, when I get to:
Query q1 = getSession().createQuery("Select userId from UserRating");
in the above HibernateUserRatingDAO I get an exception:
org.hibernate.HibernateException: createQuery is not valid without active transaction
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:352)
at com.sun.proxy.$Proxy63.createQuery(Unknown Source)
at com.estartup.dao.impl.HibernateUserRatingDAO.getAllUserIds(HibernateUserRatingDAO.java:36)
How can I configure to include my scheduled tasks in transactions ?
EDITED:
Here is the code for BaseDAO
#Repository
public class BaseDAO<T, ID extends Serializable> extends GenericDAOImpl<T, ID> {
private static final Logger logger = LoggerFactory.getLogger(BaseDAO.class);
#Autowired
#Override
public void setSessionFactory(SessionFactory sessionFactory) {
super.setSessionFactory(sessionFactory);
}
public void setTopAndForUpdate(int top, Query query){
query.setLockOptions(LockOptions.UPGRADE);
query.setFirstResult(0);
query.setMaxResults(top);
}
EDITED
Enabling Spring transaction prints the following log:
DEBUG [pool-1-thread-1] org.springframework.transaction.annotation.AnnotationTransactionAttributeSource - Adding transactional method 'updateAllUsers' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; 'txName'
What is happening in this case is that since you are using userRatingManager() inside the configuration (where the actual scheduled method exists), the proxy that Spring creates to handle the transaction management for UserRatingUpdate is not being used.
I propose you do the following:
public interface WhateverService {
void executeScheduled();
}
#Service
public class WhateverServiceImpl {
private final UserRatingManager userRatingManager;
#Autowired
public WhateverServiceImpl(UserRatingManager userRatingManager) {
this.userRatingManager = userRatingManager;
}
#Scheduled(fixedRate = 5000)
public void executeScheduled() {
userRatingManager.updateAllUsers()
}
}
Also change your transaction manager configuration code to:
#Bean(name = "txName")
#Autowired
public HibernateTransactionManager runnableTransactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory);
return htm;
}
and remove factoryBean.afterPropertiesSet(); from createBaseSessionFactory
As I already mentioned, I used your code and created a small sample that works for me. Judging by the classes used, I assumed you are using Hibernate Generic DAO Framework. It's a standalone sample, the main() class is Main. Running it you can see the transactional related DEBUG messages in logs that show when a transaction is initiated and committed. You can compare my settings, jars versions used with what you have and see if anything stands out.
Also, as I already suggested you might want to look in the logs to see if proper transactional behavior is being used and compare that with the logs my sample creates.
I tried to replicate your problem so I integrated it in my Hibernate examples on GitHub:
You can run my CompanySchedulerTest and see it's working so this is what I did to run it:
I made sure the application context is aware of our scheduler
<task:annotation-driven/>
The scheduler is defined in its own bean:
#Service
public class CompanyScheduler implements DisposableBean {
private static final Logger LOG = LoggerFactory.getLogger(CompanyScheduler.class);
#Autowired
private CompanyManager companyManager;
private volatile boolean enabled = true;
#Override
public void destroy() throws Exception {
enabled = false;
}
#Scheduled(fixedRate = 100)
public void run() {
if (enabled) {
LOG.info("Run scheduler");
companyManager.updateAllUsers();
}
}
}
My JPA/Hibernate configs are in applicationContext-test.xml and they are configured for JPA according to the Spring framework indications, so you might want to double check your Hibernate settings as well.
I have a little problem. I think this is typical question. However, I can't find good example. My application is using Jersey. And I want to test controller by client as test. Controller has private field - StudentService. When I debug test I see, that field is null. This leads to error. And I need to inject this field. I tried this:
My Controller
#Path("/student")
#Component
public class StudentResourse {
#Autowired
private StrudentService service; // this field Spring does not set
#Path("/getStudent/{id}")
#GET
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Student getStudent(#PathParam("id") long id) {
return service.get(id);
}
}
My JUnit test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:config.xml")
#TestExecutionListeners({ DbUnitTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public class StudentResourseTest extends JerseyTest {
private static final String PACKAGE_NAME = "com.example.servlet";
private static final String FILE_DATASET = "/data.xml";
#Autowired
private StudentService service; // this field is setted by Spring, but I do not need this field for test
public StudentResourseTest() {
super(new WebAppDescriptor.Builder(PACKAGE_NAME).build());
}
#Override
protected TestContainerFactory getTestContainerFactory() {
return new HTTPContainerFactory();
}
#Override
protected AppDescriptor configure() {
return new WebAppDescriptor.Builder("restful.server.resource")
.contextParam("contextConfigLocation",
"classpath:/config.xml").contextPath("/")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class).build();
}
#Test
#DatabaseSetup(FILE_DATASET)
public void test() throws UnsupportedEncodingException {
ClientResponse response = resource().path("student").path("getStudent")
.path("100500").accept(MediaType.APPLICATION_XML)
.get(ClientResponse.class);
Student student = (Student) response.getEntity(Student.class);
} }
I guees, that problem is in test class. Because, when I run my application not in test, I can directly request students and everything working fine. But when I test classes, internal field of Controller does not setted. How to fix this bug? Thanks for your answers.
This is in my config.xml
<context:component-scan base-package="com.example" />
<bean id="StudentResourse" class="com.example.servlet.StudentResourse">
<property name="service" ref="studentService" />
</bean>
<bean id="service" class="com.example.service.StudentServiceImpl" />
One issue may be that you're trying to configure your test application in constructor and in configure() method. Use one or another but not both because in this case your configure() method is not invoked and hence you may not be using SpringServlet and everything that is defined in this method.
Reference: https://github.com/jiunjiunma/spring-jersey-test and http://geek.riffpie.com/unit-testing-restful-jersey-services-glued-together-with-spring/
Idea is to get a hold of the application context inside jersey by using ApplicationContextAware interface. There after we can grab the exact bean already created by spring, in your case, StudentService. Below example shows a mocked version of the dependency, SampleService, used to test the resource layer apis.
Resource class delegating the processing to a service layer
#Component
#Path("/sample")
public class SampleResource {
#Autowired
private SampleService sampleService;
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path ("/{id}")
public Sample getSample(#PathParam("id") int id) {
Sample sample = sampleService.getSample(id);
if (sample == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return sample;
}
}
Service layer encapsulating business logic
#Service
public class SampleService {
private static final Map<Integer, Sample> samples = new HashMap<>();
static {
samples.put(1, new Sample(1, "sample1"));
samples.put(2, new Sample(2, "sample2"));
}
public Sample getSample(int id) {
return samples.get(id);
}
}
Unit test for the above resource
public class SampleResourceTest extends SpringContextAwareJerseyTest {
private SampleService mockSampleService;
// create mock object for our test
#Bean
static public SampleService sampleService() {
return Mockito.mock(SampleService.class);
}
/**
* Create our own resource here so only the test resource is loaded. If
* we use #ComponentScan, the whole package will be scanned and more
* resources may be loaded (which is usually NOT what we want in a test).
*/
#Bean
static public SampleResource sampleResource() {
return new SampleResource();
}
// get the mock objects from the internal servlet context, because
// the app context may get recreated for each test so we have to set
// it before each run
#Before
public void setupMocks() {
mockSampleService = getContext().getBean(SampleService.class);
}
#Test
public void testMock() {
Assert.assertNotNull(mockSampleService);
}
#Test
public void testGetSample() {
// see how the mock object hijack the sample service, now id 3 is valid
Sample sample3 = new Sample(3, "sample3");
Mockito.when(mockSampleService.getSample(3)).thenReturn(sample3);
expect().statusCode(200).get(SERVLET_PATH + "/sample/3");
String jsonStr = get(SERVLET_PATH + "/sample/3").asString();
Assert.assertNotNull(jsonStr);
}
}
SpringContextAwareJerseyTest
#Configuration
public class SpringContextAwareJerseyTest extends JerseyTest {
protected static String SERVLET_PATH = "/api";
final private static ThreadLocal<ApplicationContext> context =
new ThreadLocal<>();
protected String getResourceLocation() {
return "example.rest";
}
protected String getContextConfigLocation() {
return getClass().getName();
}
static private String getContextHolderConfigLocation() {
return SpringContextAwareJerseyTest.class.getName();
}
protected WebAppDescriptor configure() {
String contextConfigLocation = getContextConfigLocation() + " " +
getContextHolderConfigLocation();
Map<String, String> initParams = new HashMap<>();
initParams.put("com.sun.jersey.config.property.packages",
getResourceLocation());
initParams.put("com.sun.jersey.api.json.POJOMappingFeature", "true");
return new WebAppDescriptor.Builder(initParams)
.servletClass(SpringServlet.class)
.contextParam(
"contextClass",
"org.springframework.web.context.support.AnnotationConfigWebApplicationContext")
.contextParam("contextConfigLocation", contextConfigLocation)
.servletPath(SERVLET_PATH) // if not specified, it set to root resource
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
protected final ApplicationContext getContext() {
return context.get();
}
#Bean
public static ContextHolder contextHolder() {
return new ContextHolder();
}
private static class ContextHolder implements ApplicationContextAware {
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
context.set(applicationContext);
}
}
}
Using the above with jersey 1.8