WebSocket build configuration for STOMP/SpringBoot - java

I try to create endpoints /api/streams/positions and /api/streams/orders but confused with configuration.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/api/streams");
config.setApplicationDestinationPrefixes("streams");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/api/streams/positions", "/api/streams/orders")
.setAllowedOrigins("*")
.withSockJS();
}
}
#Controller("/api/streams")
public class WebSocketController {
private final SimpMessagingTemplate simpMessagingTemplate;
#MessageMapping("/positions")
public void positionsSendToBrowserAuthenticatedClient(PositionDto position) {
this.simpMessagingTemplate.convertAndSendToUser(user.getUsername(),"/streams/position", position);
}
}
What is the configuration I need for the controller work? And what is enableSimpleBroker() and setApplicationDestinationPrefixes() actually means? Can I have one endpoint to get and send messages at the same time?
UPDATE_1 +DTO
#Data
#NoArgsConstructor
#AllArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
public class PositionDto {
#JsonProperty("id")
private Long id;
#JsonProperty("type")
private PositionType type;
#JsonProperty("openAt")
private Timestamp openAt;
#JsonProperty("openPrice")
private BigDecimal openPrice;
#JsonProperty("orders")
private List<OrderDto> orders;
}
public enum PositionType {
LONG("LONG"), SHORT("SHORT");
public final String value;
PositionType(String value) {
this.value = value;
}
public String getType() {
return value;
}
}

Maybe I could help because it seems that you're building a kind of trading application, but need more informations: are you building a websocket yourself or do you want to get data from an existing one to build a DTO?

The SendTo is like an transmitter. You just have to "subscribe" to this ws like this, for an angular application's service (for example):
getTestnetCurrentPrice(symbol: string): Observable<any> {
const subject = webSocket(`wss://YOURWEBSOCKETADRESS?param=${param}`);
return this.http.get(`https://YOURWEBSOCKETADRESS?param=${param}`);
}
You can also test your websocket with a simple const like that:
const yourSocket = new WebSocket('wss://YOURWEBSOCKETADRESS' + this.param());
You can find a lot of good tutorials with cryptocurrencies plateform who will help you.
Here is an example of a front Angular financial service example dedicated to a websocket:
#Injectable({
providedIn: 'root'
})
export class CandlecollectService {
private baseUrl = 'http://YOURBACKENDPOINT/';
obj: any;
object: JSON;
constructor(private http: HttpClient) { }
extractCandles(symbol: string, interval: string, limit: number): Observable<any> {
return this.http.get(`${this.baseUrl}${symbol}/${interval}/param`);
}
getCurrentPrice(symbol: string): Observable<any> {
const subject = webSocket(`wss://YOURWEBSOCKETURL?symbol=${symbol}`);
return this.http.get(`https://YOURWEBSOCKERURL?symbol=${symbol}`);
}
}

Related

BeanDefinitionRegistryPostProcessor ignores Encrypted properties

I've registered a Spring Post Processor to register some beans based on application properties.
Also I would like to add support for encrypted properties.
I use spring and jasypt spring boot
Here are my configurations:
application.yml:
rest:
jwt-client:
location:
base-url: loc_URL
invoke-timeout:
connection: 5000
read: 500
jwt-config:
token: ENC(gRbmO8Gv2Yb3vUf27nykDPd5/wjVdj+r5REr5HcIh4k=)
Post Processor class:
#RequiredArgsConstructor
public class DynamicClientBeanConfiguration implements BeanDefinitionRegistryPostProcessor {
private static final String BEAN_POSTFIX = "-api-client-config";
private final Environment environment;
#Override
public void postProcessBeanDefinitionRegistry(#NonNull BeanDefinitionRegistry registry) {
Binder.get(environment)
.bind("rest.jwt-client", Bindable.mapOf(String.class, JwtClientConfig.class))
.orElse(new LinkedHashMap<>())
.forEach((key, value) -> {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setBeanClass(JwtClientConfig.class);
bd.setInstanceSupplier(() -> value);
bd.addQualifier(new AutowireCandidateQualifier(key + BEAN_POSTFIX));
registry.registerBeanDefinition(key + BEAN_POSTFIX, bd);
log.debug("Registered JwtClientConfig bean with #Qualifier(\"{}\")", key + BEAN_POSTFIX);
});
}
#Override
public void postProcessBeanFactory(#NonNull ConfigurableListableBeanFactory beanFactory) {
// Nothing to override
}
}
Custom Configuration Properties class:
#Getter
#Setter
#ConfigurationProperties
public class JwtClientConfig {
private String baseUrl;
private InvokeTimeout invokeTimeout;
private JwtConfig jwtConfig;
#Getter
#Setter
public static class InvokeTimeout {
private int connection;
private int read;
}
#Getter
#Setter
public static class JwtConfig {
private String token;
}
}
However when the following part of code in the post processor executed it binds raw data from properties like ENC(...) but not decrypted as I expect:
Binder.get(environment)
.bind("rest.jwt-client", Bindable.mapOf(String.class, JwtClientConfig.class))
.orElse(new LinkedHashMap<>())
It it possible to bind already decrypted properties in Post Processor?

