Java Spring 4 (Annotated) Rest Controller not being hit by REST Client tool in Firefox - spring

Hi,
I have a problem that is very confusing for me because the mapping should work and it looks like it does map when the Spring Boot is started in debug mode. I don't know where else I can check for an obvious solution to this problem.
Here is the application.properties:
server.port=8082
server.contextPath = /
Here is the SpringBootInitializer class that adds a further "/api" to the >Servlet registration:
public class App extends SpringBootServletInitializer {
#Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
#Bean
public ServletRegistrationBean dispatcherServletRegistration() {
final ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/api/*");
final Map<String, String> params = new HashMap<String, String>();
params.put("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
params.put("contextConfigLocation", "org.spring.sec2.spring");
params.put("dispatchOptionsRequest", "true");
registration.setInitParameters(params);
registration.setLoadOnStartup(1);
return registration;
}
//
#Override
protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.initializers(new MyApplicationContextInitializer()).sources(App.class);
}
public static void main(final String... args) {
new SpringApplicationBuilder(App.class).initializers(new MyApplicationContextInitializer()).run(args);
}
}
Here is the Controler which adds a further "users" to the mapping. The method >which I have set a debug point is the findAll and requires no futher mapping to >get to it (i.e. the root of /users/:
#Controller
#RequestMapping(value = users)
public class UserController extends AbstractController<User> {
#Autowired
private IUserService userService;
public UserController() {
super(User.class);
}
// API
// find
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public void getItsWorking() {
System.out.println("It's Working!!!");
}
}
Here is the User entity:
#Entity
public class User implements IEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="user_id")
private Long user_id;
#Column(name = "username", unique = true, nullable = false)
private String name;
#Column(unique = true, nullable = false)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private Boolean locked;
public User() {
super();
}
public User(final String nameToSet, final String passwordToSet, /*final
Set<Role> rolesToSet,*/ final Boolean lockedToSet) {
super();
name = nameToSet;
password = passwordToSet;
locked = lockedToSet;
}
// API
public Long getId() {
return user_id;
}
public void setId(final Long idToSet) {
user_id = idToSet;
}
public String getName() {
return name;
}
public void setName(final String nameToSet) {
name = nameToSet;
}
public String getEmail() {
return email;
}
public void setEmail(final String emailToSet) {
email = emailToSet;
}
public String getPassword() {
return password;
}
public void setPassword(final String passwordToSet) {
password = passwordToSet;
}
public Boolean getLocked() {
return locked;
}
public void setLocked(final Boolean lockedToSet) {
locked = lockedToSet;
}
}
Here is the output on my Spring Boot debug when it starts up:
Mapped "{[/users],methods=[GET]}" onto public
java.util.List<org.um.persistence.model.User>
org.um.web.controller.UserController.findAll(javax.servlet.http.HttpServletRequest)
So, it looks like it is mapping correctly, but when I hit it using the Rest >Client tool add on in Firefox, I get the following when doing a "GET" on the >following url: http://localhost:8082/api/users using Content-Type: application/json in my header .
What is going on? Very confused.

