public class Quadric3fIntersection
extends java.lang.Object
Quadric objects have a Quadric3f.isInside(Point3f)
Method that can be used to determine whether a point is inside
or outside the object. This would typically be used only for the camera to decide whether it has been moved into an object.
Otherwise, the logic of your call to the Quadric3f.getIntersection(Quadric3fIntersection,
Line3f, boolean)
should be
used to interpret the intersection:
From this information we can deduce the whether the point is inside (first intersection ray going in, dist 1 negative, dist 2 positive; or first intersection ray going out, dist 1 is positive or dist 2 is negative) or outside (first intersection ray going out, dist 1 negative, dist 2 positive; or first intersection ray going in and, dist 1 is positive or dist 2 is negative).
| Modifier and Type | Field and Description |
|---|---|
static int |
GOING_INTO
The first intersection (at distance m_fDist1) is going into the quadric.
|
static int |
GOING_OUT_OF
The second intersection (at distance m_fDist2) is going out of the quadric.
|
float |
m_fDist1
The smallest distance to the intersection (the first intersection for an infinite directed line) from the origin
of the ray if the code is
GOING_INTO (the only possibility for a
convex quadric); or
GOING_OUT_OF (which happens with
non-convex quadrics).
|
float |
m_fDist2
The largest distance to the intersection (the second intersection for an infinite directed line) from the origin
of the ray if the code is
GOING_INTO or
GOING_OUT_OF . |
Line3f
|
m_int1
The intersection point and associated surface normal
|
int |
m_nCode
The code describing the type of intersection.
|
Quadric3fIntersection |
m_next
A pointer to tne next Quadric3fIntersection in a cache if this object is cached.
|
static int |
NONE_INSIDE
The code for no intersection with the quadric and the ray being inside the quadric.
|
static int |
NONE_OUTSIDE
The code for no intersection with the quadric and the ray being outside the quadric.
|
| Constructor and Description |
|---|
Quadric3fIntersection()
Creates a new uninitialized instance of Quadric3fIntersection.
|
public Quadric3fIntersection m_next
public static final int NONE_OUTSIDE
public static final int NONE_INSIDE
public static final int GOING_INTO
public static final int GOING_OUT_OF
public float m_fDist1
GOING_INTO
(the only possibility for a
convex quadric); or
GOING_OUT_OF
(which happens with
non-convex quadrics). Otherwise,
this field is meaningless.
NOTE: if the distance is negative, it implies the intersection is effectively behind the start of the ray. HOWEVER, you should observe the logic of whether the ray starts inside or outside the object as numerical inconsistencies (round off) make it arbitrary whether the start of reflections or refractions from the current object numerically start inside or outside the quadric.
public Line3f m_int1
public float m_fDist2
GOING_INTO
or
GOING_OUT_OF
. Otherwise, this field is
meaningless.
NOTE: if the distance is negative, it implies the intersection is effectively behind the start of the ray. HOWEVER, you should observe the logic of whether the ray starts inside or outside the object as numerical inconsistencies (round off) make it arbitrary whether the start of reflections or refractions from the current object numerically start inside or outside the quadric.
public int m_nCode


