package io.hummer.prefetch.impl;
import io.hummer.prefetch.PrefetchingService;
import io.hummer.prefetch.context.Context;
import io.hummer.prefetch.context.Time;
import io.hummer.prefetch.context.TimeClock;
import io.hummer.prefetch.ws.W3CEndpointReferenceUtils;
import io.hummer.prefetch.ws.WSClient;
import io.hummer.util.coll.Pair;
import io.hummer.util.xml.XMLUtil;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.jws.WebService;
import javax.xml.soap.SOAPEnvelope;
import org.apache.log4j.Logger;
import org.w3c.dom.Element;
/**
* Implementation of the prefetching service.
* @author Waldemar Hummer (hummer@dsg.tuwien.ac.at)
*/
@WebService(
endpointInterface = "io.hummer.prefetch.PrefetchingService",
targetNamespace = PrefetchingService.NAMESPACE
)
public class PrefetchingServiceImpl implements PrefetchingService {
protected final Map<String,PrefetchSubscription> prefetchings = new ConcurrentHashMap<>();
private final AtomicBoolean running = new AtomicBoolean(true);
private static final Logger LOG = Logger.getLogger(PrefetchingServiceImpl.class);
private static final XMLUtil xmlUtil = new XMLUtil();
private Context<Object> context;
static {
/* set the concrete XML adapter. */
PrefetchingService.JaxbAdapter.actualAdapter =
new io.hummer.prefetch.ws.JaxbAdapter();
}
public PrefetchingServiceImpl(Context<Object> ctx) {
this.context = ctx;
}
/**
* Internal class which holds a prefetching subscription.
*/
protected static class PrefetchSubscription {
public String subscriptionID;
public PrefetchRequest request;
}
/**
* Timer task to schedule prefetching requests.
*/
private class PrefetchTask extends TimerTask {
private String subscriptionID;
public PrefetchTask(String subscriptionID) {
this.subscriptionID = subscriptionID;
}
public void run() {
PrefetchingServiceImpl.this.handleRequest(subscriptionID);
}
}
/**
* Central Web service method called by the clients.
*/
public PrefetchResponse setPrefetchingStrategy(PrefetchRequest request) {
final PrefetchSubscription s = new PrefetchSubscription();
s.request = request;
if(request.subscriptionID != null) {
s.subscriptionID = request.subscriptionID;
} else {
s.subscriptionID = UUID.randomUUID().toString();
request.subscriptionID = s.subscriptionID;
}
prefetchings.put(s.subscriptionID, s);
TimeClock.schedule(new PrefetchTask(s.subscriptionID), 0);
PrefetchResponse result = new PrefetchResponse();
result.subscriptionID = s.subscriptionID;
return result;
}
/**
* Handle a prefetching request.
* Calls to this method are triggered by the timer task of this class.
* @param subscriptionID
*/
private void handleRequest(final String subscriptionID) {
PrefetchSubscription pf = prefetchings.get(subscriptionID);
if(pf == null) {
LOG.warn("Subscription ID " + subscriptionID + " not found.");
return;
}
handleRequest(pf);
}
/**
* Handle a prefetching request.
* @param pf
*/
protected void handleRequest(PrefetchSubscription pf) {
LOG.trace("handle request: " + pf.request.invocationPredictor);
// if(pf.request.strategy == null) {
// pf.request.strategy = new PrefetchStrategyPeriodic();
// }
boolean doPrefetch = pf.request.strategy.doPrefetchNow(context);
LOG.debug("do prefetch (" + context.getTime() + "): " + doPrefetch);
if(doPrefetch) {
if(running.get()) {
Boolean hasNetwork = (Boolean)context.getAttribute(Context.ATTR_NETWORK_AVAILABLE);
if(hasNetwork != null && !hasNetwork) {
LOG.debug("We should prefetch now, but have no network connectivity...");
return;
}
// String address = W3CEndpointReferenceUtils.getAddress(
// pf.request.invocation.serviceEPR);
// LOG.info("Initiate prefetching of service: " + address);
/* do prefetching */
doPrefetch(pf.request);
/* notify strategy */
pf.request.strategy.notifyPrefetchPerformed();
}
}
if(running.get()) {
/* schedule next prefetching */
Double delay = pf.request.strategy.getNextAskTimeDelayInSecs();
if(delay != null && delay > 0) {
//timer.schedule(new PrefetchTask(pf.subscriptionID), delay);
LOG.debug("scheduling next prefetch decision in " + delay + " seconds");
TimeClock.schedule(new PrefetchTask(pf.subscriptionID), delay);
}
}
}
/**
* Perform the actual prefetching.
* @param request
*/
private void doPrefetch(PrefetchRequest request) {
// System.out.println("foo");
Time currentTime = (Time)context.getAttribute(Context.ATTR_TIME);
if(currentTime != null) {
List<Pair<Context<Object>, ServiceInvocation>> invs = request.
invocationPredictor.predictInvocations(context, currentTime,
currentTime.add(request.lookIntoFutureSecs));
// System.out.println("predict: " + invs.size());
for(Pair<Context<Object>, ServiceInvocation> inv : invs) {
Context<Object> ctx = inv.getFirst();
// String TODO = (inv + " unavailable: " + ctx.isNetworkUnavailable());
//System.out.println(TODO);
if(ctx.isNetworkUnavailable()) {
LOG.debug("here unavailable: " +
inv.getFirst().getAttribute(Context.ATTR_TIME) +
" - req: " + xmlUtil.getSOAPBodyAsString(
(Element)inv.getSecond().serviceCall));
doPrefetch(request, inv.getSecond());
} else {
LOG.debug("here available: " +
inv.getFirst().getAttribute(Context.ATTR_TIME) +
" - req: " + xmlUtil.getSOAPBodyAsString(
(Element)inv.getSecond().serviceCall));
}
}
}
}
private void doPrefetch(PrefetchRequest request, ServiceInvocation invocation) {
try {
/* check if this is a dynamic invocation builder */
// if(invocation instanceof ServiceInvocationBuilder) {
// invocation = ((ServiceInvocationBuilder)invocation).
// buildInvocation(context);
// }
/* perform invocation */
SOAPEnvelope env = WSClient.toEnvelope(
(Element)invocation.serviceCall);
SOAPEnvelope response = WSClient.invokePOST(
invocation.serviceEPR, env);
/* notify subscriber(s) */
PrefetchNotification notification = new PrefetchNotification();
notification.subscriptionID = request.subscriptionID;
notification.serviceInvocation = invocation;
notification.result = response;
if(request.notifyRemote != null) {
PrefetchingResultReceiver subscriber =
WSClient.createClientJaxws(PrefetchingResultReceiver.class, new URL(
W3CEndpointReferenceUtils.getAddress(request.notifyRemote) + "?wsdl"));
subscriber.notify(notification);
}
if(request.notifyLocal != null) {
LOG.debug("Notifying listener of prefetch result for request: " +
//request.notifyLocal
//new XMLUtil().toString(notification)
new XMLUtil().toString(env));
request.notifyLocal.notify(notification);
}
} catch (Exception e) {
LOG.warn("Unable to invoke service: " +
invocation, e);
}
}
protected SOAPEnvelope performInvocation(ServiceInvocation inv) throws IOException {
SOAPEnvelope env = WSClient.toEnvelope(
(Element)inv.serviceCall);
SOAPEnvelope response = WSClient.invokePOST(
inv.serviceEPR, env);
return response;
}
}