Categories
discuss

spring tcp socket , authorizing clients and handle pending response

The Spring framework support tcp connection as well , i wrote code below to setup a simple socket server , i am confused about adding below futures to my socket server :

  • authorizing clients based on a unique identifier ( for example a client secret received from client, maybe using TCP Connection Events )
  • send a message directly to specific client (based on identifier)
  • broadcast a message

UPDATE :

  • Config.sendMessage added to send message to single client

  • Config.broadCast added to broadcast message

  • authorizeIncomingConnection to authorize clients , accept or reject connections

  • tcpConnections static filed added to keep tcpEvent sources

Questions !

  • is using tcpConnections HashMap good idea ?!

  • is the authorization method i implemented a good one ?!

Main.java

@SpringBootApplication
public class Main {

    public static void main(final String[] args) {
        SpringApplication.run(Main.class, args);
    }

}

Config.java

@EnableIntegration
@IntegrationComponentScan
@Configuration
public class Config implements ApplicationListener<TcpConnectionEvent> {

    private static final Logger LOGGER = Logger.getLogger(Config.class.getName());

    @Bean
    public AbstractServerConnectionFactory AbstractServerConnectionFactory() {
        return new TcpNetServerConnectionFactory(8181);
    }

    @Bean
    public TcpInboundGateway TcpInboundGateway(AbstractServerConnectionFactory connectionFactory) {
        TcpInboundGateway inGate = new TcpInboundGateway();
        inGate.setConnectionFactory(connectionFactory);
        inGate.setRequestChannel(getMessageChannel());
        return inGate;
    }

    @Bean
    public MessageChannel getMessageChannel() {
        return new DirectChannel();
    }

    @MessageEndpoint
    public class Echo {

        @Transformer(inputChannel = "getMessageChannel")
        public String convert(byte[] bytes) throws Exception {
            return new String(bytes);
        }

    }

    private static ConcurrentHashMap<String, TcpConnection> tcpConnections = new ConcurrentHashMap<>();

    @Override
    public void onApplicationEvent(TcpConnectionEvent tcpEvent) {
        TcpConnection source = (TcpConnection) tcpEvent.getSource();
        if (tcpEvent instanceof TcpConnectionOpenEvent) {

            LOGGER.info("Socket Opened " + source.getConnectionId());
            tcpConnections.put(tcpEvent.getConnectionId(), source);

            if (!authorizeIncomingConnection(source.getSocketInfo())) {
                LOGGER.warn("Socket Rejected " + source.getConnectionId());
                source.close();
            }

        } else if (tcpEvent instanceof TcpConnectionCloseEvent) {
            LOGGER.info("Socket Closed " + source.getConnectionId());
            tcpConnections.remove(source.getConnectionId());
        }
    }

    private boolean authorizeIncomingConnection(SocketInfo socketInfo) {
        //Authorization Logic , Like Ip,Mac Address WhiteList or anyThing else !
        return (System.currentTimeMillis() / 1000) % 2 == 0;
    }

    public static String broadCast(String message) {
        Set<String> connectionIds = tcpConnections.keySet();
        int successCounter = 0;
        int FailureCounter = 0;
        for (String connectionId : connectionIds) {
            try {
                sendMessage(connectionId, message);
                successCounter++;
            } catch (Exception e) {
                FailureCounter++;
            }
        }
        return "BroadCast Result , Success : " + successCounter + " Failure : " + FailureCounter;
    }

    public static void sendMessage(String connectionId, final String message) throws Exception {
        tcpConnections.get(connectionId).send(new Message<String>() {
            @Override
            public String getPayload() {
                return message;
            }

            @Override
            public MessageHeaders getHeaders() {
                return null;
            }
        });
    }
}

MainController.java

@Controller
public class MainController {

    @RequestMapping("/notify/{connectionId}/{message}")
    @ResponseBody
    public String home(@PathVariable String connectionId, @PathVariable String message) {
        try {
            Config.sendMessage(connectionId, message);
            return "Client Notified !";
        } catch (Exception e) {
            return "Failed To Notify Client , cause : n " + e.toString();
        }
    }


