jruby-hornetq 0.4.0 → 0.5.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/Gemfile +9 -0
  2. data/Gemfile.lock +30 -0
  3. data/HISTORY.md +6 -0
  4. data/README.md +33 -35
  5. data/Rakefile +14 -9
  6. data/examples/advanced/batch_client.rb +8 -8
  7. data/examples/advanced/batch_requestor_pattern.rb +34 -34
  8. data/examples/advanced/bytes_producer.rb +3 -3
  9. data/examples/advanced/client.rb +5 -5
  10. data/examples/advanced/client_session_pooling.rb +3 -3
  11. data/examples/advanced/consume_on_message.rb +5 -5
  12. data/examples/advanced/consumer.rb +2 -2
  13. data/examples/advanced/multi_client.rb +3 -3
  14. data/examples/advanced/producer.rb +3 -3
  15. data/examples/advanced/server.rb +4 -4
  16. data/examples/client-server/client.rb +4 -4
  17. data/examples/client-server/server.rb +4 -4
  18. data/examples/producer-consumer/consume_all.rb +2 -2
  19. data/examples/producer-consumer/consume_on_message.rb +5 -5
  20. data/examples/producer-consumer/consumer.rb +2 -2
  21. data/examples/producer-consumer/producer.rb +3 -3
  22. data/examples/resque/hornetq_job.rb +19 -19
  23. data/examples/resque/processor.rb +4 -4
  24. data/examples/resque/sleep_job.rb +3 -3
  25. data/lib/hornetq.rb +3 -2
  26. data/lib/hornetq/client.rb +4 -2
  27. data/lib/hornetq/client/connection.rb +86 -86
  28. data/lib/hornetq/client/message_handler.rb +1 -1
  29. data/lib/hornetq/client/org_hornetq_api_core_client_client_session.rb +67 -67
  30. data/lib/hornetq/client/org_hornetq_core_client_impl_client_consumer_impl.rb +11 -11
  31. data/lib/hornetq/client/org_hornetq_core_client_impl_client_message_impl.rb +126 -126
  32. data/lib/hornetq/client/org_hornetq_core_client_impl_client_producer_impl.rb +14 -14
  33. data/lib/hornetq/client/org_hornetq_utils_typed_properties.rb +6 -6
  34. data/lib/hornetq/client/requestor_pattern.rb +24 -24
  35. data/lib/hornetq/client/server_pattern.rb +4 -4
  36. data/lib/hornetq/client/session_pool.rb +18 -18
  37. data/lib/hornetq/common/logging.rb +1 -14
  38. data/lib/hornetq/java/hornetq-bootstrap.jar +0 -0
  39. data/lib/hornetq/java/hornetq-commons.jar +0 -0
  40. data/lib/hornetq/java/hornetq-core-client.jar +0 -0
  41. data/lib/hornetq/java/hornetq-journal.jar +0 -0
  42. data/lib/hornetq/java/hornetq-server.jar +0 -0
  43. data/lib/hornetq/java/jnp-client.jar +0 -0
  44. data/lib/hornetq/java/netty.jar +0 -0
  45. data/lib/hornetq/server.rb +4 -1
  46. data/lib/hornetq/version.rb +3 -0
  47. data/nbproject/private/private.properties +4 -0
  48. data/nbproject/private/private.xml +4 -0
  49. data/nbproject/private/rake-d.txt +4 -0
  50. data/nbproject/project.properties +11 -0
  51. data/nbproject/project.xml +17 -0
  52. data/test/client_connection_test.rb +25 -25
  53. data/test/logging_test.rb +3 -3
  54. metadata +131 -125
  55. data/bin/data/bindings/hornetq-bindings-1.bindings +0 -0
  56. data/bin/data/bindings/hornetq-bindings-2.bindings +0 -0
  57. data/bin/data/journal/hornetq-data-1.hq +0 -0
  58. data/bin/data/journal/hornetq-data-2.hq +0 -0
  59. data/lib/hornetq/common/log_delegate.rb +0 -48
  60. data/lib/hornetq/common/org_hornetq_core_logging_logger.rb +0 -60
  61. data/lib/hornetq/java/hornetq-core.jar +0 -0
