Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
HAWKULAR-486 Let Pinger collect remote IP address
Browse files Browse the repository at this point in the history
  • Loading branch information
ppalaga committed Jul 26, 2015
1 parent a6d398e commit d36c887
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.hawkular.component.pinger;



/**
* An outcome of a ping.
*
Expand Down Expand Up @@ -67,9 +68,10 @@ public static final PingStatus error(PingDestination destination, int code, long
private final boolean timedOut;

/** The value of {@code System.currentTimeMillis()} when the response was received or when the timeout or other
* error was detected. */
* error was detected */
private final long timestamp;

/** The {@link Traits} collected from the ping response */
private final Traits traits;

/**
Expand All @@ -95,6 +97,7 @@ public PingStatus(PingDestination destination, int code, long timestamp, int dur
* the timeout or other error was detected
* @param duration Ping round trip duration in milliseconds or {@value #INVALID_DURATION} if the ping timed out
* @param timedOut {@code true} if the ping timed out, {@code false} otherwise
* @param remoteAddress
*
* @see #timeout(PingDestination, long, int)
* @see #error(PingDestination, int, long)
Expand All @@ -109,27 +112,46 @@ private PingStatus(PingDestination destination, int code, long timestamp, int du
this.traits = traits;
}

/**
* @return the HTTP status code of the ping response
*/
public int getCode() {
return code;
}

/**
* @return ping round trip duration in milliseconds or {@value #INVALID_DURATION} if the ping timed out
*/
public int getDuration() {
return duration;
}

/**
* @return {@code true} if the ping timed out, {@code false} otherwise
*/
public boolean isTimedOut() {
return timedOut;
}


/**
* @return the value of {@code System.currentTimeMillis()} when the response was received or when the timeout or
* other error was detected
*/
public long getTimestamp() {
return timestamp;
}

/**
* @return the destination where the ping was sent
*/
public PingDestination getDestination() {
return destination;
}