At the lowest layer, the wire transport takes the form of Tokens. These all take the shape of header/type-byte/body.
Tokens are described below as [header-TOKEN-body], where either
header
or body
may be empty. For example, [len-LIST-empty]
indicates that the length is put into the header, LIST
is the token
being used, and the body is empty.
The possible Token types are:
0x80: LIST (old): [len-LIST-empty]
This token marks the beginning of a list with LEN elements. It acts as
the open parenthesis
, and the matching close parenthesis
is
implicit, based upon the length of the list. It will be followed by LEN
things, which may be tokens like INTs or STRINGS, or which may be
sublists. Banana keeps a list stack to handle nested sublists.
This token (and the notion of length-prefixed lists in general) is from oldbanana. In newbanana it is only used during the initial dialect negotiation (so that oldbanana peers can be detected). Newbanana requires that LIST(old) tokens be followed exclusively by strings and have a rather limited allowable length (say, 640 dialects long).
0x81: INT: [value-INT-empty]
This token defines a single positive integer. The protocol defines its range as [0, 2**31), so the largest legal value is 2**31-1. The recipient is responsible for choosing an appropriate local type to hold the number. For Python, if the value represented by the incoming base-128 digits grows larger than a regular Python IntType can accomodate, the receiving system will use a LongType or a BigNum as necessary.
Anything larger than this range must be sent with a LONGINT token instead.
(oldbanana compatibility note: a python implementation can accept anything in the range [0, 2**448), limited by the 64-byte maximum header size).
The range was chosen to allow INT values to always fit in C's s_int32_t type, so an implementation that doesn't have a useful bignum type can simply reject LONGINT tokens.
0x82: STRING [len-STRING-chars]
This token defines a string. To be precise, it defines a sequence of bytes. The length is a base-128-encoded integer. The type byte is followed by LEN bytes of data which make up the string. LEN is required to be shorter than 640k: this is intended to reduce the amount of memory that can be consumed on the receiving end before user code gets to decide whether to accept the data or not.
0x83: NEG: [value-NEG-empty]
This token defines a negative integer. It is identical to the
INT
tag except that the results are negated before storage.
The range is defined as [-2**31, 0), again to make an implementation using
s_int32_t easier. Any numbers smaller (more negative) than this range must
be sent with a LONGNEG token.
Implementations should be tolerant when receiving a negative zero
and turn it into a 0, even though they should not send such things.
Note that NEG can represent a number (-2**31) whose absolute value (2**31) is one larger than the greatest number that INT can represent (2**31-1).
0x84: FLOAT [empty-FLOAT-value]
This token defines a floating-point number. There is no header, and the
type byte is followed by 8 bytes which are a 64-bit IEEE double
, as
defined by struct.pack("!d", num)
.
0x85: OLDLONGINT: [value-OLDLONGINT-empty]
0x86: OLDLONGNEG: [value-OLDLONGNEG-empty]
These were used by oldbanana to represent large numbers. Their size was limited by the number of bytes in the header (max 64), so they can represent [0, 2**448).
0x87: VOCAB: [index-VOCAB-empty]
This defines a tokenized string. Banana keeps a mapping of common strings, each one is assigned a small integer. These strings can be sent compressed as a two-byte (index, VOCAB) sequence. They are delivered to Jelly as plain strings with no indication that they were compressed for transit.
The strings in this mapping are populated by the sender when it sends a
special vocab
OPEN sequence. The intention is that this mapping
will be sent just once when the connection is first established, but a
sufficiently ambituous sender could use this to implement adaptive forward
compression.
0x88: OPEN: [[num]-OPEN-empty]
0x89: CLOSE: [[num]-CLOSE-empty]
These tokens are the newbanana parenthesis markers. They carry an optional number in their header: if present, the number counts the appearance of OPEN tokens in the stream, starting at 0 for the first OPEN used for a given connection and incrementing by 1 for each subsequent OPEN. The matching CLOSE token must contain an identical number. These numbers are solely for debugging and may be omitted. They may be removed from the protocol once development has been completed.
In contrast to oldbanana (with the LIST token), newbanana does not use length-prefixed lists. Instead it relies upon the Banana layer to track OPEN/CLOSE tokens.
OPEN markers are followed by the Open Index
tuple: one or more
tokens to indicate what kind of new sub-expression is being started. The
first token must be a string (either STRING or VOCAB), the rest may be
strings or other primitive tokens. The recipient decides when the Open
Index has finished and the body has begun.
0x8A: ABORT: [[num]-ABORT-empty]
This token indicates that something has gone wrong on the sender side, and that the resulting object must not be handed upwards in the unslicer stack. It may be impossible or inconvenient for the sender to stop sending the tokens associated with the unfortunate object, so the receiver must be prepared to silently drop all further tokens up to the matching STOP marker. The STOP token must always follow eventually: this is just a courtesy notice.
The number, if present, will be the same one used by the OPEN token.
0x8B: LONGINT: [len-LONGINT-bytes]
0x8C: LONGNEG: [len-LONGNEG-bytes]
These are processed like STRING tokens, but the bytes form a base-256 encoded number, most-significant-byte first (note that this may require several passes and some intermediate storage). The size is (barely) limited by the length field, so the theoretical range is [0, 2**(2**(64*7)-1)-1), but the receiver can impose whatever length limit they wish.
LONGNEG is handled exactly like LONGINT but the number is negated first.
0x8D: ERROR [len-ERROR-chars]
This token defines a string of ASCII characters which hold an error message. When a severe protocol violation occurs, the offended side will emit an ERROR token and then close the transport. The side which receives the ERROR token should put the message in a developer-readable logfile and close the transport as well.
The ERROR token is formatted exactly like the STRING token, except that it is defined to be encoded in ASCII (the STRING token does not claim to be encoded in any particular character set, nor does it necessarily represent human-readable characters).
The ERROR token is limited to 1000 characters.
0x8E: PING [[num]-PING-empty]
0x8F: PONG [[num]-PONG-empty]
These tokens have no semantic value, but are used to implement connection timeouts and keepalives. When one side receives a PING message, it should immediately queue a PONG message on the return stream. The optional number can be used to associate a PONG with the PING that prompted it: if present, it must be duplicated in the response.
Other than generating a PONG, these tokens are ignored by both ends. They are not delivered to higher levels. They may appear in the middle of an OPEN sequence without affecting it.
The intended use is that each side is configured with two timers: the idle timer and the disconnect timer. The idle timer specifies how long the inbound connection is allowed to remain quiet before poking it. If no data has been received for this long, a PING is sent to provoke some kind of traffic. The disconnect timer specifies how long the inbound connection is allowed to remain quiet before concluding that the other end is dead and thus terminating the connection.
These messages can also be used to estimate the connection's round-trip time (including the depth of the transmit/receive queues at either end). Just send a PING with a unique number, and measure the time until the corresponding PONG is seen.
TODO: Add TRUE, FALSE, and NONE tokens. (maybe? These are currently handled as OPEN sequences)
When serializing an object, it is useful to view it as a directed graph. The root object is the one you start with, any objects it refers to are children of that root. Those children may point back to other objects that have already been serialized, or which will be serialized later.
Banana, like pickle and other serialization schemes, does a depth-first
traversal of this graph. Serialization is begun on each node before going
down into the child nodes. Banana tracks previously-handled nodes and
replaces them with numbered reference
tokens to break loops in
the graph.
A Banana Slicer is responsible for serializing a single user
object: it slices
that object into a series of smaller pieces, either
fundamental Banana tokens or other Sliceable objects. On the receiving end,
there is a corresponding Banana Unslicer which accepts the incoming
tokens and re-creates the user object. There are different kinds of Slicers
and Unslicers for lists, tuples, dictionaries, etc. Classes can provide
their own Slicers if they want more control over the serialization
process.
In general, there is a Slicer object for each act of serialization of a given object (although this is not strictly necessary). This allows the Slicer to contain state about the serialization process, which enables producer/consumer -style pauses, and slicer-controlled streaming serialization. The entire context is stored in a small tuple (which includes the Slicer), so it can be set aside for a while. In the future, this will allow interleaved serialization of multiple objects (doing context switching on the wire), to do things like priority queues and avoid head-of-line blocking.
The most common pattern is to have the Slicer be the ISlicer
Adapter for the object, in which it gets a new Slicer case each it is
serialized. Classes which do not need to store a lot of state can have a
single Slicer per serialized object, presumably through some adapter tricks.
It is also valid to have the serialized object be its own Slicer.
The Slicer has other duties (described below), but the main one is to
implement the slice
method, which should return a sequence or
an iterable which yields the Open Index Tokens, followed by the body tokens.
(Note that the Slicer should not include the OPEN or CLOSE tokens: those are
supplied by the SendBanana wrapping code). Any item which is a fundamental
type (int, string, float) will be sent as a banana token, anything else will
be handled by recursion (with a new Slicer).
Most subclasses of BaseSlicer
implement a companion method
named sliceBody
, which supplies just the body tokens. (This
makes the code a bit easier to follow). sliceBody
is usually
just a return [token, token]
, or a series of yield
statements, one per token. However, classes which wish to have more control
over the process can implement sliceBody
or even
slice
differently.
class ThingySlicer(slicer.BaseSlicer): opentype = ('thingy',) trackReferences = True def sliceBody(self, streamable, banana): return [self.obj.attr1, self.obj.attr2]
If attr1
and attr2
are integers, the preceding Slicer would
create a token sequence like: OPEN STRING(thingy) 13 16 CLOSE. If
attr2
were actually another Thingy instance, it might produce OPEN
STRING(thingy) 13 OPEN STRING(thingy) 19 18 CLOSE CLOSE.
Doing this with a generator gives the same basic results but avoids the temporary buffer, which can be important when sending large amounts of data. The following Slicer could be combined with a concatenating Unslicer to implement the old FilePager class without the extra round-trip inefficiencies.
class DemandSlicer(slicer.BaseSlicer): opentype = ('demandy',) trackReferences = True def sliceBody(self, streamable, banana): f = open("data", "r") for chunk in f.read(2048): yield chunk
The SendBanana code controls the pacing: if the transport is full, it has the option of pausing the generator until the receiving end has caught up. It also has the option of pulling tokens out of the Slicer anyway, and buffering them in memory. This may be necessary to achieve serialization coherency, discussed below.
If the streamable
flag is set, then the slicer gets to
control the pacing too: it is allowed to yield a Deferred where it would
normally provide a regular token. This tells Banana that serialization needs
to wait for a while (perhaps we are streaming data from another source which
has run dry, or we are trying to implement some kind of rate limiting).
Banana will wait until the Deferred fires before attempting to retrieve
another token. If the streamable
flag is not set, then a
parent Slicer has decided that it is unwilling to allow streaming (perhaps
it needs to serialize a coherent state, and a pause for streaming would
allow that state to change before it was completely serialized). The Slicer
is not allowed to return a Deferred when streaming is disabled.
class URLGetterSlicer(slicer.BaseSlicer): opentype = ('urldata',) trackReferences = True def gotPage(self, page): self.page = page def sliceBody(self, streamable, banana): yield self.url d = web.client.getPage(self.url) d.addCallback(self.gotPage) yield d # here we hover in limbo until it fires yield self.page
(the code is a bit kludgy because generators have no way to pass data
back out of the yield
statement).
The Slicer can also raise a Violation
exception, in which case the
slicer will be aborted: no further tokens will be pulled from it. This
causes an ABORT token to be sent over the wire, followed immediately by a
CLOSE token. The dead Slicer's parent is notified with a
childAborted
method, then the Banana continues to extract
tokens from the parent as if the child had finished normally. (TODO: we need
a convenient way for the parent to indicate that it wishes to give up too,
such as raising a Violation from within childAborted
).
Streaming serialization means the object is serialized a little bit at a time, never consuming too much memory at once. The tradeoff is that, by doing other useful work inbetween, our object may change state while it is being serialized. In oldbanana this process was uninterruptible, so coherency was not an issue. In newbanana it is optional. Some objects may have more trouble with this than others, so Banana provides Slicers with a means to influence the process.
Banana makes certain promises about what takes place between successive
yield
statements, when the Slicer gives up control to Banana. The
most conservative approach is to:
streamableflag to tell all Slicers that they should not return Deferreds: this avoids loss of control due to child Slicers giving it away
slice
(or sliceBody
)
instead of using a generator: this fixes the object contents at a single
point in time. (you can also create a list at the beginning of that
routine and then yield pieces of it, which has exactly the same
effect)Slicers aren't supposed to do anything which changes the state observed by other Slicers: if this is really the case than it is safe to use a generator. A parent Slicer which yields a non-primitive object will give up control to the child Slicer needed to handle that object, but that child should do its business and finish quickly, so there should be no way for the parent object's state to change in the meantime.
If the SendBanana is allowed to give up control (.pauseProducing), then
arbitrary code will get to run in between yield
calls, possibly
changing the state being accessed by those yields. Likewise child Slicers
might give up control, threatening the coherency of one of their parents.
Slicers can invoke banana.inhibitStreaming()
(TODO: need a
better name) to inhibit streaming, which will cause all child serialization
to occur immediately, buffering as much data in memory as necessary to
complete the operation without give up control.
Coherency issues are a new area for Banana, so expect new tools and techniques to be developed which allow the programmer to make sensible tradeoffs.
The serialization context is stored in a SendBanana
object, which
is one of the two halves of the Banana object (a subclass of Protocol). This
holds a stack of Banana Slicers, one per object currently being serialized
(i.e. one per node in the path from the root object to the object currently
being serialized).
For example, suppose a class instance is being serialized, and this class chose to use a dictionary to hold its instance state. That dictionary holds a list of numbers in one of its values. While the list of numbers is being serialized, the Slicer Stack would hold: the RootSlicer, an InstanceSlicer, a DictSlicer, and finally a ListSlicer.
The stack is used to determine two things:
When a new object needs to be sent, it is first submitted to the top-most
Slicer (to its slicerForObject
method), which is responsible
for either returning a suitable Slicer or raising a Violation exception (if
the object is rejected by a security policy). Most Slicers will just
delegate this method up to the RootSlicer, but Slicers which wish to pass
judgement upon enclosed objects (or modify the Slicer selected) can do
something else. Unserializable objects will raise an exception here.
Once the new Slicer is obtained, the OPEN token is emitted, which
provides the openID
number (just an implicit count of how many OPEN
tokens have been sent over the wire). This is where we break cycles in the
object graph: before serializing the object, we record a reference to it
(the openID), and any time we encounter the object again, we send the
reference number instead of a new copy. This reference number is tracked in
the SlicerStack, by handing the number/object pair to the top-most Slicer's
registerReference
method. Most Slicers will delegate this up to
the RootSlicer, but again they can perform additional registrations or
consume the request entirely. This is used in PB to provide scoped
references
, where (for example) a list should be sent twice if
it occurs in two separate method calls. In this case the CallSlicer (which
sits above the PBRootSlicer) does its own registration.
The slicerForObject
process is responsible for catching the
second time the object is sent. It looks in the same mapping created by
registerReference
and returns a ReferenceSlicer
instead of the usual one.
The RootSlicer
, which sits at the bottom of the stack, is a
special case. It is never pushed or popped, and implements most of the
policy for the whole Banana process. The RootSlicer can also be interpreted
as a root object
, if you imagine that any given user object being
serialized is somehow a child of the overall serialization context. In PB,
for example, the root object would be related to the connection and needs to
track things like which remotely-invokable objects are available.
The default RootSlicer implements the following behavior:
.slicerTable
to get a Slicer for an object. If
that fails, adapt the object to ISlicer.references
dictThe RootSlicer
class only does safe
serialization:
basic types and whatever you've registered an ISlicer adapter for. The
TrustingRootSlicer
uses that .slicerTable mapping to serialize
unsafe things (arbitrary instances, classes, etc), which is suitable for
local storage instead of network communication (i.e. when you want to use
banana as a pickle replacement).
TODO: The idea is to let other serialization contexts do other things. For example, the final tokens could go to the parent slice for handling instead of straight to the Protocol, which would provide more control over turning the tokens into bytes and sending over a wire, saving to a file, etc.
Finally, the stack can be queried to find out what path leads from the
root object to the one currently being serialized. If something goes wrong
in the serialization process (an exception is thrown), this path can make it
much easier to find out when the trouble happened, as opposed to
merely where. Knowing that the .oops
method of your FooObject failed
during serialization isn't very useful when you have 500 FooObjects inside
your data structure and you need to know whether it was
bar.thisfoo
or bar.thatfoo
which caused the
problem. To this end, each Slicer has a .describe
method which
is supposed to return a short string that explains how to get to the child
node currently being processed. When an error occurs, these strings are
concatenated together and put into the failure object.
The other half of the Banana class is the ReceiveBanana
,
which accepts incoming tokens and turns them into objects. It is organized
just like the SendBanana
, with a stack of Banana
Unslicer
objects, each of which assembles tokens or child objects into a
larger one. Each Unslicer receives the tokens emitted by the matching Slicer
on the sending side. The whole stack is used to create new Unslicers,
enforce restrictions upon what objects will be accepted, and manage object
references.
Each Unslicer accepts tokens that turn into an object of some sort. They
pass this object up to their parent Unslicer. Eventually a finished object
is given to the RootUnslicer
, which decides what to do with it.
When the Banana is being used for data storage (like pickle), the root will
just deliver the object to the caller. When Banana is used in PB, the actual
work is done by some intermediate objects like the
CallUnslicer
, which is responsible for a single method
invocation.
The ReceiveBanana
itself is responsible for pulling
well-formed tokens off the incoming data stream, tracking OPEN and CLOSE
tokens, maintaining synchronization with the transmitted token stream, and
discarding tokens when the receiving Unslicers have rejected one of the
inbound objects. Unslicer methods may raise Violation exceptions: these are
caught by the Unbanana and cause the object currently being unserialized to
fail: its parent gets a UnbananaFailure instead of the dict or list or
instance that it would normally have received.
OPEN tokens are followed by a short list of tokens called the
opentype
to indicate what kind of object is being started. This is
looked up in the UnbananaRegistry just like object types are looked up in
the BananaRegistry (TODO: need sensible adapter-based registration scheme
for unslicing). The new Unslicer is pushed onto the stack.
ABORT
tokens indicate that something went wrong on the sending
side and that the current object is to be aborted. It causes the receiver to
discard all tokens until the CLOSE token which closes the current node. This
is implemented with a simple counter of how many levels of discarding we
have left to do.
CLOSE
tokens finish the current node. The Unslicer will pass its
completed object up to the receiveChild
method of its parent.
OPEN tokens are followed by an arbitrary list of other tokens which are
used to determine which UnslicerFactory should be invoked to create the new
Unslicer. Basic Python types are designated with a simple string, like (OPEN
list
) or (OPEN dict
), but instances are serialized with two
strings (OPEN instance
classname
), and various exotic PB
objects like method calls may involve a list of strings and numbers (OPEN
call
reqID objID methodname). The unbanana code works with the
unslicer stack to apply constraints to these indexing tokens and finally
obtain the new Unslicer when enough indexing tokens have been received.
The reason for assembling this opentype
list before creating the
Unslicer (instead of using a generic InstanceUnslicer which switches
behavior depending upon its first received token) is to support classes or
PB methods which wish to push custom Unslicers to handle their
deserialization process. For example, a class could push a
StreamingFileUnslicer that accepts a series of string tokens and appends
their contents to a file on disk. This Unslicer could reduce memory
consumption (by only holding one chunk at a time) and update some kind of
progress indicator as the data arrives. This particular feature was provided
by the old StringPager utility, but custom Unslicers offer more flexibility
and better efficiency (no additional round-trips).
(note: none of this affects the serialization side: those Slicers emit both their indexing tokens and their state tokens. It is only the receiving side where the index tokens are handled by a different piece of code than the content tokens).
In yet greater detail:
Index phaseand a
Contents phase. The first one (or two or three) tokens are the Index Tokens and the rest are the Body Tokens. The sequence ends with a CLOSE token.
If .inOpen is True, each new token type will be passed (through Banana.getLimit and top.openerCheckToken) to the opener's .openerCheckToken method, along with the current opentype tuple. The opener gets to decide if the token is acceptable (possibly raising a Violation exception). Note that the opener does not maintain state about what phase the decoding process is in, so it may want to condition its response upon the length of the opentype.
After each index token is complete, it is appended to .opentype, then the list is passed (through Banana.handleOpen, top.doOpen, and top.open) to the opener's .open method. This can either return an Unslicer (which will finish the index phase: all further tokens will be sent to the new Unslicer), return None (to continue the index phase), raise a Violation (which causes an UnbananaFailure to be passed to the current top unslicer), or raise another exception (which causes the connection to be abandoned).
Each Unslicer has access to the following attributes:
.parent
: This is set by the ReceiveBanana before
.start
is invoked, and provides a reference to the Unslicer
responsible for the containing object. You can follow .parent
all the way up the object graph to the single RootUnslicer
object for this connection. It is appropriate to invoke
openerCheckToken
and open
on your parent..protocol
: This is set by the ReceiveBanana before
.start
is invoked, and provides access to the Banana object
which maintains the connection on which this object is being received. It
is appropriate to examine the .debugReceive
attribute on the
protocol. It is also appropriate to invoke .setObject
on it
to register references for shared containers (like lists).openCount
: This is set by the ReceiveBanana before
.start
is invoked, and contains the optional OPEN-count for
this object, an implicit sequence number incremented for each OPEN token
seen on the wire. During protocol development and testing the OPEN tokens
may include an explicit OPEN-count value, but usually it is left out of
the packet. If present, it is used by Banana.handleClose to assert that
the CLOSE token is associated with the right OPEN token. Unslicers will
not normally have a use for it..count
: This is provided as the countargument to
.start
, and contains the object counterfor this object. This is incremented for each new object which is created by the receive Banana code. This is similar to (but not always the same as) the OPEN-count. Containers should call
self.protocol.setObject
to
register a Deferred during start
, then call it again in
receiveClose
with the real (finished) object. It is sometimes
also included in a debug message..broker
: PB objects are given .broker, which is exactly
equal to the .protocol attribute. The synonym exists because it makes
several PB routines easier to read.Each Unslicer handles a single OPEN sequence
, which starts with an
OPEN token and ends with a CLOSE token.
Acceptance of the OPEN token simply sets a flag to indicate that we are
in the Index Phase. (The OPEN token might not be accepted: it is submitted
to checkToken for approval first, as described below). During the Index
Phase, all tokens are appended to the current opentype
list and
handed as a tuple to the top-most Unslicer's doOpen
method.
This method can do one of the following things:
start
method described below. This ends the Index Phase.When a new Unslicer object is pushed on the top of the stack, it has its
.start
method called, in which it has an opportunity to create
whatever internal state is necessary to record the incoming content tokens.
Each created object will have a separate Unslicer instance. The start method
can run normally, or raise a Violation exception.
.start
is distinct from the Unslicer's constructor function
to minimize the parameter-passing requirements for doOpen() and friends. It
is also conceivable that keeping arguments out of __init__
would make it easier to use adapters in this context, although it is not
clear why that might be useful on the Unslicing side. TODO: consider merging
.start
into the constructor.
This Unslicer is responsible for all incoming tokens until either 1: it pushes a new one on the stack, or 2: it receives a CLOSE token.
Each token starts with a length sequence, up to 64 bytes which are turned
into an integer. This is followed by a single type byte, distinguished from
the length bytes by having the high bit set (the type byte is always 0x80 or
greater). When the typebyte is received, the topmost Unslicer is asked about
its suitability by calling the .checkToken
method. (note that
CLOSE and ABORT tokens are always legal, and are not submitted to
checkToken). Both the typebyte and the header's numeric value are passed to
this methoed, which is expected to do one of the following:
Violation
exception to reject the token or the
header value. This will cause the remainder of the current OPEN sequence
to be discarded (all tokens through the matching CLOSE token). Unslicers
should raise this if their constraints will not accept the incoming
object: for example a constraint which is expecting a series of integers
can accept INT/NEG/LONGINT/LONGNEG tokens and reject
OPEN/STRING/VOCAB/FLOAT tokens. They should also raise this if the header
indicates, e.g., a STRING which is longer than the constraint is willing
to accept, or a LONGINT/LONGNEG which is too large. The topmost Unslicer
(the same one which raised Violation) will receive (through its
.receiveChild
method) an UnbananaFailure object which
encapsulates the reason for the rejection If the token sequence is in the index phase
(i.e. it is just after
an OPEN token and a new Unslicer has not yet been pushed), then instead of
.checkToken
the top unslicer is sent
.openerCheckToken
. This method behaves just like checkToken,
but in addition to the type byte it is also given the opentype list (which
is built out of all the index tokens received during this index phase).
If the type byte is accepted, and the size limit is obeyed, then the rest
of the token is read and a finished (primitive) object is created: a string
or number (TODO: maybe add boolean and None). This object is handed to the
topmost Unslicer's .receiveChild
method, where again it is has
a few options:
self.abort
, which does
protocol.abandonUnslicer
If the child is handed an UnbananaFailure object, and it wishes to pass
it upwards to its parent, then self.abort
is the appropriate
thing to do. Raising a Violation will accomplish the same thing, but with a
new UnbananaFailure that describes the exception raised here instead of the
one raised by a child object. It is bad to both call abort
and
raise an exception.
When the CLOSE token arrives, the Unslicer will have its
.receiveClose
method called. This is expected to do:
.receiveChild
of the parent Unslicer.After receiveClose has finished, the child is told to clean up by calling
its .finish
method. This can complete normally or raise a
Violation.
Then, the old top-most Unslicer is popped from the stack and discarded.
Its parent is now the new top-most Unslicer, and the newly-unserialized
object is given to it with the .receiveChild
method. Note that
this method is used to deliver both primitive objects (from raw tokens)
and composite objects (from other Unslicers).
Schemas are enforced by Constraint objects which are given an opportunity
to pass judgement on each incoming token. When they do not like something
they are given, they respond by raising a Violation
exception.
The Violation exception is sometimes created with an argument that describes
the reason for the rejection, but frequently it is just a bare exception.
Most Violations are raised by the checkOpentype
and
checkObject
methods of the various classes in
schema.py
.
Violations which occur in an Unslicer can be confined to a single sub-tree of the object graph. The object being deserialized (and all of its children) is abandoned, and all remaining tokens for that object are discarded. However, the parent object (to which the abandoned object would have been given) gets to decide what happens next: it can either fail itself, or absorb the failure (much like an exception handler can choose to re-raise the exception or eat it).
When a Violation occurs, it is wrapped in an UnbananaFailure
object (just like Deferreds wrap exceptions in Failure objects). The
UnbananaFailure behaves like a regular
twisted.python.failure.Failure
object, except that it has an
attribute named .where
which indicate the object-graph pathname
where the problem occurred.
The Unslicer which caused the Violation is given a chance to do cleanup
or error-reporting by invoking its reportViolation
method. It
is given the UnbananaFailure so it can modify or copy it. The default
implementation simply returns the is expected to return the UnbananaFailure
it was given, but it is also allowed to return a different one. It must
return an UnbananaFailure: it cannot ignore the Violation by returning None.
This method should not raise any exceptions: doing so will cause the
connection to be dropped.
The UnbananaFailure returned by reportViolation
is passed up
the Unslicer stack in lieu of an actual object. Most Unslicers have code in
their receiveChild
methods to detect an UnbananaFailure and
trigger an abort (propagateUnbananaFailures
), which causes all
further tokens of the sub-tree to be discarded. The connection is not
dropped. Unslicers which partition their children's sub-graphs (like the
PBRootUnslicer, for which each child is a separate operation) can simply
ignore the UnbananaFailure, or respond to it by sending an error message to
the other end.
Other exceptions may occur during deserialization. These indicate coding
errors or severe protocol violations and cause the connection to be dropped
(they are not caught by the Banana code and thus propagate all the way up to
the reactor, which drops the socket). The exception is logged on the local
side with log.err
, but the remote end will not be told any
reason for the disconnection. The banana code uses the BananaError exception
to indicate protocol violations, but others may be encountered.
The Banana object can also choose to respond to Violations by terminating
the connection. For example, the .hangupOnLengthViolation
flag
causes string-too-long violations to be raised directly instead of being
handled, which will cause the connection to be dropped (as it occurs in the
dataReceived method).
The serialized form of ["foo",(1,2)]
is the
following token sequence: OPEN STRING(list) STRING(foo) OPEN STRING(tuple)
INT(1) INT(2) CLOSE CLOSE. In practice, the STRING(list) would really be
something like VOCAB(7), likewise the STRING(tuple) might be VOCAB(8). Here
we walk through how this sequence is processed.
The initial Unslicer stack consists of the single RootUnslicer
rootun
.
OPEN rootun.checkToken(OPEN) : must not raise Violation enter index phase VOCAB(7) (equivalent to STRING(list)) rootun.openerCheckToken(VOCAB, ()) : must not raise Violation VOCAB token is looked up in .incomingVocabulary, turned into "list" rootun.doOpen(("list",)) : looks in UnslicerRegistry, returns ListUnslicer exit index phase the ListUnslicer is pushed on the stack listun.start() STRING(foo) listun.checkToken(STRING, 3) : must return None string is assembled listun.receiveChild("foo") : appends to list OPEN listun.checkToken(OPEN) : must not raise Violation enter index phase VOCAB(8) (equivalent to STRING(tuple)) listun.openerCheckToken(VOCAB, ()) : must not raise Violation VOCAB token is looked up, turned into "tuple" listun.doOpen(("tuple",)) : delegates through: BaseUnslicer.open self.opener (usually the RootUnslicer) self.opener.open(("tuple",)) returns TupleUnslicer exit index phase TupleUnslicer is pushed on the stack tupleun.start() INT(1) tupleun.checkToken(INT) : must not raise Violation integer is assembled tupleun.receiveChild(1) : appends to list INT(2) tupleun.checkToken(INT) : must not raise Violation integer is assembled tupleun.receiveChild(2) : appends to list CLOSE tupleun.receiveClose() : creates and returns the tuple (1,2) (could also return a Deferred) TupleUnslicer is popped from the stack and discarded listun.receiveChild((1,2)) CLOSE listun.receiveClose() : creates and returns the list ["foo", (1,2)] ListUnslicer is popped from the stack and discarded rootun.receiveChild(["foo", (1,2)])
Types and classes are roughly classified into containers and non-containers. The containers are further divided into mutable and immutable. Some examples of immutable containers are tuples and bound methods. Lists and dicts are mutable containers. Ints and strings are non-containers. Non-containers are always leaf nodes in the object graph.
During unserialization, objects are in one of three states: uncreated, referenceable (but not complete), and complete. Only mutable containers can be referenceable but not complete: immutable containers have no intermediate referenceable state.
Mutable containers (like lists) are referenceable but not complete during traversal of their child nodes. This means those children can reference the list without trouble.
Immutable containers (like tuples) present challenges when unserializing. The object cannot be created until all its components are referenceable. While it is guaranteed that these component objects will be complete before the graph traversal exits the current node, the child nodes are allowed to reference the current node during that traversal. The classic example is the graph created by the following Python fragment:
a = ([],) a[0].append((a,))
To handle these cases, the TupleUnslicer installs a Deferred into the object table when it begins unserializing (in the .start method). When the tuple is finally complete, the object table is updated and the Deferred is fired with the new tuple.
Containers (both mutable and immutable) are required to pay attention to the types of their incoming children and notice when they receive Deferreds instead of normal objects. These containers are not complete (in the sense described above) until those Deferreds have been replaced with referenceable objects. When the container receives the Deferred, it should attach a callback to it which will perform the replacement. In addition, immutable containers should check after each update to see if all the Deferreds have been cleared, and if so, complete their own object (and fire their own Deferreds so any containers they are a child of may be updated and/or completed).
TODO: it would be really handy to have the RootUnslicer do Deferred Accounting: each time a Deferred is installed instead of a real object, add its the graph-path to a list. When the Deferred fires and the object becomes available, remove it. If deserialization completes and there are still Deferreds hanging around, flag an error that points to the culprits instead of returning a broken object.
Having the whole Slicer stack get a chance to pass judgement on the
outbound object is very flexible. There are optimizations possibly because
of the fact that most Slicers don't care, perhaps a separate stack for the
ones that want to participate, or a chained delegation function. The
important thing is to make sure that exception cases don't leave a
taster
stranded on the stack when the object that put it there has
gone away.
On the receiving side, the top Unslicer gets to make a decision about the
token before its body has arrived (limiting memory exposure to no more than
65 bytes). In addition, each Unslicer receives component tokens one at a
time. This lets you catch the dangerous data before it gets turned into an
object. However, tokens are a pretty low-level place to do security checks.
It might be more useful to have some kind of instance taster stack
,
with tasters that are asked specifically about (class,state) pairs and
whether they should be turned into objects or not.
Because the Unslicers receive their data one token at a time, things like
InstanceUnslicer can perform security checks one attribute at a time.
traits
-style attribute constraints (see the Chaco project or the
PyCon-2003 presentation for details) can be implemented by having a
per-class dictionary of tests that attribute values must pass before they
will be accepted. The instance will only be created if all attributes fit
the constraints. The idea is to catch violations before any code is run on
the receiving side. Typical checks would be things like .foo must be a
number
, .bar must not be an instance
, .baz must implement the
IBazzer interface
.
TODO: the rest of this section is somewhat out of date.
Using the stack instead of a single Taster object means that the rules can be changed depending upon the context of the object being processed. A class that is valid as the first argument to a method call may not be valid as the second argument, or inside a list provided as the first argument. The PBMethodArgumentsUnslicer could change the way its .taste method behaves as its state machine progresses through the argument list.
There are several different ways to implement this Taster stack:
I'm sure this is safeclasses to override higher-level paranoia.
Of course, all this holds true for the sending side as well. A Slicer could enforce a policy that no objects of type Foo will be sent while it is on the stack.
It is anticipated that something like the current Jellyable/Unjellyable classes will be created to offer control over the Slicer/Unslicers used to handle instance of that class.
One eventual goal is to allow PB to implement E-like argument constraints.
The big change from the old Jelly scheme is that now serialization/unserialization is done in a more streaming format. Individual tokens are the basic unit of information. The basic tokens are just numbers and strings: anything more complicated (starting at lists) involves composites of other tokens.
Producer/Consumer-oriented serialization means that large objects which
can't fit into the socket buffers should not consume lots of memory, sitting
around in a serialized state with nowhere to go. This must be balanced
against the confusion caused by time-distributed serialization. PB method
calls must retain their current in-order execution, and it must not be
possible to interleave serialized state (big mess). One interesting
possibility is to allow multiple parallel SlicerStacks, with a
context-switch token to let the receiving end know when they should switch
to a different UnslicerStack. This would allow cleanly interleaved streams
at the token level. Head-of-line blocking
is when a large request
prevents a smaller (quicker) one from getting through: grocery stores
attempt to relieve this frustration by grouping customers together by
expected service time (the express lane). Parallel stacks would allow the
sender to establish policies on immediacy versus minimizing context
switches.
Another goal of the Jelly+Banana->JustBanana change is the hope of writing Slicers and Unslicers in C. The CBanana module should have C objects (structs with function pointers) that can be looked up in a registry table and run to turn python objects into tokens and vice versa. This ought to be faster than running python code to implement the slices, at the cost of less flexibility. It would be nice if the resulting tokens could be sent directly to the socket at the C level without surfacing into python; barring this it is probably a good idea to accumulate the tokens into a large buffer so the code can do a few large writes instead of a gazillion small ones.
It ought to be possible to mix C and Python slices here: if the C code doesn't find the slice in the table, it can fall back to calling a python method that does a lookup in an extensible registry.
Random notes and wild speculations: take everything beyond here with two grains of salt
The oldbanana usage model has the layer above banana written in one of
two ways. The simple form is to use the banana.encode
and banana.decode
functions to turn an object into a
bytestream. This is used by twisted.spread.publish . The more flexible model
is to subclass Banana. The largest example of this technique is, of course,
twisted.spread.pb.Broker, but others which use it are twisted.trial.remote
and twisted.scripts.conch (which appears to use it over unix-domain
sockets).
Banana itself is a Protocol. The Banana subclass would generally override
the expressionReceived
method, which receives s-expressions
(lists of lists). These are processed to figure out what method should be
called, etc (processing which only has to deal with strings, numbers, and
lists). Then the serialized arguments are sent through Unjelly to produce
actual objects.
On output, the subclass usually calls self.sendEncoded
with
some set of objects. In the case of PB, the arguments to the remote method
are turned into s-expressions with jelly, then combined with the method
meta-data (object ID, method name, etc), then the whole request is sent to
sendEncoded
.
Newbanana moves the Jelly functionality into a stack of Banana Slices,
and the lowest-level token-to-bytestream conversion into the new Banana
object. Instead of overriding expressionReceived
, users could
push a different root Unslicer. to get more control over the receive
process.
Currently, Slicers call Banana.sendOpen/sendToken/sendClose/sendAbort, which
then creates bytes and does transport.write .
To move this into C, the transport should get to call CUnbanana.receiveToken
There should be CBananaUnslicers. Probably a parent.addMe(self) instead of
banana.stack.append(self), maybe addMeC for the C unslicer.
The Banana object is a Protocol, and has a dataReceived method. (maybe in
some C form, data could move directly from a CTransport to a CProtocol). It
parses tokens and hands them to its Unslicer stack. The root Unslicer is
probably created at connectionEstablished time. Subclasses of Banana could
use different RootUnslicer objects, or the users might be responsible for
setting up the root unslicer.
The Banana object is also created with a RootSlicer. Banana.writeToken
serializes the token and does transport.write . (a C form could have CSlicer
objects which hand tokens to a little CBanana which then hands bytes off to
a CTransport).
Doing the bytestream-to-Token conversion in C loses a lot of utility when
the conversion is done token at a time. It made more sense when a whole mess
of s-lists were converted at once.
All Slicers currently have a Banana pointer.. maybe they should have a
transport pointer instead? The Banana pointer is needed to get to top of the
stack.
want to be able to unserialize lists/tuples/dicts/strings/ints (basic
types
) without surfacing into python. want to deliver the completed
object to a python function.
It would be neat if a PB method could indicate that it would like to receive its arguments in a streaming fashion. This would involve calling the method early (as soon as the objectID and method name were known), then somehow feeding objects to it as they arrive. The object could return a handler or consumer sub-object which would be fed as tokens arrive over the wire. This consumer should have a way to enforce a constraint on its input.
This consumer object sounds a lot like an Unslicer, so maybe the method schema should indicate that the method will would like to be called right away so it can return an Unslicer to be pushed on the stack. That Unslicer could do whatever it wanted with the incoming tokens, and could enforce constraints with the usual checkToken/doOpen/receiveChild/receiveClose methods.
On the sending side, it would be neat to let a callRemote() invocation provide a Producer or a generator that will supply data as the network buffer becomes available. This could involve pushing a Slicer. Slicers are generators.
Any given Banana instance has a way to map objects to the Open Index
tuples needed to represent them, and a similar map from such tuples to
incoming object factories. These maps give rise to various classes
of
objects, depending upon how widespread any particular object type is. A List
is a fairly common type of object, something you would expect to find
implemented in pretty much any high-level language, so you would expect a
Banana implementation in that language to be capable of accepting an (OPEN,
'list') sequence. However, a Failure object (found in
twisted.python.failure
, providing an asynchronous-friendly way
of reporting python exceptions) is both Python- and Twisted- specific. Is it
reasonable for one program to emit an (OPEN, 'failure') sequence and expect
another speaker of the generic Banana
protocol to understand it?
This level of compatibility is (somewhat arbitrarily) named dialect
compatibility
. The set of acceptable sequences will depend upon many
things: the language in which the program at each end of the wire is
implemented, the nature of the higher-level software that is using Banana at
that moment (PB is one such layer), and application-specific registrations
that have been performed by the time the sequence is received (the set of
pb.Copyable
sequences that can be received without error will
depend upon which RemoteCopyable
class definitions and
registerRemoteCopy
calls have been made).
Ideally, when two Banana instances first establish a connection, they will go through a negotiation phase where they come to an agreement on what will be sent across the wire. There are two goals to this negotiation:
strong-typingargument. It is important to note that different arguments (both for and against strong typing) may exist when talking about remote interfaces rather than local ones.
Dialect negotiation is a very much still an active area of development.
The basic python types are considered safe
: the code which is
invoked by their receipt is well-understood and there is no way to cause
unsafe behavior during unserialization. Resource consumption attacks are
mitigated by Constraints imposed by the receiving schema.
Note that the OPEN(dict) slicer is implemented with code that sorts the list of keys before serializing them. It does this to provide deterministic behavior and make testing easier.
| IntType, LongType (small+) | INT(value) |
| IntType, LongType (small-) | NEG(value) |
| IntType, LongType (large+) | LONGINT(value) |
| IntType, LongType (large-) | LONGNEG(value) |
| FloatType | FLOAT(value) |
| StringType | STRING(value) |
| StringType (tokenized) | VOCAB(tokennum) |
| UnicodeType | OPEN(unicode) STRING(str.encode('UTF-8')) CLOSE |
| ListType | OPEN(list) elem.. CLOSE |
| TupleType | OPEN(tuple) elem.. CLOSE |
| DictType, DictionaryType | OPEN(dict) (key,value).. CLOSE |
| NoneType | OPEN(none) CLOSE |
| BooleanType | OPEN(boolean) INT(0/1) CLOSE |
To serialize arbitrary python object graphs (including instances) requires that we allow more types in. This begins to get dangerous: with complex graphs of inter-dependent objects, instances may need to be used (by referencing objects) before they are fully initialized. A schema can be used to make assertions about what object types live where, but in general the contents of those objects are difficult to constrain.
For this reason, these types should only be used in places where you trust the creator of the serialized stream (the same places where you would be willing to use the standard Pickle module). Saving application state to disk and reading it back at startup time is one example.
| Extended (unsafe) Python Types | |
| InstanceType | OPEN(instance) STRING(reflect.qual(class)) (attr,value).. CLOSE |
| ModuleType | OPEN(module) STRING(__name__) CLOSE |
| ClassType | OPEN(class) STRING(reflect.qual(class)) CLOSE |
| MethodType | OPEN(method) STRING(__name__) im_self im_class CLOSE |
| FunctionType | OPEN(function) STRING(module.__name__) CLOSE |
See the PB document for details.
The following types are not handled by any slicer, and will raise a
KeyError if one is referenced by an object being sliced. This technically
imposes a limit upon the kinds of objects that can be serialized, even by a
unsafe
serializer, but in practice it is not really an issue, as many
of these objects have no meaning outside the program invocation which
created them.
ObjectType
is the root class of all other types. All objects
are known by some other type in addition to ObjectType
, so the
fact that it is not handled explicitly does not matter.
StringTypes
is simply a list of StringType
and
UnicodeType
, so it does not need to be explicitly handled
either.
The following sequences are internal.
The OPEN(vocab) sequence is used to update the forward compression token-to-string table used by the VOCAB token. It is followed by a series of number/string pairs. All numbers that appear in VOCAB tokens must be associated with a string by appearing in the most recent OPEN(vocab) sequence.
| internal types | |
| vocab dict | OPEN(vocab) (num,string).. CLOSE |