You should put a #RequestMapping("/api") on you class, and a #RequestMapping("/users") on your method (that should preferably return something to the client).
This ways your endpoint will be exposed as /api/users and you will be able to easily add further endpoints under /api/* into this class.

Related

Can I return DTO and domain entities from services?

I have a spring-boot application and I use DTO like that:
Service
#Service
public class UnitOfMeasureServiceImpl implements IUnitOfMeasureService {
private final IUnitsOfMeasureRepository unitOfMeasureRepository;
#Autowired
public UnitOfMeasureServiceImpl(IUnitsOfMeasureRepository unitOfMeasureRepository) {
this.unitOfMeasureRepository = unitOfMeasureRepository;
}
#Override
public UnitOfMeasureDTO getUnitOfMeasureById(UUID id) {
Optional<UnitOfMeasure> optionalUnitOfMeasure = unitOfMeasureRepository.findById(id);
if (!optionalUnitOfMeasure.isPresent()){
// throw new ComponentNotFoundException(id);
return null;
}
return UnitOfMeasureDTO.factory(optionalUnitOfMeasure.get());
}
dto:
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class UnitOfMeasureDTO {
private String id;
private String name;
private String description;
private String sourceInfoCompanyName;
private String originalId;
public static UnitOfMeasureDTO factory(UnitOfMeasure unitOfMeasure) {
UnitOfMeasureDTO dto = new UnitOfMeasureDTO();
dto.id = unitOfMeasure.getId().toString();
dto.name = unitOfMeasure.getName();
dto.description = unitOfMeasure.getDescription();
dto.sourceInfoCompanyName = unitOfMeasure.getSourceInfo().getSourceCompany().getName();
dto.originalId = unitOfMeasure.getOriginalId();
return dto;
}
}
controller:
#RestController
#RequestMapping(UnitOfMeasureController.BASE_URL)
public class UnitOfMeasureController {
public static final String BASE_URL = "/api/sust/v1/unitOfMeasures";
private final IUnitOfMeasureService unitOfMeasureService;
public UnitOfMeasureController(IUnitOfMeasureService unitOfMeasureService) {
this.unitOfMeasureService = unitOfMeasureService;
}
#GetMapping(path = "/{id}")
#ResponseStatus(HttpStatus.OK)
public UnitOfMeasureDTO getUnitOfMeasureDTO(#PathVariable("id") UUID id) {
UnitOfMeasureDTO unitOfMeasureDTO = unitOfMeasureService.getUnitOfMeasureById(id);
return unitOfMeasureDTO;
}
So in my service I have getUnitOfMeasureById(UUID id) that return a UnitOfMeasureDTO.
Now I need to call, from another service, getUnitOfMeasureById(UUID id) that return the domain entity UnitOfMeasure. I think it's correct to call a service method from another service (not a controller method!) and the separation between business logic is at the service layer. So is it correct to have 2 methods: getUnitOfMeasureDTOById and getUnitOfMeasureById in the service? (getUnitOfMeasureDTOById call getUnitOfMeasureById to avoid code duplication)

Deserialising OffsetDateTime with Springboot webclient

I am facing issue related to OffsetDateTimeStamp while writing test case for my webservice. When I test from browser, it's gives right response but while writing test case it's not showing offset and because of that it's failing.
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class TestControllerTest {
#LocalServerPort
private int port;
private WebClient client;
#Test
public void test() {
Person person = new Person();
person.setId(1);
person.setBirthDate(OffsetDateTime.now());
person.setMobile(9090909090L);
person.setName("Tempo");
client = WebClient.create("http://localhost:"+port);
Person response = client.post().uri("/test1")
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(person))
.retrieve()
.bodyToMono(Person.class)
.block();
Assertions.assertEquals(person.getBirthDate(), response.getBirthDate());
}
}
Controller Code
#RestController
public class TestController {
#PostMapping("/test1")
public Mono<Person> test1(#RequestBody Person person) {
System.out.println(person.getBirthDate());
return Mono.just(person);
}
}
Mail Application code
#SpringBootApplication
public class TestAppApplication {
public static void main(String[] args) {
SpringApplication.run(TestAppApplication.class, args);
}
#Bean
public Module javaTimeModule() {
return new JavaTimeModule();
}
#Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
}
}
Person.java
public class Person {
private int id;
private String name;
private long mobile;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
private OffsetDateTime birthDate;
public Person() {}
}
application.properties
spring.jackson.serialization.write-dates-as-timestamps=false
spring.jackson.deserialization.adjust-dates-to-context-time-zone=false
spring.jackson.serialization.write-dates-with-zone-id=true
output of test case
org.opentest4j.AssertionFailedError:
Expected :2021-01-04T17:43:51.817+05:30
Actual :2021-01-04T12:13:51.817Z
<Click to see difference>
How is your property birthDate defined in your Person entity class? You need to define the format there. You can do it like this:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
private OffsetDateTime birthDate;
...
public OffsetDateTime getBirthDate() {
return birthDate;
}
See more detailed info in the answers to this question: Spring Data JPA - ZonedDateTime format for json serialization

Axon Event Handler not Working

I am developing a small cqrs implementation and I am very new to it.
I want to segregate each handlers(Command and Event) from aggregate and
make sure all are working well. The command handler are getting triggered
from controller but from there event handlers are not triggered. Could
anyone Please help on this.
public class User extends AbstractAnnotatedAggregateRoot<String> {
/**
*
*/
private static final long serialVersionUID = 1L;
#AggregateIdentifier
private String userId;
private String userName;
private String age;
public User() {
}
public User(String userid) {
this.userId=userid;
}
#Override
public String getIdentifier() {
return this.userId;
}
public void createuserEvent(UserCommand command){
apply(new UserEvent(command.getUserId()));
}
#EventSourcingHandler
public void applyAccountCreation(UserEvent event) {
this.userId = event.getUserId();
}
}
public class UserCommand {
private final String userId;
public UserCommand(String userid) {
this.userId = userid;
}
public String getUserId() {
return userId;
}
}
#Component
public class UserCommandHandler {
#CommandHandler
public void userCreateCommand(UserCommand command) {
User user = new User(command.getUserId());
user.createuserEvent(command);
}
}
public class UserEvent {
private final String userId;
public UserEvent(String userid) {
this.userId = userid;
}
public String getUserId() {
return userId;
}
}
#Component
public class UserEventHandler {
#EventHandler
public void createUser(UserEvent userEvent) {
System.out.println("Event triggered");
}
}
#Configuration
#AnnotationDriven
public class AppConfiguration {
#Bean
public SimpleCommandBus commandBus() {
SimpleCommandBus simpleCommandBus = new SimpleCommandBus();
return simpleCommandBus;
}
#Bean
public Cluster normalCluster() {
SimpleCluster simpleCluster = new SimpleCluster("simpleCluster");
return simpleCluster;
}
#Bean
public ClusterSelector clusterSelector() {
Map<String, Cluster> clusterMap = new HashMap<>();
clusterMap.put("com.user.event.handler", normalCluster());
//clusterMap.put("exploringaxon.replay", replayCluster());
return new ClassNamePrefixClusterSelector(clusterMap);
}
#Bean
public EventBus clusteringEventBus() {
ClusteringEventBus clusteringEventBus = new ClusteringEventBus(clusterSelector(), terminal());
return clusteringEventBus;
}
#Bean
public EventBusTerminal terminal() {
return new EventBusTerminal() {
#Override
public void publish(EventMessage... events) {
normalCluster().publish(events);
}
#Override
public void onClusterCreated(Cluster cluster) {
}
};
}
#Bean
public DefaultCommandGateway commandGateway() {
return new DefaultCommandGateway(commandBus());
}
#Bean
public Repository<User> eventSourcingRepository() {
EventStore eventStore = new FileSystemEventStore(new SimpleEventFileResolver(new File("D://sevents.txt")));
EventSourcingRepository eventSourcingRepository = new EventSourcingRepository(User.class, eventStore);
eventSourcingRepository.setEventBus(clusteringEventBus());
AnnotationEventListenerAdapter.subscribe(new UserEventHandler(), clusteringEventBus());
return eventSourcingRepository;
}
}
As far as I can tell, the only thing missing is that you aren't adding the User Aggregate to a Repository. By adding it to the Repository, the User is persisted (either by storing the generated events, in the case of Event Sourcing, or its state otherwise) and all Events generated by the Command Handler (including the Aggregate) are published to the Event Bus.
Note that the Aggregate's #EventSourcingHandlers are invoked immediately, but any external #EventHandlers are only invoked after the command handler has been executed.

