So I have an API using spring boot, and I try to implement a WebSocket endpoint where users get subscribed when they log in, and where they listen to notifications of different entities creation. Supposedly my WebSocket is working on the backend, here is my code.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/points_notification")
.setAllowedOrigins("localhost:3000")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
#Controller
public class NotificationsController {
#Autowired private NotificationDispatcher dispatcher;
#MessageMapping("/start")
public void start(StompHeaderAccessor stompHeaderAccessor) {
dispatcher.add(stompHeaderAccessor.getSessionId());
System.out.println("GOT a session! " + stompHeaderAccessor.getSessionId());
}
#MessageMapping("/stop")
public void stop(StompHeaderAccessor stompHeaderAccessor) {
dispatcher.remove(stompHeaderAccessor.getSessionId());
}
}
#Service
public class NotificationDispatcher {
#Autowired
private SimpMessagingTemplate template;
private final Set<String> listeners = new HashSet<>();
public <T extends Serializable> void dispatch(T addedObj) {
for (String listener : listeners) {
LOG.info("Sending notification to " + listener);
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(listener);
headerAccessor.setLeaveMutable(true);
template.convertAndSendToUser(
listener,
"/topic/item",
new ObjWithMsgResponse<T>("Point added by: " + listener, addedObj),
headerAccessor.getMessageHeaders());
}
}
//getters and event listeners
}
of course the dispatch() are injected and called when the creation of entities.
In the front I'm using react and trying to handle the socket connection with socket.io-client, I'm a little new with this, so I don't really understand how the emit and on methods work, because in the documentation they show that you call and eventName, but in spring the endpoints are using the URL, I can not find anywhere how to ping a URL and not an eventName.
I also tried to use SockJS that works like a charm but the endpoint needs Authentication and with this, I can not add that header to the socket request, which soocket.io permits me.
here is my solution, which only connects but doesn't receive or emit any request.
class SocketManager {
socketRef: Socket | undefined = undefined;
startListening(authToken: String) {
this.socketRef = io('http://localhost:11000/weblab4/', {
path: "/weblab4" + '/points_notification',
transports: ['websocket'],
extraHeaders: {
Authorization: "Bearer " + authToken
}
});
this.socketRef.emit('/app/start', {});
this.socketRef.on('/user/topic/item', (data: INewPointNotification) => {
console.log("received data from socket: " + data);
toast.success('🦄 ' + data.msg + data.point, {/*toast props*/});
});
console.log("socket connected", this.socketRef);
}
stopListening() {
if (this.socketRef !== undefined)
this.socketRef.disconnect();
console.log("socket disconnected", this.socketRef);
}
}
Related
I have created a project using abpframwork. When running swagger, swagger receives the function in the application layer is a api. I don't want that. Can you guys tell me how to remove it in swagger
Code in Application Layer
public class UserService : AdminSSOAppService, ITransientDependency, IValidationEnabled, IUserService
{
IUserRepository _userRepository;
private readonly ILogger<UserService> _log;
public UserService(IUserRepository userRepository,
ILogger<UserService> log
)
{
_userRepository = userRepository;
_log = log;
}
public async Task<List<UserDto>> GetList()
{
var list = await _userRepository.GetListAsync();
return ObjectMapper.Map<List<User>, List<UserDto>>(list);
}
public async Task<UserDto> GetUserById(int Id)
{
var user = await _userRepository.GetAsync(c=>c.Id == Id);
return ObjectMapper.Map<User, UserDto>(user);
}
}
Code in HttpApi Layer
[Area(AdminSSORemoteServiceConsts.ModuleName)]
[RemoteService(Name = AdminSSORemoteServiceConsts.RemoteServiceName)]
[Route("api/user/user-profile")]
public class UserController : ControllerBase, IUserService
{
private readonly IUserService _userAppService;
public UserController(IUserService userAppService)
{
_userAppService = userAppService;
}
[HttpGet]
[Route("get-list-httpapi")]
public Task<List<UserDto>> GetList()
{
return _userAppService.GetList();
}
[HttpGet]
[Route("get-by-id-httpapi")]
public Task<UserDto> GetUserById(int Id)
{
return _userAppService.GetUserById(Id);
}
}
I can suggest a workaround as to enable only the APIs you need to appear on swagger (though the ones that don't appear anymore will still be available for consumption).
I would suggest you add a configuration part in your *.Http.Api project module inside your ConfigureSwaggerServices, like so:
context.Services.AddSwaggerGen(options =>
{
options.DocInclusionPredicate(
(_, apiDesc) =>
apiDesc
.CustomAttributes()
.OfType<IncludeInSwaggerDocAttribute>()
.Any());
});
And for the attribute, it would be very simple, like so:
[AttributeUsage(AttributeTargets.Class)]
public class IncludeInSwaggerDocAttribute : Attribute
{
}
This will let you achieve what you want, however I still recommend reading the doc carefully to be able to implement DDD.
I'm trying to create webSocket on springBoot application.
this is config class:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/sub");
config.setApplicationDestinationPrefixes("/send");
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
}
#Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
}
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {
}
#Override
public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
return true;
}
}
Now from AngularJS I'm trying to connect to websocket using SockJS and Stomp
var socket = new SockJS('/api/ws');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function (frames) {
stompClient.subscribe('/subs/hello', function (data) {
console.log(data);
});
});
When I open console I get this message:
admin-components.js:112173 WebSocket connection to 'wss://proxy.beta.corp.payment21.com/api/ws/135/hwq2yv3q/websocket' failed: Error during WebSocket handshake: Unexpected response code: 502
After 30 seconds I get this:
VM333 sockjs.min.js:2 Uncaught Error: Incompatibile SockJS! Main site uses: "1.4.0", the iframe: "1.0.0".
at s (VM333 sockjs.min.js:2)
And after 30 more seconds:
And it is working...
When I go to network to see details in the frames it says
(Opcode -1)
What is the problem here? Is it the spring configuration or SockJS?
Based on the Spring documentation the supported client SockJS version is 1.0.x
On the browser side, applications can use the sockjs-client (version 1.0.x). It emulates the W3C WebSocket API and communicates with the server to select the best transport option, depending on the browser in which it runs.
Dear Camel/Akka/Netty Masters!
I've created UntypedConsumerActor which consumes tcp connection:
public class TcpEndpoint extends UntypedConsumerActor {
private static final Logger log = LoggerFactory.getLogger(TcpEndpoint.class);
public static Props props = Props.create(TcpEndpoint.class);
#Override
public String getEndpointUri() {
return "netty4:tcp://localhost:8000?decoders=#fdDecoder,#fdHandler";
}
#Override
public void onReceive(Object message) throws Throwable {
log.error("onReceived");
}
}
In case to configure decoders for netty component, I've created ContextProvider:
public class FDCamelContext implements ContextProvider {
public DefaultCamelContext getContext(ExtendedActorSystem system) {
JndiRegistry registry = new JndiRegistry();
registry.bind("fdDecoder", new FDDecoder());
registry.bind("fdHandler", new FDHandler());
DefaultCamelContext context = new DefaultCamelContext(registry);
return context;
}
}
Now, when I send message there is no call on onReceive method. Why? When I set DefaultContextProvider and configure netty to consumes textlines everything works as expected.
Ok, I found problem. Maybe it helps someone:
It is necesarry to fire channelRead event:
ctx.fireChannelRead(msg);
I have searched through all the tutorials and did exactly as explained there, but I can't reach my controller.
Here is my websocket xml config:
<websocket:handlers>
<websocket:mapping path="/updateHandler" handler="updateHandler"/>
<websocket:sockjs/>
</websocket:handlers>
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/update">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic"/>
</websocket:message-broker>
I don't actually know do I need the handler, but without it stomp connection fails with "whoops! Lost connection to undefined".
Any suggestion in this direction is also welcome.
Here is my empty handler:
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
public class UpdateHandler extends TextWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
}
}
And my controller
#RestController
public class WebSocketController {
#MessageMapping("/update")
#SendTo("/topic/messages")
public OutputMessage sendmMessage(Message message) {
return new OutputMessage(message, new Date());
}
}
I am using ngStomp from angular as suggested:
var message = {message: 'message body', id: 1};
$scope.testWebSocket = function () {
$stomp.setDebug(function (args) {
console.log(args + '\n');
});
$stomp.connect('/myAppContext/update', {})
.then(function (frame) {
var connected = true;
var subscription = $stomp.subscribe('/topic/messages', function (payload, headers, res) {
$scope.payload = payload;
}, {});
$stomp.send('/myAppContext/app/update', message);
subscription.unsubscribe();
$stomp.disconnect(function () {
console.error('disconnected');
});
}, function(error){
console.error(error);
});
};
My Message Class:
public class Message {
private String message;
private int id;
public Message() {
}
public Message(int id, String text) {
this.id = id;
this.message = text;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
My OutputMessage class:
public class OutputMessage extends Message {
private Date time;
public OutputMessage(Message original, Date time) {
super(original.getId(), original.getMessage());
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
when I execute the testWebSocket() I get following output:
Opening Web Socket...
Web Socket Opened...
>>> CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000
<<< CONNECTED
version:1.1
heart-beat:0,0
user-name:user#domain.com
connected to server undefined
>>> SUBSCRIBE
id:sub-0
destination:/topic/messages
>>> SEND
destination:/myAppContext/app/update
content-length:33
{"message":"message body","id":1}
Why connected to server undefined?
And why my controller never gets executed after sending a message?
I am using spring-4.1.4 with security-core-3.2.5 and Tomcat server 8.0.18
As a non-pretty workaround, I moved websocket configuration to the Java config and it works.
Config below:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/update")
.withSockJS();
}
I actually don't know why.
I am new to Apache CXF , so please help .
I dont know why the client is not getting called .
client = (BayerService) factory.create();
I did this way
public class RunBayer implements CallbackHandler
{
RunBayer()
{
init();
}
private static void init()
{
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
WSS4JOutInterceptor out = new WSS4JOutInterceptor();
out.setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.TIMESTAMP + " " + WSHandlerConstants.SIGNATURE);
out.setProperty(WSHandlerConstants.USER, "svi_ws");
out.setProperty(WSHandlerConstants.PASSWORD_TYPE, "PasswordDigest");
factory.getOutInterceptors().add(out);
factory.setServiceClass(BayerService.class);
factory.setAddress(host);
client = (BayerService) factory.create();
}
}
And I have a BayerService interface which is in this format
public interface BayerService
{
public OurServiceResponse OurView(#WebParam(name = "request") ServiceRequest request);
}
I have another class named Bayer.java which actually implements this Interafce
public class Bayer implements BayerService
{
public OurServiceResponse OurView(ServiceRequest request)
{
// code
}
}
Could anybody please tell me why the control is not coming inside when i call this way ??
Thank you very much .
I didn't see the code like below in your RunBayer class.
client.OurView(request);