I am working with Spring MVC application.
I want to create new event:
fill data at page > controller handle this info > saving new event to DB
I have already created DAO layer and repository layer. But at some places, it behaves very strangely. I have created Java configuration for all repositories in the same way.
Here is Repositories Configuration snippet:
#Configuration
#ComponentScan({ "net.lelyak.edu.repository", "net.lelyak.edu.service" })
#Import({ DatabaseDAOConfiguration.class })
public class ApplicationConfiguration {
#Autowired
private UserDAO userDAO;
#Autowired
private EventDAO eventDAO;
#Autowired
private TicketDAO ticketDAO;
#Autowired
private AuditoriumDAO auditoriumDAO;
#Bean
public AuditoriumRepository auditoriumRepository() {
AuditoriumRepository auditoriumRepository = new AuditoriumRepository();
auditoriumRepository.setDao(auditoriumDAO);
return auditoriumRepository;
}
#Bean
public EventRepository eventRepository() {
EventRepository eventRepository = new EventRepository();
eventRepository.setDao(eventDAO);
return eventRepository;
}
I have faced strange behavior at the controller level.
Here is controller code snippet:
#Controller
#RequestMapping("events")
public class EventsController {
#Autowired
private AuditoriumRepository auditoriumRepository;
#Autowired
private EventRepository eventRepository;
#RequestMapping(path = "add", method = RequestMethod.POST)
public String addEvent(#RequestParam Map<String, String> allRequestParams) {
Event newEvent = new Event();
// get auditorium id from request
String auditoryIdString = allRequestParams.get("auditorium");
Long auditoryId = Long.parseLong(auditoryIdString);
Auditorium auditorium = auditoriumRepository.getById(auditoryId);
newEvent.setAuditorium(auditorium);
AuditoriumRepository is auto-wired fine.
Here is snippet from debug view:
but EventRepository isn't:
Configuration is the same for both. One repository is auto-wired fine, second fails. I am newly at Spring. I can't get a clue why does this happen?
Here is snippet of code from EventRepository:
public class EventRepository extends BaseRepository<Event, EventDAO> {
#Autowired
private AuditoriumRepository auditoriumRepository;
#Override
public int put(Event entity) {
auditoriumRepository.put(entity.getAuditorium());
return super.put(entity);
}
AuditoriumRepository code snippet:
public class AuditoriumRepository extends BaseRepository<Auditorium, AuditoriumDAO> {
#Override
public Auditorium preSave(Auditorium entity) {
return entity;
}
For saving new event to DB I have to use exactly EventRepository. It fails, of course, with following stack trace:
java.lang.NullPointerException
at net.lelyak.edu.repository.BaseRepository.put(BaseRepository.java:23)
at net.lelyak.edu.repository.EventRepository.put(EventRepository.java:20)
at net.lelyak.edu.repository.EventRepository$$FastClassBySpringCGLIB$$5de8d2a5.invoke()
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
I am using spring 4.2.4.RELEASE on Windows 10.
UPDATE:
BaseRepository code snippet:
public abstract class BaseRepository<T extends BaseEntity, E extends BaseDAO<T>> {
private E dao;
public E getDao() {
return dao;
}
public void setDao(E dao) {
this.dao = dao;
}
public int put(T entity) {
return dao.save(preSave(entity));
}
I can't understand why with the same configuration and repository structure. One instance is auto-wired by Spring fine, but the second one fails. How to find the root of this problem? And some solution.
UPDATE 2:
I have tried recommended solution, and added next setter to EventRepository:
public void setAuditoriumRepository(AuditoriumRepository auditoriumRepository) {
System.out.println("EventRepository.setAuditoriumRepository");
this.auditoriumRepository = auditoriumRepository;
}
As log message show this setter is executed. But keep failing for the same reason.
How to solve this issue?
i saw your code and the problem is not related to Spring and autowiring process. The problem is in your save method in BaseDAO class:
#Override
public Integer save(ENTITY entity) {
if (entity.getId() == null) {
insert(entity);
} else {
update(entity);
}
return null;
}
if you change the metod to return newly inserted/updated entity id it will work fine.
try to hardcode it just for test :
#Override
public Integer save(ENTITY entity) {
if (entity.getId() == null) {
insert(entity);
} else {
update(entity);
}
return 1;
}
and the problem is raised when you return null from save method then you are doing assignment in the EventsController class which is:
int eventId = eventRepository.put(newEvent); //this is null and throws NullPointerException because you are trying to assign null to primitive variable.
Try to inject the dependencies yourself in the Java #Configuration class. Update your EventRepository so that it has a setter method for the AuditoriumRepository dependency as follows
public class EventRepository extends BaseRepository<Event, EventDAO> {
private AuditoriumRepository auditoriumRepository;
#Override
public int put(Event entity) {
auditoriumRepository.put(entity.getAuditorium());
return super.put(entity);
}
public void setAuditoriumRepository(AuditoriumRepository auditoriumRepository){
this.auditoriumRepository=auditoriumRepository;
}
}
In your ApplicationConfiguration update as follow:
#Bean
public EventRepository eventRepository() {
EventRepository eventRepository = new EventRepository();
eventRepository.setDao(eventDAO);
eventRepository.setAuditoriumRepository(auditoriumRepository());
return eventRepository;
}
The null pointer exception is because auditoriumRepository is not set in the eventRepository bean.
So while creating the EventRepository bean in ApplicationConfiguration.java, we need to set auditoriumRepository as well.
For this I have updated ApplicationConfiguration as below:
public EventRepository eventRepository() {
EventRepository eventRepository = new EventRepository();
eventRepository.setDao(eventDAO);
eventRepository.setAuditoriumRepository(auditoriumRepository());
return eventRepository;
}
and added setter for auditoriumRepository in the EventRepository class
public AuditoriumRepository getAuditoriumRepository() {
return auditoriumRepository;
}
public void setAuditoriumRepository(AuditoriumRepository auditoriumRepository) {
this.auditoriumRepository = auditoriumRepository;
}
This will ensure that the NPE you are seeing will be resolved.
But subsequently we will still get the NPE at a different point now and i.e., at BaseDAO class. The save method is returning a null object and that is causing this NPE.
To resolve this, I have updated the save method of BaseDAO to return a dummy value of 1. You may want to return an appropriate value as per you application needs.
public Integer save(ENTITY entity) {
if (entity.getId() == null) {
insert(entity);
} else {
update(entity);
}
return 1;
}
With these changes, we will able to save the event and proceed to the next screen.
Related
I need to #Autowire database services or repositores form "database" module in "game" module.
Already added those annotations in main "Application" class:
#Configuration
#EnableMethodSecurity
#SpringBootApplication
#EnableAutoConfiguration
#EntityScan(basePackages="com.rydzwr.tictactoe")
#ComponentScan(basePackages="com.rydzwr.tictactoe")
#EnableJpaRepositories(basePackages="com.rydzwr.tictactoe")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Adding them in constructor is much more difficult, becouse I'm need to create list of instances of those classes in StrategySelector:
#Service
public class GameStrategySelector {
private final List<BuildGameStrategy> strategyList = asList(
new LocalPlayerGameStrategy(),
new MultiPlayerGameStrategy()
);
public BuildGameStrategy chooseStrategy(GameDto gameDto) {
return strategyList
.stream()
.filter(strategy -> strategy.applies(gameDto))
.findFirst()
.orElse(new ErrorGameTypeStrategy());
}
}
Or, maybe someone has better idea, for StrategySelector logic?
Here is class with given problem:
#Component
public class LocalPlayerGameStrategy implements BuildGameStrategy {
#Autowired
private GameService gameService;
#Autowired
private UserService userService;
#Autowired
private PlayerService playerService;
#Override
#Transactional
public void buildGame(GameDto gameDto) {
Game game = new GameBuilder(gameDto.getGameSize(), gameDto.getGameDifficulty()).build();
gameService.save(game);
User caller = userService.findByName(SecurityContextHolder.getContext().getAuthentication().getName());
assert caller != null;
for (PlayerDto playerDto : gameDto.getPlayers()) {
Player player = new PlayerBuilder().setGame(game).setUser(caller).setPlayerDetails(playerDto).build();
playerService.save(player);
}
game.setState(GameState.IN_PROGRESS);
gameService.save(game);
}
#Override
public boolean applies(GameDto gameDto) {
return gameDto.getPlayers().stream().allMatch(p -> p.getPlayerType().equals(PlayerType.LOCAL.name()));
}
}
I tried to autowire repositores and services with implemented logic as well.
Every time all of them:
#Autowired
private GameService gameService;
#Autowired
private UserService userService;
#Autowired
private PlayerService playerService;
ARE NULL
I tried everything I found on google
You can't create new instances of LocalPlayerGameStrategy and MultiPlayerGameStrategy inside GameStrategySelector they won't be spring beans so they can't #Autowire other beans.
Modify your GameStrategySelector to inject all BuildGameStrategy instead of creating them manually
#Service
public class GameStrategySelector {
private final List<BuildGameStrategy> strategyList;
public GameStrategySelector(List<BuildGameStrategy> allStrategies){
strategyList = allStrategies;
}
public BuildGameStrategy chooseStrategy(GameDto gameDto) {
return strategyList
.stream()
.filter(strategy -> strategy.applies(gameDto))
.findFirst()
.orElse(new ErrorGameTypeStrategy());
}
}
This is what I tried:
#RunWith(SpringRunner.class)
#DataJpaTest
#ActiveProfiles("h2")
#Rollback(false)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class ServiceTest {
private EntityManager entityManager;
public ServiceTest(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Test
public void findLocation() {
Location location = entityManager.find(Location.class, 2);
assertEquals(location.getName(), "Avenue");
}
#Test
public void updateLocation() {
Location location = entityManager.find(Location.class, 2);
location.setNo_people(10);
entityManager.persist(location);
entityManager.flush();
}
}
the error that I get is ' Runner org.junit.internal.runners.ErrorReportingRunner (used on class com.unibuc.AWBD_Project_v1.services.ServiceTest) does not support filtering and will therefore be run completely. Test class should have exactly one public zero-argument constructor'
Here is the LocationService:
#Service
public class LocationService implements BaseService<Location> {
private final LocationRepository locationRepository;
#Autowired
public LocationService(com.unibuc.AWBD_Project_v1.repositories.LocationRepository locationRepository) {
this.locationRepository = locationRepository;
}
#Override
public Location insert(Location object) {
return locationRepository.save(object);
}
#Override
public Location update(Long id, Location updatedObject) {
var foundId = locationRepository.findById(id);
return foundId.map(locationRepository::save).orElse(null);
}
#Override
public List<Location> getAll() {
return locationRepository.findAll();
}
#Override
public Optional<Location> getById(Long id) {
return locationRepository.findById(id);
}
#Override
public void deleteById(Long id)
{
try {
locationRepository.deleteById(id);
} catch (LocationException e) {
throw new LocationException("Location not found");
}
}
#Override
public Page<Location> findAll(int page, int size, String sortBy, String sortType){
Sort sort = sortType.equalsIgnoreCase(Sort.Direction.ASC.name()) ? Sort.by(sortBy).ascending() :
Sort.by(sortBy).descending();
Pageable pageable = PageRequest.of(page - 1, size, sort);
return locationRepository.findAll(pageable);
}
}
Hello there is 3 issues in your test code.
1 you should remove the EntityManager entityManager from your test constructor to have a runnable test class.
2 if you want to use entityManager inside your test class you should #Autowired it
public class ServiceTest {
#Autowired
private EntityManager entityManager;
3 It's look like you are testing entityManager and not your LocationService
In an unit test you should mock dependencies like entityManager using Mockito
It's seems like you wanted to create an integration test.
The 3 steps of one integration test of a service (exemple with findLocation())
Prepare the data inside a test database
Create a new location object and save it into database using the entityManager or the testEntityManager.
Execute your findLocation methode on the id
Don't forget to Autowire your service class.
Verify if the retrieved data is as expected
Compare the retrieved Location object with the one you've saved.
Here's the code
#RunWith(SpringRunner.class)
#DataJpaTest
#ActiveProfiles("h2")
#Rollback(false)
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class ServiceTest {
#Autowired
private EntityManager entityManager;
#Autowired
private LocationService locationService;
public ServiceTest() {
}
#Test
public void findLocation() {
//given
Location location = new Location(....);
entityManager.save(location);
//when
Location foundLocation=locationService.getById(location.getId());
//then
assertTrue(foundLocation.isPresent());
assertEquals(foundLocation.get().getName(), "Avenue");
}
If you have any question I'm available to help you.
I have an XML-based database and I have defined a User model with a list of references to Role (another model). I have attached an XMLAdapter to roles property to auto-populate roles. For that, I've #autowired the RoleRepository in this adapter.
However, the repository is never autowired (always null), no matter what I do. I have configured Compile-Time Weaving, Load-Time Weaving, and also tried an instrumentation java agent that is able to load itself into the running JVM invesdwin-instrument.
#Configurable(autowire = Autowire.BY_TYPE)
public class RoleAdapter extends XmlAdapter<String, List<Role>> {
#Autowired
protected RoleRepository roleRepository;
public RoleAdapter() {
}
#Override
public List<Role> unmarshal(String nameList) throws Exception {
// code using roleRepository
}
#Override
public String marshal(List<Role> roles) throws Exception {
// some code
}
}
#SpringBootApplication
#EnableConfigurationProperties({MyProperties.class})
#EntityScan(basePackages = { ... })
#EnableDiscoveryClient
#EnableLoadTimeWeaving(aspectjWeaving=EnableLoadTimeWeaving
.AspectJWeaving.ENABLED)
#EnableSpringConfigured // tried this in a separate config
public class MyApplication {
static { // this was not here, added in a desperate move
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
}
// some code
/**
* Main method, used to run the application.
*
* #param args the command line arguments
*/
public static void main(String[] args) {
// dynamically attach java agent to jvm if not already present
DynamicInstrumentationLoader.waitForInitialized();
// weave all classes before they are loaded as beans
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
if (!InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
throw new IllegalStateException("Instrumentation not available!");
} else { // it always gets here
System.out.println("Instrumentation available!");
}
SpringApplication app = new SpringApplication(MyApplication.class);
DefaultProfileUtil.addDefaultProfile(app);
Environment env = app.run(args).getEnvironment();
logApplicationStartup(env);
}
// more code
}
And the roles field in User
#XmlElement(type = String.class)
#XmlJavaTypeAdapter(RoleAdapter.class)
#XmlSchemaType(name = "IDREFS")
protected List<Role> roles;
I would like to know what I've missed here with weaving. A simpler way to achieve these auto-populating properties is also welcome.
"Solved" the problem by forgetting Compile-Time Weaving and Load-Time Weaving, and injecting RolesRepository into UsersRepository, initializing an instance of the RoleAdapter with the injected repository and add this instance into the unmarshaller.
#Repository
public class UserRepository extends TimestampRepository<User> {
public static final String USERS_BY_USERNAME_CACHE = "usersByUsername";
public static final String USERS_BY_EMAIL_CACHE = "usersByEmail";
public UserRepository(
MyProperties myProperties,
ExistTemplate existTemplate,
Jaxb2Marshaller marshaller,
Jaxb2Marshaller unmarshaller,
RoleRepository roleRepository) {
super(
new UserEntityInformation(myProperties.getDatabase().getDbname()),
existTemplate, marshaller, unmarshaller);
marshaller.setAdapters(new RoleAdapter(roleRepository));
unmarshaller.setAdapters(new RoleAdapter(roleRepository));
}
// more code
}
public class RoleAdapter extends XmlAdapter<String, List<Role>> {
protected final RoleRepository roleRepository;
public RoleAdapter(RoleRepository roleRepository) {
this.roleRepository = roleRepository;
}
#Override
public List<Role> unmarshal(String nameList) throws Exception {
// code using roleRepository
}
#Override
public String marshal(List<Role> roles) throws Exception {
// some code
}
}
I have an interface and two implementations of that interface.
Now on the interface I am adding '#Component' annotation. One of the implementation has a '#primary' annotation which is only getting called up.
I want to call both the implementations when I call the interface's method from the autowired interface bean.
#Component
public interface CustomerPersister {
AbuserDetails setAbuserDetails(AbuserDetails customer);
}
#Primary
#Component
public class CustomerRedisPersisterImpl implements CustomerPersister{
#Autowired
private CustomerManager customerManager;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
if(customerManager.setAbuserDetails
(customer,ATSNamespaces.ABUSERDETAILS)){
return customer;
}else{
return new AbuserDetails();
}
}
#Component
public class MongoDbRepositoryImpl implements CustomerPersister{
#Autowired
MongoTemplate mongoTemplate;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
Query query = new Query(Criteria.where("login").is(customer.getLogin()));
System.out.println("query is:"+query);
Update update = new Update();
update.set("isReturnAbuser", customer.getIsReturnAbuser());
update.set("reasonReturnAbuser", customer.getReasonReturnAbuser());
update.set("isCODThrottled", customer.getIsCODThrottled());
update.set("reasonRTOAbuser", customer.getReasonRTOAbuser());
update.set("isFakeEmail", customer.getIsFakeEmail());
update.set("reasonFakeEmail", customer.getReasonFakeEmail());
update.set("amount",customer.getAmount());
WriteResult result = mongoTemplate.upsert(query, update, AbuserDetails.class);
System.out.println("This is the class that got returned:"+result.getClass());
System.out.println("New design result:"+result);
if(result!=null){
if(result.getN() != 0)
return customer;
else
return null;
}else
return null;
}
someOtherClass
#Autowired
private CustomerPersister customerPersister;
#Override
#RequestMapping(value = "/abuser/details/set", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public AbuserDetails setAbuserDetails(#RequestBody AbuserDetails customer){
return customerPersister.setAbuserDetails(customer);
}
You can tell Spring to autowire all implementations of an interface as a List and then call the method an all implementations.
class SomeClass {
List<CustomerPersister> customerPersisters;
#Autowired
SomeClass(List<CustomerPersister> customerPersisters) {
this.customerPersisters = customerPersisters;
}
public void setAbuserDetails(#RequestBody AbuserDetails customer) {
for (CustomerPersister customerPersister: customerPersisters) {
customerPersister.setAbuserDetails(customer);
}
}
}
Of course this will not allow you to return the result of customerPersister.setAbuserDetails(), because you can't return a single value from multiple persister calls. You either have to write some code in SomeClass that will determine which object should be returned or you could return a list of the results from all persisters. Or you have to redesign your interface to match the requirements.
I had already tried solutions mentioned in Why is my Spring #Autowired field null? yet the problem persists. I have tried annotating the class DevicePojo(code below) with #Configurable #Service.
Here are my beans
DistributionConfig.java
#Component
#Configuration
public class DistributionConfig {
#Qualifier("exponentialDistribution")
#Bean
#Scope("prototype")
public DistributionService exponentialDistribution() {
return new ExponentiallyDistribute();
}
#Qualifier("normalDistribution")
#Bean
#Scope("prototype")
public DistributionService normalDistribution() {
return new NormallyDistribute();
}
#Qualifier("uniformDistribution")
#Bean
#Scope("prototype")
public DistributionService uniformDistribution() {
return new UniformlyDistribute();
}
}
JsonFileConfig.java
#Configuration
public class JsonFileConfig {
private static ObjectMapper mapper=new ObjectMapper();
#Qualifier("devicesPojo")
#Bean
public DevicesPojo[] devicesPojo() throws Exception {
DevicesPojo[] devicePojo=mapper.readValue(new File(getClass().getClassLoader().getResource("Topo/esnet-devices.json").getFile()),DevicesPojo[].class);
return devicePojo;
}
#Qualifier("linksPojo")
#Bean
public LinksPojo[] linksPojo() throws Exception {
LinksPojo[] linksPojo=mapper.readValue(new File(getClass().getClassLoader().getResource("Topo/esnet-adjcies.json").getFile()),LinksPojo[].class);
return linksPojo;
}
}
Here is my DevicePojo where i get the null pointer exception.
#JsonDeserialize(using = DeviceDeserializer.class)
#Component
public class DevicesPojo {
private String device;
private List<String> ports;
private List<Integer> bandwidth;
#Autowired
#Qualifier("uniformDistribution")
private DistributionService uniformDistribution; // Here uniformDistribution is null
public DevicesPojo(String device, List<String> port, List<Integer> bandwidth) {
this.device = device;
this.ports= port;
this.bandwidth=bandwidth;
this.uniformDistribution.createUniformDistribution(1000,0,ports.size());
}
public String getDevice(){
return device;
}
public String getRandomPortForDevice()
{
return ports.get((int)uniformDistribution.getSample());
}
public List<String> getAllPorts(){
return ports;
}
public int getBandwidthForPort(String port){
return bandwidth.get(ports.indexOf(port));
}
}
However, if i replace private DistributionService uniformDistribution;with private DistributionService uniformDistribution=new UniformDistribution() the code is working fine.
There is a mix of problems here.
1. You create your DevicesPojo objects using JSON deserializer. Spring has no chance to interfere and inject the DistributionService.
2. Even if it could interfere, it would fail, since you are trying to use the 'distributionService' object in the constructor. Field injection would work only after an object is constructed.
Now regarding fixing the problems.
Long answer short - don't expect auto-injection in your POJOs.
Normally, dependencies like 'distributionService' in objects that are created on the fly, like your DevicesPojo are avoided altogether.
If you insist on having them, inject them manually at construction time:
class DevicesPojoFactory {
#Autowired #Qualifier("uniformDistribution")
private DistributionService uniformDistribution;
ObjectMapper mapper = new ObjectMapper();
DevicesPojo[] readFromFile(String path) {
DevicesPojo[] devicePojoArr = mapper.readValue(...);
for (DevicesPojo dp: devicePojoArr) {
dp.setDistribution(uniformDistribution);
}
}
}