TAB 1 content - This is BMO filler text to put in place of actual content. Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do. From mobile specific to desktop responsive, we bring vision to life
This is BMO filler text to put in place of actual content. Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do. From mobile specific to desktop responsive, we bring vision to life
Tab 2 Content Title
TAB 2 content - This is BMO filler text to put in place of actual content. Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do. From mobile specific to desktop responsive, we bring vision to life
Tab 3 Content Title
TAB 3 content - This is BMO filler text to put in place of actual content. Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do. From mobile specific to desktop responsive, we bring vision to life
Tab 4 Content Title
TAB 4 content - This is BMO filler text to put in place of actual content. Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do. From mobile specific to desktop responsive, we bring vision to life
Tab 5 Content Title
TAB 5 content - From mobile specific to desktop responsive, we bring vision to life. This is BMO filler text to put in place of actual content. Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do.
Tab 6 Content Title
TAB 6 content - This is BMO filler text to put in place of actual content. Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do. From mobile specific to desktop responsive, we bring vision to life
Tab 7 Content Title
TAB 7 content - This is BMO filler text to put in place of actual content. Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do.
Tab 8 Content Title
TAB 8 content - This is BMO filler text to put in place of actual content. Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do. From mobile specific to desktop responsive, we bring vision to life
Tab 9 Content Title
TAB 9 content - Not using lorem ipsum because it creates problems with the spell checker program. BMO digital creates cutting edge experiences that put the user at the center of everything we do. From mobile specific to desktop responsive, we bring vision to life