Multiple yml files configuring in corresponding Configuration class (Spring Boot)

I have more than one yml files in Spring Boot in resource classpath location like following structure of Spring Boot. Initially I have written only for application-abc.yml and at the time all the values of this file was loading in their corresponding class but when I have added on another file application-xyz.yml then also it loads into their corresponding configuration classes but at this time only loading the values of application-xyz.yml in both the configuration classes. So, want help to configure values of both the files in their corresponding configuration files in a single build :
-src
-main
-java
-packages
-config
-ApplicationAbcConfig.java
-ApplicationConfig.java
-ApplicationFactory.java
-ApplicationXyzConfig.java
-Authentication.java
-Operations.java
-Payload.java
-RequestPayload.java
-ResponsePayload.java
-services
-YmlConfigurationSelection.java
-resources
-application.yml
-application-abc.yml
-application-xyz.yml
-MultipleYmlDemoProject.java
Content of application-abc.yml
authentication:
name: name
type: type
payload:
request:
- sequence: 1
attributes:
- attributes1
- attributes2
response:
- sequence: 1
attributes:
- attributes3
- attributes4
operations:
name: name
type: type
payload:
request:
- sequence: 1
attributes:
- attributes5
- attributes6
response:
- sequence: 1
attributes:
- attributes7
- attributes8
Content of application-xyz.yml
authentication:
name: name
type: type
payload:
request:
- sequence: 1
attributes:
- attributes9
- attributes10
response:
- sequence: 1
attributes:
- attributes11
- attributes12
operations:
name: name
type: type
payload:
request:
- sequence: 1
attributes:
- attributes13
- attributes14
response:
- sequence: 1
attributes:
- attributes15
- attributes16
Content of ApplicationConfig.java
public interface ApplicationConfig {
public Authentication getAuthentication();
public void setAuthentication(Authentication authentication);
public Operations getOperations();
public void setOperations(Operations operations);
}
Content of Authentication.java
public class Authentication {
private String name;
private String type;
private Payload payload;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Payload getPayload() {
return payload;
}
public void setPayload(Payload payload) {
this.payload = payload;
}
}
Content of Operations.java
public class Operations {
private String name;
private String type;
private Payload payload;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Payload getPayload() {
return payload;
}
public void setPayload(Payload payload) {
this.payload = payload;
}
}
Content of Payload.java
public class Payload {
private List<RequestPayload> request;
private List<ResponsePayload> response;
public List<RequestPayload> getRequest() {
return request;
}
public void setRequest(List<RequestPayload> request) {
this.request = request;
}
public List<ResponsePayload> getResponse() {
return response;
}
public void setResponse(List<ResponsePayload> response) {
this.response = response;
}
}
Content of RequestPayload.java
public class RequestPayload {
private String sequece;
private List<String> attributes;
public String getSequece() {
return sequece;
}
public void setSequece(String sequece) {
this.sequece = sequece;
}
public List<String> getAttributes() {
return attributes;
}
public void setAttributes(List<String> attributes) {
this.attributes = attributes;
}
}
Content of ResponsePayload.java
public class ResponsePayload {
private String sequece;
private List<String> attributes;
public String getSequece() {
return sequece;
}
public void setSequece(String sequece) {
this.sequece = sequece;
}
public List<String> getAttributes() {
return attributes;
}
public void setAttributes(List<String> attributes) {
this.attributes = attributes;
}
}
Content of ApplicationAbcConfig.java
#Configuration
#SpringBootConfiguration
#EnableConfigurationProperties
#org.springframework.context.annotation.PropertySource("classpath:application-abc.yml")
public class ApplicationAbcConfig implements ApplicationConfig, PropertySourceFactory {
private Authentication authentication;
private Operations operations;
#Override
public Authentication getAuthentication() {
return authentication;
}
#Override
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}
#Override
public Operations getOperations() {
return operations;
}
#Override
public void setOperations(Operations operations) {
this.operations = operations;
}
#Override
public PropertySource<?> createPropertySource(#Nullable String name, EncodedResource resource) throws IOException {
Properties propertiesFromYaml = loadYamlIntoProperties(resource);
String sourceName = name != null ? name : resource.getResource().getFilename();
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
try {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
} catch (IllegalStateException e) {
// for ignoreResourceNotFound
Throwable cause = e.getCause();
if (cause instanceof FileNotFoundException)
throw (FileNotFoundException) e.getCause();
throw e;
}
}
}
Content of ApplicationXyzConfig.java
#Configuration
#SpringBootConfiguration
#EnableConfigurationProperties
#org.springframework.context.annotation.PropertySource("classpath:application-xyz.yml")
public class ApplicationXyzConfig implements ApplicationConfig, PropertySourceFactory {
private Authentication authentication;
private Operations operations;
#Override
public Authentication getAuthentication() {
return authentication;
}
#Override
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}
#Override
public Operations getOperations() {
return operations;
}
#Override
public void setOperations(Operations operations) {
this.operations = operations;
}
#Override
public PropertySource<?> createPropertySource(#Nullable String name, EncodedResource resource) throws IOException {
Properties propertiesFromYaml = loadYamlIntoProperties(resource);
String sourceName = name != null ? name : resource.getResource().getFilename();
return new PropertiesPropertySource(sourceName, propertiesFromYaml);
}
private Properties loadYamlIntoProperties(EncodedResource resource) throws FileNotFoundException {
try {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(resource.getResource());
factory.afterPropertiesSet();
return factory.getObject();
} catch (IllegalStateException e) {
// for ignoreResourceNotFound
Throwable cause = e.getCause();
if (cause instanceof FileNotFoundException)
throw (FileNotFoundException) e.getCause();
throw e;
}
}
}
Content of ApplicationFactory.java
#Component
public class ApplicationFactory {
#Autowired
private ApplicationAbcConfig applicationAbcConfig;
#Autowired
private ApplicationXyzConfig applicationXyzConfig;
public ApplicationConfig getApplicationPropertiesConfig(String application) {
if (application.equalsIgnoreCase("abc")) {
return applicationAbcConfig;
} else if (application.equalsIgnoreCase("xyz")) {
return applicationXyzConfig;
} else {
return null;
}
}
}
Content of YmlConfigurationSelection.java
public class YmlConfigurationSelection {
#Autowired
private ApplicationFactory applicationFactory;
private ApplicationConfig applicationConfig;
public Object accessingProperties(String application) {
applicationConfig = applicationFactory.getApplicationPropertiesConfig(application);
return null;
}
}
Content of MultipleYmlDemoProject.java
#SpringBootApplication
#SpringBootConfiguration
#PropertySource(factory = ApplicationAbcConfig.class, value = "classpath:application-abc.yml")
#PropertySource(factory = ApplicationXyzConfig.class, value = "classpath:application-xyz.yml")
public class MultipleYmlDemoProject {
public class MultipleYmlDemo {
public static void main(String[] args) {
ConfigurableApplicationContext ctx =
SpringApplication.run(YamlPropertysourceApplication.class, args);
ConfigurableEnvironment env = ctx.getEnvironment();
}
}
}
It looks like you have an old spring application that was attempted to be migrated to spring boot.
Spring boot works natively with yaml files, so if you do the integration in a spring boot way, it will be possible to delete a lot of boilerplate code that you have to maintain. Also the naming of configurations is problematic: the names application-<something>.yml are reserved to be used with spring boot profiles, maybe if you'll rename to myprops-abc/xyz.yaml it will behave in a different way, I can't say for sure.
All-in-all I suggest you the following way, which is much better IMO:
Both configuration sets must be loaded into one configuration, so create a configuration properties file that denotes this one configuration:
#ConfigurationProperties(prexix="security")
public class SecurityConfigProperties {
private SecurityRealm abc;
private SecurityRealm xyz;
// getters, setters
}
public class SecurityRealm {
private Authentication autentication;
private Operations operations;
// getters setters
}
public class Authentication {...}
private class Operations {...}
Now place all the content from abc and xyz yaml into one file application.yaml and give a 'security' prefix:
security:
abc: // note, this 'abc' matches the field name of the configuration
authentication:
...
operations:
....
xyz:
authentication:
...
operations:
....
OK, everything is mapped, create the configuration like this:
#Configuration
#EnableConfigurationProperties(SecurityConfigProperties.class)
public class SecurityConfiguration {
#Bean
public SecurityBeanForABC(SecurityConfigProperties config) {
return new SecurityBeanForABC(config.getAbc().getAuthentication(), config.getAbc().getOperations());
}
... the same for XYZ
}
Note that with this approach you only maintain a configuration mapping in java objects and there is no code for loading / resolving properties) - everything is done by spring boot automatically. If you configure a special annotation processor and have a descent IDE you can get even auto-completion facilities for those yaml properties but its out of scope for this question. The point is that doing things in a way directly supported by spring boot has many advantages :)