    @RequestMapping("/broadCast/{message}")
    @ResponseBody
    public String home(@PathVariable String message) {
        return Config.broadCast(message);
    }

}

Usage :

  1. Socket Request/Response Mode
  2. notify single client

    http://localhost:8080/notify/{connectionId}/{message}

  3. broadCast

    http://localhost:8080/broadCast/{message}

Answer

The TcpConnectionOpenEvent contains a connectionId property. Each message coming from that client will have the same property in the IpHeaders.CONNECTION_ID message header.

  1. Add a custom router that keeps track of the logged-on state of each connection.
  2. Lookup the connection id and if not authenticated, route to a challenge/response subflow.
  3. When authenticated, route to the normal flow.

To use arbitrary messaging (rather than request/response) use a TcpReceivingChannelAdapter and TcpSendingMessageHandler instead of an inbound gateway. Both configured to use the same connection factory. For each message sent to the message handler, add the IpHeaders.CONNECTION_ID header to target the specific client.

To broadcast, send a message for each connection id.

Categories
discuss

Express and calling response.json in a point free fashion doesn’t work in Promise.then

Something greatly surprised me today. I came across a bunch of express route handlers basically look like this (there are more and real function calls, but for the sake of legibility:

app.get('/api/foo', (req, resp) => {
  Promise.resolve({one: 1})
    .then(data=>resp.json(data))
})

So I, as the clever javascript programmer, think I can step away from the anonymous function and just let the then function call resp.json directly:

app.get('/api/foo', (req, resp) => {
  Promise.resolve({one: 1})
    .then(resp.json)
})

But when I try that I never get a response and see this in the node console:

Unhandled promise rejection (rejection id: 1): TypeError: Cannot read property ‘app’ of undefined

To my eyes .then(resp.json) and .then(data=>resp.json(data)) should be equivalent. It’s a scope thing, to be sure, but I’d love an explanation and perhaps a workaround.

Answer

That’s because resp is an object with it’s own properties, so more than likely the data that is used by the json function is contained within the resp object.

When you pass the function resp.json by itself to then, you aren’t passing the resp object, or any of its information, along with it. In essence, the then call is just “borrowing” the function json from the resp object. Just the function body itself though, no scope or implicit values.

More than likely, the function body of json uses this somewhere, at which point you will be getting an invalid (probably global) object, instead of resp.

To remedy, you can do

   Promise.resolve({one: 1})
    .then(resp.json.bind(resp))
Categories
discuss

Java 8 streams take first and than call forEach(…)

I have a CSV file and the first line contains the headers. So I thought it would be perfect to use Java 8 streams.

    try (Stream<String> stream = Files.lines(csv_file) ){
        stream.skip(1).forEach( line -> handleLine(line) );
    } catch ( IOException ioe ){
        handleError(ioe);
    }

Is it possible to take the first element, analyze it and then call the forEach method? Something like

stream
      .forFirst( line -> handleFirst(line) )
      .skip(1)
      .forEach( line -> handleLine(line) );

ADDITIONALLY: My CSV file contains around 1k lines and I can handle each line parallel to speed it up. Except the first line. I need the first line to initiallize other objects in my project :/ So maybe it is fast to open a BufferedReader, read the first line, close the BufferedReader and than use parallel streams?

Answer

A nice way to do that would be to get a BufferedReader reading your file, for example with the help of Files.newBufferedReader(path). Then you can call nextLine() one time to retrieve the header row, and lines() to get a Stream<String> of all the other rows:

try (BufferedReader br = Files.newBufferedReader(csv_file)){
    String header = br.readLine();
    // if header is null, the file was empty, you may want to throw an exception
    br.lines().forEach(line -> handleLine(line));
}

This works because the first call to readLine() will cause the buffered reader to read the first line, so subsequently, since lines() is a stream populated by reading the lines, it starts reading at the second line. The buffered reader is also correctly closed by the try-with-resources when the processing ends.

Potentially, the stream pipeline could be run in parallel, but for I/O-bound tasks like this one, I wouldn’t expect any performance improvement, unless it is the processing of each row that is the slower part. But be careful with the forEach in this case: it will be ran concurrently and so its code needs to be thread-safe. It’s unclear what the handleLine method does, but, generally, you do not need forEach and might prefer a mutable reduction with collect, which would be safe to use in a parallel stream.

Categories
discuss

Objects are not valid as a react child (In Internet explorer 11 for React 15.4.1)

Objects are not valid as a React child (found: object with keys {$$typeof, type, key, ref, props, _owner, _store}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of App.

AppContainer :

const mapDispatchToProps = (dispatch) => {
    return {

            }
        }
    }
}