@@ -5,29 +5,29 @@
5
5
  #
6
6
  # Other methods still directly accessible through this class:
7
7
  #
8
- # void close()
8
+ # void close()
9
9
  # Closes the consumer
10
- #
11
- # boolean closed?
10
+ #
11
+ # boolean closed?
12
12
  # Returns whether the consumer is closed or not
13
13
  #
14
- # Note: receive can be used directly, but it is recommended to use #each where possible
15
- #
14
+ # Note: receive can be used directly, but it is recommended to use #each where possible
15
+ #
16
16
  # ClientMessage receive()
17
17
  # Receives a message from a queue
18
18
  # Wait forever until a message is received
19
- # ClientMessage receive(long timeout)
19
+ # ClientMessage receive(long timeout)
20
20
  # Receives a message from a queue
21
21
  # Returns nil if no message was received after timeout milliseconds
22
22
  # ClientMessage receive_immediate()
23
23
  # Receives a message from a queue
24
24
  # Return immediately if no message is available on the queue
25
25
  # Returns nil if no message available
26
- #
26
+ #
27
27
  class Java::org.hornetq.core.client.impl::ClientConsumerImpl
28
-
28
+
29
29
  # For each message available to be consumed call the block supplied
30
- #
30
+ #
31
31
  # Returns the statistics gathered when :statistics => true, otherwise nil
32
32
  #
33
33
  # Parameters:
@@ -69,7 +69,7 @@ class Java::org.hornetq.core.client.impl::ClientConsumerImpl
69
69
  :messages_per_second => (message_count/duration).to_i}
70
70
  end
71
71
  end
72
-
72
+
73
73
  # Receive messages in a separate thread when they arrive
74
74
  # Allows messages to be received in a separate thread. I.e. Asynchronously
75
75
  # This method will return to the caller before messages are processed.
@@ -101,7 +101,7 @@ class Java::org.hornetq.core.client.impl::ClientConsumerImpl
101
101
  raise "First call Consumer::on_message with :statistics=>true before calling Consumer::statistics()" unless stats
102
102
  stats
103
103
  end
104
-
104
+
105
105
  private
106
106
  def receive_with_timeout(timeout)
107
107
  if timeout == -1
@@ -1,134 +1,134 @@
1
- #
1
+ #
2
2
  # Message
3
- #
3
+ #
4
4
  # A Message is a routable instance that has a payload.
5
- #
5
+ #
6
6
  # The payload (the "body") is opaque to the messaging system. A Message also has
7
- # a fixed set of headers (required by the messaging system) and properties
8
- # (defined by the users) that can be used by the messaging system to route the
9
- # message (e.g. to ensure it matches a queue filter).
10
- #
7
+ # a fixed set of headers (required by the messaging system) and properties
8
+ # (defined by the users) that can be used by the messaging system to route the
9
+ # message (e.g. to ensure it matches a queue filter).
10
+ #
11
11
  # See: http://hornetq.sourceforge.net/docs/hornetq-2.1.0.Final/api/org/hornetq/api/core/client/ClientMessage.html
12
- #
12
+ #
13
13
  # Other methods still directly accessible through this class:
14
14
  #
15
- # void acknowledge()
16
- # Acknowledge reception of this message. If the session responsible to
17
- # acknowledge this message has :auto_commit_acks => true, the
18
- # transaction will automatically commit the current transaction.
19
- # Otherwise, this acknowledgement will not be committed until the
15
+ # void acknowledge()
16
+ # Acknowledge reception of this message. If the session responsible to
17
+ # acknowledge this message has :auto_commit_acks => true, the
18
+ # transaction will automatically commit the current transaction.
19
+ # Otherwise, this acknowledgement will not be committed until the
20
20
  # client commits the session transaction
21
- #
21
+ #
22
22
  # Message attribute methods available directly from the Java Message class:
23
- #
23
+ #
24
24
  # String address()
