I have a Spring Boot Java REST application with many APIs exposed to our clients and UI. I was tasked with implementing a Transaction logging framework that will capture the incoming transactions along with the response we send.
I have this working with Spring AOP and an Around inspect and I'm currently utilizing the HttpServletRequest and HttpServletResponse objects to obtain a lot of the data I need.
From my local system I am not having any issues capturing the server used since I'm connecting to my system directly. However, once I deployed my code I saw that the load balancer URL was being captured instead of the actual server name.
I am also using Eureka to discover the API by name as it's only a single application running on HAProxy.
Imagine this flow:
/*
UI -> https://my-lb-url/service-sidecar/createUser
HAProxy directs traffic to -> my-lb-url/service-sidecar/ to one of below:
my-server-1:12345
my-server-2:12345
my-server-3:12345
Goal : http://my-server-1:1235/createUser
Actual: https://my-lb-url/createUser
Here is the code I am using to get the incoming URL.
String url = httpRequest.getRequestURL().toString();
if(httpRequest.getQueryString() != null){
transaction.setApi(url + "?" + httpRequest.getQueryString());
} else {
transaction.setApi(url);
}
Note:
I am not as familiar with HAProxy/Eurkea/etc. as I would like to be. If something stated above seems off or wrong then I apologize. Our system admin configured those and locked the developers out.
UPDATE
This is the new code I am using to construct the Request URL, but I am still seeing the output the same.
// Utility Class
public static String constructRequestURL(HttpServletRequest httpRequest) {
StringBuilder url = new StringBuilder(httpRequest.getScheme());
url.append("://").append(httpRequest.getServerName());
int port = httpRequest.getServerPort();
if(port != 80 && port != 443) {
url.append(":").append(port);
}
url.append(httpRequest.getContextPath()).append(httpRequest.getServletPath());
if(httpRequest.getPathInfo() != null) {
url.append(httpRequest.getPathInfo());
}
if(httpRequest.getQueryString() != null) {
url.append("?").append(httpRequest.getQueryString());
}
return url.toString();
}
// Service Class
transaction.setApi(CommonUtil.constructRequestURL(httpRequest));
I found a solution to this issue, but it's not the cleanest route and I would gladly take another suggestion if possible.
I am autowiring the port number from my application.yml.
I am running the "hostname" command on the Linux server that is hosting the application to determine the server fulfilling the request.
Now the URL stored in the Transaction Logs is accurate.
--
#Autowired
private int serverPort;
/*
* ...
*/
private String constructRequestURL(HttpServletRequest httpRequest) {
StringBuilder url = new StringBuilder(httpRequest.getScheme())
.append("://").append(findHostnameFromServer()).append(":").append(serverPort)
.append(httpRequest.getContextPath()).append(httpRequest.getServletPath());
if(httpRequest.getPathInfo() != null) {
url.append(httpRequest.getPathInfo());
}
if(httpRequest.getQueryString() != null) {
url.append("?").append(httpRequest.getQueryString());
}
return url.toString();
}
private String findHostnameFromServer(){
String hostname = null;
LOGGER.info("Attempting to Find Hostname from Server...");
try {
Process process = Runtime.getRuntime().exec(new String[]{"hostname"});
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
hostname = reader.readLine();
}
} catch (IOException e) {
LOGGER.error(CommonUtil.ERROR, e);
}
LOGGER.info("Found Hostname: {}", hostname);
return hostname;
}
I'm trying to establish a https connection between an android app (client) an my laptop (server).
My https server is running as python script (with a letsencrypt certificate) which works fine as long as I try to connect it with chrome or another python script.
Now I wanted to implement the client in my android app. Therefore I added the permission to the AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET"/>
and added following lines to my MainActivity.java (based on HttpURLConnection Reference on Android Developers!:
public void onButtonClicked(String message) {
try {
URL url = new URL("https://foo.bar.com/");
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
OutputStream output = new BufferedOutputStream(urlConnection.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
mSecondFragment.updateMessage(message);
}
At the moment I just want to establish a connection to my https server and send a simple GET request without receiving any data. But my goal would be to parse an additional key value pair together with the GET request that will be processed by the server:
"https://foo.bar.com?a=1"
I tried to keep it as simple as possible (that's the reason why I wanted to use java.net.HttpURLConnection) but I assume the problem is not as trivial as I expected.
Maybe someone ran into the same problem and can help my with that :)
EDIT (Thanks to #atomicrat2552 and #petey):
I added an additional class that handles the request as an AsyncTask:
public class NetworkConnection extends AsyncTask<String, Void, NetworkConnection.Result> {
static class Result {
public String mResultValue;
public Exception mException;
public Result(String resultValue) {
mResultValue = resultValue;
}
public Result(Exception exception){
mException = exception;
}
}
protected NetworkConnection.Result doInBackground(String... urls) {
Result result = null;
HttpsURLConnection urlConnection = null;
try {
URL url = new URL(urls[0]);
urlConnection = (HttpsURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
//urlConnection.connect();
result = new Result("Done");
}catch(Exception e) {
result = new Result(e);
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
}
return result;
}
}
This leads to a simplified onButtonClick method in MainActivity.java:
NetworkConnection nwConn = new NetworkConnection();
public void onButtonClicked(String message) {
nwConn.execute("https://foo.bar.com");
mSecondFragment.updateMessage(message);
}
Again I tried to simplify the code in order to get a small working code a can extend later.
The app doesn't crash anymore but my server still doesn't show any requests. If I'm using the browser on my phone everything works just fine. Any idea?
The most common pitfall I see here is that network code cannot run on the UI thread, so you must use some sort of background worker to do your network calls. The developer site has a basic guide on how to do this.
I've been playing around with Android Studio and trying to establish a connection to a dev environment in which reponds back with JSON. I have tested my code and Eclipse (not ADK) and it works fine. I've added the permissions to the AndroidManifest.xml file for INTERNET and ACCESS_NETWORK_STATE so that has been covered. I dont know what else to do?
My Java:
public class GetClientDetails {
public void jsonResponse () {
try{
URL url = new URL("http://invsydmdev069:7080/IFXJSON?type=PartyRq&RqUID=123456789&PartyId=1093300");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestProperty("Accept", "application/json");
conn.setReadTimeout(10000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader (is, "UTF-8") );
try {
String preJsonObj = reader.readLine();
JSONObject jsonObj = new JSONObject(preJsonObj);
String strPartyId = jsonObj.getJSONObject("PartyRs").getJSONObject("PartyRec").getString("PartyId");
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
catch(Exception e)
{
System.out.println("its fked");
}
}
}
The android.os.NetworkOnMainThreadException is self explainatory. You are not supposed to do any network operations on Main Thread which causes UI to be non-responsive. You need to run your network operations in an AsyncTask.
Check this post on SO.
Android 4.+ explicitly forbids network communication in the main thread (the thread that updates the user interface). It does that to avoid freezing the UI, causing a bad user experience.
Like bhargavg said, you should wrap that code in another thread, possibly using an AsyncTask or any other strategy that uses (or creates) a new thread.
I am trying to check if the URL is accessible or not. I am using HttpURLConnection for it. This is now I am implementing it.
public static boolean isUrlAccessible(final String urlToValidate)
throws WAGException {
URL url = null;
HttpURLConnection huc = null;
int responseCode = -1;
try {
url = new URL(urlToValidate);
huc = (HttpURLConnection) url.openConnection();
huc.setRequestMethod("HEAD");
huc.connect();
responseCode = huc.getResponseCode();
} catch (final UnknownHostException e) {
e.printStackTrace();
System.out.println(e.getMessage()+" "+e.getLocalizedMessage());
return false;
} catch (final MalformedURLException e){
e.printStackTrace();
System.out.println(e.getMessage()+" "+e.getLocalizedMessage());
return false;
} catch (ProtocolException e) {
e.printStackTrace();
System.out.println(e.getMessage()+" "+e.getLocalizedMessage());
return false;
} catch (IOException e) {
e.printStackTrace();
System.out.println(e.getMessage()+" "+e.getLocalizedMessage());
return false;
} finally {
if (huc != null) {
huc.disconnect();
}
}
return responseCode == 200;
}
When the Internet is down it throws an UnknownHostException, I wanted to know how do I check if a fire wall is blocking a URL and thats why I get an exception and not because that the URL is not accessible. Also, I am just checking for response code 200 to make sure that the URL is accessible. Are there any other checks I need to perform?
When the Internet is down it throws an UnknownHostException
No, it throws that when the DNS is down or the host isn't known to DNS.
I wanted to know how do I check if a fire wall is blocking a URL
You will get a connect timeout. In rare cases with obsolete hardware you may get a connection refusal, but I haven't heard of that this century. But you will also get a connect timeout if the host is down.
I am just checking for response code 200 to make sure that the URL is accessible. Are there any other checks I need to perform?
No. But URLs aren't blocked by firewalls. Ports are blocked by firewalls.
The exception I have usually seen when a firewall is blocking the connection is "java.net.NoRouteToHostException". Try catching that and see if it helps.
As others have said, the answer is "it depends".
Our perimeter firewall for example does a redirect, because we want to show the user a custom screen.
In this case, I would try to look into the HTTP Status code (30x).
I think it's hard to write a generic function for something like this, you need to tailor this to your setting or make it very configurable.
Just make sure to remain as generic as possible.
If your code for example assumes a redirect to a specific URL, this will beak once the infrastructure changes (which happens more often than anticipated).
How do you check if you can connect to the internet via java? One way would be:
final URL url = new URL("http://www.google.com");
final URLConnection conn = url.openConnection();
... if we got here, we should have net ...
But is there something more appropriate to perform that task, especially if you need to do consecutive checks very often and a loss of internet connection is highly probable?
You should connect to the place that your actual application needs. Otherwise you're testing whether you have a connection to somewhere irrelevant (Google in this case).
In particular, if you're trying to talk to a web service, and if you're in control of the web service, it would be a good idea to have some sort of cheap "get the status" web method. That way you have a much better idea of whether your "real" call is likely to work.
In other cases, just opening a connection to a port that should be open may be enough - or sending a ping. InetAddress.isReachable may well be an appropriate API for your needs here.
The code you basically provided, plus a call to connect should be sufficient. So yeah, it could be that just Google's not available but some other site you need to contact is on but how likely is that? Also, this code should only execute when you actually fail to access your external resource (in a catch block to try and figure out what the cause of the failure was) so I'd say that if both your external resource of interest and Google are not available chances are you have a net connectivity problem.
private static boolean netIsAvailable() {
try {
final URL url = new URL("http://www.google.com");
final URLConnection conn = url.openConnection();
conn.connect();
conn.getInputStream().close();
return true;
} catch (MalformedURLException e) {
throw new RuntimeException(e);
} catch (IOException e) {
return false;
}
}
People have suggested using INetAddress.isReachable. The problem is that some sites configure their firewalls to block ICMP Ping messages. So a "ping" might fail even though the web service is accessible.
And of course, the reverse is true as well. A host may respond to a ping even though the webserver is down.
And of course, a machine may be unable to connect directly to certain (or all) web servers due to local firewall restrictions.
The fundamental problem is that "can connect to the internet" is an ill-defined question, and this kind of thing is difficult to test without:
information on the user's machine and "local" networking environment, and
information on what the app needs to access.
So generally, the simplest solution is for an app to just try to access whatever it needs to access, and fall back on human intelligence to do the diagnosis.
If you're on java 6 can use NetworkInterface to check for available network interfaces.
I.e. something like this:
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface interf = interfaces.nextElement();
if (interf.isUp() && !interf.isLoopback())
return true;
}
Haven't tried it myself, yet.
This code should do the job reliably.
Note that when using the try-with-resources statement we don't need to close the resources.
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class InternetAvailabilityChecker
{
public static boolean isInternetAvailable() throws IOException
{
return isHostAvailable("google.com") || isHostAvailable("amazon.com")
|| isHostAvailable("facebook.com")|| isHostAvailable("apple.com");
}
private static boolean isHostAvailable(String hostName) throws IOException
{
try(Socket socket = new Socket())
{
int port = 80;
InetSocketAddress socketAddress = new InetSocketAddress(hostName, port);
socket.connect(socketAddress, 3000);
return true;
}
catch(UnknownHostException unknownHost)
{
return false;
}
}
}
This code:
"127.0.0.1".equals(InetAddress.getLocalHost().getHostAddress().toString());
Returns - to me - true if offline, and false, otherwise. (well, I don't know if this true to all computers).
This works much faster than the other approaches, up here.
EDIT: I found this only working, if the "flip switch" (on a laptop), or some other system-defined option, for the internet connection, is off. That's, the system itself knows not to look for any IP addresses.
InetAddress.isReachable sometime return false if internet connection exist.
An alternative method to check internet availability in java is : This function make a real ICMP ECHO ping.
public static boolean isReachableByPing(String host) {
try{
String cmd = "";
if(System.getProperty("os.name").startsWith("Windows")) {
// For Windows
cmd = "ping -n 1 " + host;
} else {
// For Linux and OSX
cmd = "ping -c 1 " + host;
}
Process myProcess = Runtime.getRuntime().exec(cmd);
myProcess.waitFor();
if(myProcess.exitValue() == 0) {
return true;
} else {
return false;
}
} catch( Exception e ) {
e.printStackTrace();
return false;
}
}
I usually break it down into three steps.
I first see if I can resolve the domain name to an IP address.
I then try to connect via TCP (port 80 and/or 443) and close gracefully.
Finally, I'll issue an HTTP request and check for a 200 response back.
If it fails at any point, I provide the appropriate error message to the user.
URL url=new URL("http://[any domain]");
URLConnection con=url.openConnection();
/*now errors WILL arise here, i hav tried myself and it always shows "connected" so we'll open an InputStream on the connection, this way we know for sure that we're connected to d internet */
/* Get input stream */
con.getInputStream();
Put the above statements in try catch blocks and if an exception in caught means that there's no internet connection established. :-)
The code using NetworkInterface to wait for the network worked for me until I switched from fixed network address to DHCP. A slight enhancement makes it work also with DHCP:
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface interf = interfaces.nextElement();
if (interf.isUp() && !interf.isLoopback()) {
List<InterfaceAddress> adrs = interf.getInterfaceAddresses();
for (Iterator<InterfaceAddress> iter = adrs.iterator(); iter.hasNext();) {
InterfaceAddress adr = iter.next();
InetAddress inadr = adr.getAddress();
if (inadr instanceof Inet4Address) return true;
}
}
}
This works for Java 7 in openSuse 13.1 for IPv4 network. The problem with the original code is that although the interface was up after resuming from suspend, an IPv4 network address was not yet assigned. After waiting for this assignment, the program can connect to servers. But I have no idea what to do in case of IPv6.
1) Figure out where your application needs to be connecting to.
2) Set up a worker process to check InetAddress.isReachable to monitor the connection to that address.
This code is contained within a jUnit test class I use to test if a connection is available. I always receive a connection, but if you check the content length it should be -1 if not known :
try {
URL url = new URL("http://www.google.com");
URLConnection connection = url.openConnection();
if(connection.getContentLength() == -1){
fail("Failed to verify connection");
}
}
catch (IOException e) {
fail("Failed to open a connection");
e.printStackTrace();
}
public boolean checkInternetConnection()
{
boolean status = false;
Socket sock = new Socket();
InetSocketAddress address = new InetSocketAddress("www.google.com", 80);
try
{
sock.connect(address, 3000);
if(sock.isConnected()) status = true;
}
catch(Exception e)
{
status = false;
}
finally
{
try
{
sock.close();
}
catch(Exception e){}
}
return status;
}
You can simply write like this
import java.net.InetAddress;
import java.net.UnknownHostException;
public class Main {
private static final String HOST = "localhost";
public static void main(String[] args) throws UnknownHostException {
boolean isConnected = !HOST.equals(InetAddress.getLocalHost().getHostAddress().toString());
if (isConnected) System.out.println("Connected");
else System.out.println("Not connected");
}
}
There are (nowadays) APIs for this, but they are platform specific:
On Android ConnectivityManager (https://developer.android.com/training/basics/network-ops/reading-network-state) does everything you need.
On Windows INetworkListManager::GetConnectivity (for which you'll need a JNI)
On generic Linux, you are probably stuck with testing if you have access to a DNS server and Google, as above.
there is probably an Apple way to do this as well
(I'd use the specific tools where available)
This have worked well for me.
try{
InetAddress addr = InetAddress.getByName("google.com" );
}catch(IOException e){
JOptionPane.showMessageDialog(new JFrame(),"No Internet connection.\nTry again later", "Network Error", JOptionPane.ERROR_MESSAGE);
}
There is also a gradle option --offline which maybe results in the behavior you want.
The following piece of code allows us to get the status of the network on our Android device
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView mtv=findViewById(R.id.textv);
ConnectivityManager connectivityManager=
(ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(((Network)connectivityManager.getActiveNetwork())!=null)
mtv.setText("true");
else
mtv.setText("fasle");
}
}
}