Commit Graph

1457 Commits

Author SHA1 Message Date
Trustin Lee
63a02fc04e Revamp DNS codec
Motivation:

There are various known issues in netty-codec-dns:

- Message types are not interfaces, which can make it difficult for a
  user to implement his/her own message implementation.
- Some class names and field names do not match with the terms in the
  RFC.
- The support for decoding a DNS record was limited. A user had to
  encode and decode by him/herself.
- The separation of DnsHeader from DnsMessage was unnecessary, although
  it is fine conceptually.
- Buffer leak caused by DnsMessage was difficult to analyze, because the
  leak detector tracks down the underlying ByteBuf rather than the
  DnsMessage itself.
- DnsMessage assumes DNS-over-UDP.
- To send an EDNS message, a user have to create a new DNS record class
  instance unnecessarily.

Modifications:

- Make all message types interfaces and add default implementations
- Rename some classes, properties, and constants to match the RFCs
  - DnsResource -> DnsRecord
  - DnsType -> DnsRecordType
  - and many more
- Remove DnsClass and use an integer to support EDNS better
- Add DnsRecordEncoder/DnsRecordDecoder and their default
  implementations
  - DnsRecord does not require RDATA to be ByteBuf anymore.
  - Add DnsRawRecord as the catch-all record type
- Merge DnsHeader into DnsMessage
- Make ResourceLeakDetector track AbstractDnsMessage
- Remove DnsMessage.sender/recipient properties
  - Wrap DnsMessage with AddressedEnvelope
  - Add DatagramDnsQuest and DatagramDnsResponse for ease of use
  - Rename DnsQueryEncoder to DatagramDnsQueryEncoder
  - Rename DnsResponseDecoder to DatagramDnsResponseDecoder
- Miscellaneous changes
  - Add StringUtil.TAB

Result:

- Cleaner APi
- Can support DNS-over-TCP more easily in the future
- Reduced memory footprint in the default DnsQuery/Response
  implementations
- Better leak tracking for DnsMessages
- Possibility to introduce new DnsRecord types in the future and provide
  full record encoder/decoder implementation.
