in that it delays serialization until the message is requested. This should
fix the issue where the initial server messages would sometimes fail to
deserialize because the serialization registration message hadn't been processed
before the other messages in the block were deserialized. Now each message will
be deserialized as it is encountered.
In the end the fix was simple enough that it's probably only a 2-3 line change to
MessageProtocol in JME 3.2 if someone wants to back port the fix. (Just queue
up ByteBuffer instead of Message.)
interface and strictly implement the to/from ByteBuffer protocol. In the future
this will allow the message protocol to be swappable. Also moved out the buffering
aspect since often buffers need to be created 'on the fly' and are separate from
the protocol. This will allow me to play with fixing the issue related to messages
being deserialized before the serialization registry message has been processed by
swapping out the GreedyMessageBuffer implementation for a LazyMessageBuffer implementation.
Added some gating to the endpoint closure to try to delay
some NPE I'm seeing caused be a race condition somewhere when
client and server in the same VM and shutting down at the same
time.
Closing the underlying kernel didn't wake up any readers
and so the read() operation in the adapter was never returning.
KernelAdapter.close() was modified to call join() after closing
the underlying kernel so that the method won't complete until
all of the threads are done.
Then the kernels were modified to wakeup the readers (and this
is now standardized in a base class method) so that they don't
hang forever.
but it could catch the user unaware for any registered non-leaf
class. When a class has a field of a specific final type then
the type information is implicit in the outer object... and
the proper serialization information can be written. It is then
sometimes useful to register a generic serializer for something
like Enum to catch these cases. However, there are many times where
the type cannot be implicitly known and so must be specifically
registered. Prior to this fix, having a generic Enum serializer
registered would cause problems on the reading end because it
wouldn't know what real enum class to use.
Now we catch the issue on the write side where enough information
is known to properly report this to the user. Only a few Bothans
died to bring this info.
In one case, closing a client while it was already closing on
another thread (say because the server is shutting down and
you are exiting at the same time) would cause an NPE if you
caught it just right. Now the thing checking and setting the
connection state is synchronized to avoid the race.
The other more subtle one was caused by sending out the 'connected'
event before the services were all started. It's quite common for
application code to start doing stuff when the 'connected' event comes
through like sending messages and stuff. If the services hadn't been
fully started then even the serializers might not be registered yet...
and that = bad.
Now the client doesn't send the 'connected' event until the services
are started. This should be safe and one could argue that it's more
'correct' but there is some small chance that it screws up certain
use-cases. However, if a real use-case comes up that's not solved by
a service then we can always add some kind of prestarted event.
used to either watch traffic or debug serialization
issues, etc..
Went ahead and instrumented the service manager while
I was at it... and fixed a potential NPE in the AbstractService's
toString() method.
Fixed a bug in the DefaultClient where it couldn't be shutdown
if attempted before the services had been started.
a client are running on the same instance. This should cover
99% of the cases where this would come up... and the others
can't really use this service anyway and so must disable it.
already registered. This avoids one of the issues of a client
running in the same JVM as a server that already registered these
classes. This was the easy fix.
the autohost, start/stopHostingOnConnection support built
into it. This is a very common things for connection based
services and I got tired of cutting/pasting it all the time.
RpcHostedService was modified to extend this base class
instead of the more basic one.
exception. Two things can cause bad reads:
1) bad data in the stream... in which the extra info
is useless or confusing.
2) unregistered classes or bad timing, either way,
knowing the message type ID might be useful.
the channels even though the negative channels would
pass through as the default channels just fine.
The key is avoiding UDP calls... they will get
translated into a regular send.
in "read only" mode. Modified the SerializerRegistrationMessage
to put the serializer registry into read only after
it compiles the message so that the server won't accidentally
register messages after they've been compiled.
info message to indicate that all of the local hosted
services have been notified about the new connection.
Modified DefaultClient to wait to start its services
until it has seen this second message.
Client services may want to send things to the server during
their start() method but it's important that things like
the serializer registry service has already processed its
messages or any sends might fail. The client generally has
the luxury of being able to register handlers/listeners/etc
during initialize where as the server must do this when the
connection arrives. So it seems reasonable to delay client
service start() until all of the server-side hosted services
have had a chance to initialize themselves.