Component:

class App extends Component {
    render() {
        return (
        );
    }
}

Above is my render function for app.js. This Code is working fine in google chrome, but when coming to Internet explorer It is not working and it is throwing the above error.

Answer

A problem since React 15.4 with IE11

If you still have this issue, you may have a look at this react issue #8379 about React 15.4 and IE11. I had the same problem with webpack dev mode / IE11 / React 15.4, and it seems that React and ReactDom each use their version of a Symbol polyfill (this is new with 15.4):

Somehow react and react-dom no longer “agree” on the $$typeof value

which should be typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103.

Solution

I’ve resolved this issue by reordering polyfill and react / react-dom to be sure that the polyfill Symbol is loaded before React and ReactDom’s Symbol… Now they “agree” on $$typeof value.

Example solution for webpack:

entry: [
 'babel-polyfill', // Load this first
 'react-hot-loader/patch', // This package already requires/loads react (but not react-dom). It must be loaded after babel-polyfill to ensure both react and react-dom use the same Symbol.
 'react', // Include this to enforce order
 'react-dom', // Include this to enforce order
 './index.js' // Path to your app's entry file
]
Categories
discuss

My scroll handler JavaScript doesn’t work in Internet Explorer

I have a script on my site that works in every browser, except Internet Explorer. Can someone explain why this doesn’t work?

var header = document.getElementById('header');
var picturebg = document.getElementsByClassName('picturebg')[0];
var arrow = document.getElementsByClassName('toTop-transparent')[0];
var supportPageOffset = window.pageXOffset !== undefined;

window.onscroll = function() {
  "use strict";
  var y = window.scrollY;

  if (y > 7) {
    header.className = 'header-colored';
    arrow.className = 'toTop';
    picturebg.className = 'picturebgns';
  } else {
    header.className = 'header-transparent';
    arrow.className = 'toTop-transparent';
    picturebg.className = 'picturebg';
  }
};

The console doesn’t give any errors, it just doesn’t work. I have another jQuery script that runs just fine.

I’ve seen the other question here about the same thing, but it didn’t help in any way.

Answer

Certain properties used in your snippet are not supported by IE.

From the MDN page on scrollY:

For cross-browser compatibility, use window.pageYOffset instead of window.scrollY. Additionally, older versions of Internet Explorer (< 9) do not support either property and must be worked around by checking other non-standard properties.1

It appears you already have found the check for pageOffset support, so also check if non-standard properties are supported (e.g. CSS1 is compatible):

var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");

var y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;

Try this sample, using addEventListener() instead of onscroll.

var header = document.getElementById('header');
var picturebg = document.getElementsByClassName('picturebg')[0];
var arrow = document.getElementsByClassName('toTop-transparent')[0];
var supportPageOffset = window.pageXOffset !== undefined;
var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");



header.addEventListener('scroll', function(event) {
  "use strict";
  var y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;
console.log('y: ',y);
  if (y > 7) {
    header.className = 'header-colored';
    arrow.className = 'toTop';
    picturebg.className = 'picturebgns';
  } else {
    header.className = 'header-transparent';
    arrow.className = 'toTop-transparent';
    picturebg.className = 'picturebg';
  }
});
<div id="header" style="height: 50px; overflow: scroll;">
  <img  class="picturebg" src="https://www.gravatar.com/avatar/684fa9ff80577cbde88ffbdebafac5b4?s=32&d=identicon&r=PG&f=1" />
  <div id="arrow" class="toTop-transparent">&darr;</div>
  </div>

1https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY#Notes

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..