25
25
  # Returns the address this message is sent to.
26
- # void address=(SimpleString address)
26
+ # void address=(SimpleString address)
27
27
  # Sets the address to send this message to
28
- #
29
- # int body_size()
28
+ #
29
+ # int body_size()
30
30
  # Return the size (in bytes) of this message's body
31
- #
32
- # int delivery_count()
31
+ #
32
+ # int delivery_count()
33
33
  # Returns the number of times this message was delivered
34
- #
34
+ #
35
35
  # boolean durable?()
36
36
  # Returns whether this message is durable or not
37
- # void durable=(boolean durable)
37
+ # void durable=(boolean durable)
38
38
  # Sets whether this message is durable or not.
39
- #
40
- # int encode_size()
39
+ #
40
+ # int encode_size()
41
41
  # Returns the size of the encoded message
42
- #
42
+ #
43
43
  # boolean expired?()
44
44
  # Returns whether this message is expired or not
45
- #
46
- # long expiration()
45
+ #
46
+ # long expiration()
47
47
  # Returns the expiration time of this message
48
- # void expiration=(long expiration)
48
+ # void expiration=(long expiration)
49
49
  # Sets the expiration of this message.
50
- #
50
+ #
51
51
  # boolean large_message?()
52
52
  # Returns whether this message is a large message or a regular message
53
- #
54
- # long message_id()
53
+ #
54
+ # long message_id()
55
55
  # Returns the messageID
56
56
  # This is an internal message id used by HornetQ itself, it cannot be
57
57
  # set by the user.
58
58
  # The message is only visible when consuming messages, when producing
59
59
  # messages the message_id is Not returned to the caller
60
- # Use user_id to carry user supplied message id's for correlating
60
+ # Use user_id to carry user supplied message id's for correlating
61
61
  # responses to requests
62
- #
63
- # byte priority()
64
- # Returns the message priority.
65
- # Values range from 0 (less priority) to 9 (more priority) inclusive.
66
- # void priority=(byte priority)
62
+ #
63
+ # byte priority()
64
+ # Returns the message priority.
65
+ # Values range from 0 (less priority) to 9 (more priority) inclusive.
66
+ # void priority=(byte priority)
67
67
  # Sets the message priority.
68
- # Value must be between 0 and 9 inclusive.
69
- #
68
+ # Value must be between 0 and 9 inclusive.
69
+ #
70
70
  # #TODO Add timestamp_time attribute that converts to/from expiration under the covers
71
- # long timestamp()
72
- # Returns the message timestamp. The timestamp corresponds to the time
73
- # this message was handled by a HornetQ server.
74
- # void timestamp=(long timestamp)
71
+ # long timestamp()
72
+ # Returns the message timestamp. The timestamp corresponds to the time
73
+ # this message was handled by a HornetQ server.
74
+ # void timestamp=(long timestamp)
75
75
  # Sets the message timestamp.
76
- #
77
- # byte type()
76
+ #
77
+ # byte type()
78
78
  # Returns this message type
79
79
  # See: type_sym below for dealing with message types using Ruby Symbols
80
- #
81
- # org.hornetq.utils.UUID user_id()
80
+ #
81
+ # org.hornetq.utils.UUID user_id()
82
82
  # Returns the userID - this is an optional user specified UUID that can be set to identify the message and will be passed around with the message
83
- # void user_id=(org.hornetq.utils.UUID userID)
83
+ # void user_id=(org.hornetq.utils.UUID userID)
84
84
  # Sets the user ID
85
85
  #
86
86
  # Methods available directly for dealing with properties:
87
- #
88
- # boolean contains_property?(key)
87
+ #
88
+ # boolean contains_property?(key)
89
89
  # Returns true if this message contains a property with the given key, false else
90
- #
90
+ #
91
91
  # Note: Several other property methods are available directly, but since JRuby
92
92
  # deals with the conversion for you they are not documented here
93
93
  #
94
94
  # Other methods still directly accessible through this class from its child classes:
95
- #
96
- # HornetQBuffer body_buffer()
95
+ #
96
+ # HornetQBuffer body_buffer()
97
97
  # Returns the message body as a HornetQBuffer
98
- #
99
- # Map<String,Object> toMap()
100
- #
101
- #
98
+ #
99
+ # Map<String,Object> toMap()
100
+ #
101
+ #
102
102
  # Methods for dealing with large messages:
103
- #
104
- # void save_to_output_stream(OutputStream out)
105
- # Saves the content of the message to the OutputStream.
106
- # It will block until the entire content is transfered to the OutputStream.
107
- #
108
- # void body_input_stream=(InputStream bodyInputStream)
103
+ #
104
+ # void save_to_output_stream(OutputStream out)
105
+ # Saves the content of the message to the OutputStream.
106
+ # It will block until the entire content is transfered to the OutputStream.
107
+ #
108
+ # void body_input_stream=(InputStream bodyInputStream)
109
109
  # Sets the body's IntputStream.
110
- # This method is used when sending large messages
111
- #
112
- # void output_stream=(OutputStream out)
113
- # Sets the OutputStream that will receive the content of a message received
114
- # in a non blocking way. This method is used when consuming large messages
115
- #
116
- # boolean wait_output_stream_completion(long timeMilliseconds)
117
- # Wait the outputStream completion of the message. This method is used when consuming large messages
118
- # timeMilliseconds - - 0 means wait forever
119
- #
120
- # Developer notes:
110
+ # This method is used when sending large messages
111
+ #
112
+ # void output_stream=(OutputStream out)
113
+ # Sets the OutputStream that will receive the content of a message received
114
+ # in a non blocking way. This method is used when consuming large messages
115
+ #
116
+ # boolean wait_output_stream_completion(long timeMilliseconds)
117
+ # Wait the outputStream completion of the message. This method is used when consuming large messages
118
+ # timeMilliseconds - - 0 means wait forever
119
+ #
120
+ # Developer notes:
121
121
  # Cannot add to the interface Java::org.hornetq.api.core::Message because these
122
122
  # methods access instance variables in the Java object
123
123
  class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
124
124
  # Attributes
125
125
  # attr_accessor :address, :type, :durable, :expiration, :priority, :timestamp, :user_id
126
-
126
+
127
127
  # Is this a request message for which a reply is expected?
128
128
  def request?
129
129
  contains_property(Java::OrgHornetqCoreClientImpl::ClientMessageImpl::REPLYTO_HEADER_NAME)
130
130
  end
131
-
131
+
132
132
  # Return the Reply To Address as a string
133
133
  def reply_to_address
134
134
  get_string_property(Java::OrgHornetqCoreClientImpl::ClientMessageImpl::REPLYTO_HEADER_NAME)
@@ -137,29 +137,29 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
137
137
  # Set the Reply To Address
138
138
  # When supplied, the consumer of the message is expected to send a response to the
139
139
  # specified address. However, this is by convention, so no response is guaranteed
140
- #
140
+ #
141
141
  # Note: Rather than set this directly, consider creating a Client::Requestor:
142
142
  # requestor = session.create_requestor('Request Queue')
143
143
  #
144
144
  def reply_to_address=(name)
145
145
  put_string_property(Java::OrgHornetqCoreClientImpl::ClientMessageImpl::REPLYTO_HEADER_NAME, HornetQ::as_simple_string(name))
146
146
  end
147
-
147
+
148
148
  # Generate a new user_id
149
149
  #
150
150
  # Sets the user_id to a newly generated id, using a UUID algorithm
151
- #
151
+ #
152
152
  # The user_id is similar to the message_id in other JMS based messaging systems
153
153
  # in fact the HornetQ JMS API uses the user_id as the JMS Message ID.
154
- #
154
+ #
155
155
  # The internal message_id is set by the HornetQ Server and is Not returned
156
156
  # when sending messages
157
- #
157
+ #
158
158
  # Returns generated user_id
159
159
  def generate_user_id