- No unnecessary instantiation for an EDNS pseudo resource record
2015-05-01 11:33:16 +09:00
Norman Maurer
56c98839c3 [#3218] Add ChannelPool / ChannelPoolMap abstraction and implementations
Motivation:

Many projects need some kind a Channel/Connection pool implementation. While the protocols are different many things can be shared, so we should provide a generic API and implementation.

Modifications:

Add ChannelPool / ChannelPoolMap API and implementations.

Result:

Reusable / Generic pool implementation that users can use.
2015-04-30 12:13:19 +02:00
Norman Maurer
1cce998bb0 [#3662] Fail the connect future on close
Motivation:

Because of a bug we missed to fail the connect future when doClose() is called. This can lead to a future which is never notified and so may lead to deadlocks in user-programs.

Modifications:

Correctly fail the connect future when doClose() is called and the connection was not established yet.

Result:

Connect future is always notified.
2015-04-27 20:05:00 +02:00
Norman Maurer
ccde870b38 Revert "Ensure channelReadComplete() is called only when necessary"
This reverts commit 27a25e29f7.
2015-04-20 09:10:41 +02:00
Norman Maurer
bb692816d2 Revert "Do not suppress channelReadComplete() when a handler was just added"
This reverts commit 720faa4df1.
2015-04-20 09:10:29 +02:00
Norman Maurer
a123c495e1 Revert "Add another test case for channelReadComplete() suppression"
This reverts commit daa04cb4f1.
2015-04-20 09:10:14 +02:00
Norman Maurer
b4b14ea19f Ensure backward-compability with 4.0
Motivation:

Each different *ChannelOption did extend ChannelOption in 4.0, which we changed in 4.1. This is a breaking change in terms of the API so we need to ensure we keep the old hierarchy.

Modifications:

- Let all *ChannelOption extend ChannelOption
- Add back constructor and mark it as @deprecated

Result:

No API breakage between 4.0 and 4.1
2015-04-19 13:25:42 +02:00
Pierre DAL-PRA
2ebf07e622 Small typos fixes in Channel's Javadoc 2015-03-21 16:10:24 +01:00
Wouter
baa5990e34 Fix typo in javadoc 2015-03-18 17:15:17 +01:00
Trustin Lee
068132f1ea Fix SocketException in NioSocketChannelUnsafe.closeExecutor()
Related: #3464

Motivation:

When a connection attempt is failed,
NioSocketChannelUnsafe.closeExecutor() triggers a SocketException,
suppressing the channelUnregistered() event.

Modification:

Do not attempt to get SO_LINGER value when a socket is not open yet.

Result:

One less bug
2015-03-05 15:18:10 +09:00
Norman Maurer
fce0989844 [maven-release-plugin] prepare for next development iteration 2015-03-03 02:06:47 -05:00
Norman Maurer
ca3b1bc4b7 [maven-release-plugin] prepare release netty-4.1.0.Beta4 2015-03-03 02:05:52 -05:00
jongyoon lim
e3edec25c5 typo_fix in the comment 2015-02-25 20:30:40 +01:00
Norman Maurer
a709553819 Allow to use EmbeddedChannel.schedule*(...)
Motivation:

At the moment when EmbeddedChannel is used and a ChannelHandler tries to schedule and task it will throw an UnsupportedOperationException. This makes it impossible to test these handlers or even reuse them with EmbeddedChannel.

Modifications:

- Factor out reusable scheduling code into AbstractSchedulingEventExecutor
- Let EmbeddedEventLoop and SingleThreadEventExecutor extend AbstractSchedulingEventExecutor
- add EmbbededChannel.runScheduledPendingTasks() which allows to run all scheduled tasks that are ready

Result:

Embeddedchannel is now usable even with ChannelHandler that try to schedule tasks.
2015-02-20 11:57:32 +01:00
jongyoon lim
1e09409604 removed an unnecessary local variable for consistency 2015-02-18 20:10:42 +01:00
Norman Maurer
99bd43ed51 Allow to get existing ChannelOption / AttributeKey from String
Motivation:

We should allow to get a ChannelOption/AttributeKey from a String. This will make it a lot easier to make use of configuration files in applications.

Modifications:

- Add exists(...), newInstance(...) method to ChannelOption and AttributeKey and alter valueOf(...) to return an existing instance for a String or create one.
- Add unit tests.

Result:

Much more flexible usage of ChannelOption and AttributeKey.
2015-02-18 09:29:37 +01:00
Norman Maurer
6e508e60e7 Move generic unix classes/interfaces out of epoll package
Motivation:

As we plan to have other native transports soon (like a kqueue transport) we should move unix classes/interfaces out of the epoll package so we
introduce other implementations without breaking stuff before the next stable release.

Modifications:

Create a new io.netty.channel.unix package and move stuff over there.

Result:

Possible to introduce other native impls beside epoll.
2015-02-17 17:56:01 +01:00
Cristian
4b83eee133 Fixed ServerBootstrap#group(EventLoopGroup, EventLoopGroup) javadoc 2015-02-16 11:21:50 +01:00
Trustin Lee
daa04cb4f1 Add another test case for channelReadComplete() suppression
Related commit:
- a41b46ff43

Motivation:

We need a test case for the commit above.
2015-02-09 16:29:21 +09:00
Norman Maurer
a69fbaeed9 Not execute shutdownOutput(...) and close(...) in the EventLoop if SO_LINGER is used.
Motivation:

If SO_LINGER is used shutdownOutput() and close() syscalls will block until either all data was send or until the timeout exceed. This is a problem when we try to execute them on the EventLoop as this means the EventLoop may be blocked and so can not process any other I/O.

Modifications:

- Add AbstractUnsafe.closeExecutor() which returns null by default and use this Executor for close if not null.
- Override the closeExecutor() in NioSocketChannel and EpollSocketChannel and return GlobalEventExecutor.INSTANCE if getSoLinger() > 0
- use closeExecutor() in shutdownInput(...) in NioSocketChannel and EpollSocketChannel

 Result:

No more blocking of the EventLoop if SO_LINGER is used and shutdownOutput() or close() is called.
2015-02-08 20:15:47 +01:00
Trustin Lee
a1efd1871b Reorder PlatformDependent.isRoot() check
Motivation:

isRoot() is an expensive operation. We should avoid calling it if
possible.

Modifications:

Move the isRoot() checks to the end of the 'if' block, so that isRoot()
is evaluated only when really necessary.

Result:

isRoot() is evaluated only when SO_BROADCAST is set and the bind address
is anylocal address.
2015-02-08 12:00:16 +09:00
Trustin Lee
720faa4df1 Do not suppress channelReadComplete() when a handler was just added
Related:
- 27a25e29f7

Motivation:

The commit mentioned above introduced a regression where
channelReadComplete() event is swallowed by a handler which was added
dynamically.

Modifications:

Do not suppress channelReadComplete() if the current handler's
channelRead() method was not invoked at all, so that a just-added
handler does not suppress channelReadComplete().

Result:

Regression is gone, and channelReadComplete() is invoked when necessary.
2015-02-07 23:09:07 +09:00
Trustin Lee
27a25e29f7 Ensure channelReadComplete() is called only when necessary
Motivation:

Even if a handler called ctx.fireChannelReadComplete(), the next handler
should not get its channelReadComplete() invoked if fireChannelRead()
was not invoked before.

Modifications:

- Ensure channelReadComplete() is invoked only when the handler of the
  current context actually produced a message, because otherwise there's
  no point of triggering channelReadComplete().
  i.e. channelReadComplete() must follow channelRead().
- Fix a bug where ctx.read() was not called if the handler of the
  current context did not produce any message, making the connection
  stall. Read the new comment for more information.

Result:

- channelReadComplete() is invoked only when it makes sense.
- No stale connection
2015-02-07 16:13:56 +09:00
Norman Maurer
abf7afca76 [#3367] Fix re-entrance bug in PendingWriteQueue
Motivation:

Because of a re-entrance bug in PendingWriteQueue it was possible to get the queue corrupted and also trigger an IllegalStateException caused by multiple recycling of the internal PendingWrite objects.

Modifications:

- Correctly guard against re-entrance

Result:

No more IllegalStateException possible
2015-02-06 19:49:34 +01:00
Norman Maurer
3030b4afe3 Allow to recv and send file descriptors when using EpollDomainSocketChannel.
Motiviation:

When using domain sockets on linux it is supported to recv and send file descriptors. This can be used to pass around for example sockets.

Modifications:
- Add support for recv and send file descriptors when using EpollDomainSocketChannel.
- Allow to obtain the file descriptor for an Epoll*Channel so it can be send via domain sockets.

Result:
recv and send of file descriptors is supported now.
2015-02-04 20:07:43 +01:00
Marco Craveiro
6d07264412 Minor idiomatic changes to java docs 2015-02-04 08:28:29 +01:00
haohao
4bafb4f95b [#3368] Ensure ByteBuf is not release two times
Motivation:

As the ByteBuf is not set to null after release it we may try to release it again in handleReadException()

Modifications:

-  set ByteBuf to null to avoid another byteBuf.release() to be called in handleReadException()

Result:

No IllegalReferenceCountException anymore
2015-01-29 18:26:08 +01:00
ysammy
0bc0851569 Fix documentation for ChannelHandlerContext#fireChannelReadComplete
Motivation:
Fix a minor documentation bug in
ChannelHandlerContext#fireChannelReadComplete.

Modifications:
ChannelHandlerContext#fireChannelReadComplete no longer references an
incorrect method in its javadoc.

Results:
Documentation is correct.
2014-12-12 18:43:06 +01:00
Norman Maurer
12c9ce7f67 Allow to lazy create a DefaultFileRegion from a File
Motivation:

We only provided a constructor in DefaultFileRegion that takes a FileChannel which means the File itself needs to get opened on construction. This has the problem that if you want to write a lot of Files very fast you may end up with may open FD's even if they are not needed yet. This can lead to hit the open FD limit of the OS.

Modifications:

Add a new constructor to DefaultFileRegion which allows to construct it from a File. The FileChannel will only be obtained when transferTo(...) is called or the DefaultFileRegion is explicit open'ed via open() (this is needed for the native epoll transport)

Result:

Less resource usage when writing a lot of DefaultFileRegion.
2014-12-11 12:04:17 +01:00
Trustin Lee
298e7af647 Trigger channelWritabilityChanged() later to avoid reentrance
Related: #3212

Motivation:

When SslHandler and ChunkedWriteHandler exists in a pipeline together,
it is possible that ChunkedWriteHandler.channelWritabilityChanged()
invokes SslHandler.flush() and vice versa. Because they can feed each
other (i.e. ChunkedWriteHandler.channelWritabilityChanged() ->
SslHandler.flush() -> ChunkedWriteHandler.channelWritabilityChanged() ->
..), they can fall into an inconsistent state due to reentrance (e.g.
bad MAC record at the remote peer due to incorrect ordering.)

Modifications:

- Trigger channelWritabilityChanged() using EventLoop.execute() when
  there's a chance where channelWritabilityChanged() can cause a
  reentrance issue
- Fix test failures caused by the modification

Result:

Fix the handler reentrance issues related with a
channelWritabilityChanged() event
2014-12-10 18:40:26 +09:00
Trustin Lee
3957a88a94 Make PendingWriteQueue.recycle() update its state before triggering an event
Related: #3212

Motivation:

PendingWriteQueue.recycle() updates its data structure after triggering
a channelWritabilityChanged() event. It causes a rare corruption such as
double free when channelWritabilityChanged() method accesses the
PendingWriteQueue.

Modifications:

Update the state of PendingWriteQueue before triggering an event.

Result:

Fix a rare double-free problem
2014-12-07 23:27:33 +09:00
Trustin Lee
1ef6f14734 Trigger exceptionCaught() when VoidChannelPromise fails
Related: #3190

Motivation:

When an outbound handler method raises an exception, its promise is
marked as failed.  If the promise is done already, the exception is
logged.

When the promise is void, exceptionCaught() must be triggered to notify
a user. However, ChannelHandlerInvokerUtil simply swallows it.

Modifications:

Do not swallow an exception when the promise is void.

Result:

A user who uses a void promise for an outbound operation will be
notified on failure.
2014-12-07 16:06:55 +09:00
Trustin Lee
3dbca4a9e2 Fire channelRead() event immediately in OIO message channels
Related: #3189

Motivation:

OIO transport implementations block for at most 1 second to wait for
additional messages (or accepted connections).

However, because AbstractOioMessageChannel defers the channelRead()
events for the messages read so far until there's nothing to read up to
maxMessagesPerRead, any read operation will be followed by a 1-second
delay.

Modifications:

Fire channelRead() events as soon as doRead() returns so that there is
no 1 second delay between the actual read and the channelRead() event.

Result:

No more weird 1-second delay
2014-12-07 12:13:26 +09:00
Trustin Lee
46576fd2ff Fix a race condition where handler is removed before unregistration
Related: #3156

Motivation:

Let's say we have a channel with the following pipeline configuration:

  HEAD --> [E1] H1 --> [E2] H2 --> TAIL

when the channel is deregistered, the channelUnregistered() methods of
H1 and H2 will be invoked from the executor thread of E1 and E2
respectively. To ensure that the channelUnregistered() methods are
invoked from the correct thread, new one-time tasks will be created
accordingly and be scheduled via Executor.execute(Runnable).

As soon as the one-time tasks are scheduled,
DefaultChannelPipeline.fireChannelUnregistered() will start to remove
all handlers from the pipeline via teardownAll(). This process is
performed in reversed order of event propagation. i.e. H2 is removed
first, and then H1 is removed.

If the channelUnregistered() event has been passed to H2 before H2 is
removed, a user does not see any problem.

If H2 has been removed before channelUnregistered() event is passed to
H2, a user will often see the following confusing warning message:

  An exceptionCaught() event was fired, and it reached at the tail of
  the pipeline. It usually means the last handler in the pipeline did
  not handle the exception.

Modifications:

To ensure that the handlers are removed *after* all events are
propagated, traverse the pipeline in ascending order before performing
the actual removal.

Result:

A user does not get the confusing warning message anymore.
2014-12-05 16:09:00 +09:00
Trustin Lee
46ef370ee1 Copy the resolver configuration when cloning Bootstrap
Motivation:

Bootstrap.clone() does not copy the resolver configuration.

Modifications:

Copy the resolver configuration when cloning.

Result:

Bug fixed
2014-12-01 19:50:12 +09:00
Frank Barber
f4d3f81d6c Prevent channel re-registration from firing channelActive
Motivation:

AbstractUnsafe considers two possibilities during channel registration. First,
the channel may be an outgoing connection, in which case it will be registered
before becoming active. Second, the channel may be an incoming connection in,
which case the channel will already be active when it is registered. To handle
the second case, AbstractUnsafe checks if the channel is active after
registration and calls ChannelPipeline.fireChannelActive() if so.  However, if
an active channel is deregistered and then re-registered this logic causes a
second fireChannelActive() to be invoked. This is unexpected; it is reasonable
for handlers to assume that this method will only be invoked once per channel.

Modifications:

This change introduces a flag into AbstractUnsafe to recognize if this is the
first or a subsequent registration. ChannelPipeline.fireChannelActive() is only
possible for the first registration.

Result:

ChannelPipeline.fireChannelActive() is only called once.
2014-11-30 19:51:30 +01:00
Ronald Chen
e1273147fa replaced broken &lt with < and same for gt 2014-11-29 19:33:50 +01:00
Idel Pivnitskiy
35db3c6710 Small performance improvements
Motivation:

Found performance issues via FindBugs and PMD.

Modifications:

- Removed unnecessary boxing/unboxing operations in DefaultTextHeaders.convertToInt(CharSequence) and DefaultTextHeaders.convertToLong(CharSequence). A boxed primitive is created from a string, just to extract the unboxed primitive value.
- Added a static modifier for DefaultHttp2Connection.ParentChangedEvent class. This class is an inner class, but does not use its embedded reference to the object which created it. This reference makes the instances of the class larger, and may keep the reference to the creator object alive longer than necessary.
- Added a static compiled Pattern to avoid compile it each time it is used when we need to replace some part of authority.
- Improved using of StringBuilders.

Result:

Performance improvements.
2014-11-20 00:10:06 -05:00
Sam Young
9ba3126bd4 Add generic versions of PromiseAggregator and PromiseNotifier.
Motivation:

ChannelPromiseAggregator and ChannelPromiseNotifiers only allow
consumers to work with Channels as the result type. Generic versions
of these classes allow consumers to aggregate or broadcast the results
of an asynchronous execution with other result types.

Modifications:

Add PromiseAggregator and PromiseNotifier. Add unit tests for both.
Remove code in ChannelPromiseAggregator and ChannelPromiseNotifier and
modify them to extend the new base classes.

Result:

Consumers can now aggregate or broadcast the results of an asynchronous
execution with results types other than Channel.
2014-11-07 09:06:58 +01:00
Trustin Lee
9826d9bc1a Fix compilation errors in ChannelOutboundBufferTest 2014-10-25 16:57:22 +09:00
Trustin Lee
d59629377c Implement user-defined writability flags
Related: #2945

Motivation:

Some special handlers such as TrafficShapingHandler need to override the
writability of a Channel to throttle the outbound traffic.

Modifications:

Add a new indexed property called 'user-defined writability flag' to
ChannelOutboundBuffer so that a handler can override the writability of
a Channel easily.

Result:

A handler can override the writability of a Channel using an unsafe API.
For example:

  Channel ch = ...;
  ch.unsafe().outboundBuffer().setUserDefinedWritability(1, false);
2014-10-25 15:59:13 +09:00
Trustin Lee
7ddc8a445c Make Bootstrap and ServerBootstrap fully overridable
Related: #2034

Motivation:

Some users want to mock Bootstrap (or ServerBootstrap), and thus they
should not be final but be fully overridable and extensible.

Modifications:

Remove finals wherever possible

Result:

@daschl is happy.
2014-10-17 16:17:42 +09:00
Trustin Lee
f3a2c22738 Fix an infinite loop when writing a zero-length FileRegion
Related: #2964

Motivation:

Writing a zero-length FileRegion to an NIO channel will lead to an
infinite loop.

Modification:

- Do not write a zero-length FileRegion by protecting with proper 'if'.
- Update the testsuite

Result:

Another bug fixed
2014-10-17 16:06:51 +09:00
Trustin Lee
e848066cab Name resolver API and DNS-based name resolver
Motivation:

So far, we relied on the domain name resolution mechanism provided by
JDK.  It served its purpose very well, but had the following
shortcomings:

- Domain name resolution is performed in a blocking manner.
  This becomes a problem when a user has to connect to thousands of
  different hosts. e.g. web crawlers
- It is impossible to employ an alternative cache/retry policy.
  e.g. lower/upper bound in TTL, round-robin
- It is impossible to employ an alternative name resolution mechanism.
  e.g. Zookeeper-based name resolver

Modification:

- Add the resolver API in the new module: netty-resolver
- Implement the DNS-based resolver: netty-resolver-dns
  .. which uses netty-codec-dns
- Make ChannelFactory reusable because it's now used by
  io.netty.bootstrap, io.netty.resolver.dns, and potentially by other
  modules in the future
  - Move ChannelFactory from io.netty.bootstrap to io.netty.channel
  - Deprecate the old ChannelFactory
  - Add ReflectiveChannelFactory

Result:

It is trivial to resolve a large number of domain names asynchronously.
2014-10-16 17:05:20 +09:00
Trustin Lee
87c82d4845 Do not consider PortUnreachableException to require channel closure
Motivation:

When a datagram packet is sent to a destination where nobody actually listens to,
the server O/S will respond with an ICMP Port Unreachable packet.
The ICMP Port Unreachable packet is translated into PortUnreachableException by JDK.
PortUnreachableException is not a harmful exception that prevents a user from sending a datagram.
Therefore, we should not close a datagram channel when PortUnreachableException is caught.

Modifications:

- Do not close a channel when the caught exception is PortUnreachableException.

Result:

A datagram channel is not closed unexpectedly anymore.
2014-10-16 17:04:25 +09:00
Trustin Lee
f8349f8dc5 Add AbstractUnsafe.annotateConnectException()
Motivation:

JDK's exception messages triggered by a connection attempt failure do
not contain the related remote address in its message.  We currently
append the remote address to ConnectException's message, but I found
that we need to cover more exception types such as SocketException.

Modifications:

- Add AbstractUnsafe.annotateConnectException() to de-duplicate the
  code that appends the remote address

Result:

- Less duplication
- A transport implementor can annotate connection attempt failure
  message more easily
2014-10-14 12:29:08 +09:00
Trustin Lee
fe05b6e514 Auto-generate the handler name when null is specified as a name
Motivation:

There's no way to generate the name of a handler being newly added
automatically and reliably.

For example, let's say you have a routine that adds a set of handlers to
a pipeline using addBefore() or addAfter().  Because addBefore() and
addAfter() always require non-conflicting non-null handler name, making
the multiple invocation of the routine on the same pipeline is
non-trivial.

Modifications:

- If a user specifies null as the name of the new handler,
  DefaultChannelPipeline generates one.
- Update the documentation of ChannelPipeline to match the new behavior

Result:

A user doesn't need to worry about name conflicts anymore.
2014-10-14 12:29:08 +09:00
Luke Wood
a64484249c Access autoRead via an AtomicIntegerFieldUpdater.
Motiviation:

Before this change, autoRead was a volatile boolean accessed directly.  Any thread that invoked the DefaultChannelConfig#setAutoRead(boolean) method would read the current value of autoRead, and then set a new value.  If the old value did not match the new value, some action would be immediately taken as part of the same method call.

As volatile only provides happens-before consistency, there was no guarantee that the calling thread was actually the thread mutating the state of the autoRead variable (such that it should be the one to invoke the follow-up actions).  For example, with 3 threads:
 * Thread 1: get = false
 * Thread 1: set = true
 * Thread 1: invokes read()
 * Thread 2: get = true
 * Thread 3: get = true
 * Thread 2: set = false
 * Thread 2: invokes autoReadCleared()
 * Event Loop receives notification from the Selector that data is available, but as autoRead has been cleared, cancels the operation and removes read interest
 * Thread 3: set = true

This results in a livelock - autoRead is set true, but no reads will happen even if data is available (as readyOps).  The only way around this livelock currently is to set autoRead to false, and then back to true.

Modifications:

Write access to the autoRead variable is now made using the getAndSet() method of an AtomicIntegerFieldUpdater, AUTOREAD_UPDATER.  This also changed the type of the underlying autoRead variable to be an integer, as no AtomicBooleanFieldUpdater class exists.  Boolean logic is retained by assuming that 1 is true and 0 is false.

Result:

There is no longer a race condition between retrieving the old value of the autoRead variable and setting a new value.
2014-10-13 15:15:58 +02:00
Norman Maurer
246b65c6b6 [#2586] Use correct EventExecutor to notify for bind failures on late registration
Motivation:

We used the wrong EventExecutor to notify for bind failures if a late registration was done.

Modifications:

Use the correct EventExecutor to notify and only use the GlobelEventExecutor if the registration fails itself.

Result:

The correct Thread will do the notification.
2014-08-20 16:34:57 +02:00
Trustin Lee
b5f61d0de5 [maven-release-plugin] prepare for next development iteration 2014-08-16 03:27:42 +09:00