Send Data from Angular to Springboot over POST

I am trying to send one json from my frontend angular project to the backend which is springboot.
It is the first time I am using these 2 technologies so I lack in experience.
I am not quite sure if my http post method in Angular is wrong or if my backend isn't listening to the data which are supposed to come.
I will attach both code parts so that you can help me. Thank you in advance!
Here is a picture of the chrome console:
Http Errorcode 404
http error image
Backend:
#RestController
#RequestMapping
#CrossOrigin(origins = "http://localhost:4200")
public class RequestController {
private RolesRequestRepository rolesRequestRepository;
#PostMapping("/sendrolesrequest")
void addRequest(#RequestBody RolesRequest rolesRequest) {
rolesRequestRepository.save(rolesRequest);
}
#GetMapping("/sendrolesrequest")
public List<RolesRequest> getRequests() {
return (List<RolesRequest>) rolesRequestRepository.findAll();
}
}
#Entity
public class RolesRequest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String requester = "";
private String recipient = "";
public RolesRequest(String recipient, String requester) {
this.recipient = recipient;
this.requester = requester;
}
public RolesRequest(){
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getRequester() {
return requester;
}
public void setRequester(String requester) {
this.requester = requester;
}
public String getRecipient() {
return recipient;
}
public void setRecipient(String recipient) {
this.recipient = recipient;
}
}
Here is the angular frontend part:
#Injectable()
export class RequestService {
sendRolesRequestUrl = 'sendrolesrequest'; // URL to web api
private handleError: HandleError;
constructor(
private http: HttpClient,
httpErrorHandler: HttpErrorHandler) {
this.handleError = httpErrorHandler.createHandleError('RequestService');
}
sendRolesRequest (rolesRequest: RequestModel): Observable<RequestModel> {
//console.log("addRolesRequest try post:" + rolesRequest.print());
return this.http.post<RequestModel>(this.sendRolesRequestUrl, rolesRequest, httpOptions)
.pipe(
catchError(this.handleError('sendRolesRequest', rolesRequest))
);
}
testPost() {
const headers = new Headers();
headers.append('Content-Type', 'application/json; charset=utf-8');
this.http.post(this.sendRolesRequestUrl, {'key1': 'value1', 'key2': 'value2'}, httpOptions)
.subscribe(() => {}, err => console.error(err));
}
}
export class RequestFormulaComponent implements OnInit {
onSendRequest() {
this.requestService
.sendRolesRequest(this.rolesRequest)
.subscribe();
}
}
I would be very happy if someone helps me out here. I am struggling on this topic over a week.
Is that even the way how a backend application should communicate with the webpage? If not, how can I do it otherwise?
You're using JPA entity as DTO, and have no setters/getters, also there is no default constuctor, modify your RolesRequest like this:
#Entity
public class RolesRequest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String requester = "";
private String recipient = "";
public RolesRequest() { }
public RolesRequest(String recipient, String requester) {
this.recipient = recipient;
this.requester = requester;
}
public String getRequester() { return this.requester; }
public void setRequester(String r) { this.requester = r;}
public String getRecipient() { return this.recipient; }
public void setRecipient(String r) { this.recipient = r;}
Are your backend and angular app running on the same port? (backend and frontend are combined in the same application)
You are calling http://localhost:4200/sendrolesrequest and I think that's a request on the Angular app itself. You get a HTTP 404 error code (NOT FOUND)
You should call the endpoint of the backend application. It's running on port 8080 for example so call http://localhost:8080/sendrolesrequest (or with other port if backend is running on another port)
Change:
sendRolesRequestUrl = 'sendrolesrequest';
to:
sendRolesRequestUrl = 'http://localhost:8080/sendrolesrequest';