160
160
  self.user_id = Java::org.hornetq.utils::UUIDGenerator.instance.generateUUID
161
161
  end
162
-
162
+
163
163
  # Returns the message type as one of the following symbols
164
164
  # :text => org.hornetq.api.core.Message::TEXT_TYPE
165
165
  # :bytes => org.hornetq.api.core.Message::BYTES_TYPE
@@ -168,22 +168,22 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
168
168
  # :stream => org.hornetq.api.core.Message::STREAM_TYPE
169
169
  # :default => org.hornetq.api.core.Message::DEFAULT_TYPE
170
170
  # :unknown => Any other value for message type
171
- #
171
+ #
172
172
  # If the type is none of the above, nil is returned
173
173
  #
174
174
  def type_sym
175
175
  case self.type
176
- when Java::org.hornetq.api.core.Message::TEXT_TYPE #3
176
+ when Java::org.hornetq.api.core.Message::TEXT_TYPE #3
177
177
  :text
178
178
  when Java::org.hornetq.api.core.Message::BYTES_TYPE #4
179
179
  :bytes
180
- when Java::org.hornetq.api.core.Message::MAP_TYPE #5
180
+ when Java::org.hornetq.api.core.Message::MAP_TYPE #5
181
181
  :map
182
- when Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
182
+ when Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
183
183
  :object
184
- when Java::org.hornetq.api.core.Message::STREAM_TYPE #6
184
+ when Java::org.hornetq.api.core.Message::STREAM_TYPE #6
185
185
  :stream
186
- when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
186
+ when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
187
187
  :default
188
188
  else
189
189
  :unknown
@@ -199,21 +199,21 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
199
199
  # :object => org.hornetq.api.core.Message::OBJECT_TYPE
200
200
  # :stream => org.hornetq.api.core.Message::STREAM_TYPE
201
201
  # :default => org.hornetq.api.core.Message::DEFAULT_TYPE
202
- #
202
+ #
203
203
  def type_sym=(sym)
204
204
  case sym
205
205
  when :text
206
- self.type = Java::org.hornetq.api.core.Message::TEXT_TYPE #3
206
+ self.type = Java::org.hornetq.api.core.Message::TEXT_TYPE #3
207
207
  when :bytes
208
208
  self.type = Java::org.hornetq.api.core.Message::BYTES_TYPE #4
209
209
  when :map
210
- self.type = Java::org.hornetq.api.core.Message::MAP_TYPE #5
210
+ self.type = Java::org.hornetq.api.core.Message::MAP_TYPE #5
211
211
  when :object
212
- self.type = Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
212
+ self.type = Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
213
213
  when :stream
214
- self.type = Java::org.hornetq.api.core.Message::STREAM_TYPE #6
214
+ self.type = Java::org.hornetq.api.core.Message::STREAM_TYPE #6
215
215
  when :default
216
- self.type = Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
216
+ self.type = Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
217
217
  else
218
218
  raise "Invalid message type_sym:#{sym.to_s}"
219
219
  end
@@ -226,15 +226,15 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
226
226
  buf = body_buffer
227
227
  buf.reset_reader_index
228
228
  available = body_size
229
-
229
+
230
230
  return nil if available == 0
231
231
 
232
232
  case type
233
233
  when Java::org.hornetq.api.core.Message::BYTES_TYPE #4
234
- result = ""
234
+ result = ""
235
235
  bytes_size = 1024
236
236
  bytes = Java::byte[bytes_size].new
237
-
237
+
238
238
  while (n = available < bytes_size ? available : bytes_size) > 0
239
239
  buf.read_bytes(bytes, 0, n)
240
240
  if n == bytes_size
@@ -245,40 +245,40 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
245
245
  available -= n
246
246
  end
247
247
  result
248
-
249
- when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
248
+
249
+ when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
250
250
  #TODO Default Type?
251
-
252
- when Java::org.hornetq.api.core.Message::MAP_TYPE #5
251
+
252
+ when Java::org.hornetq.api.core.Message::MAP_TYPE #5
253
253
  Java::org.hornetq.utils::TypedProperties.new.decode(body_buffer)