/**
* @return the {@link Traits} collected from the ping response
*/
public Traits getTraits() {
return traits;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.hawkular.component.pinger;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
Expand All @@ -27,16 +29,23 @@
import javax.ejb.Stateless;
import javax.net.ssl.SSLContext;

import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

/**
Expand All @@ -50,12 +59,37 @@
@Stateless
public class Pinger {

/** A key to use when storing and retrieving remote IP address from and to {@link HttpContext} */
static final String REMOTE_ADDRESS_ATTRIBUTE = Pinger.class.getPackage().getName() + ".remoteAddress";

/** A custom connnection manager used by this pinger */
private final HttpClientConnectionManager connectionManager;

public Pinger() throws Exception {
connectionManager = createConnectionManager();
}

/**
* SSL Context trusting all certificates.
* Creates a custom {@link HttpClientConnectionManager} that will be used by this pinger. The returned connection
* manager accepts all SSL certificates, and stores remote IP address into {@link HttpContext} under
* {@link #REMOTE_ADDRESS_ATTRIBUTE}.
*
* @return a new {@link HttpClientConnectionManager}
*/
private final SSLContext sslContext;
private HttpClientConnectionManager createConnectionManager() {

PlainConnectionSocketFactory plainSf = new PlainConnectionSocketFactory() {
@Override
public Socket connectSocket(int connectTimeout, Socket socket, org.apache.http.HttpHost host,
java.net.InetSocketAddress remoteAddress, java.net.InetSocketAddress localAddress,
HttpContext context) throws IOException {
InetAddress remoteInetAddress = remoteAddress.getAddress();
Log.LOG.tracef("Putting remote IP address to HttpContext %s", remoteInetAddress);
context.setAttribute(REMOTE_ADDRESS_ATTRIBUTE, remoteInetAddress);
return super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
}
};

public Pinger() throws Exception {
SSLContext tmpSslContext;

try {
Expand All @@ -72,17 +106,11 @@ public boolean isTrusted(X509Certificate[] chain, String authType) throws Certif
tmpSslContext = null;
}

sslContext = tmpSslContext;
}
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(tmpSslContext, null, null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

private CloseableHttpClient getHttpClient(final String url) {
if (url != null && url.startsWith("https://") && sslContext != null) {
return HttpClientBuilder.create()
.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
.setSslcontext(sslContext).build();
} else {
return HttpClientBuilder.create().build();
}
return new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", plainSf).register("https", sslSocketFactory).build());
}

/**
Expand All @@ -96,19 +124,22 @@ public Future<PingStatus> ping(final PingDestination destination) {
Log.LOG.debugf("About to ping %s", destination.getUrl());
HttpUriRequest request = RequestBuilder.create(destination.getMethod()).setUri(destination.getUrl()).build();

try (CloseableHttpClient client = getHttpClient(destination.getUrl())) {
try (CloseableHttpClient client = HttpClientBuilder.create().setConnectionManager(connectionManager).build()) {
long start = System.currentTimeMillis();
HttpResponse httpResponse = client.execute(request);
StatusLine statusLine = httpResponse.getStatusLine();
EntityUtils.consumeQuietly(httpResponse.getEntity());
long now = System.currentTimeMillis();

final int code = statusLine.getStatusCode();
final int duration = (int) (now - start);
Traits traits = Traits.collect(httpResponse, now);
PingStatus result = new PingStatus(destination, code, now, duration, traits);
Log.LOG.debugf("Got status code %d from %s", code, destination.getUrl());
return new AsyncResult<>(result);
HttpClientContext context = HttpClientContext.create();
try (CloseableHttpResponse httpResponse = client.execute(request, context)) {
InetAddress remoteAddress = (InetAddress) context.getAttribute(REMOTE_ADDRESS_ATTRIBUTE);
StatusLine statusLine = httpResponse.getStatusLine();
EntityUtils.consumeQuietly(httpResponse.getEntity());
long now = System.currentTimeMillis();

final int code = statusLine.getStatusCode();
final int duration = (int) (now - start);
Traits traits = Traits.collect(httpResponse, now, remoteAddress);
PingStatus result = new PingStatus(destination, code, now, duration, traits);
Log.LOG.debugf("Got status code %d from %s", code, destination.getUrl());
return new AsyncResult<>(result);
}
} catch (UnknownHostException e) {
PingStatus result = PingStatus.error(destination, 404, System.currentTimeMillis());
Log.LOG.debugf("Got UnknownHostException for %s", destination.getUrl());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package org.hawkular.component.pinger;

import java.net.InetAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
Expand Down Expand Up @@ -96,7 +97,7 @@ public String toString() {
* @param timestamp the UNIX timestamp when the response was received
* @return a new {@link Traits}
*/
public static Traits collect(HttpResponse httpResponse, long timestamp) {
public static Traits collect(HttpResponse httpResponse, long timestamp, InetAddress remoteAddress) {
/* We assume that the header keys will typically be unique
* Therefore, we store the value into items Map on first hit
* and only if there is a second hit, we lazily create a sorted Set in multiItems.
Expand All @@ -107,8 +108,11 @@ public static Traits collect(HttpResponse httpResponse, long timestamp) {
HeaderIterator headers = httpResponse.headerIterator();
while (headers.hasNext()) {
Header header = headers.nextHeader();
Log.LOG.tracef("Is this a trait header? %s:%s from %s", header.getName(), header.getValue(), remoteAddress);
TraitHeader traitHeader = TraitHeader.fastValueOf(header.getName());
if (traitHeader != null) {
Log.LOG.tracef("Found a trait header: %s:%s from %s", header.getName(), header.getValue(),
remoteAddress);
if (items == null) {
items = new HashMap<>();
items.put(traitHeader, header.getValue());
Expand Down Expand Up @@ -149,7 +153,8 @@ public static Traits collect(HttpResponse httpResponse, long timestamp) {
}
}

return new Traits(timestamp, items == null ? Collections.emptyMap() : Collections.unmodifiableMap(items));
return new Traits(timestamp, remoteAddress, items == null ? Collections.emptyMap()
: Collections.unmodifiableMap(items));
};

/**
Expand All @@ -159,12 +164,15 @@ public static Traits collect(HttpResponse httpResponse, long timestamp) {
* @return a new {@link Traits} with the given {@code timestamp} and no {@link #items}
*/
public static Traits empty(long timestamp) {
return new Traits(timestamp, NO_ITEMS);
return new Traits(timestamp, null, NO_ITEMS);
}

/** The header name - header value map storing the traits */
private final Map<TraitHeader, String> items;

/** The remote IP address that replied to the ping, can be {@code null} */
private final InetAddress remoteAddress;

/** The UNIX timestamp when the response was received */
private final long timestamp;

Expand All @@ -176,9 +184,10 @@ public static Traits empty(long timestamp) {
*
* @see #collect(HttpResponse, long)
*/
Traits(long timestamp, Map<TraitHeader, String> items) {
Traits(long timestamp, InetAddress remoteAddress, Map<TraitHeader, String> items) {
super();
this.timestamp = timestamp;
this.remoteAddress = remoteAddress;
this.items = items;
}

Expand All @@ -189,6 +198,13 @@ public Map<TraitHeader, String> getItems() {
return items;
}

/**
* @return the remote IP address that replied to the ping, can be {@code null}
*/
public InetAddress getRemoteAddress() {
return remoteAddress;
}

/**
* @return the UNIX timestamp when these {@link Traits} were collected
*/
Expand All @@ -206,13 +222,18 @@ public boolean equals(Object obj) {
if (getClass() != obj.getClass())
return false;
Traits other = (Traits) obj;
if (timestamp != other.timestamp)
return false;
if (remoteAddress == null) {
if (other.remoteAddress != null)
return false;
} else if (!remoteAddress.equals(other.remoteAddress))
return false;
if (items == null) {
if (other.items != null)
return false;
} else if (!items.equals(other.items))
return false;
if (timestamp != other.timestamp)
return false;
return true;
}

Expand All @@ -223,13 +244,14 @@ public int hashCode() {
int result = 1;
result = prime * result + ((items == null) ? 0 : items.hashCode());
result = prime * result + (int) (timestamp ^ (timestamp >>> 32));
result = prime * result + ((remoteAddress == null) ? 0 : remoteAddress.hashCode());
return result;
}

/** @see java.lang.Object#toString() */
@Override
public String toString() {
return "Traits [items=" + items + ", timestamp=" + timestamp + "]";
return "Traits [items=" + items + ", timestamp=" + timestamp + ", remoteAddress=" + remoteAddress + "]";
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@
*/
package org.hawkular.component.pinger;

import java.net.InetAddress;
import java.util.Map.Entry;

import javax.ejb.Asynchronous;
import javax.ejb.Stateless;

import org.hawkular.component.pinger.Traits.TraitHeader;
import org.hawkular.inventory.api.EntityNotFoundException;
import org.hawkular.inventory.api.Inventory;
import org.hawkular.inventory.api.Resources;
import org.hawkular.inventory.api.model.Resource;
import org.hawkular.inventory.api.model.Resource.Update.Builder;

import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import java.util.Map.Entry;

/**
* Stores ping results to Hawkular Inventory.
*
Expand All @@ -35,14 +37,15 @@
@Stateless
public class TraitsPublisher {

private static final String TRAIT_PROPERTY_PREFIX = "trait-";

@javax.annotation.Resource(lookup = "java:global/Hawkular/Inventory")
private Inventory inventory;

/**
* Stores the {@link Traits} of the given {@link PingStatus} in Hawkular Inventory.
*
* @param status
* the {@link PingStatus} to publish
* @param status the {@link PingStatus} to publish
*/
@Asynchronous
public void publish(PingStatus status) {
Expand All @@ -58,13 +61,20 @@ public void publish(PingStatus status) {

Builder updateBuilder = Resource.Update.builder();

//keep the properties already present on the resource
updateBuilder.withProperties(resource.getProperties());
/* keep the props not starting with TRAIT_PROPERTY_PREFIX */
resource.getProperties().entrySet().stream()
.filter(e -> !e.getKey().startsWith(TRAIT_PROPERTY_PREFIX))
.forEach(e -> updateBuilder.withProperty(e.getKey(), e.getValue()));

/* add the new trait props */
updateBuilder.withProperty(TRAIT_PROPERTY_PREFIX + "collected-on", traits.getTimestamp());

//add/modify our own
updateBuilder.withProperty("traits-collected-on", traits.getTimestamp());
InetAddress remoteAddress = traits.getRemoteAddress();
if (remoteAddress != null) {
updateBuilder.withProperty(TRAIT_PROPERTY_PREFIX + "remote-address", remoteAddress.getHostAddress());
}
for (Entry<TraitHeader, String> entry : traits.getItems().entrySet()) {
updateBuilder.withProperty("trait-" + entry.getKey().toString(), entry.getValue());
updateBuilder.withProperty(TRAIT_PROPERTY_PREFIX + entry.getKey().toString(), entry.getValue());
}

inventory.tenants().get(dest.getTenantId()).environments().get(dest.getEnvironmentId()).feedlessResources()
Expand Down

0 comments on commit d36c887

Please sign in to comment.