Create a Post method to save an object with multiples id

I have in my controller:
#RestController
public class OneTwoController {
private OnTwoService _service;
//... more code
#PostMapping("/api/one-two")
#CrossOrigin
public ResponseEntity<ServiceResponse> save(#RequestBody OneTwo model) {
return ResponseEntity.ok().body( _service.Save(model));
}
In my entity:
#Entity(name = "OneTwo")
#Where (clause = "deleted='false'")
public class OneTwo{
#EmbeddedId
private OneTwoKey_id;
public OneTwo(OneTwoKey id) {
this._id = id;
}
#JsonProperty("oneTwo")
public void setId(OneTwoKey value) {
this._id = value;
}
The OneTwoKey class:
public class OneTwoKey implements Serializable {
#Column(name = "OneID")
private int _oneID;
#Column(name = "TwoID")
private int _twoID;
public OneTwoKey(int oneID, int twoID) {
this._oneID = oneID;
this._twoID = twoID;
}
}
The json that I send to the Rest API:
{
"oneTwo": {
"oneID": 83,
"twoID": 69
},
"deleted": true
}
The issue is that both ids arrive null, so the service can't do the insert on the DB.
How can I deal with those cases when the ids are more than one?
Try adding setters in the OneTwoKey class to make it easier for the JSON deserializer:
#JsonProperty("oneID")
public void setOneID(int oneID) {
this._oneID = oneID;
}
#JsonProperty("twoID")
public void setTwoID(int twoID) {
this._twoID = twoID;
}
Another solution is to create a DTO, use it to receive the data in the controller and then convert it to your entity:
public class OneTwoDTO {
private Map<String, Int> oneTwo;
private boolean deleted;
// setters & getters
}
Simply what you can do is instead of using
public ResponseEntity<ServiceResponse> save(#RequestBody OneTwo model) {
you can use
public ResponseEntity<ServiceResponse> save(#RequestBody String model) {
Now convert the String to json and get all the key value pairs, it would be easier if you have dynamic number of variables and you want to capture them all.
or you can use tools like jsonschema2pojo whick take a json schema and generate a pojo. In the json schema if you set
"additionalProperties": true
you can capture all the values.
Could you make sure the problem is not because of case sensitivity?
Lower case the column names. Also could you use public access on those variables as well? These are my initial guesses as to why the payload is not being binded correctly.
public class OneTwoKey implements Serializable {
#Column(name = "oneID")
public int _oneID;
#Column(name = "twoID")
public int _twoID;

Unable to make restTemplate call with Generics for nested Json

I am trying to make a restTemplate call for API testing. The json returned is a nested one with multiple levels.
{
"code": 200,
"data": {
"result": {
"publicId": "xyz"
}
}
}
I have the following classes acting as wrapper :
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public abstract class RestCallResponse<T> {
private int code;
protected RestCallResponse(int code) {
this.code = code;
}
protected RestCallResponse(){}
#JsonProperty("data")
public Map<?, ?> getRestCallResponse() {
return ImmutableMap.of("result", getResult());
}
#JsonIgnore
protected abstract T getResult();
public int getCode() {
return code;
}
}
And then a SuccessRestResponse class extending this abstract class :
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE)
public class SuccessRestResponse<T> extends RestCallResponse<T> {
#JsonProperty("result")
private T result;
public SuccessRestResponse() {
}
public SuccessRestResponse(T result) {
super(HttpStatus.SC_OK);
this.result = result;
}
protected T getResult() {
return this.result;
}
}
Then finally I have the actual data POJO :
public final class CreatedResponse {
#JsonProperty
private final EntityId publicId;
public CreateCreativeResponse(EntityId publicId) {
this.publicId = publicId;
}
}
In the test case, I am making a call as such :
ResponseEntity<SuccessRestResponse<CreatedResponse>> newResponse =
restTemplate.exchange(requestEntity, new ParameterizedTypeReference<SuccessRestResponse<CreatedResponse>>() {});
But I am getting the following error :
nested exception is org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: null value in entry: result=null (through reference chain: com.inmobi.helix.test.api.dao.SuccessRestResponse["data"]);
Any suggestions? Where am I going wrong?
I solved the problem with a workaround. Still don't know what's wrong with the above piece of code though.
I got rid of the class RestCallResponse<T> and edited the field members in SuccessRestResponse<T> to look like this :
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class SuccessRestResponse<T> {
private int code;
private Map<String, T> data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Map<String, T> getData() {
return data;
}
#JsonIgnore
public T getResult() {
return data.get("result");
}
public void setData(Map<String, T> data) {
this.data = data;
}
}
This corresponds to the nested json while deserialization.
P.S. - Would still like to know what went wrong in my above code
though. As in, why did class hierarchy not work for me.

Categories