254
-
255
- when Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
254
+
255
+ when Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
256
256
  # TODO Java Object Type
257
-
258
- when Java::org.hornetq.api.core.Message::STREAM_TYPE #6
257
+
258
+ when Java::org.hornetq.api.core.Message::STREAM_TYPE #6
259
259
  #TODO Stream Type
260
-
261
- when Java::org.hornetq.api.core.Message::TEXT_TYPE #3
260
+
261
+ when Java::org.hornetq.api.core.Message::TEXT_TYPE #3
262
262
  body_buffer.read_nullable_simple_string.to_string
263
263
  else
264
264
  raise "Unknown Message Type, use Message#body_buffer instead"
265
265
  end
266
266
  end
267
-
267
+
268
268
  # Write data into the message body
269
- #
269
+ #
270
270
  # Note: The message type Must be set before calling this method
271
- #
271
+ #
272
272
  # Data is automatically converted based on the message type
273
- #
273
+ #
274
274
  def body=(data)
275
275
  body_buffer.reset_writer_index
276
276
  case type
277
-
277
+
278
278
  when Java::org.hornetq.api.core.Message::BYTES_TYPE #4
279
279
  body_buffer.write_bytes(data.respond_to?(:to_java_bytes) ? data.to_java_bytes : data)
280
-
281
- when Java::org.hornetq.api.core.Message::MAP_TYPE #5
280
+
281
+ when Java::org.hornetq.api.core.Message::MAP_TYPE #5
282
282
  if data.kind_of? Java::org.hornetq.utils::TypedProperties
283
283
  data.encode(body_buffer)
284
284
  elsif data.responds_to? :each_pair
@@ -292,30 +292,30 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
292
292
  else
293
293
  raise "Unrecognized data type #{data.class.name} being set when the message type is MAP"
294
294
  end
295
-
296
- when Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
295
+
296
+ when Java::org.hornetq.api.core.Message::OBJECT_TYPE #2
297
297
  # Serialize Java Object
298
298
  # TODO Should we do the serialize here?
299
299
  body_buffer.write_bytes(data)
300
-
301
- when Java::org.hornetq.api.core.Message::STREAM_TYPE #6
300
+
301
+ when Java::org.hornetq.api.core.Message::STREAM_TYPE #6
302
302
  # TODO Stream Type
303
-
304
- when Java::org.hornetq.api.core.Message::TEXT_TYPE #3
303
+
304
+ when Java::org.hornetq.api.core.Message::TEXT_TYPE #3
305
305
  if data.kind_of? Java::org.hornetq.api.core::SimpleString
306
306
  body_buffer.writeNullableSimpleString(data)
307
307
  else
308
308
  body_buffer.writeNullableSimpleString(Java::org.hornetq.api.core::SimpleString.new(data.to_s))
309
309
  end
310
-
311
- when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
310
+
311
+ when Java::org.hornetq.api.core.Message::DEFAULT_TYPE #0
312
312
  raise "The Message#type must be set before attempting to set the message body"
313
-
313
+
314
314
  else
315
315
  raise "Unknown Message Type, use Message#body_buffer directly"
316
316
  end
317
317
  end
318
-
318
+
319
319
  # Get a property
320
320
  def [](key)
321
321
  getObjectProperty(key.to_s)
@@ -360,7 +360,7 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
360
360
  :expiration => expiration,
361
361
  :large_message? => large_message?,
362
362
  :message_id => message_id,
363
- :priority => priority,
363
+ :priority => priority,
364
364
  :timestamp => timestamp,
365
365
  :type_sym => type_sym,
366
366
  :user_id => user_id.nil? ? nil : user_id.to_s,
@@ -371,5 +371,5 @@ class Java::OrgHornetqCoreClientImpl::ClientMessageImpl
371
371
  def inspect
372
372
  "#{self.class.name}:\nBody: #{body.inspect}\nAttributes: #{attributes.inspect}\nProperties: #{properties.inspect}"
373
373
  end
374
-
374
+
375
375
  end