_ _ _ _
| | _| |__ | | __ _ ___| | __
| |/ / '_ \| |/ _` |/ __| |/ /
| <| |_) | | (_| | (__| <
|_|\_\_.__/|_|\__,_|\___|_|\_\ -- Yiran Zhou
Last login: Sun Jul 12 23:59:59 2020 from <IP_ADDRESS>
kblack:~ $ whoami
kblack # just kidding :p
kblack:~ $ ./whoami
Hi there!
My name is Yiran Zhou, or you can call me kblack.
I've spent three years on my bachelor of Computer Science at ECNU (East China Normal University).
My GitHub homepage is kblackcn, and you can also find some of my works at my private git.
kblack:~ $ ./experience --type code
Actually, I started learning to code when I was 10, and it has really been an adventurous decade.
In the beginning, it's just about competitive programming, and I use Pascal. Then I realize it's an interesting thing to do, so I started learning more languages and more knowledge about computers. And now, here I am.
kblack:~ $ ./experience --type work
In the past few months, I worked as an intern at Bytedance. My job is to refactor and maintain an important service in the ad pipeline.
Now I'm at Tencent, as an intern again. This time my job is more about distributed storage system.
kblack:~ $ ./gallery --inline
This is beautiful Porto.
kblack:~ $ ./comments --all
kblack:~ $ ./comments ''
kblack:~ $ # THE END


Type: Light Fog
Difficulty Class: DC 10
Heavily Obscured: 60ft
CHECKS
The Captain: Intelligence (water vehicles)
Quartermaster: Wisdom (Nature)
Crew: [[1d20]]
CHECK RESULTS
Total Success: The fog has no effect on navigation, and the crew's quality increases by 1 for [[1d3]] days.
Success: The fog has no effect on navigation.
Failure: The fog slows the ship, reducing its travel pace and speed by half for the day.
Total Failure: The fog slows the ship and disorients the crew, reducing the vessel's travel pace and speed by half for the day and causing it to move in a random direction.
Type: Moderate Fog
Difficulty Class: DC 15
Heavily Obscured: 40ft
CHECKS
The Captain: Intelligence (water vehicles)
Quartermaster: Wisdom (Nature)
Crew: [[1d20]]
CHECK RESULTS
Total Success: The fog has no effect on navigation, and the crew's quality increases by 1 for [[1d3]] days.
Success: The fog has no effect on navigation.
Failure: The fog slows the ship, reducing its travel pace and speed by half for the day.
Total Failure: The fog slows the ship and disorients the crew, reducing the vessel's travel pace and speed by half for the day and causing it to move in a random direction.
Type: Heavy Fog
Difficulty Class: DC 20
Heavily Obscured: 20ft
CHECKS
The Captain: Intelligence (water vehicles)
Quartermaster: Wisdom (Nature)
Crew: [[1d20]]
CHECK RESULTS
Total Success: The fog has no effect on navigation, and the crew's quality increases by 1 for [[1d3]] days.
Success: The fog has no effect on navigation.
Failure: The fog slows the ship, reducing its travel pace and speed by half for the day.
Total Failure: The fog slows the ship and disorients the crew, reducing the vessel's travel pace and speed by half for the day and causing it to move in a random direction.
Type: Very Heavy Fog
Difficulty Class: DC 25
Heavily Obscured: 10ft
CHECKS
The Captain: Intelligence (water vehicles)
Quartermaster: Wisdom (Nature)
Crew: [[1d20]]
CHECK RESULTS
Total Success: The fog has no effect on navigation, and the crew's quality increases by 1 for [[1d3]] days.
Success: The fog has no effect on navigation.
Failure: The fog slows the ship, reducing its travel pace and speed by half for the day.


play.modules.reactivemongo.json.collection
(Since version 0.12.0) Use reactivemongo.play.json.collection.JSONBatchCommands
(Since version 0.12.0) Use reactivemongo.play.json.collection.JSONBatchCommands


Project Details
Our original and best selling reusable bag isn’t just for the grocery store; it goes everywhere and hauls practically anything.
A set of Standard Baggus in a delightful reusable pouch. Each set contains three unique prints, ideal for gifting.


|
j, s
Scroll down (scrollDown)
|
k, w
Scroll up (scrollUp)
|
|
d
Scroll half-page down (scrollPageDown)
|
u, e
Scroll half-page up (scrollPageUp)
|
|
h
Scroll left (scrollLeft)
|
l
Scroll right (scrollRight)
|
|
gi
Go to first input box (tab to cycle through) (goToInput)
|
zz
Center page to current search match (middle) (centerMatchH)
|
|
zt
Center page to current search match (top) (centerMatchT)
|
zb
Center page to current search match (bottom) (centerMatchB)
|
|
f
Open link in current tab (createHint)
|
F
Open link in new tab (createTabbedHint)
|
|
Y
Copy Url from link to clipboard (yankUrl)
|
gr
Reverse image search (Google) an image on the page (reverseImage)
|
|
I
Search through history (:hist(ory))
|
b
Search through bookmarks (:b(ook)marks)
|
|
gt, K, R
Navigate to the next tab (nextTab)
|
gT, J, E
Navigate to the previous tab (previousTab)
|
|
x
Close the current tab (closeTab)
|
t + <Enter> (:tabopen)
Open a new tab
|
|
H, S (goBack)
Go back
|
L, D
Go forward (goForward)
|
|
<
Move current tab left (moveTabLeft)
|
>
Move current tab right (moveTabRight)
|
|
gp
Pin/unpin the current tab (pinTab)
|
|
n
Go to next search result (nextSearchResult)
|
N
Go to previous search result (previousSearchResult)
|
|
:t(ab)o(pen) <SEARCH/AUTOCOMPLETE>
Open a new tab with the typed/completed Url or Google search
|
|
:o(pen) <SEARCH/AUTOCOMPLETE>
Open the typed/completed Url or Google search in the same tab
|
|
:hist(ory) <SEARCH/AUTOCOMPLETE>
Search through browsing history
|
|
:tabclose
Close the current tab
|
|
:b(ook)marks <SEARCH/AUTOCOMPLETE>
Search through your bookmarks
|
|
:nohl
Clears the page highlighting from the last search
|
|
:ex(tensions)
Open the chrome://extensions page in a new tab
|
|
:fl(ags)
Open the chrome://flags page in a new tab
|
|
:h(elp)
Show this help dialogue
|
| Use smooth scrolling |
| Scroll step size |
| Command bar on bottom |
| Link hint characters |
| Command bar search length (bookmark/history search only) |
| Custom mappings ? |
| Blacklisted Domains |
| CSS |


This is a short summary of blog post and can be used to give a gist to google and others...
Read MoreThis is a short summary of blog post and can be used to give a gist to google and others This is a short summary of blog post and can be used to give a gist to google and others...
Read More 30This is a short summary of blog post and can be used to give a gist to google and others This is a short summary of blog post and can be used to give a gist to google and others...
Read More 30This is a short summary of blog post and can be used to give a gist to google and others This is a short summary of blog post and can be used to give a gist to google and others...
Read More 30


THE CELLULOID CLOSET A film review by Ben Hoffman Copyright 1996 Ben Hoffman
The dictionaries define "unique" as "one of a kind." No more apt description fits The Celluloid Closet. Narrated by Lily Tomlin, using over one hundred film clips from the Thomas Edison experimental film to the Oscar-winning Philadelphia, the movie is frequently hilarious, thoroughly entertaining and always informative. The film is based on the book by Vito Russo, the story of how Hollywood has over the years shown gays and Lesbians.
Having seen most of the films from the Chaplin days through the present, I was surprised to find how little I knew about them. I never thought of the comic Edward Everett Horton as supposed to be portraying a gay character. He was just funny and made us laugh. The truth is I did not even know what a "gay" was.
We knew about the Hays Office that had the power to censor films. We knew that the Catholic Legion of Decency made a list of films which good Catholics were forbidden to see. That was then and this is now but we still have those who would censor what can be seen, The rating system is still with us.
How we learned to view gays and Lesbians and how they learned to view themselves came in large part from the Silver Screen. Any reference to homosexuality was sure to bring a laugh just as comedians used Brooklyn and Cucamonga as laugh=getters. Gayness on the screen could be funny but it could also make you pity the poor unfortunates A male costume designer was good for a laugh. . . . or a flouncy waiter . . . . or any of a dozen stereotypes. No one thought of them as being homosexual; they were just sissies. On the one hand there were the John Waynes and on the other were the sissies. Quentin Crisp remarks that when a man dresses as a woman, we find it funny (SOME LIKE IT HOT) but a woman dressed as a man (Dietrich, Garbo) was not only accepted but even was a source of titillation.
Again, the film is unique. My own favorite scene is at the end, a clip of Tom Hanks and Anthony Banderas in their Navy uniforms, and the voice over commentary that sums up this wonderful film.
Directed by Bob Epstein and Jeffrey.
4 Bytes
4 Bytes = Superior 3 Bytes = Not to be missed 2 Bytes = Average 1 Bytes = Save your money.
Ben Hoffman
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Range open everyday at 10:30
03/06/2020: Due to issues with the boundary fence on the outdoor range we ask all patrons to refrain from using drivers on the outdoor range. Drivers will be restricted to the indoor range only.
18/05/2020: Frankfield House is happy to announce we have partially reopened for business! In line with COVID-19 safety measures we have taken some percautions in order to protect our staff and our customers.
Entrance has been restricted to the Seven Oaks entrance only with a security checkpoint at the gate. At this time we are only open to GUI or Range members.


PART VI. ENFORCEMENT
[§392-91] Enforcement by the director. The director shall enforce the provisions of this chapter. The director may appoint such assistants and such clerical, stenographic and other help as may be necessary for the proper enforcement of this chapter subject to any civil service act relating to state employees. The salaries of all the foregoing appointees and employees shall be as fixed by law.
The director may adopt, amend, or repeal such rules and regulations as the director deems necessary or suitable for the proper enforcement of this chapter. [L 1969, c 148, pt of §1; gen ch 1985]
Cross References
Civil service law, see chapter 76.
Classification law, see §76-13.5.
Rulemaking, see chapter 91.


It seems JavaScript in your browser disabled or not supported.
To use Web app TA Chart, please, enable JavaScript in the browser and reload the page.


黒の手番です。キューブアクションは？
8-8:11pt
pips Black=8 White=8
(CorrectAnswer= 0/0, ErrorScore= 0)
No redouble Player Winning Chances: 87.63% (G:0.00% B:0.00%) Opponent Winning Chances: 12.37% (G:0.00% B:0.00%) Redouble/Take Player Winning Chances: 87.63% (G:0.00% B:0.00%) Opponent Winning Chances: 12.37% (G:0.00% B:0.00%) Cubeless Equities: No double=+0.753, Double=+1.501 Cubeful Equities: No redouble: +0.795 (-0.205) Redouble/Take: +1.501 (+0.501) Redouble/Pass: +1.000 Best Cube action: Redouble / Pass


"Intellectual property has the shelf life of a banana."
—Bill Gates
"Bananas are great, as I believe them to be the only known cure for existential dread. Also, Mother Teresa said that in India, a woman dying in the street will share her banana with anyone who needs it, whereas in America, people amass and hoard as many bananas as they can to sell for an exorbitant profit. So half of them go bad, anyway."
—Anne Lamott
Who knew bananas could be so fascinating? It's been a part of popular culture since the late 1800s and popular ever since it was introduced to Americans. It's hard to imagine something so normal and mundane as bananas having a fragile existence in the world but we hope that That's Bananas introduced you to thinking about the delicate future of this fruit and the reasons for its demise.
We hope that you pursue your banana interests and discover more about how its cultivation has developed and impacted our world, for better or for worse.
Here's a list of resources that informed the creation of this interactive documentary.
Be in touch and thanks for visiting.
That's Bananas! is a collaboration between Bruna Calheiros, Hanna Kang-Brown, and Zena Koo, graduate students of NYU's ITP program. This project was developed for the class Connected Documentary.


{% load i18n %}
{% trans "Hi" %} {{ user.get_username }},
{% trans "You have requested a password reset for" %} {{ site_name }}.
{% trans "Please go to the following page and choose a new password:" %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% trans "Thanks for using this site!" %}
{% trans "If you have not requested a password change, somebody is trying to access your account. Make sure the password of both your account and your email address are strong, and if you suspect that somebody has access to your email address, change its password immediately." %}
---
{% trans "This is an auto-generated email. Please don't reply." %}


# Copyright (c) 2020, Soohwan Kim. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import random
import numpy as np
from torch import Tensor
from kospeech.utils import logger
from kospeech.data.audio.core import split
[docs]class SpecAugment(object):
"""
Provides Spec Augment. A simple data augmentation method for speech recognition.
This concept proposed in https://arxiv.org/abs/1904.08779
Args:
freq_mask_para (int): maximum frequency masking length
time_mask_num (int): how many times to apply time masking
freq_mask_num (int): how many times to apply frequency masking
Inputs: feature_vector
- **feature_vector** (torch.FloatTensor): feature vector from audio file.
Returns: feature_vector:
- **feature_vector**: masked feature vector.
"""
def __init__(self, freq_mask_para: int = 18, time_mask_num: int = 10, freq_mask_num: int = 2) -> None:
self.freq_mask_para = freq_mask_para
self.time_mask_num = time_mask_num
self.freq_mask_num = freq_mask_num
def __call__(self, feature: Tensor) -> Tensor:
""" Provides SpecAugmentation for audio """
time_axis_length = feature.size(0)
freq_axis_length = feature.size(1)
time_mask_para = time_axis_length / 20 # Refer to "Specaugment on large scale dataset" paper
# time mask
for _ in range(self.time_mask_num):
t = int(np.random.uniform(low=0.0, high=time_mask_para))
t0 = random.randint(0, time_axis_length - t)
feature[t0: t0 + t, :] = 0
# freq mask
for _ in range(self.freq_mask_num):
f = int(np.random.uniform(low=0.0, high=self.freq_mask_para))
f0 = random.randint(0, freq_axis_length - f)
feature[:, f0: f0 + f] = 0
return feature
[docs]class NoiseInjector(object):
"""
Provides noise injection for noise augmentation.
The noise augmentation process is as follows:
Step 1: Randomly sample audios by `noise_size` from dataset
Step 2: Extract noise from `audio_paths`
Step 3: Add noise to sound
Args:
dataset_path (str): path of dataset
noiseset_size (int): size of noise dataset
sample_rate (int): sampling rate
noise_level (float): level of noise
Inputs: signal
- **signal**: signal from pcm file
Returns: signal
- **signal**: noise added signal
"""
def __init__(self, dataset_path, noiseset_size, sample_rate=16000, noise_level=0.7):
if not os.path.exists(dataset_path):
logger.info("Directory doesn`t exist: {0}".format(dataset_path))
raise IOError
logger.info("Create Noise injector...")
self.noiseset_size = noiseset_size
self.sample_rate = sample_rate
self.noise_level = noise_level
self.audio_paths = self.create_audio_paths(dataset_path)
self.dataset = self.create_noiseset(dataset_path)
logger.info("Create Noise injector complete !!")
def __call__(self, signal):
noise = np.random.choice(self.dataset)
noise_level = np.random.uniform(0, self.noise_level)
signal_length = len(signal)
noise_length = len(noise)
if signal_length >= noise_length:
noise_start = int(np.random.rand() * (signal_length - noise_length))
noise_end = int(noise_start + noise_length)
signal[noise_start: noise_end] += noise * noise_level
else:
signal += noise[:signal_length] * noise_level
return signal
def create_audio_paths(self, dataset_path):
audio_paths = list()
data_list = os.listdir(dataset_path)
data_list_size = len(data_list)
while True:
index = int(random.random() * data_list_size)
if data_list[index].endswith('.pcm'):
audio_paths.append(data_list[index])
if len(audio_paths) == self.noiseset_size:
break
return audio_paths
def create_noiseset(self, dataset_path):
dataset = list()
for audio_path in self.audio_paths:
path = os.path.join(dataset_path, audio_path)
noise = self.extract_noise(path)
if noise is not None:
dataset.append(noise)
return dataset
def extract_noise(self, audio_path):
try:
signal = np.memmap(audio_path, dtype='h', mode='r').astype('float32')
non_silence_indices = split(signal, top_db=30)
for (start, end) in non_silence_indices:
signal[start:end] = 0
noise = signal[signal != 0]
return noise / 32767
except RuntimeError:
logger.info("RuntimeError in {0}".format(audio_path))
return None
except ValueError:
logger.info("RuntimeError in {0}".format(audio_path))
return None


This tutorial shows you the steps to follow to create a distributed version of the classic "Hello World" program using Java Remote Method Invocation (RMI) over Internet Inter-ORB Protocol (IIOP). RMI-IIOP adds CORBA (Common Object Request Broker Architecture) capability to Java RMI, providing standards-based interoperability and connectivity to many other programming languages and platforms. RMI-IIOP enables distributed Web-enabled Java applications to transparently invoke operations on remote network services using the industry standard IIOP defined by the Object Management Group. Runtime components include a Java ORB for distributed computing using IIOP communication.
RMI-IIOP is for Java programmers who want to program to the RMI interfaces, but use IIOP as the underlying transport. RMI-IIOP provides interoperability with other CORBA objects implemented in various languages - but only if all the remote interfaces are originally defined as Java RMI interfaces. It is of particular interest to programmers using Enterprise JavaBeans (EJB), since the remote object model for EJBs is RMI-based.
Other options for creating distributed applications are:
Java IDL is for CORBA programmers who want to program in the Java programming language based on interfaces defined in CORBA Interface Definition Language (IDL). This is "business as usual" CORBA programming, supporting Java in exactly the same way as other languages like C++ or COBOL.
The Java RMI system allows an object running in one Java Virtual Machine (VM) to invoke methods on an object running in another Java VM. RMI provides for remote communication between programs written in the Java programming language via the Java Remote Method Protocol (JRMP).
The distributed Hello World example uses a client application to make a remote method call via IIOP to a server running on the host from which the client was downloaded. When the client runs, "Hello from MARS!" is displayed.
This tutorial is organized as follows:
Each step in the tutorial is indicated by this symbol.
There are three tasks to complete in this section:
HelloInterface.java
- a
remote interfaceHelloImpl.java
- a remote
object implementation that implements
HelloInterface
HelloServer.java
- an
RMI server that creates an instance of the remote object
implementation and binds that instance to a name in the Naming
ServiceHelloClient.java
- a client
application that invokes the remote method,
sayHello()
Follow the steps below to create the source files or download and unzip HelloRMIIIOP.zip.
Remote
interface. Your
remote interface will declare each of the methods that you would
like to call from other machines. Remote interfaces have the
following characteristics:
public
.
Otherwise, a client will get an error when attempting to load a
remote object that implements the remote interface, unless that
client is in the same package as the remote interface.java.rmi.Remote
interface.java.rmi.RemoteException
(or a superclass of RemoteException
) in its
throws
clause, in addition to any application-specific
exceptions.HelloInterface
) not the implementation
class (HelloImpl
).For this example, create all of the source files in the same
directory, for example, $HOME/mysrc/helloWorld
.
Create the file
HelloInterface.java. The following code is the interface
definition for the remote interface, HelloInterface
,
which contains just one method, sayHello
:
//HelloInterface.java import java.rmi.Remote; public interface HelloInterface extends java.rmi.Remote { public void sayHello( String from ) throws java.rmi.RemoteException; }
java.rmi.RemoteException
.
At a minimum, a remote object implementation class,
HelloImpl.java
must:
Create the file HelloImpl.java. The code for this file follows. An explanation of each of the preceding steps follows the source code:
//HelloImpl.java import javax.rmi.PortableRemoteObject; public class HelloImpl extends PortableRemoteObject implements HelloInterface { public HelloImpl() throws java.rmi.RemoteException { super(); // invoke rmi linking and remote object initialization } public void sayHello( String from ) throws java.rmi.RemoteException { System.out.println( "Hello from " + from + "!!" ); System.out.flush(); } }
In the Java programming language, when a class declares that it
implements an interface, a contract is formed between the class and
the compiler. By entering into this contract, the class is
promising that it will provide method bodies, or definitions, for
each of the method signatures declared in that interface. Interface
methods are implicitly public
and
abstract
, so if the implementation class doesn't
fulfill its contract, it becomes by definition an
abstract
class, and the compiler will point out this
fact if the class was not declared abstract
.
The implementation class in this example is
HelloImpl
. The implementation class declares which
remote interface(s) it is implementing. Here is the
HelloImpl
class declaration:
public class HelloImpl extends PortableRemoteObject implements HelloInterface{As a convenience, the implementation class can extend a remote class, which in this example is
javax.rmi.PortableRemoteObject
. By extending
PortableRemoteObject
, the HelloImpl
class
can be used to create a remote object that uses IIOP-based
transport for communication.
In addition, the remote object instance will need to be
"exported". Exporting a remote object makes it available to accept
incoming remote method requests, by listening for incoming calls to
the remote object on an anonymous port. When you extend
javax.rmi.PortableRemoteObject
, your class will be
exported automatically upon creation.
Because the object export could potentially throw a
java.rmi.RemoteException
, you must define a
constructor that throws a RemoteException
, even if the
constructor does nothing else. If you forget the constructor,
javac
will produce the following error message:
HelloImpl.java:3: unreported exception java.rmi.RemoteException; must be caught or declared to be thrown. public class HelloImpl extends PortableRemoteObject implements HelloInterface{ ^ 1 errorTo review: The implementation class for a remote object needs to:
java.rmi.RemoteException
HelloImpl
class:
public HelloImpl() throws java.rmi.RemoteException { super(); }Note the following:
super
method call invokes the no-argument
constructor of javax.rmi.PortableRemoteObject
, which
exports the remote object.java.rmi.RemoteException
, because RMI's attempt to
export a remote object during construction might fail if
communication resources are not available.Although the call to the superclass's no-argument constructor,
super()
, occurs by default (even if omitted), it is
included in this example to make clear the fact that the superclass
will be constructed before the class.
sayHello()
method, which returns the string "Hello
from MARS!!" to the caller:
public void sayHello( String from ) throws java.rmi.RemoteException { System.out.println( "Hello from " + from + "!!"); System.out.flush(); }Arguments to, or return values from, remote methods can be any data type for the Java platform, including objects, as long as those objects implement the interface
java.io.Serializable
.
Most of the core classes in java.lang
and
java.util
implement the Serializable
interface. In RMI:
static
or transient
. Please
refer to the Java Object Serialization Specification for
information on how to alter the default serialization
behavior.rmic
to generate stubs and skeletons.A server class is the class which has a main
method
that creates an instance of the remote object implementation, and
binds that instance to a name in the Naming Service. The class that
contains this main
method could be the implementation
class itself, or another class entirely.
In this example, the main
method is part of
HelloServer.java
, which does the following:
Create the file HelloServer.java. The source code for this file follows. An explanation of each of the preceding steps follows the source code:
//HelloServer.java import javax.naming.InitialContext; import javax.naming.Context; public class HelloServer { public static void main(String[] args) { try { // Step 1: Instantiate the Hello servant HelloImpl helloRef = new HelloImpl(); // Step 2: Publish the reference in the Naming Service // using JNDI API Context initialNamingContext = new InitialContext(); initialNamingContext.rebind("HelloService", helloRef ); System.out.println("Hello Server: Ready..."); } catch (Exception e) { System.out.println("Trouble: " + e); e.printStackTrace(); } } }
main
method of the server needs to create an
instance of the remote object implementation, or Servant.
For example:
HelloImpl helloRef = new HelloImpl();The constructor exports the remote object, which means that once created, the remote object is ready to accept incoming calls.
Once a remote object is registered on the server, callers can
look up the object by name (using a naming service), obtain a
remote object reference, and then remotely invoke methods on the
object. In this example, we use the Naming Service that is part of
the Object Request Broker Daemon (orbd
for Solaris, Linux, or Mac OS X or Windows).
For example, the following code binds the name "HelloService" to a reference for the remote object:
// Step 2: Publish the reference in the Naming Service // using JNDI API Context initialNamingContext = new InitialContext(); initialNamingContext.rebind("HelloService", helloRef );
Note the following about the arguments to the
rebind
method call:
"HelloService"
, is a
java.lang.String
, representing the name of the remote
object to bindhelloRef
is the object id of
the remote object to bindThe client application in this example remotely invokes the
sayHello
method in order to get the string "Hello from
MARS!!" to display when the client application runs.
Create the file HelloClient.java. Here is the source code for the client application:
//HelloClient.java import java.rmi.RemoteException; import java.net.MalformedURLException; import java.rmi.NotBoundException; import javax.rmi.*; import java.util.Vector; import javax.naming.NamingException; import javax.naming.InitialContext; import javax.naming.Context; public class HelloClient { public static void main( String args[] ) { Context ic; Object objref; HelloInterface hi; try { ic = new InitialContext(); // STEP 1: Get the Object reference from the Name Service // using JNDI call. objref = ic.lookup("HelloService"); System.out.println("Client: Obtained a ref. to Hello server."); // STEP 2: Narrow the object reference to the concrete type and // invoke the method. hi = (HelloInterface) PortableRemoteObject.narrow( objref, HelloInterface.class); hi.sayHello( " MARS " ); } catch( Exception e ) { System.err.println( "Exception " + e + "Caught" ); e.printStackTrace( ); return; } } }
First, the client application gets
a reference to the remote object implementation (advertised as
"HelloService") from the Naming Service using Java Naming and
Directory Interface (JNDI) calls. Like the
Naming.rebind
method, the Naming.lookup
method takes java.lang.String
value representing the
name of the object to look up. You supply Naming.lookup()
the name of the object you want to look up, and it returns the
object bound to that name. Naming.lookup() returns the
stub for the remote implementation of the Hello
interface to its caller (HelloClient
).
sayHello()
method on the server's remote object,
causing the string "Hello from MARS!!" to be displayed on the
command line.HelloInterface.java
contains the source code for
the remote interfaceHelloImpl.java
contains the source code for the
remote object implementationHelloServer.java
contains the source code for the
serverHelloClient.java
contains the source code for the
client applicationHelloImpl.java
, in order to create the
.class
files needed to run rmic
. You then
run the rmic
compiler to create stubs and skeletons. A
stub is a client-side proxy for a remote object which forwards
RMI-IIOP calls to the server-side dispatcher, which in turn
forwards the call to the actual remote object implementation. The
last task is to compile the remaining .java
source
files to create .class
files.
The following tasks will be completed in this section:
rmic
to generate stubs and
skeletonsTo create stub and skeleton files, the rmic
compiler must be run on the fully-qualified package names of
compiled class files that contain remote object implementations. In
this example, the file that contains the remote object
implementations is HelloImpl.java
. To generate the
stubs and skeletons:
Compile
HelloImpl.java
, as follows:
javac -d . -classpath . HelloImpl.java
The "-d .
" option indicates that the generated
files should be placed in the directory from which you are running
the compiler. The "-classpath .
" option indicates that
files on which HelloImpl.java
is dependent can be
found in this directory.
rmic
to generate skeletons and stubsrmic
compiler with the -iiop
option. The
rmic -iiop
command takes one or more class names
as an argument and produces class files of the form
_HelloImpl_Tie.class
and
_HelloInterface_Stub.class
. The remote implementation
file, HelloImpl.class
, is the class name to pass in
this example.
For an explanation of rmic
options, you can refer
to the Solaris, Linux, or Mac OS X rmic
man page or the Windows
rmic
man page.
To create the
stub and skeleton for the HelloImpl
remote object
implementation, run rmic
like this:
rmic -iiop HelloImpl
The preceding command creates the following files:
_HelloInterface_Stub.class
- the client stub_HelloImpl_Tie.class
- the server skeletonCompile the source files as follows:
javac -d . -classpath . HelloInterface.java HelloServer.java HelloClient.java
This command creates the class files
HelloInterface.class
, HelloServer.class
,
and HelloClient.class
. These are the remote interface,
the server, and the client application respectively. For an
explanation of javac
options, you can refer to the
Solaris, Linux, or Mac OS X javac
man
page or the Windows
javac
man page.
orbd
, which includes both a Transient and a Persistent
Naming Service, and is available with every download of the JDK.
For a caller (client, peer, or client application) to be able to invoke a method on a remote object, that caller must first obtain a reference to the remote object.
Once a remote object is registered on the server, callers can look up the object by name, obtain a remote object reference, and then remotely invoke methods on the object.
Start the
Naming Service by running orbd
from the command
line.
For this example, on the Solaris operating system:
orbd -ORBInitialPort 1050&
or, on the Windows operating system:
start orbd -ORBInitialPort 1050
You must specify a port on which to run orbd
. For
this example the port of 1050
is chosen because in the
Solaris operating environment, a user must become root to start a
process on a port under 1024. For more on the orbd
tool, you can refer to the orbd man page (Solaris, Linux, or Mac OS X or Windows).
You must stop and restart the server any time you modify a remote interface or use modified/additional remote interfaces in a remote object implementation. Otherwise, the type of the object reference bound in the Naming Service will not match the modified class.
Open another terminal window and change to the directory
containing the source files for this example. The command for
running the client has been spread out below to make it easier to
read, but should be typed without returns between the lines. The
following command shows how to start the HelloServer
server. If you used a port other than 1050 or a host other than
localhost
when starting the orbd
tool,
replace those values in the command below with the actual values
used to start orbd
.
Start the Hello server, as follows:
java -classpath . -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory -Djava.naming.provider.url=iiop://localhost:1050 HelloServer
For an explanation of java
options, you can refer
to the Solaris, Linux, or Mac OS X java
man page or the Windows
java
man page.
The output should look like this:
Hello Server: Ready ...
localhost
when starting
the orbd
tool, replace those values in the command
below with the actual values used to start orbd
.
Start the client application, as follows:
java -classpath . -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory -Djava.naming.provider.url=iiop://localhost:1050 HelloClientAfter running the client application, you will see output similar to the following on your display in the client window:
Client: Obtained a ref. to Hello server.
The following message will display in the server window:
Hello from MARS
ORBD and the Hello server will continue to run until they are
explicitly stopped. On Solaris, Linux, or Mac OS X,
you can stop these processes using the
pkill orbd
and pkill HelloServer
commands
from a terminal window. On Windows, you can type
Ctrl+C
in a prompt window to kill the process.
This completes the basic RMI-IIOP tutorial. If you are ready to move on to more complicated applications, here are some sources that may help:


{% extends 'layout.html' %} {% block body %}
Kredo is a free portfolio WebApp that works on creative thinking in students to identify their strengths in AI or Datascience. And not only allows you to learn but identify the you in you, work on this global-app and discover the real you.
{% endblock%}


Equipment
Our manual J-head Bridgeport
A Standard Modern 13" 'Utilathe'
Sunnen honing machine, used for honing engine blocks
Hermle UWF1000, with Heidenhain TNC355 control. We made an ersatz enclosure ourselves, which is still WIP.
Mechanical reciprocating saw
Norton D-bit grinder for making tools.
Brown and Sharpe Surface Grinder
18" Grob Bandsaw
Our large drill press.
SIP (Société d’Instruments de Précision, from Geneva, Switzerland) jig boring machine. Good to about 2 micron over it's range of travel.
Our 1981 Mori-Seiki SL-1 on the trailer from bringing it home. A portion of our large radial arm drill also shown.
The SL-1 moved into the shop, cat for scale. We removed much of the enclosure during installation, and never put it all back together for the time being.
Some equipment we keep in the barn, none of it fully set up yet. Left-to-right: BOY 22 ton injection molding machine, the Sunnen honing machine, and a Blue M furnace.
Our largest "Machine," our 1948 circular sawmill, with 60in. blade.
Big Allis; our largest piece of earthmoving equipment, an Allis-Chalmers HD11-G Tractor Shovel


Your budget has been registered correctly.
Name: {{customer.name}} {{customer.lastname}}
Date:
Company: {{customer.company.name}}
Industry: {{customer.company.industry}}
Country: {{customer.company.country}}
Total implantation hours: {{lastBudget.total}}