I have a Java Akka application and I want to set a separate MDC context for each message handling based on information inside the message, for example I have the following base interface for all messages:
public interface IdMessage {
String getId();
}
Also I have the following base actor for all actors:
public class BaseActor extends AbstractActor {
private final DiagnosticLoggingAdapter log = Logging.apply(this);
#Override
public void aroundReceive(PartialFunction<Object, BoxedUnit> receive, Object msg) {
if (msg instanceof IdMessage) {
final Map<String, Object> originalMDC = log.getMDC();
log.setMDC(ImmutableMap.of("id", ((IdMessage) msg).getId()));
try {
super.aroundReceive(receive, msg);
} finally {
if (originalMDC != null) {
log.setMDC(originalMDC);
} else {
log.clearMDC();
}
}
} else {
super.aroundReceive(receive, msg);
}
}
}
And the actual actor implementation:
public class SomeActor extends BaseActor {
SomeActor() {
receive(ReceiveBuilder
.match(SomeMessage.class, message -> {
...
}
}
}
I would like to make sure that all logs entries inside SomeActor#receive() will have MDC context set in the BaseActor. To make this work SomeActor#receice() need to be executed in the same thread as BaseActor#aroundReceive() method.
I didn't find any information about the behaviour of aroundReceive - is that going to be always executed in the same thread as the actual receive method? Based on my testing it's always executed in the same thread.
I was able to figure out the proper implementation by myself and would like to share it in case someone face with the same issue.
The aroundReceive is going to be executed in the same thread as receive, so this is the right place to set MDC context.
I used org.slf4j.MDC for setting the MDC context, here is the full code:
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import com.google.common.collect.ImmutableMap;
import akka.actor.AbstractActor;
import scala.PartialFunction;
import scala.runtime.BoxedUnit;
public class BaseActor extends AbstractActor {
private final Logger log = LoggerFactory.getLogger(BaseActor.class);
#Override
public void aroundReceive(PartialFunction<Object, BoxedUnit> receive, Object msg) {
if (msg instanceof IdMessage) {
final Map<String, Object> originalMDC = log.getMDC();
MDC.setContextMap(ImmutableMap.of("id", ((IdMessage) msg).getId()));
try {
super.aroundReceive(receive, msg);
} finally {
if (originalMDC != null) {
MDC.setContextMap(originalMDC);
} else {
MDC.clear();
}
}
} else {
super.aroundReceive(receive, msg);
}
}
}
With that implementation of BaseActor all log entries in receive are logged with a proper MDC context. Additional information could be found in this interesting blog post (with Scala implementation).
Note: I was not able to reach the same functionality with Akka DiagnosticLoggingAdapter although it has methods to set MDC context.
Related
I'm reading Akka documentation and now I'm at the section about UntypedActors. I decided to try some examples:
Here are my actors:
Parent
private static class MyUntypedActor extends UntypedActor{
public void onReceive(Object message) throws Exception {
System.out.println("Recieved: " + message);
}
#Override
public void preStart(){
getContext().actorOf(AnotherUntypedActor.props()).tell("Process it", getSelf());
}
public static Props props(){
return Props.create(MyUntypedActor.class);
}
}
Child
private static class AnotherUntypedActor extends UntypedActor{
public static Props props(){
return Props.create(AnotherUntypedActor.class);
}
public void onReceive(Object message) throws Exception {
System.out.println("My: " + message);
throw new RuntimeException("Crashed: " + getSelf());
}
}
main:
public static void main(String[] args) throws TimeoutException {
ActorSystem system = ActorSystem.create();
Inbox inbox = Inbox.create(system);
ActorRef actorRef = system.actorOf(MyUntypedActor.props());
inbox.send(actorRef, "Message");
}
so, my child actors experienced failure and I thought the it should have notified the parent somehow.
But what I recieved was this:
Recieved: Message
My: Process it
[ERROR] [07/14/2016 19:05:13.726] [default-akka.actor.default-dispatcher-4] [akka://default/user/$a/$a] Crashed: Actor[akka://default/user/$a/$a#-950392568]
What does the supervision do actually? Child actor had faulted and what? I got just an error message in a log. What does supervisorStrategy mean? It's by default set to
OneForOneStrategy(-1,Duration.Inf,true)
By using supervisor strategy, you decide what should be done with supervised actor if it fails. You have to override supervisionStrategy() method within your parent actor and define strategy. I.e. (not sure if it's correct since I use Scala for Akka)
#Override
public SupervisorStrategy supervisorStrategy() {
return strategy;
}
private static SupervisorStrategy strategy =
new OneForOneStrategy(10, Duration.create("1 minute"),
t -> {
if (t instanceof SomeException) {
return restart();
} else {
return stop();
}
});
In this case, if SomeException occurs, actor will be restarted. Otherwise, it will be stopped. You can choose one of four strategies.
Read documentation
Tip: Create specific exceptions!
I am creating an Android app prototype which functions like this:
When users launch the app, they can browse through article title list. When they click one of them, the app switches to another activity, which contains the article contents and so on. Users can also post an article when they are on the article title list.
I am trying to create a utility class, let's say ServerConnect.java, to handle all the REST request and response. In the beginning, the class has 3 public static methods to send requests for different purpose.
1. requestArticleList
2. requestArticleContent
3. userSendArticle (user post a new article)
Then I find out that I cannot just call ServerConnect.requestArticleList(), as Android forbids network behavior on UI thread. So I change the ServerConnect class to extends AsyncTask class. And put requestArticleList() in doInBackground(), then do things like new ConnectServer().execute()
Well, this gives me what I what when I lauch the app and request for article list. However, my problem is, this ConnectServer class can only do requestArticleList as it is hardcoded in doInBackground #Override
protected String doInBackground(Location userLocation) {
return requestArticleList(userLocation);
})
Is there any ways I can handle all these 3 method as public static method in one utility class?
Thanks all for your patience for such a long story.
I don't think that putting this in one class is a good idea. If you afraid that some function will repeat in 3 different class you can put it in some base class like below.
Create abstract base class with common functions
public abstract class ConnectionUtils<A,B,C> extends AsyncTask<A,B,C> {
// put common functions here
}
Then create 3 separate class for each request
public class RequestArticleList extends ConnectionUtils<Location, Void, String> {
#Override
protected String doInBackground(Location... userLocation) {
return requestArticleList(userLocation);
}
}
If you really want all this functionality in one class you can create callback interface
public interface Callback<ResultType> {
void onResult(ResultType result);
}
and then in each function create new Thread
public void requestArticleList(final Callback<MyResult> callback) {
new Thread(new Runnable() {
#Override
public void run() {
// your code
callback.onResult(result);
}
}).start();
}
Drawback is that its require switching to main thread before ui update.
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
public class NetworkUtil {
public static int TYPE_WIFI = 1;
public static int TYPE_MOBILE = 2;
public static int TYPE_NOT_CONNECTED = 0;
public static int getConnectivityStatus(Context context) {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (null != activeNetwork) {
if(activeNetwork.getType() == ConnectivityManager.TYPE_WIFI)
return TYPE_WIFI;
if(activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE)
return TYPE_MOBILE;
}
return TYPE_NOT_CONNECTED;
}
public static String getConnectivityStatusString(Context context) {
int conn = NetworkUtil.getConnectivityStatus(context);
String status = null;
if (conn == NetworkUtil.TYPE_WIFI) {
status = "true";
} else if (conn == NetworkUtil.TYPE_MOBILE) {
status = "true";
} else if (conn == NetworkUtil.TYPE_NOT_CONNECTED) {
status = "false";
}
return status;
}
}
i am trying mdc logging in play filter in java for all requests i followed this tutorial in scala and tried converting to java http://yanns.github.io/blog/2014/05/04/slf4j-mapped-diagnostic-context-mdc-with-play-framework/
but still the mdc is not propagated to all execution contexts.
i am using this dispathcher as default dispatcher but there are many execution contexts for it. i need the mdc propagated to all execution contexts
below is my java code
import java.util.Map;
import org.slf4j.MDC;
import scala.concurrent.ExecutionContext;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import akka.dispatch.Dispatcher;
import akka.dispatch.ExecutorServiceFactoryProvider;
import akka.dispatch.MessageDispatcherConfigurator;
public class MDCPropagatingDispatcher extends Dispatcher {
public MDCPropagatingDispatcher(
MessageDispatcherConfigurator _configurator, String id,
int throughput, Duration throughputDeadlineTime,
ExecutorServiceFactoryProvider executorServiceFactoryProvider,
FiniteDuration shutdownTimeout) {
super(_configurator, id, throughput, throughputDeadlineTime,
executorServiceFactoryProvider, shutdownTimeout);
}
#Override
public ExecutionContext prepare() {
final Map<String, String> mdcContext = MDC.getCopyOfContextMap();
return new ExecutionContext() {
#Override
public void execute(Runnable r) {
Map<String, String> oldMDCContext = MDC.getCopyOfContextMap();
setContextMap(mdcContext);
try {
r.run();
} finally {
setContextMap(oldMDCContext);
}
}
#Override
public ExecutionContext prepare() {
return this;
}
#Override
public void reportFailure(Throwable t) {
play.Logger.info("error occured in dispatcher");
}
};
}
private void setContextMap(Map<String, String> context) {
if (context == null) {
MDC.clear();
} else {
play.Logger.info("set context "+ context.toString());
MDC.setContextMap(context);
}
}
}
import java.util.concurrent.TimeUnit;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import com.typesafe.config.Config;
import akka.dispatch.DispatcherPrerequisites;
import akka.dispatch.MessageDispatcher;
import akka.dispatch.MessageDispatcherConfigurator;
public class MDCPropagatingDispatcherConfigurator extends
MessageDispatcherConfigurator {
private MessageDispatcher instance;
public MDCPropagatingDispatcherConfigurator(Config config,
DispatcherPrerequisites prerequisites) {
super(config, prerequisites);
Duration throughputDeadlineTime = new FiniteDuration(-1,
TimeUnit.MILLISECONDS);
FiniteDuration shutDownDuration = new FiniteDuration(1,
TimeUnit.MILLISECONDS);
instance = new MDCPropagatingDispatcher(this, "play.akka.actor.contexts.play-filter-context",
100, throughputDeadlineTime,
configureExecutor(), shutDownDuration);
}
public MessageDispatcher dispatcher() {
return instance;
}
}
filter interceptor
public class MdcLogFilter implements EssentialFilter {
#Override
public EssentialAction apply(final EssentialAction next) {
return new MdcLogAction() {
#Override
public Iteratee<byte[], SimpleResult> apply(
final RequestHeader requestHeader) {
final String uuid = Utils.generateRandomUUID();
MDC.put("uuid", uuid);
play.Logger.info("request started"+uuid);
final ExecutionContext playFilterContext = Akka.system()
.dispatchers()
.lookup("play.akka.actor.contexts.play-custom-filter-context");
return next.apply(requestHeader).map(
new AbstractFunction1<SimpleResult, SimpleResult>() {
#Override
public SimpleResult apply(SimpleResult simpleResult) {
play.Logger.info("request ended"+uuid);
MDC.remove("uuid");
return simpleResult;
}
}, playFilterContext);
}
#Override
public EssentialAction apply() {
return next.apply();
}
};
}
}
Below is my solution, proven in real life. It's in Scala, and not for Play, but for Scalatra, but the underlying concept is the same. Hope you'll be able to figure out how to port this to Java.
import org.slf4j.MDC
import java.util.{Map => JMap}
import scala.concurrent.{ExecutionContextExecutor, ExecutionContext}
object MDCHttpExecutionContext {
def fromExecutionContextWithCurrentMDC(delegate: ExecutionContext): ExecutionContextExecutor =
new MDCHttpExecutionContext(MDC.getCopyOfContextMap(), delegate)
}
class MDCHttpExecutionContext(mdcContext: JMap[String, String], delegate: ExecutionContext)
extends ExecutionContextExecutor {
def execute(runnable: Runnable): Unit = {
val callingThreadMDC = MDC.getCopyOfContextMap()
delegate.execute(new Runnable {
def run() {
val currentThreadMDC = MDC.getCopyOfContextMap()
setContextMap(callingThreadMDC)
try {
runnable.run()
} finally {
setContextMap(currentThreadMDC)
}
}
})
}
private[this] def setContextMap(context: JMap[String, String]): Unit = {
Option(context) match {
case Some(ctx) => {
MDC.setContextMap(context)
}
case None => {
MDC.clear()
}
}
}
def reportFailure(t: Throwable): Unit = delegate.reportFailure(t)
}
You'll have to make sure that this ExecutionContext is used in all of your asynchronous calls. I achieve this through Dependency Injection, but there are different ways. That's how I do it with subcut:
bind[ExecutionContext] idBy BindingIds.GlobalExecutionContext toSingle {
MDCHttpExecutionContext.fromExecutionContextWithCurrentMDC(
ExecutionContext.fromExecutorService(
Executors.newFixedThreadPool(globalThreadPoolSize)
)
)
}
The idea behind this approach is as follows. MDC uses thread-local storage for the attributes and their values. If a single request of yours can run on a multiple threads, then you need to make sure the new thread you start uses the right MDC. For that, you create a custom executor that ensures the proper copying of the MDC values into the new thread before it starts executing the task you assign to it. You also must ensure that when the thread finishes your task and continues with something else, you put the old values into its MDC, because threads from a pool can switch between different requests.
I've adapted the Quote Of The Moment (QOTM) a bit and would like to build a GUI front-end. It's simple enough to pass objects from the DatagramClientHandler to the GUI. However, it seems intractable for the GUI to reference the handler.
The QuotesGUI class extends JFrame to take advantage of the Netbeans drag-and-drop palette to add Swing components easily. It's quite verbose.
Apparently, the solution is to:
Well It depends as there are more then one solution. One could be to
inject a listener to the ChannelHandler which then will get notified
once the message was received. An other solution could be to send
events to a topic once a message was received and register the
interested swing parts on the topic, so they get notified.
https://stackoverflow.com/a/8780410/262852
DatagramClientHandler:
package net.bounceme.dur.netty;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import java.net.InetSocketAddress;
import java.util.logging.Logger;
import net.bounceme.dur.client.gui.QuotesGUI;
public class DatagramClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private static final Logger log = Logger.getLogger(DatagramClientHandler.class.getName());
private final QuotesGUI gui = new QuotesGUI();
private volatile Channel channel = null;
DatagramClientHandler() {
log.info("starting..");
gui.setVisible(true);
}
private DatagramPacket getNext() {
DatagramPacket packet = new DatagramPacket(
Unpooled.copiedBuffer("QOTM?", CharsetUtil.UTF_8),
new InetSocketAddress("localhost", 4454));
return packet;
}
#Override
public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
String response = msg.content().toString(CharsetUtil.UTF_8);
log.info(response);
gui.setQuote(response);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.severe(cause.toString());
ctx.close();
}
}
sample method from the GUI.
public void setQuote(String packet) {
text.setText(packet);
}
Start by separating your layers of responsibilities...
I would probably start by defining some kind of listener interface which can registered with an instance of DatagramClientHandler. This interface would allow interested parties to be notified of changes or events within DatagramClientHandler and deal with those events as they see fit...
public interface MessageListener {
public void quoteRecieved(SimpleChannelInboundHandler source, String quote);
public void errorOccured(SimpleChannelInboundHandler source, Throwable cause);
}
Then you would need to provide support for the listener...
public class DatagramClientHandler extends SimpleChannelInboundHandler<DatagramPacket> {
private static final Logger log = Logger.getLogger(DatagramClientHandler.class.getName());
//private final QuotesGUI gui = new QuotesGUI();
private volatile Channel channel = null;
private List<MessageListener> listeners;
DatagramClientHandler() {
listeners = new ArrayList<MessageListener>(25);
//...
}
public synchronized void addMessageListener(MessageListener listener) {
listeners.add(listener);
}
public synchronized void removeMessageListener(MessageListener listener) {
listeners.remove(listener);
}
protected synchronized void fireQuoteRecieved(String quote) {
for (MessageListener listener : listeners) {
listener.quoteRecieved(this, quote);
}
}
#Override
public void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
String response = msg.content().toString(CharsetUtil.UTF_8);
log.info(response);
fireQuoteRecieved(response);
}
//...etc...
Now, when you want to receive notifications, you would register an instance of MessageListener with an instance of DatagramClientHandler...
The problem you will have, is ensuring that any updates you make to the UI are carried out in the EDT correctly...
//...
public void quoteRecieved(SimpleChannelInboundHandler source, final String quote) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
text.setText(packet);
}
});
}
Now, if you really wanted to, you could further decouple the code with another interface...
public interface QuoteFactory {
public synchronized void addMessageListener(MessageListener listener);
public synchronized void removeMessageListener(MessageListener listener);
}
This would then be implemented by DatagramClientHandler and you UI would require an instance of QuoteFactory to be passed to it so that it could register interest in been notified when something happens...
I've looked at the java tutorials online and they all seem concerned with catching ActionEvents given out by other components that are already written. Is it possible to write your own objects that have there own set of criteria that trigger actionEvents that can then be caught by other classes that have registered as listeners?
So for example: If I wanted an object that was counting sheep to send out an actionEvent when 100 sheep had been counted to all the sleeper objects that had registered as listeners.
Is there a way to do this are there any tutorials online?
Any help is greatly appreciated.
Yes, it's pretty straightforward, once someone shows you how to create your own listeners.
First, you create your own EventObject. Here's an example from one of my projects.
import gov.bop.rabid.datahandler.bean.InmateDataBean;
import java.util.EventObject;
public class InmatePhotoEventObject extends EventObject {
private static final long serialVersionUID = 1L;
protected InmateDataBean inmate;
public InmatePhotoEventObject(Object source) {
super(source);
}
public InmateDataBean getInmate() {
return inmate;
}
public void setInmate(InmateDataBean inmate) {
this.inmate = inmate;
}
}
There's nothing special about this class, other than it extends EventObject. Your constructor is defined by EventObject, but you can create any methods you want.
Second, you define an EventListener interface.
public interface EventListener {
public void handleEvent(InmatePhotoEventObject eo);
}
You would use the EventObject you created. You can use any method name or names that you want. This is the interface for the code that will be written as a response to the listener.
Third, you write a ListenerHandler. Here's mine from the same project.
import gov.bop.rabid.datahandler.bean.InmateDataBean;
import gov.bop.rabid.datahandler.main.EventListener;
import gov.bop.rabid.datahandler.main.InmatePhotoEventListener;
import gov.bop.rabid.datahandler.main.InmatePhotoEventObject;
import java.util.ArrayList;
import java.util.List;
public class InmatePhotoListenerHandler {
protected List<EventListener> listeners;
public InmatePhotoListenerHandler() {
listeners = new ArrayList<EventListener>();
}
public void addListener(EventListener listener) {
listeners.add(listener);
}
public void removeListener(EventListener listener) {
for (int i = listeners.size() - 1; i >= 0; i--) {
EventListener instance = listeners.get(i);
if (instance.equals(listener)) {
listeners.remove(i);
}
}
}
public void fireEvent(final InmatePhotoEventObject eo,
final InmateDataBean inmate) {
for (int i = 0; i < listeners.size(); i++) {
final EventListener instance = listeners.get(i);
Runnable runnable = new Runnable() {
public void run() {
eo.setInmate(inmate);
instance.handleEvent(eo);
}
};
new Thread(runnable).start();
}
}
public static void main(String[] args) {
System.out.println("This line goes in your DataHandlerMain class "
+ "constructor.");
InmatePhotoListenerHandler handler = new InmatePhotoListenerHandler();
System.out.println("I need you to put the commented method in "
+ "DataHandlerMain so I can use the handler instance.");
// public InmatePhotoListenerHandler getInmatePhotoListenerHandler() {
// return handler;
// }
System.out.println("This line goes in the GUI code.");
handler.addListener(new InmatePhotoEventListener());
System.out.println("Later, when you've received the response from "
+ "the web service...");
InmateDataBean inmate = new InmateDataBean();
inmate.setIntKey(23);
handler.fireEvent(new InmatePhotoEventObject(handler), inmate);
}
}
The main method in this class shows you how you use a ListenerHandler. The rest of the methods in the class are standard. You would use your own EventObject and EventListener.
Yes.
I suggest you look at the java API documentation for ActionEvent and EventListenerList.
I also suggest that you read about the Listener (also called Observer) pattern.