Spring roo and One-To-Many relationship in GUI generation

I cannot generate an appropriate GUI via roo for a one-to-many relationship. In particular, I would need a multiple choice element to select among the authorities (spring security) to associate to the user.
I created my RegisteredUser class:
#RooJavaBean
#RooToString
#RooJpaActiveRecord
public class RegisteredUser extends MyUser implements UserDetails,
CredentialsContainer {
private String password;
private String username;
private Boolean enabled = true;
private Boolean accountNonExpired = true;
private Boolean credentialsNonExpired = true;
private Boolean accountNonLocked = true;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<MyBaseAuthority> authorities = new HashSet<MyBaseAuthority>();
#Override
public void eraseCredentials() {
password = null;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
#Override
public String getPassword() {
return password;
}
#Override
public String getUsername() {
return username;
}
#Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
#Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
#Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
#Override
public boolean isEnabled() {
return enabled;
}
}
Then MyBaseAuthority class:
#RooJavaBean
#RooToString
#RooJpaActiveRecord
public class MyBaseAuthority extends ObjectWithId implements
GrantedAuthority {
private String authority;
#Override
public String getAuthority() {
return authority;
}
}
Then I had to manually create the controller for MyBaseAuthority, but not for RegisteredUser (generated by webmvc command):
#RequestMapping("/registeredusers")
#Controller
#RooWebScaffold(path = "registeredusers", formBackingObject = RegisteredUser.class)
public class RegisteredUserController {
}
#RequestMapping("/authorities")
#Controller
#RooWebScaffold(path = "authorities", formBackingObject = MyBaseAuthority.class)
public class MyBaseAuthorityController {
}
On the GUI, I can create and list all authorities and registered users. However, when creating a registered user, I can only set string fields and boolean fields, but not the one-to-many relationship. How can I fix that?
If I were trying to acomplish this task I would print out all of my checkboxes with the available options as array keys with a name like so:
<input type="checkbox" name="role[]" value="ROLE_ONE">
<input type="checkbox" name="role[]" value="ROLE_TWO">
Then, I would map these parameters to a String[] array like in this post
#RequestParam(value="myParam[]" String roles)
I would then loop over the strings and add create the MyBaseAuthority objects, attach your user and persist() them.

Problems with WebDataBinder and Set.Class

i am having trouble with binding my data from a form :
I have two class
#Entity
#Table(name = "ROLES")
public class Role implements GenericDomain {
private Long id;
private String code;
private String name;
private Set<Privilege> privileges = new HashSet<Privilege>(0);
public Role() {}
/* getter and setter*/
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "ROLES_PRIVILEGES"
, joinColumns = { #JoinColumn(name = "ROLE_ID") }
, inverseJoinColumns = { #JoinColumn(name = "PRIVILEGE_ID") }
)
public Set<Privilege> getPrivileges() {
return this.privileges;
}
public void setPrivileges(Set<Privilege> privileges) {
this.privileges = privileges;
}
/* overide of hascode, equals*/
}
And
#Entity
#Table(name = "PRIVILEGES")
public class Privilege implements GenericDomain {
private Long id;
private String code;
private Set<Role> roles = new HashSet<Role>(0);
public Privilege() {}
/* getter and setter*/
#ManyToMany(cascade=CascadeType.REFRESH, mappedBy="privileges")
public Set<Role> getRoles() {
return this.roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
#Override
public String toString(){
return this.getCode() + this.getComment();
}
/*overide equals and hascode*/
and in my controller i have :
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Set.class, "privileges", new CustomCollectionEditor(Set.class) {
#Override
protected Object convertElement(Object element) {
return (element == null)?null:privilegeService.getOne(Integer.parseInt((String)element));
}
});
}
#RequestMapping(value = "edit", method = RequestMethod.POST)
public String saveOldRole( #ModelAttribute("role") Role role
, BindingResult result
, ModelMap model
) {
validator.validate(role, result);
if (result.hasErrors()){
logger.error(result.getAllErrors());
model.addAllAttributes(result.getModel());
return "/admin/role/edit";
}
logger.info(role.getPrivileges());
Iterator p = role.getPrivileges().iterator();
while(p.hasNext()){
logger.info(p.next().getClass());
}
roleService.saveOrUpdate(role);
model.addAttribute("roles", roleService.getAll());
sessionStatus.setComplete();
return "redirect:/admin/role/list.do";
}
and my debug is
role.RoleController:93 - [[MANAGE_USERS], [MANAGE_ROLES]]
role.RoleController:96 - class java.util.LinkedHashSet
role.RoleController:96 - class java.util.LinkedHashSet
22:29:44,915 ERROR tomcat-http--7 property.BasicPropertyAccessor:194 - IllegalArgumentException in class: com.stunaz.domain.Privilege, getter method of property: id
I dont understand why at 96, the class type is java.util.LinkedHashSet, i thought it should be Privileges.
I dont understand why my role.getPrivileges() is a Set of Set, it should be a Set of Privilege.
Of course at saveOrUpdate am getting an error.
finaly!!!
there were no bug at all!
i updated my spring jar from 3.0.5.RELEASE to 3.1.0.M1, and voila : somthing stopped working with webdatabinder and CustomCollectionEditor.
i just rollback to 3.0.5.RELEASE and everything is fine.

Resources