diff options
| author | Martin Ritchie <ritchiem@apache.org> | 2007-06-15 16:28:46 +0000 |
|---|---|---|
| committer | Martin Ritchie <ritchiem@apache.org> | 2007-06-15 16:28:46 +0000 |
| commit | 1d23b9ec5e7296aecefe94a532a9f22dfb5fa5bb (patch) | |
| tree | ef50cdbac062e1fa4f3d35796ac3b05221e8fabe /qpid/java/client/src/main | |
| parent | 2ac0e89032b5e8ebd72a62b83451a943dd9541d9 (diff) | |
| download | qpid-python-1d23b9ec5e7296aecefe94a532a9f22dfb5fa5bb.tar.gz | |
Merged revisions 439476-447993,447995-448007,448009-448141,448143-448157,448161-448194,448196-448210,448212-448218,448220-448223,448225-448233,448235,448237-448241,448243-448596,448598-448623,448625-448850,448852-448880,448882-448982,448984-449635,449637-449639,449641-449642,449644-449645,449647-449674,449676-449719,449721-449749,449751-449762,449764-449933,449935-449941,449943-450383,450385,450387-450400,450402-450433,450435-450503,450505-450555,450557-450860,450862-451024,451026-451149,451151-451316,451318-451931,451933-452139,452141-452162,452164-452320,452322,452324-452325,452327-452333,452335-452429,452431-452528,452530-452545,452547-453192,453194-453195,453197-453536,453538,453540-453656,453658-454676,454678-454735,454737,454739-454781,454783-462728,462730-462819,462821-462833,462835-462839,462841-463071,463073-463178,463180-463308,463310-463362,463364-463375,463377-463396,463398-463402,463404-463409,463411-463661,463663-463670,463672-463673,463675-464493,464495-464502,464504-464576,464578-464613,464615-464628,464630,464632-464866,464868-464899,464901-464942,464944-464949,464951-465004,465006-465016,465018-465053,465055-465165,465167-465321,465323-465406,465408-465427,465429-465431,465433-465548,465550-466044,466047-466075,466077,466079-466081,466083-466099,466101-466112,466114-466126,466128-466240,466242-466971,466973-466978,466980-467309,467311-467312,467316-467328,467330-467485,467487-467588,467590-467604,467606-467699,467701-467706,467708-467749,467751-468069,468071-468537,468539-469241,469244-469246,469248-469318,469320-469421,469423,469425-469429,469431-469435,469437-469462,469464-469469,469472-469477,469479-469490,469492-469503,469505-469529,469531-469598,469600-469624,469626-469737,469739-469752,469754-469806,469808-469928,469930-469953,469955-470011,470013-470109,470111-470335,470338-470339,470341-470379,470381,470383-470399,470401-470446,470448-470741,470743-470758,470760-470809,470811-470817,470819-470993,470995-471001,471003-471788,471790-471792,471794-472028,472030-472032,472034-472036,472038,472040,472043,472045-472059,472061,472063,472065-472066,472068,472070-472072,472074-472080,472082,472084-472092,472094-472107,472109-472123,472125-472158,472160-472165,472167-472172,472174-472457,472459-472460,472462-472464,472466-472470,472472-472483,472486-472491,472493-472494,472496-472497,472499,472501-472503,472505-472512,472514-472544,472546-472556,472558-472560,472562-472572,472574-472587,472589-472591,472593-472605,472607,472609-472731,472733-472786,472788-472843,472845-472849,472851-472859,472861-472878,472880-472903,472905,472907-472988,472990-472991,472993-473071,473073-473086,473088-473090,473093,473095-473096,473098-473106,473108-473110,473112-473185,473187-473260,473262,473268-473270,473275-473279,473281,473284-473287,473289-473295,473297-473306,473308-473330,473332-473335,473337,473339-473344,473346-473351,473353-473355,473357-473358,473361-473471,473473-473497,473499-473535,473537-473567,473569-473888,473890-474451,474454-474492,474494-474563,474565-474843,474845-474865,474867-474932,474934-475035,475037-475144,475146-475180,475182-475265,475267-475285,475287,475289-475293,475295-475296,475298-475302,475304-475631,475633-475649,475651-475748,475750-475752,475754-476107,476109-476302,476304-476413,476415-476430,476432-476700,476702-476868,476870-477147,477149-477213,477215-477263,477265-477340,477342-477635,477637-477789,477791-477825,477827-477841,477843,477846-477852,477854,477856,477858-477865,477867-477894,477896-478022,478024-478182,478184-478211,478213-478233,478235-478236,478238-478241,478243-478252,478254-478259,478261-478263,478265,478267-478269,478271-478286,478288-478342,478344-478379,478381-478412,478414-478443,478445-478636,478639-478658,478660-478821,478823-478853,478855-478922,478924-478962,478965-478974,478976-479029,479031-479049,479051-479210,479212-479214,479216-479407,479409-479415,479417-479425,479427-479559,479561-479639,479641-479676,479678-479685,479687-480030,480033-480086,480091-480093,480095-480118,480120-480139,480141,480143-480148,480150-480156,480158-480163,480165-480177,480179-480189,480191-480193,480195-480198,480200-480220,480222-480282,480284-480292,480294-480308,480310-480317,480320-480422,480424,480426-480581,480583-480656,480658-480692,480695-480702,480704,480706-480710,480712-480910,480913-480933,480935-480945,480947-480972,480974-480993,480995-481034,481036-481158,481161-481174,481176-481220,481222-481234,481236-481260,481263-481264,481266-481296,481298-481304,481306-481311,481313-481332,481334,481336-481380,481382-481441,481443-482144,482146-482180,482182-482193,482195-482232,482234-482236,482239,482241-482242,482244-482247,482250-482251,482253,482256-482261,482264-482288,482290-482364,482366,482368,482370-482554,482556,482558-482569,482572-482636,482638,482640-482696,482698-482722,482724-482732,482734-482771,482774-482957,482959-483045,483047-483105,483108,483110-483115,483117,483119-483127,483130-483134,483136-483148,483150-483158,483160-483164,483166-483178,483180-483391,483393-483400,483402-483403,483405-483418,483420-483421,483425-483436,483438-483470,483472-483502,483504-483558,483560-483599,483601-483637,483639-483644,483646-483659,483661-483670,483672-483878,483880-483910,483912-483915,483917-483940,483942,483944-483968,483970-483972,483974-483976,483978,483980-484612,484614-484657,484659-484693,484695-484718,484720-484842,484844-484847,484849-484986,484988-485019,485021-485489,485491-485544,485546-485591,485593,485595-485697,485699-485729,485731-485734,485736-485779,485781-485787,485789-485851,485853,485855-486007,486009,486011-486020,486022-486083,486085-486097,486099-486117,486120-486131,486133-486148,486150-486161,486163-486164,486166-486197,486199-486205,486208-486247,486249-486253,486256-486427,486429-486431,486433-486554,486556-486573,486575-486593,486595,486597-486609,486611-486619,486622,486625,486627-486641,486643-486645,486649-486687,486689-486721,486723-486730,486732-486746,486748-486759,486761,486763-486777,486779-486782,486784-486788,486790,486792,486794-486796,486798-487175,487178,487180-487213,487215,487217-487267,487269-487284,487286-487298,487300-487358,487360-487367,487369-487382,487384-487434,487436-487480,487482-487547,487549-487561,487563-487565,487567-487578,487580-487615,487617-487622,487624,487626,487628,487630-487635,487637-487703,487705-487777,487780-487781,487783-487800,487802-487803,487805-487820,487822-487848,487850-487902,487904-488103,488105-488133,488135-488158,488160-488163,488165-488187,488189-488216,488218-488248,488250-488278,488280,488282-488303,488305-488313,488315-488342,488344-488351,488353-488376,488378-488449,488451-488593,488595,488597-488623,488625-488700,488702-488704,488706-488710,488714,488716-488725,488727-488744,488746-488770,488772-488798,488800,488802-488807,488809,488811-488829,488831-488843,488845-488851,488853-489069,489071-489077,489079-489081,489084-489102,489104-489105,489107-489109,489111-489112,489114-489139,489141-489178,489181-489203,489205-489211,489213,489216-489329,489332-489402,489404-489417,489419-489421,489423-489643,489645-489690,489692-489703,489705-489714,489716-489747,489749-489753,489755-489803,489805-489904,489906-490372,490374-490504,490506-490604,490606-490707,490710-490733,490735-490871,490873-490984,490986-491028,491030,491032-491071,491073-491119,491121-491576,491578-491672,491674-491800,491802-491838,491840-491878,491880-492183,492185-492279,492281-492317,492319-492513,492515-492584,492586-492587,492589-492601,492603-492635,492637-492640,492642-492717,492719-492723,492725-492729,492731-492755,492757-492901,492903-492955,492957-492962,492964-492997,492999-493002,493004-493041,493043-493059,493062-493063,493065-493086,493088-493125,493127-493139,493141-493150,493152-493871,493873-494017,494019-494030,494032-494041,494043-494091,494093-494120,494122-494354,494356-494436,494438-494539,494541-494552,494554-494586,494588-494649,494651,494653-494654,494656-494657,494659-494764,494766-494768,494770-494796,494798-494799,494802,494804-494860,494862-494903,494905-494906,494908-495019,495021-495160,495162-495168,495171-495188,495190-495229,495231-495254,495256-495303,495305-495313,495315-495336,495338-495372,495374-495379,495381-495454,495457-495459,495462-495516,495518-495524,495526-495531,495533-495548,495551-495553,495555,495557-495558,495560,495562-495573,495575-495583,495585-495594,495596-495628,495630-495638,495640-495651,495653-495660,495662-495753,495755-496259,496261-496262,496264-496269,496271-496275,496277-496301,496303-496316,496318-496383,496385-496413,496415-496495,496497-496625,496627-496636,496638-496640,496642-496647,496650-496657,496659-496660,496663-496664,496666-496677,496679-496681,496683-496730,496732-496750,496752,496754-496784,496786-496832,496834-496840,496842-496990,496992-496995,496997-497340,497343-497351,497353-497403,497405-497424,497426-497438,497440-497481,497483-497497,497499-497765,497767-497769,497771-497775,497777-497778,497780,497782-497783,497785,497787-497812,497814-497871,497873-497877,497879-498573,498575-498588,498590,498592,498594-498636,498638-498669,498671-498686,498688-498689,498691-498719,498721-498964,498966-498969,498971-498973,498975-498982,498985-499035,499037-499040,499042,499044-499048,499050-499082,499084-499086,499088-499164,499167-499169,499171-499355,499357-499370,499372-499373,499375-499391,499393,499395-499425,499428,499430-499445,499447-499455,499457-499460,499462-499465,499467,499469-499489,499491-499492,499494-499531,499533-499562,499566-499627,499629-499715,499717-499732,499734-499755,499758-499763,499765-499780,499782-499795,499797-499802,499804-499844,499846,499848-499850,499852-499863,499865-499873,499875-499974,499976-499978,499980-500263,500265-500283,500285-500309,500311-501000,501002,501012-501057,501059-501095,501097-501390,501392-501410,501413-501447,501449-501454,501456,501458-501464,501466-501471,501473-501803,501805-501913,501915-501916,501918-501919,501921-501944,501946-502171,502173-502177,502181,502183-502247,502250-502252,502254-502260,502262-502267,502270,502272,502274-502575,502577-502609,502611-502619,502621-502626,502628-502654,502656-503592,503594-503603,503605-503608,503610-503636,503638-503645,503647-503705,503707-503789,503791-504024,504026-504111,504113-504506,504508-504735,504737-504863,504865-504867,504869-504914,504916-505241,505243-505254,505257-505267,505269-505354,505356-505891,505893-505971,505973-506400,506402-506404,506407-506438,506440-506516,506518-506541,506543-506966,506968-506971,506973-507095,507097-507108,507111-507454,507456,507459-507471,507473-507556,507558,507560-507581,507585-507594,507597,507599-507608,507610-507728,507730-507893,507895-507937,507940-508234,508236-508350,508352-508365,508367-508380,508383,508386-508415,508417-508648,508650-508941,508943-509146,509148-509171,509173-509175,509179-509201,509203-509207,509209-509215,509217-509222,509224-509477,509480-509627,509629-509634,509636-509641,509643-509736,509738-509931,509933-510059,510061-510075,510077-510158,510161-510896,510898-510938,510940-511388,511390-511922,511924-512287,512289-512698,512702-512813,512815-512817,512819-513359,513361-513370,513372-514702,514704-514886,514888-514902,514904-515126,515129-515141,515143-515516,515518-515534,515536-515538,515540-515648,515650-515651,515653-516070,516072-516411,516413-516448,516450,516452-517637,517639-517647,517649-517659,517661-517663,517665-517677,517679-517682,517684-517744,517746-518085,518087-518175,518177-518558,518560-518568,518571-518666,518668,518670-518699,518701-518987,518990-518992,518994-519908,519910-519932,519934-520414,520416-520842,520844-520937,520939-521362,521364-521681,521683-521704,521706-521709,521711-521714,521716-521781,521783-521792,521794-522462,522464-522527,522529-522534,522536-522566,522568-522958,522960,522962-522966,522968-522976,522978-522980,522982-522988,522992-522993,522995-523244,523246-523746,523748-524049,524051-524738,524741-524742,524744-524762,524764,524766,524768-525486,525488-525530,525532,525534,525537-525552,525554-525765,525767-525776,525778-525784,525789-525803,525805-525816,525818-525828,525830-525861,525863-525866,525868-526090,526092-526112,526114-526116,526119-526121,526123-526149,526151-526153,526155-526156,526160-526165,526167-526186,526188-526193,526196-526197,526200-526665,526667-526682,526686-526690,526693,526695-526708,526710-526713,526715-526775,526777-526802,526804-526806,526808-527048,527051-527052,527054-527181,527183-527486,527488-527492,527494-527498,527500-527508,527510-527517,527519-527536,527538-527555,527559-527802,527804-527842,527844-527847,527849-527875,527877-527940,527942-527958,527960-527971,527973-528002,528004,528006-528423,528425-529232,529234-529245,529247-529296,529298-529634,529636-529658,529660-529665,529667-529668,529670-530033,530035-530036,530038-530040,530045-530046,530050-530051,530053-530431,530433-530436,530439-530440,530443,530445-530446,530448,530450-530682,530684,530687-530696,530698-530733,530735-530776,530778-530795,530799,530801-530811,530813-530818,530820-530837,530839-531436,531438-531455,531457,531459-531511,531514,531516,531519-531523,531525,531528-531858,531860-531864,531866-531907,531909-531916,531918-531936,531938-531988,531990-532001,532003-532371,532373-532465,532467-532727,532729-532765,532767-532785,532788-532790,532792-532793,532795-533064,533066-533074,533076,533080-533130,533132-533139,533142-533703,533705-533720,533722-533763,533766-533818,533820-533839,533841-533859,533862-534035,534037-534112,534114-534116,534118-534472,534474-534477,534479-534762,534764-534896,534898-534902,534904-535253,535255-535308,535310-535808,535810-535873,535875-536007,536009-536140,536142-536162,536165-536242,536244-536252,536254-536278,536280-536338,536340-536448,536450-536479,536481-536482,536484-536485,536487-536495,536497,536500-536505,536507-536561,536563-536570,536572,536574-536583,536586-536823,536825-537014,537016-537018,537020-537025,537027-537028,537030-537160,537162-537170,537172-537672,537674-537781,537783-537833,537836-537840,537842-537844,537846-537953,537955-538034,538036-538078,538080-538083,538085-538097,538099-538108,538110-538239,538241-538881,538883-538906,538908-538911,538913-538921,538923-539177,539179-539190,539192-539475,539477-539500,539502-539593,539595-539782,539784-539787,539789-540106,540108-540168,540170-540510,540512-541919,541921-544507,544509-544865,544867-545145,545147-547177,547179-547627 via svnmerge from
https://svn.apache.org/repos/asf/incubator/qpid/branches/M2
........
r539470 | ritchiem | 2007-05-18 14:50:59 +0100 (Fri, 18 May 2007) | 1 line
QPID-401 Integrated python tests with maven tested on windows CMD.exe and linux FC5
........
r539481 | ritchiem | 2007-05-18 15:30:06 +0100 (Fri, 18 May 2007) | 1 line
QPID-401 Update to allow -Dskip-python-tests to disable python checking
........
r539484 | ritchiem | 2007-05-18 15:35:13 +0100 (Fri, 18 May 2007) | 1 line
QPID-401 Update to allow -Dskip-python-tests to disable python checking
........
r541247 | rgodfrey | 2007-05-24 10:57:00 +0100 (Thu, 24 May 2007) | 1 line
QPID-482 : Small performance tweaks
........
r542484 | rupertlssmith | 2007-05-29 11:52:29 +0100 (Tue, 29 May 2007) | 1 line
Can now pass property to skip python tests, set in settings.xml
........
r542789 | ritchiem | 2007-05-30 11:09:28 +0100 (Wed, 30 May 2007) | 1 line
Update to ensure fastinstall profile skips the broker python tests.
........
r543496 | rupertlssmith | 2007-06-01 15:33:07 +0100 (Fri, 01 Jun 2007) | 1 line
QPID-402: FailoverException falling through to client. All blocking operations now wrapped in failover support wrappers.
........
r544109 | ritchiem | 2007-06-04 10:47:53 +0100 (Mon, 04 Jun 2007) | 7 lines
Addition of a sustained test client. This is currently setup for running a pub/sub test.
The test allows for multiple clients to connect and participate in testing the broker throughput.
A single producer continually sends messages to a topic which the clients then send batched results back about.
The producer uses the timings in the reports to update the rate at which it sends messages. Ideally reaching a steady state where all messages produced are received by everyone within a specified time frame.
........
r544422 | ritchiem | 2007-06-05 09:50:54 +0100 (Tue, 05 Jun 2007) | 1 line
Added documentation on how to run the sustained tests.
........
r546096 | rupertlssmith | 2007-06-11 12:23:08 +0100 (Mon, 11 Jun 2007) | 1 line
Set up top dir location and path to parent pom. Needed in preparation for deploy and release plugins.
........
r546190 | rupertlssmith | 2007-06-11 17:43:57 +0100 (Mon, 11 Jun 2007) | 1 line
Removed log4j dependency from client. Using slf4j instead, end-user to supply logging implementation as desired. Log4j used for tests.
........
r546441 | rupertlssmith | 2007-06-12 10:52:29 +0100 (Tue, 12 Jun 2007) | 1 line
QPID-465, now throws UnsupportedOperationException when sending to a null queue in QueueSender.
........
r546458 | ritchiem | 2007-06-12 12:41:17 +0100 (Tue, 12 Jun 2007) | 1 line
Added repository info for running mvn rat:check
........
r547627 | ritchiem | 2007-06-15 12:21:40 +0100 (Fri, 15 Jun 2007) | 1 line
QPID-511 adjusted to use the ReadWriteThreadModel rather than setting editing the filterChain directly which could cause problems when using an InVM transport due to the way the InVM transport alters the filter chain during a connect call.
........
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk@547730 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'qpid/java/client/src/main')
51 files changed, 3436 insertions, 2679 deletions
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java index 2c92cfb85e..ebbbac5e6e 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQConnection.java @@ -20,34 +20,14 @@ */ package org.apache.qpid.client; -import java.io.IOException; -import java.net.ConnectException; -import java.nio.channels.UnresolvedAddressException; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.jms.*; -import javax.jms.IllegalStateException; -import javax.naming.NamingException; -import javax.naming.Reference; -import javax.naming.Referenceable; -import javax.naming.StringRefAddr; - import org.apache.log4j.Logger; - import org.apache.qpid.AMQConnectionFailureException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQUndeliveredException; import org.apache.qpid.AMQUnresolvedAddressException; -import org.apache.qpid.client.failover.FailoverSupport; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.client.failover.FailoverRetrySupport; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.state.AMQState; import org.apache.qpid.client.transport.TransportConnection; @@ -68,6 +48,37 @@ import org.apache.qpid.jms.FailoverPolicy; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.url.URLSyntaxException; +import javax.jms.ConnectionConsumer; +import javax.jms.ConnectionMetaData; +import javax.jms.Destination; +import javax.jms.ExceptionListener; +import javax.jms.IllegalStateException; +import javax.jms.JMSException; +import javax.jms.Queue; +import javax.jms.QueueConnection; +import javax.jms.QueueSession; +import javax.jms.ServerSessionPool; +import javax.jms.Topic; +import javax.jms.TopicConnection; +import javax.jms.TopicSession; +import javax.naming.NamingException; +import javax.naming.Reference; +import javax.naming.Referenceable; +import javax.naming.StringRefAddr; +import java.io.IOException; +import java.net.ConnectException; +import java.nio.channels.UnresolvedAddressException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + public class AMQConnection extends Closeable implements Connection, QueueConnection, TopicConnection, Referenceable { private static final Logger _logger = Logger.getLogger(AMQConnection.class); @@ -163,7 +174,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect * @throws URLSyntaxException */ public AMQConnection(String broker, String username, String password, String clientName, String virtualHost) - throws AMQException, URLSyntaxException + throws AMQException, URLSyntaxException { this(new AMQConnectionURL( ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" @@ -182,7 +193,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect * @throws URLSyntaxException */ public AMQConnection(String broker, String username, String password, String clientName, String virtualHost, - SSLConfiguration sslConfig) throws AMQException, URLSyntaxException + SSLConfiguration sslConfig) throws AMQException, URLSyntaxException { this(new AMQConnectionURL( ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" @@ -191,28 +202,28 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } public AMQConnection(String host, int port, String username, String password, String clientName, String virtualHost) - throws AMQException, URLSyntaxException + throws AMQException, URLSyntaxException { this(host, port, false, username, password, clientName, virtualHost, null); } public AMQConnection(String host, int port, String username, String password, String clientName, String virtualHost, - SSLConfiguration sslConfig) throws AMQException, URLSyntaxException + SSLConfiguration sslConfig) throws AMQException, URLSyntaxException { this(host, port, false, username, password, clientName, virtualHost, sslConfig); } public AMQConnection(String host, int port, boolean useSSL, String username, String password, String clientName, - String virtualHost, SSLConfiguration sslConfig) throws AMQException, URLSyntaxException + String virtualHost, SSLConfiguration sslConfig) throws AMQException, URLSyntaxException { this(new AMQConnectionURL( useSSL ? (ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" - + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port - + "'" + "," + ConnectionURL.OPTIONS_SSL + "='true'") + + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port + + "'" + "," + ConnectionURL.OPTIONS_SSL + "='true'") : (ConnectionURL.AMQ_PROTOCOL + "://" + username + ":" + password + "@" - + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port - + "'" + "," + ConnectionURL.OPTIONS_SSL + "='false'")), sslConfig); + + ((clientName == null) ? "" : clientName) + virtualHost + "?brokerlist='tcp://" + host + ":" + port + + "'" + "," + ConnectionURL.OPTIONS_SSL + "='false'")), sslConfig); } public AMQConnection(String connection) throws AMQException, URLSyntaxException @@ -226,8 +237,8 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } /** - * @todo Some horrible stuff going on here with setting exceptions to be non-null to detect if an exception - * was thrown during the connection! Intention not clear. Use a flag anyway, not exceptions... Will fix soon. + * @todo Some horrible stuff going on here with setting exceptions to be non-null to detect if an exception was + * thrown during the connection! Intention not clear. Use a flag anyway, not exceptions... Will fix soon. */ public AMQConnection(ConnectionURL connectionURL, SSLConfiguration sslConfig) throws AMQException { @@ -294,7 +305,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (_logger.isInfoEnabled()) { _logger.info("Unable to connect to broker at " + _failoverPolicy.getCurrentBrokerDetails(), - e.getCause()); + e.getCause()); } } } @@ -332,7 +343,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (lastException instanceof UnresolvedAddressException) { e = new AMQUnresolvedAddressException(message, _failoverPolicy.getCurrentBrokerDetails().toString(), - null); + null); } if (e.getCause() != null) @@ -446,7 +457,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (_logger.isInfoEnabled()) { _logger.info(e.getMessage() + ":Unable to connect to broker at " - + _failoverPolicy.getCurrentBrokerDetails()); + + _failoverPolicy.getCurrentBrokerDetails()); } } } @@ -485,96 +496,96 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, final int prefetch) - throws JMSException + throws JMSException { return createSession(transacted, acknowledgeMode, prefetch, prefetch); } public org.apache.qpid.jms.Session createSession(final boolean transacted, final int acknowledgeMode, - final int prefetchHigh, final int prefetchLow) throws JMSException + final int prefetchHigh, final int prefetchLow) throws JMSException { checkNotClosed(); + if (channelLimitReached()) { throw new ChannelLimitReachedException(_maximumChannelCount); } - else - { - return (org.apache.qpid.jms.Session) new FailoverSupport() + + return new FailoverRetrySupport<org.apache.qpid.jms.Session, JMSException>( + new FailoverProtectedOperation<org.apache.qpid.jms.Session, JMSException>() + { + public org.apache.qpid.jms.Session execute() throws JMSException, FailoverException { - public Object operation() throws JMSException + int channelId = _idFactory.incrementAndGet(); + + if (_logger.isDebugEnabled()) { - int channelId = _idFactory.incrementAndGet(); + _logger.debug("Write channel open frame for channel id " + channelId); + } - if (_logger.isDebugEnabled()) + // We must create the session and register it before actually sending the frame to the server to + // open it, so that there is no window where we could receive data on the channel and not be set + // up to handle it appropriately. + AMQSession session = + new AMQSession(AMQConnection.this, channelId, transacted, acknowledgeMode, prefetchHigh, + prefetchLow); + // _protocolHandler.addSessionByChannel(channelId, session); + registerSession(channelId, session); + + boolean success = false; + try + { + createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted); + success = true; + } + catch (AMQException e) + { + JMSException jmse = new JMSException("Error creating session: " + e); + jmse.setLinkedException(e); + throw jmse; + } + finally + { + if (!success) { - _logger.debug("Write channel open frame for channel id " + channelId); + deregisterSession(channelId); } + } - // We must create the session and register it before actually sending the frame to the server to - // open it, so that there is no window where we could receive data on the channel and not be set - // up to handle it appropriately. - AMQSession session = - new AMQSession(AMQConnection.this, channelId, transacted, acknowledgeMode, prefetchHigh, - prefetchLow); - // _protocolHandler.addSessionByChannel(channelId, session); - registerSession(channelId, session); - - boolean success = false; + if (_started) + { try { - createChannelOverWire(channelId, prefetchHigh, prefetchLow, transacted); - success = true; + session.start(); } catch (AMQException e) { - JMSException jmse = new JMSException("Error creating session: " + e); - jmse.setLinkedException(e); - throw jmse; + throw new JMSAMQException(e); } - finally - { - if (!success) - { - deregisterSession(channelId); - } - } - - if (_started) - { - try - { - session.start(); - } - catch (AMQException e) - { - throw new JMSAMQException(e); - } - } - - return session; } - }.execute(this); - } + + return session; + } + }, this).execute(); } private void createChannelOverWire(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) - throws AMQException + throws AMQException, FailoverException { // TODO: Be aware of possible changes to parameter order as versions change. _protocolHandler.syncWrite(ChannelOpenBody.createAMQFrame(channelId, _protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion(), null), // outOfBand - ChannelOpenOkBody.class); + _protocolHandler.getProtocolMinorVersion(), null), // outOfBand + ChannelOpenOkBody.class); // todo send low water mark when protocol allows. // todo Be aware of possible changes to parameter order as versions change. _protocolHandler.syncWrite(BasicQosBody.createAMQFrame(channelId, _protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion(), false, // global - prefetchHigh, // prefetchCount - 0), // prefetchSize - BasicQosOkBody.class); + _protocolHandler.getProtocolMinorVersion(), false, // global + prefetchHigh, // prefetchCount + 0), // prefetchSize + BasicQosOkBody.class); if (transacted) { @@ -585,11 +596,12 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect // TODO: Be aware of possible changes to parameter order as versions change. _protocolHandler.syncWrite(TxSelectBody.createAMQFrame(channelId, _protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion()), TxSelectOkBody.class); + _protocolHandler.getProtocolMinorVersion()), TxSelectOkBody.class); } } - private void reopenChannel(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) throws AMQException + private void reopenChannel(int channelId, int prefetchHigh, int prefetchLow, boolean transacted) + throws AMQException, FailoverException { try { @@ -861,7 +873,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } public ConnectionConsumer createConnectionConsumer(Destination destination, String messageSelector, - ServerSessionPool sessionPool, int maxMessages) throws JMSException + ServerSessionPool sessionPool, int maxMessages) throws JMSException { checkNotClosed(); @@ -869,7 +881,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } public ConnectionConsumer createConnectionConsumer(Queue queue, String messageSelector, ServerSessionPool sessionPool, - int maxMessages) throws JMSException + int maxMessages) throws JMSException { checkNotClosed(); @@ -877,7 +889,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } public ConnectionConsumer createConnectionConsumer(Topic topic, String messageSelector, ServerSessionPool sessionPool, - int maxMessages) throws JMSException + int maxMessages) throws JMSException { checkNotClosed(); @@ -885,7 +897,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect } public ConnectionConsumer createDurableConnectionConsumer(Topic topic, String subscriptionName, String messageSelector, - ServerSessionPool sessionPool, int maxMessages) throws JMSException + ServerSessionPool sessionPool, int maxMessages) throws JMSException { // TODO Auto-generated method stub checkNotClosed(); @@ -1077,8 +1089,8 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect if (code != null) { je = - new JMSException(Integer.toString(code.getCode()), "Exception thrown against " + toString() + ": " - + cause); + new JMSException(Integer.toString(code.getCode()), "Exception thrown against " + toString() + ": " + + cause); } else { @@ -1142,7 +1154,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect * For all sessions, and for all consumers in those sessions, resubscribe. This is called during failover handling. * The caller must hold the failover mutex before calling this method. */ - public void resubscribeSessions() throws JMSException, AMQException + public void resubscribeSessions() throws JMSException, AMQException, FailoverException { ArrayList sessions = new ArrayList(_sessions.values()); _logger.info(MessageFormat.format("Resubscribing sessions = {0} sessions.size={1}", sessions, sessions.size())); // FIXME: removeKey? @@ -1184,7 +1196,7 @@ public class AMQConnection extends Closeable implements Connection, QueueConnect public Reference getReference() throws NamingException { return new Reference(AMQConnection.class.getName(), new StringRefAddr(AMQConnection.class.getName(), toURL()), - AMQConnectionFactory.class.getName(), null); // factory location + AMQConnectionFactory.class.getName(), null); // factory location } public SSLConfiguration getSSLConfiguration() diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java index 585991d905..cc5af07b20 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQDestination.java @@ -60,9 +60,9 @@ public abstract class AMQDestination implements Destination, Referenceable private static final int IS_EXCLUSIVE_MASK = 0x2; private static final int IS_AUTODELETE_MASK = 0x4; - public static final byte QUEUE_TYPE = 1; - public static final byte TOPIC_TYPE = 2; - public static final byte UNKNOWN_TYPE = 3; + public static final Integer QUEUE_TYPE = Integer.valueOf(1); + public static final Integer TOPIC_TYPE = Integer.valueOf(2); + public static final Integer UNKNOWN_TYPE = Integer.valueOf(3); protected AMQDestination(String url) throws URLSyntaxException { @@ -213,7 +213,7 @@ public abstract class AMQDestination implements Destination, Referenceable } public String toURL() - { + { String url = _url; if(url == null) { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java index 4662f80c5b..28e5992b26 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQQueueBrowser.java @@ -20,9 +20,8 @@ */ package org.apache.qpid.client; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.jms.IllegalStateException; import javax.jms.JMSException; @@ -30,12 +29,13 @@ import javax.jms.Message; import javax.jms.Queue; import javax.jms.QueueBrowser; -import org.apache.log4j.Logger; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.concurrent.atomic.AtomicBoolean; public class AMQQueueBrowser implements QueueBrowser { - private static final Logger _logger = Logger.getLogger(AMQQueueBrowser.class); - + private static final Logger _logger = LoggerFactory.getLogger(AMQQueueBrowser.class); private AtomicBoolean _isClosed = new AtomicBoolean(); private final AMQSession _session; @@ -43,20 +43,21 @@ public class AMQQueueBrowser implements QueueBrowser private final ArrayList<BasicMessageConsumer> _consumers = new ArrayList<BasicMessageConsumer>(); private final String _messageSelector; - AMQQueueBrowser(AMQSession session, AMQQueue queue, String messageSelector) throws JMSException { _session = session; _queue = queue; - _messageSelector = (messageSelector == null) || (messageSelector.trim().length() == 0) ? null : messageSelector; + _messageSelector = ((messageSelector == null) || (messageSelector.trim().length() == 0)) ? null : messageSelector; // Create Consumer to verify message selector. - BasicMessageConsumer consumer = (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); - consumer.close(); + BasicMessageConsumer consumer = + (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); + consumer.close(); } public Queue getQueue() throws JMSException { checkState(); + return _queue; } @@ -66,6 +67,7 @@ public class AMQQueueBrowser implements QueueBrowser { throw new IllegalStateException("Queue Browser"); } + if (_session.isClosed()) { throw new IllegalStateException("Session is closed"); @@ -77,47 +79,48 @@ public class AMQQueueBrowser implements QueueBrowser { checkState(); + return _messageSelector; } public Enumeration getEnumeration() throws JMSException { checkState(); - final BasicMessageConsumer consumer = (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); + final BasicMessageConsumer consumer = + (BasicMessageConsumer) _session.createBrowserConsumer(_queue, _messageSelector, false); _consumers.add(consumer); return new Enumeration() - { - - - Message _nextMessage = consumer.receive(); - - - public boolean hasMoreElements() { - _logger.info("QB:hasMoreElements:" + (_nextMessage != null)); - return (_nextMessage != null); - } - public Object nextElement() - { - Message msg = _nextMessage; - try + Message _nextMessage = consumer.receive(); + + public boolean hasMoreElements() { - _logger.info("QB:nextElement about to receive"); + _logger.info("QB:hasMoreElements:" + (_nextMessage != null)); - _nextMessage = consumer.receive(); - _logger.info("QB:nextElement received:" + _nextMessage); + return (_nextMessage != null); } - catch (JMSException e) + + public Object nextElement() { - _logger.warn("Exception caught while queue browsing", e); - _nextMessage = null; + Message msg = _nextMessage; + try + { + _logger.info("QB:nextElement about to receive"); + + _nextMessage = consumer.receive(); + _logger.info("QB:nextElement received:" + _nextMessage); + } + catch (JMSException e) + { + _logger.warn("Exception caught while queue browsing", e); + _nextMessage = null; + } + + return msg; } - - return msg; - } - }; + }; } public void close() throws JMSException @@ -126,8 +129,8 @@ public class AMQQueueBrowser implements QueueBrowser { consumer.close(); } + _consumers.clear(); } - } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java index 8796a225ba..c2b7bc26c4 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/AMQSession.java @@ -20,47 +20,15 @@ */ package org.apache.qpid.client; -import java.io.Serializable; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; -import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.jms.BytesMessage; -import javax.jms.Destination; -import javax.jms.IllegalStateException; -import javax.jms.InvalidDestinationException; -import javax.jms.InvalidSelectorException; -import javax.jms.JMSException; -import javax.jms.MapMessage; -import javax.jms.MessageConsumer; -import javax.jms.MessageListener; -import javax.jms.MessageProducer; -import javax.jms.ObjectMessage; -import javax.jms.Queue; -import javax.jms.QueueBrowser; -import javax.jms.QueueReceiver; -import javax.jms.QueueSender; -import javax.jms.QueueSession; -import javax.jms.StreamMessage; -import javax.jms.TemporaryQueue; -import javax.jms.TemporaryTopic; -import javax.jms.TextMessage; -import javax.jms.Topic; -import javax.jms.TopicPublisher; -import javax.jms.TopicSession; -import javax.jms.TopicSubscriber; - import org.apache.log4j.Logger; import org.apache.qpid.AMQException; -import org.apache.qpid.AMQUndeliveredException; -import org.apache.qpid.AMQInvalidRoutingKeyException; import org.apache.qpid.AMQInvalidArgumentException; -import org.apache.qpid.client.failover.FailoverSupport; +import org.apache.qpid.AMQInvalidRoutingKeyException; +import org.apache.qpid.AMQUndeliveredException; +import org.apache.qpid.client.failover.FailoverException; +import org.apache.qpid.client.failover.FailoverNoopSupport; +import org.apache.qpid.client.failover.FailoverProtectedOperation; +import org.apache.qpid.client.failover.FailoverRetrySupport; import org.apache.qpid.client.message.AbstractJMSMessage; import org.apache.qpid.client.message.JMSBytesMessage; import org.apache.qpid.client.message.JMSMapMessage; @@ -70,21 +38,20 @@ import org.apache.qpid.client.message.JMSTextMessage; import org.apache.qpid.client.message.MessageFactoryRegistry; import org.apache.qpid.client.message.UnprocessedMessage; import org.apache.qpid.client.protocol.AMQProtocolHandler; -import org.apache.qpid.client.protocol.BlockingMethodFrameListener; import org.apache.qpid.client.util.FlowControllingBlockingQueue; import org.apache.qpid.common.AMQPFilterTypes; import org.apache.qpid.framing.AMQFrame; -import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.AccessRequestBody; -import org.apache.qpid.framing.AccessRequestOkBody; import org.apache.qpid.framing.BasicAckBody; import org.apache.qpid.framing.BasicConsumeBody; import org.apache.qpid.framing.BasicConsumeOkBody; import org.apache.qpid.framing.BasicRecoverBody; +import org.apache.qpid.framing.BasicRecoverOkBody; +import org.apache.qpid.framing.BasicRejectBody; import org.apache.qpid.framing.ChannelCloseBody; import org.apache.qpid.framing.ChannelCloseOkBody; import org.apache.qpid.framing.ChannelFlowBody; +import org.apache.qpid.framing.ChannelFlowOkBody; import org.apache.qpid.framing.ExchangeBoundBody; import org.apache.qpid.framing.ExchangeBoundOkBody; import org.apache.qpid.framing.ExchangeDeclareBody; @@ -92,358 +59,242 @@ import org.apache.qpid.framing.ExchangeDeclareOkBody; import org.apache.qpid.framing.FieldTable; import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.framing.QueueBindBody; +import org.apache.qpid.framing.QueueBindOkBody; import org.apache.qpid.framing.QueueDeclareBody; +import org.apache.qpid.framing.QueueDeclareOkBody; import org.apache.qpid.framing.QueueDeleteBody; import org.apache.qpid.framing.QueueDeleteOkBody; import org.apache.qpid.framing.TxCommitBody; import org.apache.qpid.framing.TxCommitOkBody; import org.apache.qpid.framing.TxRollbackBody; import org.apache.qpid.framing.TxRollbackOkBody; -import org.apache.qpid.framing.QueueBindOkBody; -import org.apache.qpid.framing.QueueDeclareOkBody; -import org.apache.qpid.framing.ChannelFlowOkBody; -import org.apache.qpid.framing.BasicRecoverOkBody; -import org.apache.qpid.framing.BasicRejectBody; import org.apache.qpid.jms.Session; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.url.AMQBindingURL; import org.apache.qpid.url.URLSyntaxException; +import javax.jms.BytesMessage; +import javax.jms.Destination; +import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; +import javax.jms.InvalidSelectorException; +import javax.jms.JMSException; +import javax.jms.MapMessage; +import javax.jms.MessageConsumer; +import javax.jms.MessageListener; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Queue; +import javax.jms.QueueBrowser; +import javax.jms.QueueReceiver; +import javax.jms.QueueSender; +import javax.jms.QueueSession; +import javax.jms.StreamMessage; +import javax.jms.TemporaryQueue; +import javax.jms.TemporaryTopic; +import javax.jms.TextMessage; +import javax.jms.Topic; +import javax.jms.TopicPublisher; +import javax.jms.TopicSession; +import javax.jms.TopicSubscriber; +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + */ public class AMQSession extends Closeable implements Session, QueueSession, TopicSession { + /** Used for debugging. */ private static final Logger _logger = Logger.getLogger(AMQSession.class); + /** Used for debugging in the dispatcher. */ + private static final Logger _dispatcherLogger = Logger.getLogger(Dispatcher.class); + + /** The default maximum number of prefetched message at which to suspend the channel. */ public static final int DEFAULT_PREFETCH_HIGH_MARK = 5000; + + /** The default minimum number of prefetched messages at which to resume the channel. */ public static final int DEFAULT_PREFETCH_LOW_MARK = 2500; + /** + * The default value for immediate flag used by producers created by this session is false. That is, a consumer does + * not need to be attached to a queue. + */ + protected static final boolean DEFAULT_IMMEDIATE = false; + + /** + * The default value for mandatory flag used by producers created by this session is true. That is, server will not + * silently drop messages where no queue is connected to the exchange for the message. + */ + protected static final boolean DEFAULT_MANDATORY = true; + + /** System property to enable strict AMQP compliance. */ + public static final String STRICT_AMQP = "STRICT_AMQP"; + + /** Strict AMQP default setting. */ + public static final String STRICT_AMQP_DEFAULT = "false"; + + /** System property to enable failure if strict AMQP compliance is violated. */ + public static final String STRICT_AMQP_FATAL = "STRICT_AMQP_FATAL"; + + /** Strickt AMQP failure default. */ + public static final String STRICT_AMQP_FATAL_DEFAULT = "true"; + + /** System property to enable immediate message prefetching. */ + public static final String IMMEDIATE_PREFETCH = "IMMEDIATE_PREFETCH"; + + /** Immediate message prefetch default. */ + public static final String IMMEDIATE_PREFETCH_DEFAULT = "false"; + + /** The connection to which this session belongs. */ private AMQConnection _connection; + /** Used to indicate whether or not this is a transactional session. */ private boolean _transacted; + /** Holds the sessions acknowledgement mode. */ private int _acknowledgeMode; + /** Holds this session unique identifier, used to distinguish it from other sessions. */ private int _channelId; + /** @todo This does not appear to be set? */ private int _ticket; + /** Holds the high mark for prefetched message, at which the session is suspended. */ private int _defaultPrefetchHighMark = DEFAULT_PREFETCH_HIGH_MARK; + + /** Holds the low mark for prefetched messages, below which the session is resumed. */ private int _defaultPrefetchLowMark = DEFAULT_PREFETCH_LOW_MARK; + /** Holds the message listener, if any, which is attached to this session. */ private MessageListener _messageListener = null; + /** Used to indicate that this session has been started at least once. */ private AtomicBoolean _startedAtLeastOnce = new AtomicBoolean(false); /** - * Used to reference durable subscribers so they requests for unsubscribe can be handled correctly. Note this only - * keeps a record of subscriptions which have been created in the current instance. It does not remember - * subscriptions between executions of the client + * Used to reference durable subscribers so that requests for unsubscribe can be handled correctly. Note this only + * keeps a record of subscriptions which have been created in the current instance. It does not remember + * subscriptions between executions of the client. */ private final ConcurrentHashMap<String, TopicSubscriberAdaptor> _subscriptions = new ConcurrentHashMap<String, TopicSubscriberAdaptor>(); + + /** + * Holds a mapping from message consumers to their identifying names, so that their subscriptions may be looked up + * in the {@link #_subscriptions} map. + */ private final ConcurrentHashMap<BasicMessageConsumer, String> _reverseSubscriptionMap = new ConcurrentHashMap<BasicMessageConsumer, String>(); - /** Used in the consume method. We generate the consume tag on the client so that we can use the nowait feature. */ - private int _nextTag = 1; - - /** This queue is bounded and is used to store messages before being dispatched to the consumer */ + /** + * Used to hold incoming messages. + * + * @todo Weaken the type once {@link FlowControllingBlockingQueue} implements Queue. + */ private final FlowControllingBlockingQueue _queue; + /** Holds the dispatcher thread for this session. */ private Dispatcher _dispatcher; + /** Holds the message factory factory for this session. */ private MessageFactoryRegistry _messageFactoryRegistry; - /** Set of all producers created by this session */ - private Map _producers = new ConcurrentHashMap(); - - /** Maps from consumer tag (String) to JMSMessageConsumer instance */ - private Map<AMQShortString, BasicMessageConsumer> _consumers = new ConcurrentHashMap<AMQShortString, BasicMessageConsumer>(); - - /** Maps from destination to count of JMSMessageConsumers */ - private ConcurrentHashMap<Destination, AtomicInteger> _destinationConsumerCount = - new ConcurrentHashMap<Destination, AtomicInteger>(); + /** Holds all of the producers created by this session, keyed by their unique identifiers. */ + private Map<Long, MessageProducer> _producers = new ConcurrentHashMap<Long, MessageProducer>(); /** - * Default value for immediate flag used by producers created by this session is false, i.e. a consumer does not - * need to be attached to a queue + * Used as a source of unique identifiers so that the consumers can be tagged to match them to BasicConsume + * methods. */ - protected static final boolean DEFAULT_IMMEDIATE = false; + private int _nextTag = 1; /** - * Default value for mandatory flag used by producers created by this sessio is true, i.e. server will not silently - * drop messages where no queue is connected to the exchange for the message + * Maps from identifying tags to message consumers, in order to pass dispatch incoming messages to the right + * consumer. */ - protected static final boolean DEFAULT_MANDATORY = true; + private Map<AMQShortString, BasicMessageConsumer> _consumers = + new ConcurrentHashMap<AMQShortString, BasicMessageConsumer>(); + + /** Provides a count of consumers on destinations, in order to be able to know if a destination has consumers. */ + private ConcurrentHashMap<Destination, AtomicInteger> _destinationConsumerCount = + new ConcurrentHashMap<Destination, AtomicInteger>(); /** - * The counter of the next producer id. This id is generated by the session and used only to allow the producer to - * identify itself to the session when deregistering itself. <p/> Access to this id does not require to be - * synchronized since according to the JMS specification only one thread of control is allowed to create producers - * for any given session instance. + * Used as a source of unique identifiers for producers within the session. + * + * <p/> Access to this id does not require to be synchronized since according to the JMS specification only one + * thread of control is allowed to create producers for any given session instance. */ private long _nextProducerId; - /** * Set when recover is called. This is to handle the case where recover() is called by application code during - * onMessage() processing. We need to make sure we do not send an auto ack if recover was called. + * onMessage() processing to enure that an auto ack is not sent. */ private boolean _inRecovery; + /** Used to indicates that the connection to which this session belongs, has been stopped. */ private boolean _connectionStopped; + /** Used to indicate that this session has a message listener attached to it. */ private boolean _hasMessageListeners; + /** Used to indicate that this session has been suspended. */ private boolean _suspended; + /** + * Used to protect the suspension of this session, so that critical code can be executed during suspension, without + * the session being resumed by other threads. + */ private final Object _suspensionLock = new Object(); - /** Boolean to control immediate prefetch . Records the first call to the dispatcher to prevent further flow(true) */ + /** + * Used to ensure that onlt the first call to start the dispatcher can unsuspend the channel. + * + * @todo This is accessed only within a synchronized method, so does not need to be atomic. + */ private final AtomicBoolean _firstDispatcher = new AtomicBoolean(true); - /** System property to enable strickt AMQP compliance */ - public static final String STRICT_AMQP = "STRICT_AMQP"; - /** Strickt AMQP default */ - public static final String STRICT_AMQP_DEFAULT = "false"; + /** Used to indicate that the session should start pre-fetching messages as soon as it is started. */ + private final boolean _immediatePrefetch; + /** Indicates that warnings should be generated on violations of the strict AMQP. */ private final boolean _strictAMQP; - /** System property to enable strickt AMQP compliance */ - public static final String STRICT_AMQP_FATAL = "STRICT_AMQP_FATAL"; - /** Strickt AMQP default */ - public static final String STRICT_AMQP_FATAL_DEFAULT = "true"; - + /** Indicates that runtime exceptions should be generated on vilations of the strict AMQP. */ private final boolean _strictAMQPFATAL; - /** System property to enable immediate message prefetching */ - public static final String IMMEDIATE_PREFETCH = "IMMEDIATE_PREFETCH"; - /** Immediate message prefetch default */ - public static final String IMMEDIATE_PREFETCH_DEFAULT = "false"; - - private final boolean _immediatePrefetch; - - private static final Logger _dispatcherLogger = Logger.getLogger(Dispatcher.class); - - /** Responsible for decoding a message fragment and passing it to the appropriate message consumer. */ - private class Dispatcher extends Thread - { - - /** Track the 'stopped' state of the dispatcher, a session starts in the stopped state. */ - private final AtomicBoolean _closed = new AtomicBoolean(false); - - private final Object _lock = new Object(); - - public Dispatcher() - { - super("Dispatcher-Channel-" + _channelId); - if (_dispatcherLogger.isInfoEnabled()) - { - _dispatcherLogger.info(getName() + " created"); - } - } - - public void run() - { - if (_dispatcherLogger.isInfoEnabled()) - { - _dispatcherLogger.info(getName() + " started"); - } - - UnprocessedMessage message; - - // Allow disptacher to start stopped - synchronized (_lock) - { - while (connectionStopped()) - { - try - { - _lock.wait(); - } - catch (InterruptedException e) - { - // ignore - } - } - } - - try - { - while (!_closed.get() && (message = (UnprocessedMessage) _queue.take()) != null) - { - synchronized (_lock) - { - - while (connectionStopped()) - { - _lock.wait(); - } - - dispatchMessage(message); - - while (connectionStopped()) - { - _lock.wait(); - } - - } - - } - } - catch (InterruptedException e) - { - //ignore - } - if (_dispatcherLogger.isInfoEnabled()) - { - _dispatcherLogger.info(getName() + " thread terminating for channel " + _channelId); - } - } - - // only call while holding lock - final boolean connectionStopped() - { - return _connectionStopped; - } - - boolean setConnectionStopped(boolean connectionStopped) - { - boolean currently; - synchronized (_lock) - { - currently = _connectionStopped; - _connectionStopped = connectionStopped; - _lock.notify(); - - if (_dispatcherLogger.isDebugEnabled()) - { - _dispatcherLogger.debug("Set Dispatcher Connection " + (connectionStopped ? "Stopped" : "Started") + - ": Currently " + (currently ? "Stopped" : "Started")); - } - } - return currently; - } - - private void dispatchMessage(UnprocessedMessage message) - { - if (message.getDeliverBody() != null) - { - final BasicMessageConsumer consumer = (BasicMessageConsumer) _consumers.get(message.getDeliverBody().consumerTag); - - if (consumer == null || consumer.isClosed()) - { - if (_dispatcherLogger.isInfoEnabled()) - { - if (consumer == null) - { - _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + - "[" + message.getDeliverBody().deliveryTag + "] from queue " + - message.getDeliverBody().consumerTag + - " )without a handler - rejecting(requeue)..."); - } - else - { - _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + - "[" + message.getDeliverBody().deliveryTag + "] from queue " + - " consumer(" + consumer.debugIdentity() + - ") is closed rejecting(requeue)..."); - } - } - // Don't reject if we're already closing - if (!_closed.get()) - { - rejectMessage(message, true); - } - } - else - { - consumer.notifyMessage(message, _channelId); - } - } - } - - public void close() - { - _closed.set(true); - interrupt(); - - //fixme awaitTermination - - } - - public void rollback() - { - - synchronized (_lock) - { - boolean isStopped = connectionStopped(); - - if (!isStopped) - { - setConnectionStopped(true); - } - - rejectAllMessages(true); - - _dispatcherLogger.debug("Session Pre Dispatch Queue cleared"); - - for (BasicMessageConsumer consumer : _consumers.values()) - { - if (!consumer.isNoConsume()) - { - consumer.rollback(); - } - else - { - // should perhaps clear the _SQ here. - //consumer._synchronousQueue.clear(); - consumer.clearReceiveQueue(); - } - - - } - - setConnectionStopped(isStopped); - } - - } - - public void rejectPending(BasicMessageConsumer consumer) - { - synchronized (_lock) - { - boolean stopped = _dispatcher.connectionStopped(); - - if (!stopped) - { - _dispatcher.setConnectionStopped(true); - } - - // Reject messages on pre-receive queue - consumer.rollback(); - - // Reject messages on pre-dispatch queue - rejectMessagesForConsumerTag(consumer.getConsumerTag(), true); - - // closeConsumer - consumer.markClosed(); - - _dispatcher.setConnectionStopped(stopped); - - } - } - } - - - + /** + * Creates a new session on a connection. + * + * @param con The connection on which to create the session. + * @param channelId The unique identifier for the session. + * @param transacted Indicates whether or not the session is transactional. + * @param acknowledgeMode The acknoledgement mode for the session. + * @param messageFactoryRegistry The message factory factory for the session. + * @param defaultPrefetchHighMark The maximum number of messages to prefetched before suspending the session. + * @param defaultPrefetchLowMark The number of prefetched messages at which to resume the session. + */ AMQSession(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, MessageFactoryRegistry messageFactoryRegistry, int defaultPrefetchHighMark, int defaultPrefetchLowMark) { _strictAMQP = Boolean.parseBoolean(System.getProperties().getProperty(STRICT_AMQP, STRICT_AMQP_DEFAULT)); - _strictAMQPFATAL = Boolean.parseBoolean(System.getProperties().getProperty(STRICT_AMQP_FATAL, STRICT_AMQP_FATAL_DEFAULT)); - _immediatePrefetch = _strictAMQP || Boolean.parseBoolean(System.getProperties().getProperty(IMMEDIATE_PREFETCH, IMMEDIATE_PREFETCH_DEFAULT)); + _strictAMQPFATAL = + Boolean.parseBoolean(System.getProperties().getProperty(STRICT_AMQP_FATAL, STRICT_AMQP_FATAL_DEFAULT)); + _immediatePrefetch = + _strictAMQP + || Boolean.parseBoolean(System.getProperties().getProperty(IMMEDIATE_PREFETCH, IMMEDIATE_PREFETCH_DEFAULT)); _connection = con; _transacted = transacted; @@ -455,6 +306,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi { _acknowledgeMode = acknowledgeMode; } + _channelId = channelId; _messageFactoryRegistry = messageFactoryRegistry; _defaultPrefetchHighMark = defaultPrefetchHighMark; @@ -462,27 +314,32 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi if (_acknowledgeMode == NO_ACKNOWLEDGE) { - _queue = new FlowControllingBlockingQueue(_defaultPrefetchHighMark, _defaultPrefetchLowMark, - new FlowControllingBlockingQueue.ThresholdListener() - { - public void aboveThreshold(int currentValue) - { - if (_acknowledgeMode == NO_ACKNOWLEDGE) - { - _logger.warn("Above threshold(" + _defaultPrefetchHighMark + ") so suspending channel. Current value is " + currentValue); - new Thread(new SuspenderRunner(true)).start(); - } - } - - public void underThreshold(int currentValue) - { - if (_acknowledgeMode == NO_ACKNOWLEDGE) - { - _logger.warn("Below threshold(" + _defaultPrefetchLowMark + ") so unsuspending channel. Current value is " + currentValue); - new Thread(new SuspenderRunner(false)).start(); - } - } - }); + _queue = + new FlowControllingBlockingQueue(_defaultPrefetchHighMark, _defaultPrefetchLowMark, + new FlowControllingBlockingQueue.ThresholdListener() + { + public void aboveThreshold(int currentValue) + { + if (_acknowledgeMode == NO_ACKNOWLEDGE) + { + _logger.debug( + "Above threshold(" + _defaultPrefetchHighMark + + ") so suspending channel. Current value is " + currentValue); + new Thread(new SuspenderRunner(true)).start(); + } + } + + public void underThreshold(int currentValue) + { + if (_acknowledgeMode == NO_ACKNOWLEDGE) + { + _logger.debug( + "Below threshold(" + _defaultPrefetchLowMark + + ") so unsuspending channel. Current value is " + currentValue); + new Thread(new SuspenderRunner(false)).start(); + } + } + }); } else { @@ -490,183 +347,141 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi } } - - AMQSession(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, int defaultPrefetchHigh, int defaultPrefetchLow) - { - this(con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, defaultPrefetchLow); - } - - public AMQConnection getAMQConnection() - { - return _connection; - } - - public BytesMessage createBytesMessage() throws JMSException + /** + * Creates a new session on a connection with the default message factory factory. + * + * @param con The connection on which to create the session. + * @param channelId The unique identifier for the session. + * @param transacted Indicates whether or not the session is transactional. + * @param acknowledgeMode The acknoledgement mode for the session. + * @param defaultPrefetchHigh The maximum number of messages to prefetched before suspending the session. + * @param defaultPrefetchLow The number of prefetched messages at which to resume the session. + */ + AMQSession(AMQConnection con, int channelId, boolean transacted, int acknowledgeMode, int defaultPrefetchHigh, + int defaultPrefetchLow) { - synchronized (_connection.getFailoverMutex()) - { - checkNotClosed(); - return new JMSBytesMessage(); - } + this(con, channelId, transacted, acknowledgeMode, MessageFactoryRegistry.newDefaultRegistry(), defaultPrefetchHigh, + defaultPrefetchLow); } - public MapMessage createMapMessage() throws JMSException + /** + * Acknowledges all unacknowledged messages on the session, for all message consumers on the session. + * + * @throws IllegalStateException If the session is closed. + */ + public void acknowledge() throws IllegalStateException { - synchronized (_connection.getFailoverMutex()) + if (isClosed()) { - checkNotClosed(); - return new JMSMapMessage(); + throw new IllegalStateException("Session is already closed"); } - } - - public javax.jms.Message createMessage() throws JMSException - { - return createBytesMessage(); - } - public ObjectMessage createObjectMessage() throws JMSException - { - synchronized (_connection.getFailoverMutex()) + for (BasicMessageConsumer consumer : _consumers.values()) { - checkNotClosed(); - return (ObjectMessage) new JMSObjectMessage(); + consumer.acknowledge(); } } - public ObjectMessage createObjectMessage(Serializable object) throws JMSException - { - ObjectMessage msg = createObjectMessage(); - msg.setObject(object); - return msg; - } - - public StreamMessage createStreamMessage() throws JMSException + /** + * Acknowledge one or many messages. + * + * @param deliveryTag The tag of the last message to be acknowledged. + * @param multiple <tt>true</tt> to acknowledge all messages up to and including the one specified by the + * delivery tag, <tt>false</tt> to just acknowledge that message. + * + * @todo Be aware of possible changes to parameter order as versions change. + */ + public void acknowledgeMessage(long deliveryTag, boolean multiple) { - synchronized (_connection.getFailoverMutex()) - { - checkNotClosed(); - - return new JMSStreamMessage(); - } - } + final AMQFrame ackFrame = + BasicAckBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag, + multiple); - public TextMessage createTextMessage() throws JMSException - { - synchronized (_connection.getFailoverMutex()) + if (_logger.isDebugEnabled()) { - checkNotClosed(); - - return new JMSTextMessage(); + _logger.debug("Sending ack for delivery tag " + deliveryTag + " on channel " + _channelId); } - } - - public TextMessage createTextMessage(String text) throws JMSException - { - - TextMessage msg = createTextMessage(); - msg.setText(text); - return msg; - } - - public boolean getTransacted() throws JMSException - { - checkNotClosed(); - return _transacted; - } - - public int getAcknowledgeMode() throws JMSException - { - checkNotClosed(); - return _acknowledgeMode; - } - - public void commit() throws JMSException - { - checkTransacted(); - try - { - // Acknowledge up to message last delivered (if any) for each consumer. - //need to send ack for messages delivered to consumers so far - for (Iterator<BasicMessageConsumer> i = _consumers.values().iterator(); i.hasNext();) - { - //Sends acknowledgement to server - i.next().acknowledgeLastDelivered(); - } - - // Commits outstanding messages sent and outstanding acknowledgements. - // TODO: Be aware of possible changes to parameter order as versions change. - final AMQProtocolHandler handler = getProtocolHandler(); - handler.syncWrite(TxCommitBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), - getProtocolMinorVersion()), - TxCommitOkBody.class); - } - catch (AMQException e) - { - JMSException exception = new JMSException("Failed to commit: " + e.getMessage()); - exception.setLinkedException(e); - throw exception; - } + getProtocolHandler().writeFrame(ackFrame); } - - public void rollback() throws JMSException + /** + * Binds the named queue, with the specified routing key, to the named exchange. + * + * <p/>Note that this operation automatically retries in the event of fail-over. + * + * @param queueName The name of the queue to bind. + * @param routingKey The routing key to bind the queue with. + * @param arguments Additional arguments. + * @param exchangeName The exchange to bind the queue on. + * + * @throws AMQException If the queue cannot be bound for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + * @todo Document the additional arguments that may be passed in the field table. Are these for headers exchanges? + */ + public void bindQueue(final AMQShortString queueName, final AMQShortString routingKey, final FieldTable arguments, + final AMQShortString exchangeName) throws AMQException { - synchronized (_suspensionLock) + /*new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>()*/ + new FailoverNoopSupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>() { - checkTransacted(); - try - { - // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) - // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. - // Be aware of possible changes to parameter order as versions change. - - boolean isSuspended = isSuspended(); - - if (!isSuspended) - { - suspendChannel(true); - } - - if (_dispatcher != null) - { - _dispatcher.rollback(); - } - - _connection.getProtocolHandler().syncWrite( - TxRollbackBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion()), TxRollbackOkBody.class); - - - if (!isSuspended) - { - suspendChannel(false); - } - } - catch (AMQException e) + public Object execute() throws AMQException, FailoverException { - throw (JMSException) (new JMSException("Failed to rollback: " + e).initCause(e)); + AMQFrame queueBind = + QueueBindBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + arguments, // arguments + exchangeName, // exchange + false, // nowait + queueName, // queue + routingKey, // routingKey + getTicket()); // ticket + + getProtocolHandler().syncWrite(queueBind, QueueBindOkBody.class); + + return null; } - } + }, _connection).execute(); } + /** + * Closes the session with no timeout. + * + * @throws JMSException If the JMS provider fails to close the session due to some internal error. + */ public void close() throws JMSException { close(-1); } + /** + * Closes the session. + * + * <p/>Note that this operation succeeds automatically if a fail-over interupts the sycnronous request to close the + * channel. This is because the channel is marked as closed before the request to close it is made, so the fail-over + * should not re-open it. + * + * @param timeout The timeout in milliseconds to wait for the session close acknoledgement from the broker. + * + * @throws JMSException If the JMS provider fails to close the session due to some internal error. + * @todo Be aware of possible changes to parameter order as versions change. + * @todo Not certain about the logic of ignoring the failover exception, because the channel won't be re-opened. May + * need to examine this more carefully. + * @todo Note that taking the failover mutex doesn't prevent this operation being interrupted by a failover, because + * the failover process sends the failover event before acquiring the mutex itself. + */ public void close(long timeout) throws JMSException { if (_logger.isInfoEnabled()) { - _logger.info("Closing session: " + this + ":" + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6)); + _logger.info("Closing session: " + this + ":" + + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6)); } // We must close down all producers and consumers in an orderly fashion. This is the only method - // that can be called from a different thread of control from the one controlling the session + // that can be called from a different thread of control from the one controlling the session. synchronized (_connection.getFailoverMutex()) { - //Ensure we only try and close an open session. + // Ensure we only try and close an open session. if (!_closed.getAndSet(true)) { // we pass null since this is not an error case @@ -676,18 +491,18 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi { getProtocolHandler().closeSession(this); - // TODO: Be aware of possible changes to parameter order as versions change. - final AMQFrame frame = ChannelCloseBody.createAMQFrame(getChannelId(), - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - 0, // classId - 0, // methodId - AMQConstant.REPLY_SUCCESS.getCode(), // replyCode - new AMQShortString("JMS client closing channel")); // replyText + + final AMQFrame frame = + ChannelCloseBody.createAMQFrame(getChannelId(), getProtocolMajorVersion(), getProtocolMinorVersion(), + 0, // classId + 0, // methodId + AMQConstant.REPLY_SUCCESS.getCode(), // replyCode + new AMQShortString("JMS client closing channel")); // replyText getProtocolHandler().syncWrite(frame, ChannelCloseOkBody.class, timeout); - // When control resumes at this point, a reply will have been received that - // indicates the broker has closed the channel successfully + // When control resumes at this point, a reply will have been received that + // indicates the broker has closed the channel successfully. } catch (AMQException e) { @@ -695,6 +510,13 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi jmse.setLinkedException(e); throw jmse; } + // This is ignored because the channel is already marked as closed so the fail-over process will + // not re-open it. + catch (FailoverException e) + { + _logger.debug( + "Got FailoverException during channel close, ignored as channel already marked as closed."); + } finally { _connection.deregisterSession(_channelId); @@ -703,65 +525,6 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi } } - private AMQProtocolHandler getProtocolHandler() - { - return _connection.getProtocolHandler(); - } - - - private byte getProtocolMinorVersion() - { - return getProtocolHandler().getProtocolMinorVersion(); - } - - private byte getProtocolMajorVersion() - { - return getProtocolHandler().getProtocolMajorVersion(); - } - - - /** - * Close all producers or consumers. This is called either in the error case or when closing the session normally. - * - * @param amqe the exception, may be null to indicate no error has occurred - */ - private void closeProducersAndConsumers(AMQException amqe) throws JMSException - { - JMSException jmse = null; - try - { - closeProducers(); - } - catch (JMSException e) - { - _logger.error("Error closing session: " + e, e); - jmse = e; - } - try - { - closeConsumers(amqe); - } - catch (JMSException e) - { - _logger.error("Error closing session: " + e, e); - if (jmse == null) - { - jmse = e; - } - } - if (jmse != null) - { - throw jmse; - } - } - - - public boolean isSuspended() - { - return _suspended; - } - - /** * Called when the server initiates the closure of the session unilaterally. * @@ -783,738 +546,341 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi { amqe = new AMQException(null, "Closing session forcibly", e); } + _connection.deregisterSession(_channelId); closeProducersAndConsumers(amqe); } } /** - * Called to mark the session as being closed. Useful when the session needs to be made invalid, e.g. after failover - * when the client has veoted resubscription. <p/> The caller of this method must already hold the failover mutex. + * Commits all messages done in this transaction and releases any locks currently held. + * + * <p/>If the commit fails, because the commit itself is interrupted by a fail-over between requesting that the + * commit be done, and receiving an acknowledgement that it has been done, then a JMSException will be thrown. The + * client will be unable to determine whether or not the commit actually happened on the broker in this case. + * + * @throws JMSException If the JMS provider fails to commit the transaction due to some internal error. This does + * not mean that the commit is known to have failed, merely that it is not known whether it + * failed or not. + * @todo Be aware of possible changes to parameter order as versions change. */ - void markClosed() + public void commit() throws JMSException { - _closed.set(true); - _connection.deregisterSession(_channelId); - markClosedProducersAndConsumers(); - - } + checkTransacted(); - private void markClosedProducersAndConsumers() - { - try - { - // no need for a markClosed* method in this case since there is no protocol traffic closing a producer - closeProducers(); - } - catch (JMSException e) - { - _logger.error("Error closing session: " + e, e); - } try { - markClosedConsumers(); - } - catch (JMSException e) - { - _logger.error("Error closing session: " + e, e); - } - } + // Acknowledge up to message last delivered (if any) for each consumer. + // need to send ack for messages delivered to consumers so far + for (Iterator<BasicMessageConsumer> i = _consumers.values().iterator(); i.hasNext();) + { + // Sends acknowledgement to server + i.next().acknowledgeLastDelivered(); + } - /** - * Called to close message producers cleanly. This may or may <b>not</b> be as a result of an error. There is - * currently no way of propagating errors to message producers (this is a JMS limitation). - */ - private void closeProducers() throws JMSException - { - // we need to clone the list of producers since the close() method updates the _producers collection - // which would result in a concurrent modification exception - final ArrayList clonedProducers = new ArrayList(_producers.values()); + // Commits outstanding messages sent and outstanding acknowledgements. + final AMQProtocolHandler handler = getProtocolHandler(); - final Iterator it = clonedProducers.iterator(); - while (it.hasNext()) - { - final BasicMessageProducer prod = (BasicMessageProducer) it.next(); - prod.close(); + handler.syncWrite(TxCommitBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion()), + TxCommitOkBody.class); } - // at this point the _producers map is empty - } - - /** - * Called to close message consumers cleanly. This may or may <b>not</b> be as a result of an error. - * - * @param error not null if this is a result of an error occurring at the connection level - */ - private void closeConsumers(Throwable error) throws JMSException - { - if (_dispatcher != null) + catch (AMQException e) { - _dispatcher.close(); - _dispatcher = null; + throw new JMSAMQException("Failed to commit: " + e.getMessage(), e); } - // we need to clone the list of consumers since the close() method updates the _consumers collection - // which would result in a concurrent modification exception - final ArrayList<BasicMessageConsumer> clonedConsumers = new ArrayList(_consumers.values()); - - final Iterator<BasicMessageConsumer> it = clonedConsumers.iterator(); - while (it.hasNext()) + catch (FailoverException e) { - final BasicMessageConsumer con = it.next(); - if (error != null) - { - con.notifyError(error); - } - else - { - con.close(); - } + throw new JMSAMQException("Fail-over interrupted commit. Status of the commit is uncertain.", e); } - // at this point the _consumers map will be empty } - private void markClosedConsumers() throws JMSException + public void confirmConsumerCancelled(AMQShortString consumerTag) { - if (_dispatcher != null) - { - _dispatcher.close(); - _dispatcher = null; - } - // we need to clone the list of consumers since the close() method updates the _consumers collection - // which would result in a concurrent modification exception - final ArrayList<BasicMessageConsumer> clonedConsumers = new ArrayList<BasicMessageConsumer>(_consumers.values()); - - final Iterator<BasicMessageConsumer> it = clonedConsumers.iterator(); - while (it.hasNext()) - { - final BasicMessageConsumer con = it.next(); - con.markClosed(); - } - // at this point the _consumers map will be empty - } - /** - * Asks the broker to resend all unacknowledged messages for the session. - * - * @throws JMSException - */ - public void recover() throws JMSException - { - checkNotClosed(); - checkNotTransacted(); // throws IllegalStateException if a transacted session - // this is set only here, and the before the consumer's onMessage is called it is set to false - _inRecovery = true; - try + // Remove the consumer from the map + BasicMessageConsumer consumer = (BasicMessageConsumer) _consumers.get(consumerTag); + if (consumer != null) { - - boolean isSuspended = isSuspended(); - - if (!isSuspended) + // fixme this isn't right.. needs to check if _queue contains data for this consumer + if (consumer.isAutoClose()) // && _queue.isEmpty()) { - suspendChannel(true); + consumer.closeWhenNoMessages(true); } - for (BasicMessageConsumer consumer : _consumers.values()) + if (!consumer.isNoConsume()) { - consumer.clearUnackedMessages(); - } + // Clean the Maps up first + // Flush any pending messages for this consumerTag + if (_dispatcher != null) + { + _logger.info("Dispatcher is not null"); + } + else + { + _logger.info("Dispatcher is null so created stopped dispatcher"); - if (_dispatcher != null) - { - _dispatcher.rollback(); - } + startDistpatcherIfNecessary(true); + } - if (isStrictAMQP()) - { - // We can't use the BasicRecoverBody-OK method as it isn't part of the spec. - _connection.getProtocolHandler().writeFrame(BasicRecoverBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), - getProtocolMinorVersion(), - false)); // requeue - _logger.warn("Session Recover cannot be guaranteed with STRICT_AMQP. Messages may arrive out of order."); + _dispatcher.rejectPending(consumer); } else { + // Just close the consumer + // fixme the CancelOK is being processed before the arriving messages.. + // The dispatcher is still to process them so the server sent in order but the client + // has yet to receive before the close comes in. - // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) - // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. - // Be aware of possible changes to parameter order as versions change. - _connection.getProtocolHandler().syncWrite(BasicRecoverBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), - getProtocolMinorVersion(), - false) // requeue - , BasicRecoverOkBody.class); - } - if (!isSuspended) - { - suspendChannel(false); + // consumer.markClosed(); } } - catch (AMQException e) + else { - throw new JMSAMQException(e); + _logger.warn("Unable to confirm cancellation of consumer (" + consumerTag + "). Not found in consumer map."); } - } - - boolean isInRecovery() - { - return _inRecovery; - } - void setInRecovery(boolean inRecovery) - { - _inRecovery = inRecovery; } - public void acknowledge() throws JMSException + public QueueBrowser createBrowser(Queue queue) throws JMSException { - if (isClosed()) - { - throw new IllegalStateException("Session is already closed"); - } - for (BasicMessageConsumer consumer : _consumers.values()) + if (isStrictAMQP()) { - consumer.acknowledge(); + throw new UnsupportedOperationException(); } - - } - - - public MessageListener getMessageListener() throws JMSException - { -// checkNotClosed(); - return _messageListener; - } - - public void setMessageListener(MessageListener listener) throws JMSException - { -// checkNotClosed(); -// -// if (_dispatcher != null && !_dispatcher.connectionStopped()) -// { -// throw new javax.jms.IllegalStateException("Attempt to set listener while session is started."); -// } -// -// // We are stopped -// for (Iterator<BasicMessageConsumer> i = _consumers.values().iterator(); i.hasNext();) -// { -// BasicMessageConsumer consumer = i.next(); -// -// if (consumer.isReceiving()) -// { -// throw new javax.jms.IllegalStateException("Another thread is already receiving synchronously."); -// } -// } -// -// _messageListener = listener; -// -// for (Iterator<BasicMessageConsumer> i = _consumers.values().iterator(); i.hasNext();) -// { -// i.next().setMessageListener(_messageListener); -// } - + return createBrowser(queue, null); } - public void run() + public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException { - throw new java.lang.UnsupportedOperationException(); - } + if (isStrictAMQP()) + { + throw new UnsupportedOperationException(); + } - public BasicMessageProducer createProducer(Destination destination, boolean mandatory, - boolean immediate, boolean waitUntilSent) - throws JMSException - { - return createProducerImpl(destination, mandatory, immediate, waitUntilSent); - } + checkNotClosed(); + checkValidQueue(queue); - public BasicMessageProducer createProducer(Destination destination, boolean mandatory, boolean immediate) - throws JMSException - { - return createProducerImpl(destination, mandatory, immediate); + return new AMQQueueBrowser(this, (AMQQueue) queue, messageSelector); } - public BasicMessageProducer createProducer(Destination destination, boolean immediate) + public MessageConsumer createBrowserConsumer(Destination destination, String messageSelector, boolean noLocal) throws JMSException { - return createProducerImpl(destination, DEFAULT_MANDATORY, immediate); - } - - public BasicMessageProducer createProducer(Destination destination) throws JMSException - { - return createProducerImpl(destination, DEFAULT_MANDATORY, DEFAULT_IMMEDIATE); - } + checkValidDestination(destination); - private BasicMessageProducer createProducerImpl(Destination destination, boolean mandatory, - boolean immediate) - throws JMSException - { - return createProducerImpl(destination, mandatory, immediate, false); + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false, + messageSelector, null, true, true); } - private BasicMessageProducer createProducerImpl(final Destination destination, final boolean mandatory, - final boolean immediate, final boolean waitUntilSent) - throws JMSException + public BytesMessage createBytesMessage() throws JMSException { - return (BasicMessageProducer) new FailoverSupport() + synchronized (_connection.getFailoverMutex()) { - public Object operation() throws JMSException - { - checkNotClosed(); - long producerId = getNextProducerId(); - BasicMessageProducer producer = new BasicMessageProducer(_connection, (AMQDestination) destination, _transacted, _channelId, - AMQSession.this, getProtocolHandler(), - producerId, immediate, mandatory, waitUntilSent); - registerProducer(producerId, producer); - return producer; - } - }.execute(_connection); - } - - /** - * Creates a QueueReceiver - * - * @param destination - * - * @return QueueReceiver - a wrapper around our MessageConsumer - * - * @throws JMSException - */ - public QueueReceiver createQueueReceiver(Destination destination) throws JMSException - { - checkValidDestination(destination); - AMQQueue dest = (AMQQueue) destination; - BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(destination); - return new QueueReceiverAdaptor(dest, consumer); - } + checkNotClosed(); - /** - * Creates a QueueReceiver using a message selector - * - * @param destination - * @param messageSelector - * - * @return QueueReceiver - a wrapper around our MessageConsumer - * - * @throws JMSException - */ - public QueueReceiver createQueueReceiver(Destination destination, String messageSelector) throws JMSException - { - checkValidDestination(destination); - AMQQueue dest = (AMQQueue) destination; - BasicMessageConsumer consumer = (BasicMessageConsumer) - createConsumer(destination, messageSelector); - return new QueueReceiverAdaptor(dest, consumer); + return new JMSBytesMessage(); + } } public MessageConsumer createConsumer(Destination destination) throws JMSException { checkValidDestination(destination); - return createConsumerImpl(destination, - _defaultPrefetchHighMark, - _defaultPrefetchLowMark, - false, - false, - null, - null, - false, - false); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false, null, null, + false, false); } public MessageConsumer createConsumer(Destination destination, String messageSelector) throws JMSException { checkValidDestination(destination); - return createConsumerImpl(destination, - _defaultPrefetchHighMark, - _defaultPrefetchLowMark, - false, - false, - messageSelector, - null, - false, - false); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, false, false, + messageSelector, null, false, false); } public MessageConsumer createConsumer(Destination destination, String messageSelector, boolean noLocal) throws JMSException { checkValidDestination(destination); - return createConsumerImpl(destination, - _defaultPrefetchHighMark, - _defaultPrefetchLowMark, - noLocal, - false, - messageSelector, - null, - false, - false); + + return createConsumerImpl(destination, _defaultPrefetchHighMark, _defaultPrefetchLowMark, noLocal, false, + messageSelector, null, false, false); } - public MessageConsumer createBrowserConsumer(Destination destination, - String messageSelector, - boolean noLocal) - throws JMSException - { - checkValidDestination(destination); - return createConsumerImpl(destination, - _defaultPrefetchHighMark, - _defaultPrefetchLowMark, - noLocal, - false, - messageSelector, - null, - true, - true); - } - - public MessageConsumer createConsumer(Destination destination, - int prefetch, - boolean noLocal, - boolean exclusive, + public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive, String selector) throws JMSException { checkValidDestination(destination); + return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, selector, null, false, false); } - - public MessageConsumer createConsumer(Destination destination, - int prefetchHigh, - int prefetchLow, - boolean noLocal, - boolean exclusive, - String selector) throws JMSException + public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal, + boolean exclusive, String selector) throws JMSException { checkValidDestination(destination); + return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, null, false, false); } - public MessageConsumer createConsumer(Destination destination, - int prefetch, - boolean noLocal, - boolean exclusive, - String selector, - FieldTable rawSelector) throws JMSException + public MessageConsumer createConsumer(Destination destination, int prefetch, boolean noLocal, boolean exclusive, + String selector, FieldTable rawSelector) throws JMSException { checkValidDestination(destination); - return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, - selector, rawSelector, false, false); + + return createConsumerImpl(destination, prefetch, prefetch, noLocal, exclusive, selector, rawSelector, false, false); } - public MessageConsumer createConsumer(Destination destination, - int prefetchHigh, - int prefetchLow, - boolean noLocal, - boolean exclusive, - String selector, - FieldTable rawSelector) throws JMSException + public MessageConsumer createConsumer(Destination destination, int prefetchHigh, int prefetchLow, boolean noLocal, + boolean exclusive, String selector, FieldTable rawSelector) throws JMSException { checkValidDestination(destination); - return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, - selector, rawSelector, false, false); + + return createConsumerImpl(destination, prefetchHigh, prefetchLow, noLocal, exclusive, selector, rawSelector, false, + false); } - protected MessageConsumer createConsumerImpl(final Destination destination, - final int prefetchHigh, - final int prefetchLow, - final boolean noLocal, - final boolean exclusive, - String selector, - final FieldTable rawSelector, - final boolean noConsume, - final boolean autoClose) throws JMSException + public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException { - checkTemporaryDestination(destination); - - final String messageSelector; - if (_strictAMQP && !(selector == null || selector.equals(""))) + checkNotClosed(); + AMQTopic origTopic = checkValidTopic(topic); + AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection); + TopicSubscriberAdaptor subscriber = _subscriptions.get(name); + if (subscriber != null) { - if (_strictAMQPFATAL) + if (subscriber.getTopic().equals(topic)) { - throw new UnsupportedOperationException("Selectors not currently supported by AMQP."); + throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange " + + name); } else { - messageSelector = null; + unsubscribe(name); } } else { - messageSelector = selector; - } - - return (org.apache.qpid.jms.MessageConsumer) new FailoverSupport() - { - public Object operation() throws JMSException + AMQShortString topicName; + if (topic instanceof AMQTopic) { - checkNotClosed(); - - AMQDestination amqd = (AMQDestination) destination; + topicName = ((AMQTopic) topic).getDestinationName(); + } + else + { + topicName = new AMQShortString(topic.getTopicName()); + } - final AMQProtocolHandler protocolHandler = getProtocolHandler(); - // TODO: Define selectors in AMQP - // TODO: construct the rawSelector from the selector string if rawSelector == null - final FieldTable ft = FieldTableFactory.newFieldTable(); - //if (rawSelector != null) - // ft.put("headers", rawSelector.getDataAsBytes()); - if (rawSelector != null) + if (_strictAMQP) + { + if (_strictAMQPFATAL) { - ft.addAll(rawSelector); + throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP."); } - - BasicMessageConsumer consumer = new BasicMessageConsumer(_channelId, _connection, amqd, messageSelector, noLocal, - _messageFactoryRegistry, AMQSession.this, - protocolHandler, ft, prefetchHigh, prefetchLow, exclusive, - _acknowledgeMode, noConsume, autoClose); - - if (_messageListener != null) + else { - consumer.setMessageListener(_messageListener); + _logger.warn("Unable to determine if subscription already exists for '" + topicName + "' " + + "for creation durableSubscriber. Requesting queue deletion regardless."); } - try - { - registerConsumer(consumer, false); - } - catch (AMQInvalidArgumentException ise) - { - JMSException ex = new InvalidSelectorException(ise.getMessage()); - ex.setLinkedException(ise); - throw ex; - } - catch (AMQInvalidRoutingKeyException e) + deleteQueue(dest.getAMQQueueName()); + } + else + { + // if the queue is bound to the exchange but NOT for this topic, then the JMS spec + // says we must trash the subscription. + if (isQueueBound(dest.getExchangeName(), dest.getAMQQueueName()) + && !isQueueBound(dest.getExchangeName(), dest.getAMQQueueName(), topicName)) { - JMSException ide = new InvalidDestinationException("Invalid routing key:" + amqd.getRoutingKey().toString()); - ide.setLinkedException(e); - throw ide; + deleteQueue(dest.getAMQQueueName()); } - catch (AMQException e) - { - JMSException ex = new JMSException("Error registering consumer: " + e); + } + } - if (_logger.isDebugEnabled()) - { - e.printStackTrace(); - } - ex.setLinkedException(e); - throw ex; - } + subscriber = new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest)); - synchronized (destination) - { - _destinationConsumerCount.putIfAbsent(destination, new AtomicInteger()); - _destinationConsumerCount.get(destination).incrementAndGet(); - } + _subscriptions.put(name, subscriber); + _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name); - return consumer; - } - }.execute(_connection); + return subscriber; } - private void checkTemporaryDestination(Destination destination) + /** Note, currently this does not handle reuse of the same name with different topics correctly. */ + public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) throws JMSException { - if ((destination instanceof TemporaryDestination)) - { - _logger.debug("destination is temporary"); - final TemporaryDestination tempDest = (TemporaryDestination) destination; - if (tempDest.getSession() != this) - { - _logger.debug("destination is on different session"); - throw new JMSException("Cannot consume from a temporary destination created onanother session"); - } - if (tempDest.isDeleted()) - { - _logger.debug("destination is deleted"); - throw new JMSException("Cannot consume from a deleted destination"); - } - } - } + checkNotClosed(); + checkValidTopic(topic); + AMQTopic dest = AMQTopic.createDurableTopic((AMQTopic) topic, name, _connection); + BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal); + TopicSubscriberAdaptor subscriber = new TopicSubscriberAdaptor(dest, consumer); + _subscriptions.put(name, subscriber); + _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name); + return subscriber; + } - public boolean hasConsumer(Destination destination) + public MapMessage createMapMessage() throws JMSException { - AtomicInteger counter = _destinationConsumerCount.get(destination); + synchronized (_connection.getFailoverMutex()) + { + checkNotClosed(); - return (counter != null) && (counter.get() != 0); + return new JMSMapMessage(); + } } - public void declareExchange(AMQShortString name, AMQShortString type, boolean nowait) throws AMQException + public javax.jms.Message createMessage() throws JMSException { - declareExchange(name, type, getProtocolHandler(), nowait); + return createBytesMessage(); } - private void declareExchange(AMQDestination amqd, AMQProtocolHandler protocolHandler, boolean nowait) throws AMQException + public ObjectMessage createObjectMessage() throws JMSException { - declareExchange(amqd.getExchangeName(), amqd.getExchangeClass(), protocolHandler, nowait); + synchronized (_connection.getFailoverMutex()) + { + checkNotClosed(); + + return (ObjectMessage) new JMSObjectMessage(); + } } - private void declareExchange(AMQShortString name, AMQShortString type, AMQProtocolHandler protocolHandler, boolean nowait) throws AMQException + public ObjectMessage createObjectMessage(Serializable object) throws JMSException { - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame exchangeDeclare = ExchangeDeclareBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - null, // arguments - false, // autoDelete - false, // durable - name, // exchange - false, // internal - nowait, // nowait - false, // passive - getTicket(), // ticket - type); // type + ObjectMessage msg = createObjectMessage(); + msg.setObject(object); - protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class); + return msg; } - - public void createQueue(AMQShortString name, boolean autoDelete, boolean durable, boolean exclusive) throws AMQException + public BasicMessageProducer createProducer(Destination destination) throws JMSException { - AMQFrame queueDeclare = QueueDeclareBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - null, // arguments - autoDelete, // autoDelete - durable, // durable - exclusive, // exclusive - false, // nowait - false, // passive - name, // queue - getTicket()); // ticket - - getProtocolHandler().syncWrite(queueDeclare, QueueDeclareOkBody.class); - + return createProducerImpl(destination, DEFAULT_MANDATORY, DEFAULT_IMMEDIATE); } - - public void bindQueue(AMQShortString queueName, AMQShortString routingKey, FieldTable arguments, AMQShortString exchangeName) throws AMQException + public BasicMessageProducer createProducer(Destination destination, boolean immediate) throws JMSException { - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame queueBind = QueueBindBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - arguments, // arguments - exchangeName, // exchange - false, // nowait - queueName, // queue - routingKey, // routingKey - getTicket()); // ticket - - - getProtocolHandler().syncWrite(queueBind, QueueBindOkBody.class); + return createProducerImpl(destination, DEFAULT_MANDATORY, immediate); } - /** - * Declare the queue. - * - * @param amqd - * @param protocolHandler - * - * @return the queue name. This is useful where the broker is generating a queue name on behalf of the client. - * - * @throws AMQException - */ - private AMQShortString declareQueue(AMQDestination amqd, AMQProtocolHandler protocolHandler) throws AMQException + public BasicMessageProducer createProducer(Destination destination, boolean mandatory, boolean immediate) + throws JMSException { - // For queues (but not topics) we generate the name in the client rather than the - // server. This allows the name to be reused on failover if required. In general, - // the destination indicates whether it wants a name generated or not. - if (amqd.isNameRequired()) - { - amqd.setQueueName(protocolHandler.generateQueueName()); - } - - //TODO verify the destiation is valid. else throw - - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame queueDeclare = QueueDeclareBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - null, // arguments - amqd.isAutoDelete(), // autoDelete - amqd.isDurable(), // durable - amqd.isExclusive(), // exclusive - false, // nowait - false, // passive - amqd.getAMQQueueName(), // queue - getTicket()); // ticket - - protocolHandler.syncWrite(queueDeclare, QueueDeclareOkBody.class); - return amqd.getAMQQueueName(); + return createProducerImpl(destination, mandatory, immediate); } - private void bindQueue(AMQDestination amqd, AMQShortString queueName, AMQProtocolHandler protocolHandler, FieldTable ft) throws AMQException + public BasicMessageProducer createProducer(Destination destination, boolean mandatory, boolean immediate, + boolean waitUntilSent) throws JMSException { - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame queueBind = QueueBindBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - ft, // arguments - amqd.getExchangeName(), // exchange - false, // nowait - queueName, // queue - amqd.getRoutingKey(), // routingKey - getTicket()); // ticket - - - protocolHandler.syncWrite(queueBind, QueueBindOkBody.class); + return createProducerImpl(destination, mandatory, immediate, waitUntilSent); } - /** - * Register to consume from the queue. - * - * @param queueName - * - * @return the consumer tag generated by the broker - */ - private void consumeFromQueue(BasicMessageConsumer consumer, AMQShortString queueName, AMQProtocolHandler protocolHandler, - boolean nowait, String messageSelector) throws AMQException + public TopicPublisher createPublisher(Topic topic) throws JMSException { - //need to generate a consumer tag on the client so we can exploit the nowait flag - AMQShortString tag = new AMQShortString(Integer.toString(_nextTag++)); - - FieldTable arguments = FieldTableFactory.newFieldTable(); - if (messageSelector != null && !messageSelector.equals("")) - { - arguments.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), messageSelector); - } - if (consumer.isAutoClose()) - { - arguments.put(AMQPFilterTypes.AUTO_CLOSE.getValue(), Boolean.TRUE); - } - if (consumer.isNoConsume()) - { - arguments.put(AMQPFilterTypes.NO_CONSUME.getValue(), Boolean.TRUE); - } - - consumer.setConsumerTag(tag); - // we must register the consumer in the map before we actually start listening - _consumers.put(tag, consumer); + checkNotClosed(); - try - { - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame jmsConsume = BasicConsumeBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - arguments, // arguments - tag, // consumerTag - consumer.isExclusive(), // exclusive - consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE, // noAck - consumer.isNoLocal(), // noLocal - nowait, // nowait - queueName, // queue - getTicket()); // ticket - if (nowait) - { - protocolHandler.writeFrame(jmsConsume); - } - else - { - protocolHandler.syncWrite(jmsConsume, BasicConsumeOkBody.class); - } - } - catch (AMQException e) - { - // clean-up the map in the event of an error - _consumers.remove(tag); - throw e; - } + return new TopicPublisherAdapter((BasicMessageProducer) createProducer(topic), topic); } public Queue createQueue(String queueName) throws JMSException @@ -1540,9 +906,79 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi } } - public AMQShortString getDefaultQueueExchangeName() + /** + * Declares the named queue. + * + * <p/>Note that this operation automatically retries in the event of fail-over. + * + * @param name The name of the queue to declare. + * @param autoDelete + * @param durable Flag to indicate that the queue is durable. + * @param exclusive Flag to indicate that the queue is exclusive to this client. + * + * @throws AMQException If the queue cannot be declared for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + public void createQueue(final AMQShortString name, final boolean autoDelete, final boolean durable, + final boolean exclusive) throws AMQException { - return _connection.getDefaultQueueExchangeName(); + new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>() + { + public Object execute() throws AMQException, FailoverException + { + AMQFrame queueDeclare = + QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + null, // arguments + autoDelete, // autoDelete + durable, // durable + exclusive, // exclusive + false, // nowait + false, // passive + name, // queue + getTicket()); // ticket + + getProtocolHandler().syncWrite(queueDeclare, QueueDeclareOkBody.class); + + return null; + } + }, _connection).execute(); + } + + /** + * Creates a QueueReceiver + * + * @param destination + * + * @return QueueReceiver - a wrapper around our MessageConsumer + * + * @throws JMSException + */ + public QueueReceiver createQueueReceiver(Destination destination) throws JMSException + { + checkValidDestination(destination); + AMQQueue dest = (AMQQueue) destination; + BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(destination); + + return new QueueReceiverAdaptor(dest, consumer); + } + + /** + * Creates a QueueReceiver using a message selector + * + * @param destination + * @param messageSelector + * + * @return QueueReceiver - a wrapper around our MessageConsumer + * + * @throws JMSException + */ + public QueueReceiver createQueueReceiver(Destination destination, String messageSelector) throws JMSException + { + checkValidDestination(destination); + AMQQueue dest = (AMQQueue) destination; + BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(destination, messageSelector); + + return new QueueReceiverAdaptor(dest, consumer); } /** @@ -1559,6 +995,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi checkNotClosed(); AMQQueue dest = (AMQQueue) queue; BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(dest); + return new QueueReceiverAdaptor(dest, consumer); } @@ -1576,47 +1013,29 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi { checkNotClosed(); AMQQueue dest = (AMQQueue) queue; - BasicMessageConsumer consumer = (BasicMessageConsumer) - createConsumer(dest, messageSelector); + BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(dest, messageSelector); + return new QueueReceiverAdaptor(dest, consumer); } public QueueSender createSender(Queue queue) throws JMSException { checkNotClosed(); - //return (QueueSender) createProducer(queue); + + // return (QueueSender) createProducer(queue); return new QueueSenderAdapter(createProducer(queue), queue); } - public Topic createTopic(String topicName) throws JMSException + public StreamMessage createStreamMessage() throws JMSException { - checkNotClosed(); - - if (topicName.indexOf('/') == -1) - { - return new AMQTopic(getDefaultTopicExchangeName(), new AMQShortString(topicName)); - } - else + synchronized (_connection.getFailoverMutex()) { - try - { - return new AMQTopic(new AMQBindingURL(topicName)); - } - catch (URLSyntaxException urlse) - { - JMSException jmse = new JMSException(urlse.getReason()); - jmse.setLinkedException(urlse); + checkNotClosed(); - throw jmse; - } + return new JMSStreamMessage(); } } - public AMQShortString getDefaultTopicExchangeName() - { - return _connection.getDefaultTopicExchangeName(); - } - /** * Creates a non-durable subscriber * @@ -1630,7 +1049,8 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi { checkNotClosed(); AMQTopic dest = checkValidTopic(topic); - //AMQTopic dest = new AMQTopic(topic.getTopicName()); + + // AMQTopic dest = new AMQTopic(topic.getTopicName()); return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest)); } @@ -1649,150 +1069,396 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi { checkNotClosed(); AMQTopic dest = checkValidTopic(topic); - //AMQTopic dest = new AMQTopic(topic.getTopicName()); + + // AMQTopic dest = new AMQTopic(topic.getTopicName()); return new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal)); } - public TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException + public TemporaryQueue createTemporaryQueue() throws JMSException { + checkNotClosed(); + return new AMQTemporaryQueue(this); + } + public TemporaryTopic createTemporaryTopic() throws JMSException + { checkNotClosed(); - AMQTopic origTopic = checkValidTopic(topic); - AMQTopic dest = AMQTopic.createDurableTopic(origTopic, name, _connection); - TopicSubscriberAdaptor subscriber = _subscriptions.get(name); - if (subscriber != null) + + return new AMQTemporaryTopic(this); + } + + public TextMessage createTextMessage() throws JMSException + { + synchronized (_connection.getFailoverMutex()) { - if (subscriber.getTopic().equals(topic)) + checkNotClosed(); + + return new JMSTextMessage(); + } + } + + public TextMessage createTextMessage(String text) throws JMSException + { + + TextMessage msg = createTextMessage(); + msg.setText(text); + + return msg; + } + + public Topic createTopic(String topicName) throws JMSException + { + checkNotClosed(); + + if (topicName.indexOf('/') == -1) + { + return new AMQTopic(getDefaultTopicExchangeName(), new AMQShortString(topicName)); + } + else + { + try { - throw new IllegalStateException("Already subscribed to topic " + topic + " with subscription exchange " + - name); + return new AMQTopic(new AMQBindingURL(topicName)); } - else + catch (URLSyntaxException urlse) { - unsubscribe(name); + JMSException jmse = new JMSException(urlse.getReason()); + jmse.setLinkedException(urlse); + + throw jmse; } } + } + + public void declareExchange(AMQShortString name, AMQShortString type, boolean nowait) throws AMQException + { + declareExchange(name, type, getProtocolHandler(), nowait); + } + + public int getAcknowledgeMode() throws JMSException + { + checkNotClosed(); + + return _acknowledgeMode; + } + + public AMQConnection getAMQConnection() + { + return _connection; + } + + public int getChannelId() + { + return _channelId; + } + + public int getDefaultPrefetch() + { + return _defaultPrefetchHighMark; + } + + public int getDefaultPrefetchHigh() + { + return _defaultPrefetchHighMark; + } + + public int getDefaultPrefetchLow() + { + return _defaultPrefetchLowMark; + } + + public AMQShortString getDefaultQueueExchangeName() + { + return _connection.getDefaultQueueExchangeName(); + } + + public AMQShortString getDefaultTopicExchangeName() + { + return _connection.getDefaultTopicExchangeName(); + } + + public MessageListener getMessageListener() throws JMSException + { + // checkNotClosed(); + return _messageListener; + } + + public AMQShortString getTemporaryQueueExchangeName() + { + return _connection.getTemporaryQueueExchangeName(); + } + + public AMQShortString getTemporaryTopicExchangeName() + { + return _connection.getTemporaryTopicExchangeName(); + } + + public int getTicket() + { + return _ticket; + } + + public boolean getTransacted() throws JMSException + { + checkNotClosed(); + + return _transacted; + } + + public boolean hasConsumer(Destination destination) + { + AtomicInteger counter = _destinationConsumerCount.get(destination); + + return (counter != null) && (counter.get() != 0); + } + + public boolean isStrictAMQP() + { + return _strictAMQP; + } + + public boolean isSuspended() + { + return _suspended; + } + + /** + * Invoked by the MINA IO thread (indirectly) when a message is received from the transport. Puts the message onto + * the queue read by the dispatcher. + * + * @param message the message that has been received + */ + public void messageReceived(UnprocessedMessage message) + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Message[" + + ((message.getDeliverBody() == null) ? ("B:" + message.getBounceBody()) : ("D:" + message.getDeliverBody())) + + "] received in session with channel id " + _channelId); + } + + if (message.getDeliverBody() == null) + { + // Return of the bounced message. + returnBouncedMessage(message); + } else { - AMQShortString topicName; - if (topic instanceof AMQTopic) + _queue.add(message); + } + } + + /** + * Stops message delivery in this session, and restarts message delivery with the oldest unacknowledged message. + * + * <p/>All consumers deliver messages in a serial order. Acknowledging a received message automatically acknowledges + * all messages that have been delivered to the client. + * + * <p/>Restarting a session causes it to take the following actions: + * + * <ul> <li>Stop message delivery.</li> <li>Mark all messages that might have been delivered but not acknowledged as + * "redelivered". <li>Restart the delivery sequence including all unacknowledged messages that had been previously + * delivered. Redelivered messages do not have to be delivered in exactly their original delivery order.</li> </ul> + * + * <p/>If the recover operation is interrupted by a fail-over, between asking that the broker begin recovery and + * receiving acknolwedgement that it hasm then a JMSException will be thrown. In this case it will not be possible + * for the client to determine whether the broker is going to recover the session or not. + * + * @throws JMSException If the JMS provider fails to stop and restart message delivery due to some internal error. + * Not that this does not necessarily mean that the recovery has failed, but simply that it is + * not possible to tell if it has or not. + * @todo Be aware of possible changes to parameter order as versions change. + */ + public void recover() throws JMSException + { + // Ensure that the session is open. + checkNotClosed(); + + // Ensure that the session is not transacted. + checkNotTransacted(); + + // this is set only here, and the before the consumer's onMessage is called it is set to false + _inRecovery = true; + try + { + + boolean isSuspended = isSuspended(); + + if (!isSuspended) { - topicName = ((AMQTopic) topic).getDestinationName(); + suspendChannel(true); } - else + + for (BasicMessageConsumer consumer : _consumers.values()) { - topicName = new AMQShortString(topic.getTopicName()); + consumer.clearUnackedMessages(); } - if (_strictAMQP) + if (_dispatcher != null) { - if (_strictAMQPFATAL) - { - throw new UnsupportedOperationException("JMS Durable not currently supported by AMQP."); - } - else - { - _logger.warn("Unable to determine if subscription already exists for '" + topicName + "' " - + "for creation durableSubscriber. Requesting queue deletion regardless."); - } + _dispatcher.rollback(); + } - deleteQueue(dest.getAMQQueueName()); + if (isStrictAMQP()) + { + // We can't use the BasicRecoverBody-OK method as it isn't part of the spec. + _connection.getProtocolHandler().writeFrame(BasicRecoverBody.createAMQFrame(_channelId, + getProtocolMajorVersion(), getProtocolMinorVersion(), false)); // requeue + _logger.warn("Session Recover cannot be guaranteed with STRICT_AMQP. Messages may arrive out of order."); } else { - // if the queue is bound to the exchange but NOT for this topic, then the JMS spec - // says we must trash the subscription. - if (isQueueBound(dest.getExchangeName(), dest.getAMQQueueName()) && - !isQueueBound(dest.getExchangeName(), dest.getAMQQueueName(), topicName)) - { - deleteQueue(dest.getAMQQueueName()); - } - } - } - - subscriber = new TopicSubscriberAdaptor(dest, (BasicMessageConsumer) createConsumer(dest)); - _subscriptions.put(name, subscriber); - _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name); - - return subscriber; - } + _connection.getProtocolHandler().syncWrite( + BasicRecoverBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), false) // requeue + , BasicRecoverOkBody.class); + } - void deleteQueue(AMQShortString queueName) throws JMSException - { - try - { - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame queueDeleteFrame = QueueDeleteBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - false, // ifEmpty - false, // ifUnused - true, // nowait - queueName, // queue - getTicket()); // ticket - getProtocolHandler().syncWrite(queueDeleteFrame, QueueDeleteOkBody.class); + if (!isSuspended) + { + suspendChannel(false); + } } catch (AMQException e) { - throw new JMSAMQException(e); + throw new JMSAMQException("Recover failed: " + e.getMessage(), e); + } + catch (FailoverException e) + { + throw new JMSAMQException("Recovery was interrupted by fail-over. Recovery status is not known.", e); } } - /** Note, currently this does not handle reuse of the same name with different topics correctly. */ - public TopicSubscriber createDurableSubscriber(Topic topic, String name, String messageSelector, boolean noLocal) - throws JMSException + public void rejectMessage(UnprocessedMessage message, boolean requeue) { - checkNotClosed(); - checkValidTopic(topic); - AMQTopic dest = AMQTopic.createDurableTopic((AMQTopic) topic, name, _connection); - BasicMessageConsumer consumer = (BasicMessageConsumer) createConsumer(dest, messageSelector, noLocal); - TopicSubscriberAdaptor subscriber = new TopicSubscriberAdaptor(dest, consumer); - _subscriptions.put(name, subscriber); - _reverseSubscriptionMap.put(subscriber.getMessageConsumer(), name); - return subscriber; - } - public TopicPublisher createPublisher(Topic topic) throws JMSException - { - checkNotClosed(); - return new TopicPublisherAdapter((BasicMessageProducer) createProducer(topic), topic); + if (_logger.isTraceEnabled()) + { + _logger.trace("Rejecting Unacked message:" + message.getDeliverBody().deliveryTag); + } + + rejectMessage(message.getDeliverBody().deliveryTag, requeue); } - public QueueBrowser createBrowser(Queue queue) throws JMSException + public void rejectMessage(AbstractJMSMessage message, boolean requeue) { - if (isStrictAMQP()) + if (_logger.isTraceEnabled()) { - throw new UnsupportedOperationException(); + _logger.trace("Rejecting Abstract message:" + message.getDeliveryTag()); } - return createBrowser(queue, null); + rejectMessage(message.getDeliveryTag(), requeue); + } - public QueueBrowser createBrowser(Queue queue, String messageSelector) throws JMSException + public void rejectMessage(long deliveryTag, boolean requeue) { - if (isStrictAMQP()) + if ((_acknowledgeMode == CLIENT_ACKNOWLEDGE) || (_acknowledgeMode == SESSION_TRANSACTED)) { - throw new UnsupportedOperationException(); - } + if (_logger.isDebugEnabled()) + { + _logger.debug("Rejecting delivery tag:" + deliveryTag); + } - checkNotClosed(); - checkValidQueue(queue); - return new AMQQueueBrowser(this, (AMQQueue) queue, messageSelector); + AMQFrame basicRejectBody = + BasicRejectBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), deliveryTag, + requeue); + + _connection.getProtocolHandler().writeFrame(basicRejectBody); + } } - public TemporaryQueue createTemporaryQueue() throws JMSException + /** + * Commits all messages done in this transaction and releases any locks currently held. + * + * <p/>If the rollback fails, because the rollback itself is interrupted by a fail-over between requesting that the + * rollback be done, and receiving an acknowledgement that it has been done, then a JMSException will be thrown. The + * client will be unable to determine whether or not the rollback actually happened on the broker in this case. + * + * @throws JMSException If the JMS provider fails to rollback the transaction due to some internal error. This does + * not mean that the rollback is known to have failed, merely that it is not known whether it + * failed or not. + * @todo Be aware of possible changes to parameter order as versions change. + */ + public void rollback() throws JMSException { - checkNotClosed(); - return new AMQTemporaryQueue(this); + synchronized (_suspensionLock) + { + checkTransacted(); + + try + { + boolean isSuspended = isSuspended(); + + if (!isSuspended) + { + suspendChannel(true); + } + + if (_dispatcher != null) + { + _dispatcher.rollback(); + } + + _connection.getProtocolHandler().syncWrite(TxRollbackBody.createAMQFrame(_channelId, + getProtocolMajorVersion(), getProtocolMinorVersion()), TxRollbackOkBody.class); + + if (!isSuspended) + { + suspendChannel(false); + } + } + catch (AMQException e) + { + throw new JMSAMQException("Failed to rollback: " + e, e); + } + catch (FailoverException e) + { + throw new JMSAMQException("Fail-over interrupted rollback. Status of the rollback is uncertain.", e); + } + } } - public TemporaryTopic createTemporaryTopic() throws JMSException + public void run() { - checkNotClosed(); - return new AMQTemporaryTopic(this); + throw new java.lang.UnsupportedOperationException(); } + public void setMessageListener(MessageListener listener) throws JMSException + { + // checkNotClosed(); + // + // if (_dispatcher != null && !_dispatcher.connectionStopped()) + // { + // throw new javax.jms.IllegalStateException("Attempt to set listener while session is started."); + // } + // + // // We are stopped + // for (Iterator<BasicMessageConsumer> i = _consumers.values().iterator(); i.hasNext();) + // { + // BasicMessageConsumer consumer = i.next(); + // + // if (consumer.isReceiving()) + // { + // throw new javax.jms.IllegalStateException("Another thread is already receiving synchronously."); + // } + // } + // + // _messageListener = listener; + // + // for (Iterator<BasicMessageConsumer> i = _consumers.values().iterator(); i.hasNext();) + // { + // i.next().setMessageListener(_messageListener); + // } + + } + + /*public void setTicket(int ticket) + { + _ticket = ticket; + }*/ + public void unsubscribe(String name) throws JMSException { checkNotClosed(); @@ -1835,189 +1501,246 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi } } - boolean isQueueBound(AMQShortString exchangeName, AMQShortString queueName) throws JMSException + protected MessageConsumer createConsumerImpl(final Destination destination, final int prefetchHigh, + final int prefetchLow, final boolean noLocal, final boolean exclusive, String selector, final FieldTable rawSelector, + final boolean noConsume, final boolean autoClose) throws JMSException { - return isQueueBound(exchangeName, queueName, null); - } + checkTemporaryDestination(destination); - boolean isQueueBound(AMQShortString exchangeName, AMQShortString queueName, AMQShortString routingKey) throws JMSException - { + final String messageSelector; - // TODO: Be aware of possible changes to parameter order as versions change. - AMQFrame boundFrame = ExchangeBoundBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - exchangeName, // exchange - queueName, // queue - routingKey); // routingKey - AMQMethodEvent response = null; - try + if (_strictAMQP && !((selector == null) || selector.equals(""))) { - response = getProtocolHandler().syncWrite(boundFrame, ExchangeBoundOkBody.class); + if (_strictAMQPFATAL) + { + throw new UnsupportedOperationException("Selectors not currently supported by AMQP."); + } + else + { + messageSelector = null; + } } - catch (AMQException e) + else { - throw new JMSAMQException(e); + messageSelector = selector; } - ExchangeBoundOkBody responseBody = (ExchangeBoundOkBody) response.getMethod(); - return (responseBody.replyCode == 0); //ExchangeBoundHandler.OK); Remove Broker compile dependency - } - private void checkTransacted() throws JMSException - { - if (!getTransacted()) - { - throw new IllegalStateException("Session is not transacted"); - } - } + return new FailoverRetrySupport<MessageConsumer, JMSException>( + new FailoverProtectedOperation<MessageConsumer, JMSException>() + { + public MessageConsumer execute() throws JMSException, FailoverException + { + checkNotClosed(); - private void checkNotTransacted() throws JMSException - { - if (getTransacted()) - { - throw new IllegalStateException("Session is transacted"); - } + AMQDestination amqd = (AMQDestination) destination; + + final AMQProtocolHandler protocolHandler = getProtocolHandler(); + // TODO: Define selectors in AMQP + // TODO: construct the rawSelector from the selector string if rawSelector == null + final FieldTable ft = FieldTableFactory.newFieldTable(); + // if (rawSelector != null) + // ft.put("headers", rawSelector.getDataAsBytes()); + if (rawSelector != null) + { + ft.addAll(rawSelector); + } + + BasicMessageConsumer consumer = + new BasicMessageConsumer(_channelId, _connection, amqd, messageSelector, noLocal, + _messageFactoryRegistry, AMQSession.this, protocolHandler, ft, prefetchHigh, prefetchLow, + exclusive, _acknowledgeMode, noConsume, autoClose); + + if (_messageListener != null) + { + consumer.setMessageListener(_messageListener); + } + + try + { + registerConsumer(consumer, false); + } + catch (AMQInvalidArgumentException ise) + { + JMSException ex = new InvalidSelectorException(ise.getMessage()); + ex.setLinkedException(ise); + throw ex; + } + catch (AMQInvalidRoutingKeyException e) + { + JMSException ide = + new InvalidDestinationException("Invalid routing key:" + amqd.getRoutingKey().toString()); + ide.setLinkedException(e); + throw ide; + } + catch (AMQException e) + { + JMSException ex = new JMSException("Error registering consumer: " + e); + + if (_logger.isDebugEnabled()) + { + e.printStackTrace(); + } + + ex.setLinkedException(e); + throw ex; + } + + synchronized (destination) + { + _destinationConsumerCount.putIfAbsent(destination, new AtomicInteger()); + _destinationConsumerCount.get(destination).incrementAndGet(); + } + + return consumer; + } + }, _connection).execute(); } /** - * Invoked by the MINA IO thread (indirectly) when a message is received from the transport. Puts the message onto - * the queue read by the dispatcher. + * Called by the MessageConsumer when closing, to deregister the consumer from the map from consumerTag to consumer + * instance. * - * @param message the message that has been received + * @param consumer the consum */ - public void messageReceived(UnprocessedMessage message) + void deregisterConsumer(BasicMessageConsumer consumer) { - if (_logger.isDebugEnabled()) + if (_consumers.remove(consumer.getConsumerTag()) != null) { - _logger.debug("Message[" + (message.getDeliverBody() == null ? - "B:" + message.getBounceBody() : "D:" + message.getDeliverBody()) - + "] received in session with channel id " + _channelId); - } + String subscriptionName = _reverseSubscriptionMap.remove(consumer); + if (subscriptionName != null) + { + _subscriptions.remove(subscriptionName); + } - if (message.getDeliverBody() == null) - { - // Return of the bounced message. - returnBouncedMessage(message); - } - else - { - _queue.add(message); + Destination dest = consumer.getDestination(); + synchronized (dest) + { + if (_destinationConsumerCount.get(dest).decrementAndGet() == 0) + { + _destinationConsumerCount.remove(dest); + } + } } } - private void returnBouncedMessage(final UnprocessedMessage message) + void deregisterProducer(long producerId) { - _connection.performConnectionTask( - new Runnable() - { - public void run() - { - try - { - // Bounced message is processed here, away from the mina thread - AbstractJMSMessage bouncedMessage = _messageFactoryRegistry.createMessage(0, - false, - message.getBounceBody().exchange, - message.getBounceBody().routingKey, - message.getContentHeader(), - message.getBodies()); - - AMQConstant errorCode = AMQConstant.getConstant(message.getBounceBody().replyCode); - AMQShortString reason = message.getBounceBody().replyText; - _logger.debug("Message returned with error code " + errorCode + " (" + reason + ")"); - - //@TODO should this be moved to an exception handler of sorts. Somewhere errors are converted to correct execeptions. - if (errorCode == AMQConstant.NO_CONSUMERS) - { - _connection.exceptionReceived(new AMQNoConsumersException("Error: " + reason, bouncedMessage, null)); - } - else if (errorCode == AMQConstant.NO_ROUTE) - { - _connection.exceptionReceived(new AMQNoRouteException("Error: " + reason, bouncedMessage, null)); - } - else - { - _connection.exceptionReceived(new AMQUndeliveredException(errorCode, "Error: " + reason, bouncedMessage, null)); - } + _producers.remove(new Long(producerId)); + } - } - catch (Exception e) - { - _logger.error("Caught exception trying to raise undelivered message exception (dump follows) - ignoring...", e); - } - } - }); + boolean isInRecovery() + { + return _inRecovery; + } + + boolean isQueueBound(AMQShortString exchangeName, AMQShortString queueName) throws JMSException + { + return isQueueBound(exchangeName, queueName, null); } /** - * Acknowledge a message or several messages. This method can be called via AbstractJMSMessage or from a - * BasicConsumer. The former where the mode is CLIENT_ACK and the latter where the mode is AUTO_ACK or similar. + * Tests whether or not the specified queue is bound to the specified exchange under a particular routing key. * - * @param deliveryTag the tag of the last message to be acknowledged - * @param multiple if true will acknowledge all messages up to and including the one specified by the delivery - * tag + * <p/>Note that this operation automatically retries in the event of fail-over. + * + * @param exchangeName The exchange name to test for binding against. + * @param queueName The queue name to check if bound. + * @param routingKey The routing key to check if the queue is bound under. + * + * @return <tt>true</tt> if the queue is bound to the exchange and routing key, <tt>false</tt> if not. + * + * @throws JMSException If the query fails for any reason. + * @todo Be aware of possible changes to parameter order as versions change. */ - public void acknowledgeMessage(long deliveryTag, boolean multiple) + boolean isQueueBound(final AMQShortString exchangeName, final AMQShortString queueName, final AMQShortString routingKey) + throws JMSException { - // TODO: Be aware of possible changes to parameter order as versions change. - final AMQFrame ackFrame = BasicAckBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), getProtocolMinorVersion(), // AMQP version (major, minor) - deliveryTag, // deliveryTag - multiple); // multiple - if (_logger.isDebugEnabled()) + try { - _logger.debug("Sending ack for delivery tag " + deliveryTag + " on channel " + _channelId); + AMQMethodEvent response = + new FailoverRetrySupport<AMQMethodEvent, AMQException>( + new FailoverProtectedOperation<AMQMethodEvent, AMQException>() + { + public AMQMethodEvent execute() throws AMQException, FailoverException + { + AMQFrame boundFrame = + ExchangeBoundBody.createAMQFrame(_channelId, getProtocolMajorVersion(), + getProtocolMinorVersion(), exchangeName, // exchange + queueName, // queue + routingKey); // routingKey + + return getProtocolHandler().syncWrite(boundFrame, ExchangeBoundOkBody.class); + + } + }, _connection).execute(); + + // Extract and return the response code from the query. + ExchangeBoundOkBody responseBody = (ExchangeBoundOkBody) response.getMethod(); + + return (responseBody.replyCode == 0); + } + catch (AMQException e) + { + throw new JMSAMQException("Queue bound query failed: " + e.getMessage(), e); } - getProtocolHandler().writeFrame(ackFrame); } - public int getDefaultPrefetch() + /** + * Called to mark the session as being closed. Useful when the session needs to be made invalid, e.g. after failover + * when the client has veoted resubscription. <p/> The caller of this method must already hold the failover mutex. + */ + void markClosed() { - return _defaultPrefetchHighMark; + _closed.set(true); + _connection.deregisterSession(_channelId); + markClosedProducersAndConsumers(); + } - public int getDefaultPrefetchHigh() + /** + * Resubscribes all producers and consumers. This is called when performing failover. + * + * @throws AMQException + */ + void resubscribe() throws AMQException { - return _defaultPrefetchHighMark; + resubscribeProducers(); + resubscribeConsumers(); } - public int getDefaultPrefetchLow() + void setHasMessageListeners() { - return _defaultPrefetchLowMark; + _hasMessageListeners = true; } - public int getChannelId() + void setInRecovery(boolean inRecovery) { - return _channelId; + _inRecovery = inRecovery; } + /** + * Starts the session, which ensures that it is not suspended and that its event dispatcher is running. + * + * @throws AMQException If the session cannot be started for any reason. + * @todo This should be controlled by _stopped as it pairs with the stop method fixme or check the + * FlowControlledBlockingQueue _queue to see if we have flow controlled. will result in sending Flow messages for + * each subsequent call to flow.. only need to do this if we have called stop. + */ void start() throws AMQException { - //fixme This should be controlled by _stopped as it pairs with the stop method - //fixme or check the FlowControlledBlockingQueue _queue to see if we have flow controlled. - //will result in sending Flow messages for each subsequent call to flow.. only need to do this - // if we have called stop. + // Check if the session has perviously been started and suspended, in which case it must be unsuspended. if (_startedAtLeastOnce.getAndSet(true)) { - //then we stopped this and are restarting, so signal server to resume delivery suspendChannel(false); } + // If the event dispatcher is not running then start it too. if (hasMessageListeners()) { startDistpatcherIfNecessary(); } } - private boolean hasMessageListeners() - { - return _hasMessageListeners; - } - - void setHasMessageListeners() - { - _hasMessageListeners = true; - } - synchronized void startDistpatcherIfNecessary() { // If IMMEDIATE_PREFETCH is not set then we need to start fetching @@ -2032,7 +1755,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi } catch (AMQException e) { - _logger.info("Suspending channel threw an exception:" + e); + _logger.info("Unsuspending channel threw an exception:" + e); } } } @@ -2057,7 +1780,7 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi void stop() throws AMQException { - //stop the server delivering messages to this session + // Stop the server delivering messages to this session. suspendChannel(true); if (_dispatcher != null) @@ -2066,320 +1789,552 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi } } - /** - * Callers must hold the failover mutex before calling this method. + /* + * Binds the named queue, with the specified routing key, to the named exchange. * - * @param consumer + * <p/>Note that this operation automatically retries in the event of fail-over. * - * @throws AMQException + * @param queueName The name of the queue to bind. + * @param routingKey The routing key to bind the queue with. + * @param arguments Additional arguments. + * @param exchangeName The exchange to bind the queue on. + * + * @throws AMQException If the queue cannot be bound for any reason. */ - void registerConsumer(BasicMessageConsumer consumer, boolean nowait) throws AMQException + /*private void bindQueue(AMQDestination amqd, AMQShortString queueName, AMQProtocolHandler protocolHandler, FieldTable ft) + throws AMQException, FailoverException { - AMQDestination amqd = consumer.getDestination(); - - AMQProtocolHandler protocolHandler = getProtocolHandler(); - - declareExchange(amqd, protocolHandler, false); - - AMQShortString queueName = declareQueue(amqd, protocolHandler); - - bindQueue(amqd, queueName, protocolHandler, consumer.getRawSelectorFieldTable()); + AMQFrame queueBind = + QueueBindBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), ft, // arguments + amqd.getExchangeName(), // exchange + false, // nowait + queueName, // queue + amqd.getRoutingKey(), // routingKey + getTicket()); // ticket - // If IMMEDIATE_PREFETCH is not required then suspsend the channel to delay prefetch - if (!_immediatePrefetch) - { - // The dispatcher will be null if we have just created this session - // so suspend the channel before we register our consumer so that we don't - // start prefetching until a receive/mListener is set. - if (_dispatcher == null) - { - if (!isSuspended()) - { - try - { - suspendChannel(true); - _logger.info("Prefetching delayed existing messages will not flow until requested via receive*() or setML()."); - } - catch (AMQException e) - { - _logger.info("Suspending channel threw an exception:" + e); - } - } - } - } - else - { - _logger.info("Immediately prefetching existing messages to new consumer."); - } + protocolHandler.syncWrite(queueBind, QueueBindOkBody.class); + }*/ - try - { - consumeFromQueue(consumer, queueName, protocolHandler, nowait, consumer.getMessageSelector()); - } - catch (JMSException e) //thrown by getMessageSelector + private void checkNotTransacted() throws JMSException + { + if (getTransacted()) { - throw new AMQException(null, e.getMessage(), e); + throw new IllegalStateException("Session is transacted"); } } - /** - * Called by the MessageConsumer when closing, to deregister the consumer from the map from consumerTag to consumer - * instance. - * - * @param consumer the consum - */ - void deregisterConsumer(BasicMessageConsumer consumer) + private void checkTemporaryDestination(Destination destination) throws JMSException { - if (_consumers.remove(consumer.getConsumerTag()) != null) + if ((destination instanceof TemporaryDestination)) { - String subscriptionName = _reverseSubscriptionMap.remove(consumer); - if (subscriptionName != null) + _logger.debug("destination is temporary"); + final TemporaryDestination tempDest = (TemporaryDestination) destination; + if (tempDest.getSession() != this) { - _subscriptions.remove(subscriptionName); + _logger.debug("destination is on different session"); + throw new JMSException("Cannot consume from a temporary destination created onanother session"); } - Destination dest = consumer.getDestination(); - synchronized (dest) + if (tempDest.isDeleted()) { - if (_destinationConsumerCount.get(dest).decrementAndGet() == 0) - { - _destinationConsumerCount.remove(dest); - } + _logger.debug("destination is deleted"); + throw new JMSException("Cannot consume from a deleted destination"); } } } - private void registerProducer(long producerId, MessageProducer producer) + private void checkTransacted() throws JMSException { - _producers.put(new Long(producerId), producer); + if (!getTransacted()) + { + throw new IllegalStateException("Session is not transacted"); + } } - void deregisterProducer(long producerId) + private void checkValidDestination(Destination destination) throws InvalidDestinationException { - _producers.remove(new Long(producerId)); + if (destination == null) + { + throw new javax.jms.InvalidDestinationException("Invalid Queue"); + } } - private long getNextProducerId() + private void checkValidQueue(Queue queue) throws InvalidDestinationException { - return ++_nextProducerId; + if (queue == null) + { + throw new javax.jms.InvalidDestinationException("Invalid Queue"); + } } - /** - * Resubscribes all producers and consumers. This is called when performing failover. - * - * @throws AMQException + /* + * I could have combined the last 3 methods, but this way it improves readability */ - void resubscribe() throws AMQException + private AMQTopic checkValidTopic(Topic topic) throws JMSException { - resubscribeProducers(); - resubscribeConsumers(); + if (topic == null) + { + throw new javax.jms.InvalidDestinationException("Invalid Topic"); + } + + if ((topic instanceof TemporaryDestination) && (((TemporaryDestination) topic).getSession() != this)) + { + throw new javax.jms.InvalidDestinationException( + "Cannot create a subscription on a temporary topic created in another session"); + } + + if (!(topic instanceof AMQTopic)) + { + throw new javax.jms.InvalidDestinationException( + "Cannot create a subscription on topic created for another JMS Provider, class of topic provided is: " + + topic.getClass().getName()); + } + + return (AMQTopic) topic; } - private void resubscribeProducers() throws AMQException + /** + * Called to close message consumers cleanly. This may or may <b>not</b> be as a result of an error. + * + * @param error not null if this is a result of an error occurring at the connection level + */ + private void closeConsumers(Throwable error) throws JMSException { - ArrayList producers = new ArrayList(_producers.values()); - _logger.info(MessageFormat.format("Resubscribing producers = {0} producers.size={1}", producers, producers.size())); // FIXME: removeKey - for (Iterator it = producers.iterator(); it.hasNext();) + if (_dispatcher != null) { - BasicMessageProducer producer = (BasicMessageProducer) it.next(); - producer.resubscribe(); + _dispatcher.close(); + _dispatcher = null; } + // we need to clone the list of consumers since the close() method updates the _consumers collection + // which would result in a concurrent modification exception + final ArrayList<BasicMessageConsumer> clonedConsumers = new ArrayList<BasicMessageConsumer>(_consumers.values()); + + final Iterator<BasicMessageConsumer> it = clonedConsumers.iterator(); + while (it.hasNext()) + { + final BasicMessageConsumer con = it.next(); + if (error != null) + { + con.notifyError(error); + } + else + { + con.close(); + } + } + // at this point the _consumers map will be empty } - private void resubscribeConsumers() throws AMQException + /** + * Called to close message producers cleanly. This may or may <b>not</b> be as a result of an error. There is + * currently no way of propagating errors to message producers (this is a JMS limitation). + */ + private void closeProducers() throws JMSException { - ArrayList consumers = new ArrayList(_consumers.values()); - _consumers.clear(); + // we need to clone the list of producers since the close() method updates the _producers collection + // which would result in a concurrent modification exception + final ArrayList clonedProducers = new ArrayList(_producers.values()); - for (Iterator it = consumers.iterator(); it.hasNext();) + final Iterator it = clonedProducers.iterator(); + while (it.hasNext()) { - BasicMessageConsumer consumer = (BasicMessageConsumer) it.next(); - registerConsumer(consumer, true); + final BasicMessageProducer prod = (BasicMessageProducer) it.next(); + prod.close(); } + // at this point the _producers map is empty } - private void suspendChannel(boolean suspend) throws AMQException + /** + * Close all producers or consumers. This is called either in the error case or when closing the session normally. + * + * @param amqe the exception, may be null to indicate no error has occurred + */ + private void closeProducersAndConsumers(AMQException amqe) throws JMSException { - synchronized (_suspensionLock) + JMSException jmse = null; + try { - if (_logger.isDebugEnabled()) + closeProducers(); + } + catch (JMSException e) + { + _logger.error("Error closing session: " + e, e); + jmse = e; + } + + try + { + closeConsumers(amqe); + } + catch (JMSException e) + { + _logger.error("Error closing session: " + e, e); + if (jmse == null) { - _logger.debug("Setting channel flow : " + (suspend ? "suspended" : "unsuspended")); + jmse = e; } + } - _suspended = suspend; - - // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. - // Be aware of possible changes to parameter order as versions change. - AMQFrame channelFlowFrame = ChannelFlowBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), - getProtocolMinorVersion(), - !suspend); // active - - _connection.getProtocolHandler().syncWrite(channelFlowFrame, ChannelFlowOkBody.class); + if (jmse != null) + { + throw jmse; } } - - public void confirmConsumerCancelled(AMQShortString consumerTag) + /** + * Register to consume from the queue. + * + * @param queueName + */ + private void consumeFromQueue(BasicMessageConsumer consumer, AMQShortString queueName, + AMQProtocolHandler protocolHandler, boolean nowait, String messageSelector) throws AMQException, FailoverException { + // need to generate a consumer tag on the client so we can exploit the nowait flag + AMQShortString tag = new AMQShortString(Integer.toString(_nextTag++)); - // Remove the consumer from the map - BasicMessageConsumer consumer = (BasicMessageConsumer) _consumers.get(consumerTag); - if (consumer != null) + FieldTable arguments = FieldTableFactory.newFieldTable(); + if ((messageSelector != null) && !messageSelector.equals("")) { -// fixme this isn't right.. needs to check if _queue contains data for this consumer - if (consumer.isAutoClose())// && _queue.isEmpty()) - { - consumer.closeWhenNoMessages(true); - } + arguments.put(AMQPFilterTypes.JMS_SELECTOR.getValue(), messageSelector); + } - if (!consumer.isNoConsume()) - { - //Clean the Maps up first - //Flush any pending messages for this consumerTag - if (_dispatcher != null) - { - _logger.info("Dispatcher is not null"); - } - else - { - _logger.info("Dispatcher is null so created stopped dispatcher"); + if (consumer.isAutoClose()) + { + arguments.put(AMQPFilterTypes.AUTO_CLOSE.getValue(), Boolean.TRUE); + } - startDistpatcherIfNecessary(true); - } + if (consumer.isNoConsume()) + { + arguments.put(AMQPFilterTypes.NO_CONSUME.getValue(), Boolean.TRUE); + } - _dispatcher.rejectPending(consumer); + consumer.setConsumerTag(tag); + // we must register the consumer in the map before we actually start listening + _consumers.put(tag, consumer); + + try + { + // TODO: Be aware of possible changes to parameter order as versions change. + AMQFrame jmsConsume = + BasicConsumeBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), arguments, // arguments + tag, // consumerTag + consumer.isExclusive(), // exclusive + consumer.getAcknowledgeMode() == Session.NO_ACKNOWLEDGE, // noAck + consumer.isNoLocal(), // noLocal + nowait, // nowait + queueName, // queue + getTicket()); // ticket + + if (nowait) + { + protocolHandler.writeFrame(jmsConsume); } else { - //Just close the consumer - //fixme the CancelOK is being processed before the arriving messages.. - // The dispatcher is still to process them so the server sent in order but the client - // has yet to receive before the close comes in. - -// consumer.markClosed(); + protocolHandler.syncWrite(jmsConsume, BasicConsumeOkBody.class); } } - else + catch (AMQException e) { - _logger.warn("Unable to confirm cancellation of consumer (" + consumerTag + "). Not found in consumer map."); + // clean-up the map in the event of an error + _consumers.remove(tag); + throw e; } + } + private BasicMessageProducer createProducerImpl(Destination destination, boolean mandatory, boolean immediate) + throws JMSException + { + return createProducerImpl(destination, mandatory, immediate, false); + } + private BasicMessageProducer createProducerImpl(final Destination destination, final boolean mandatory, + final boolean immediate, final boolean waitUntilSent) throws JMSException + { + return new FailoverRetrySupport<BasicMessageProducer, JMSException>( + new FailoverProtectedOperation<BasicMessageProducer, JMSException>() + { + public BasicMessageProducer execute() throws JMSException, FailoverException + { + checkNotClosed(); + long producerId = getNextProducerId(); + BasicMessageProducer producer = + new BasicMessageProducer(_connection, (AMQDestination) destination, _transacted, _channelId, + AMQSession.this, getProtocolHandler(), producerId, immediate, mandatory, waitUntilSent); + registerProducer(producerId, producer); + + return producer; + } + }, _connection).execute(); } - /* - * I could have combined the last 3 methods, but this way it improves readability - */ - private AMQTopic checkValidTopic(Topic topic) throws JMSException + private void declareExchange(AMQDestination amqd, AMQProtocolHandler protocolHandler, boolean nowait) throws AMQException { - if (topic == null) - { - throw new javax.jms.InvalidDestinationException("Invalid Topic"); - } - if ((topic instanceof TemporaryDestination) && ((TemporaryDestination) topic).getSession() != this) - { - throw new javax.jms.InvalidDestinationException("Cannot create a subscription on a temporary topic created in another session"); - } - if (!(topic instanceof AMQTopic)) - { - throw new javax.jms.InvalidDestinationException("Cannot create a subscription on topic created for another JMS Provider, class of topic provided is: " + topic.getClass().getName()); - } - return (AMQTopic) topic; + declareExchange(amqd.getExchangeName(), amqd.getExchangeClass(), protocolHandler, nowait); } - private void checkValidQueue(Queue queue) throws InvalidDestinationException + /** + * Declares the named exchange and type of exchange. + * + * <p/>Note that this operation automatically retries in the event of fail-over. + * + * @param name The name of the exchange to declare. + * @param type The type of the exchange to declare. + * @param protocolHandler The protocol handler to process the communication through. + * @param nowait + * + * @throws AMQException If the exchange cannot be declared for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + private void declareExchange(final AMQShortString name, final AMQShortString type, + final AMQProtocolHandler protocolHandler, final boolean nowait) throws AMQException { - if (queue == null) + new FailoverNoopSupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>() { - throw new javax.jms.InvalidDestinationException("Invalid Queue"); - } + public Object execute() throws AMQException, FailoverException + { + AMQFrame exchangeDeclare = + ExchangeDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + null, // arguments + false, // autoDelete + false, // durable + name, // exchange + false, // internal + nowait, // nowait + false, // passive + getTicket(), // ticket + type); // type + + protocolHandler.syncWrite(exchangeDeclare, ExchangeDeclareOkBody.class); + + return null; + } + }, _connection).execute(); } - private void checkValidDestination(Destination destination) throws InvalidDestinationException + /** + * Declares a queue for a JMS destination. + * + * <p/>Note that for queues but not topics the name is generated in the client rather than the server. This allows + * the name to be reused on failover if required. In general, the destination indicates whether it wants a name + * generated or not. + * + * <p/>Note that this operation automatically retries in the event of fail-over. + * + * @param amqd The destination to declare as a queue. + * @param protocolHandler The protocol handler to communicate through. + * + * @return The name of the decalred queue. This is useful where the broker is generating a queue name on behalf of + * the client. + * + * @throws AMQException If the queue cannot be declared for any reason. + * @todo Verify the destiation is valid or throw an exception. + * @todo Be aware of possible changes to parameter order as versions change. + */ + private AMQShortString declareQueue(final AMQDestination amqd, final AMQProtocolHandler protocolHandler) + throws AMQException { - if (destination == null) + /*return new FailoverRetrySupport<AMQShortString, AMQException>(*/ + return new FailoverNoopSupport<AMQShortString, AMQException>( + new FailoverProtectedOperation<AMQShortString, AMQException>() + { + public AMQShortString execute() throws AMQException, FailoverException + { + // Generate the queue name if the destination indicates that a client generated name is to be used. + if (amqd.isNameRequired()) + { + amqd.setQueueName(protocolHandler.generateQueueName()); + } + + AMQFrame queueDeclare = + QueueDeclareBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + null, // arguments + amqd.isAutoDelete(), // autoDelete + amqd.isDurable(), // durable + amqd.isExclusive(), // exclusive + false, // nowait + false, // passive + amqd.getAMQQueueName(), // queue + getTicket()); // ticket + + protocolHandler.syncWrite(queueDeclare, QueueDeclareOkBody.class); + + return amqd.getAMQQueueName(); + } + }, _connection).execute(); + } + + /** + * Undeclares the specified queue. + * + * <p/>Note that this operation automatically retries in the event of fail-over. + * + * @param queueName The name of the queue to delete. + * + * @throws JMSException If the queue could not be deleted for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + private void deleteQueue(final AMQShortString queueName) throws JMSException + { + try { - throw new javax.jms.InvalidDestinationException("Invalid Queue"); + new FailoverRetrySupport<Object, AMQException>(new FailoverProtectedOperation<Object, AMQException>() + { + public Object execute() throws AMQException, FailoverException + { + AMQFrame queueDeleteFrame = + QueueDeleteBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + false, // ifEmpty + false, // ifUnused + true, // nowait + queueName, // queue + getTicket()); // ticket + + getProtocolHandler().syncWrite(queueDeleteFrame, QueueDeleteOkBody.class); + + return null; + } + }, _connection).execute(); + } + catch (AMQException e) + { + throw new JMSAMQException("The queue deletion failed: " + e.getMessage(), e); } } - - public AMQShortString getTemporaryTopicExchangeName() + private long getNextProducerId() { - return _connection.getTemporaryTopicExchangeName(); + return ++_nextProducerId; } - public AMQShortString getTemporaryQueueExchangeName() + private AMQProtocolHandler getProtocolHandler() { - return _connection.getTemporaryQueueExchangeName(); + return _connection.getProtocolHandler(); } - - public int getTicket() + private byte getProtocolMajorVersion() { - return _ticket; + return getProtocolHandler().getProtocolMajorVersion(); } - public void setTicket(int ticket) + private byte getProtocolMinorVersion() { - _ticket = ticket; + return getProtocolHandler().getProtocolMinorVersion(); } + private boolean hasMessageListeners() + { + return _hasMessageListeners; + } - public void requestAccess(AMQShortString realm, boolean exclusive, boolean passive, boolean active, boolean write, boolean read) throws AMQException + private void markClosedConsumers() throws JMSException { - getProtocolHandler().writeCommandFrameAndWaitForReply(AccessRequestBody.createAMQFrame(getChannelId(), - getProtocolMajorVersion(), - getProtocolMinorVersion(), - active, - exclusive, - passive, - read, - realm, - write), - new BlockingMethodFrameListener(_channelId) - { - - public boolean processMethod(int channelId, AMQMethodBody frame) throws AMQException - { - if (frame instanceof AccessRequestOkBody) - { - setTicket(((AccessRequestOkBody) frame).getTicket()); - return true; - } - else - { - return false; - } - } - }); + if (_dispatcher != null) + { + _dispatcher.close(); + _dispatcher = null; + } + // we need to clone the list of consumers since the close() method updates the _consumers collection + // which would result in a concurrent modification exception + final ArrayList<BasicMessageConsumer> clonedConsumers = new ArrayList<BasicMessageConsumer>(_consumers.values()); + final Iterator<BasicMessageConsumer> it = clonedConsumers.iterator(); + while (it.hasNext()) + { + final BasicMessageConsumer con = it.next(); + con.markClosed(); + } + // at this point the _consumers map will be empty } - private class SuspenderRunner implements Runnable + private void markClosedProducersAndConsumers() { - private boolean _suspend; + try + { + // no need for a markClosed* method in this case since there is no protocol traffic closing a producer + closeProducers(); + } + catch (JMSException e) + { + _logger.error("Error closing session: " + e, e); + } - public SuspenderRunner(boolean suspend) + try { - _suspend = suspend; + markClosedConsumers(); } + catch (JMSException e) + { + _logger.error("Error closing session: " + e, e); + } + } - public void run() + /** + * Callers must hold the failover mutex before calling this method. + * + * @param consumer + * + * @throws AMQException + */ + private void registerConsumer(BasicMessageConsumer consumer, boolean nowait) throws AMQException // , FailoverException + { + AMQDestination amqd = consumer.getDestination(); + + AMQProtocolHandler protocolHandler = getProtocolHandler(); + + declareExchange(amqd, protocolHandler, false); + + AMQShortString queueName = declareQueue(amqd, protocolHandler); + + // bindQueue(amqd, queueName, protocolHandler, consumer.getRawSelectorFieldTable()); + bindQueue(queueName, amqd.getRoutingKey(), consumer.getRawSelectorFieldTable(), amqd.getExchangeName()); + + // If IMMEDIATE_PREFETCH is not required then suspsend the channel to delay prefetch + if (!_immediatePrefetch) { - try - { - suspendChannel(_suspend); - } - catch (AMQException e) + // The dispatcher will be null if we have just created this session + // so suspend the channel before we register our consumer so that we don't + // start prefetching until a receive/mListener is set. + if (_dispatcher == null) { - _logger.warn("Unable to suspend channel"); + if (!isSuspended()) + { + try + { + suspendChannel(true); + _logger.info( + "Prefetching delayed existing messages will not flow until requested via receive*() or setML()."); + } + catch (AMQException e) + { + _logger.info("Suspending channel threw an exception:" + e); + } + } } } + else + { + _logger.info("Immediately prefetching existing messages to new consumer."); + } + + try + { + consumeFromQueue(consumer, queueName, protocolHandler, nowait, consumer.getMessageSelector()); + } + catch (JMSException e) // thrown by getMessageSelector + { + throw new AMQException(null, e.getMessage(), e); + } + catch (FailoverException e) + { + throw new AMQException(null, "Fail-over exception interrupted basic consume.", e); + } } + private void registerProducer(long producerId, MessageProducer producer) + { + _producers.put(new Long(producerId), producer); + } private void rejectAllMessages(boolean requeue) { @@ -2396,8 +2351,8 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi Iterator messages = _queue.iterator(); if (_logger.isInfoEnabled()) { - _logger.info("Rejecting messages from _queue for Consumer tag(" + consumerTag + - ") (PDispatchQ) requeue:" + requeue); + _logger.info("Rejecting messages from _queue for Consumer tag(" + consumerTag + ") (PDispatchQ) requeue:" + + requeue); if (messages.hasNext()) { @@ -2412,12 +2367,12 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi { UnprocessedMessage message = (UnprocessedMessage) messages.next(); - if (consumerTag == null || message.getDeliverBody().consumerTag.equals(consumerTag)) + if ((consumerTag == null) || message.getDeliverBody().consumerTag.equals(consumerTag)) { if (_logger.isDebugEnabled()) { - _logger.debug("Removing message(" + System.identityHashCode(message) + - ") from _queue DT:" + message.getDeliverBody().deliveryTag); + _logger.debug("Removing message(" + System.identityHashCode(message) + ") from _queue DT:" + + message.getDeliverBody().deliveryTag); } messages.remove(); @@ -2432,50 +2387,360 @@ public class AMQSession extends Closeable implements Session, QueueSession, Topi } } - - public void rejectMessage(UnprocessedMessage message, boolean requeue) + private void resubscribeConsumers() throws AMQException { + ArrayList consumers = new ArrayList(_consumers.values()); + _consumers.clear(); - if (_logger.isTraceEnabled()) + for (Iterator it = consumers.iterator(); it.hasNext();) { - _logger.trace("Rejecting Unacked message:" + message.getDeliverBody().deliveryTag); + BasicMessageConsumer consumer = (BasicMessageConsumer) it.next(); + registerConsumer(consumer, true); } - - rejectMessage(message.getDeliverBody().deliveryTag, requeue); } - public void rejectMessage(AbstractJMSMessage message, boolean requeue) + private void resubscribeProducers() throws AMQException { - if (_logger.isTraceEnabled()) + ArrayList producers = new ArrayList(_producers.values()); + _logger.info(MessageFormat.format("Resubscribing producers = {0} producers.size={1}", producers, producers.size())); // FIXME: removeKey + for (Iterator it = producers.iterator(); it.hasNext();) { - _logger.trace("Rejecting Abstract message:" + message.getDeliveryTag()); + BasicMessageProducer producer = (BasicMessageProducer) it.next(); + producer.resubscribe(); } - rejectMessage(message.getDeliveryTag(), requeue); - } - public void rejectMessage(long deliveryTag, boolean requeue) + private void returnBouncedMessage(final UnprocessedMessage message) { - if (_acknowledgeMode == CLIENT_ACKNOWLEDGE || - _acknowledgeMode == SESSION_TRANSACTED) + _connection.performConnectionTask(new Runnable() { - if (_logger.isDebugEnabled()) + public void run() { - _logger.debug("Rejecting delivery tag:" + deliveryTag); + try + { + // Bounced message is processed here, away from the mina thread + AbstractJMSMessage bouncedMessage = + _messageFactoryRegistry.createMessage(0, false, message.getBounceBody().exchange, + message.getBounceBody().routingKey, message.getContentHeader(), message.getBodies()); + + AMQConstant errorCode = AMQConstant.getConstant(message.getBounceBody().replyCode); + AMQShortString reason = message.getBounceBody().replyText; + _logger.debug("Message returned with error code " + errorCode + " (" + reason + ")"); + + // @TODO should this be moved to an exception handler of sorts. Somewhere errors are converted to correct execeptions. + if (errorCode == AMQConstant.NO_CONSUMERS) + { + _connection.exceptionReceived(new AMQNoConsumersException("Error: " + reason, bouncedMessage, null)); + } + else if (errorCode == AMQConstant.NO_ROUTE) + { + _connection.exceptionReceived(new AMQNoRouteException("Error: " + reason, bouncedMessage, null)); + } + else + { + _connection.exceptionReceived( + new AMQUndeliveredException(errorCode, "Error: " + reason, bouncedMessage, null)); + } + + } + catch (Exception e) + { + _logger.error( + "Caught exception trying to raise undelivered message exception (dump follows) - ignoring...", + e); + } } - AMQFrame basicRejectBody = BasicRejectBody.createAMQFrame(_channelId, - getProtocolMajorVersion(), - getProtocolMinorVersion(), - deliveryTag, - requeue); + }); + } - _connection.getProtocolHandler().writeFrame(basicRejectBody); + /** + * Suspends or unsuspends this session. + * + * @param suspend <tt>true</tt> indicates that the session should be suspended, <tt>false<tt> indicates that it + * should be unsuspended. + * + * @throws AMQException If the session cannot be suspended for any reason. + * @todo Be aware of possible changes to parameter order as versions change. + */ + private void suspendChannel(boolean suspend) throws AMQException // , FailoverException + { + synchronized (_suspensionLock) + { + try + { + if (_logger.isDebugEnabled()) + { + _logger.debug("Setting channel flow : " + (suspend ? "suspended" : "unsuspended")); + } + + _suspended = suspend; + + AMQFrame channelFlowFrame = + ChannelFlowBody.createAMQFrame(_channelId, getProtocolMajorVersion(), getProtocolMinorVersion(), + !suspend); + + _connection.getProtocolHandler().syncWrite(channelFlowFrame, ChannelFlowOkBody.class); + } + catch (FailoverException e) + { + throw new AMQException(null, "Fail-over interrupted suspend/unsuspend channel.", e); + } } } - public boolean isStrictAMQP() + /** Responsible for decoding a message fragment and passing it to the appropriate message consumer. */ + private class Dispatcher extends Thread { - return _strictAMQP; + + /** Track the 'stopped' state of the dispatcher, a session starts in the stopped state. */ + private final AtomicBoolean _closed = new AtomicBoolean(false); + + private final Object _lock = new Object(); + + public Dispatcher() + { + super("Dispatcher-Channel-" + _channelId); + if (_dispatcherLogger.isInfoEnabled()) + { + _dispatcherLogger.info(getName() + " created"); + } + } + + public void close() + { + _closed.set(true); + interrupt(); + + // fixme awaitTermination + + } + + public void rejectPending(BasicMessageConsumer consumer) + { + synchronized (_lock) + { + boolean stopped = _dispatcher.connectionStopped(); + + if (!stopped) + { + _dispatcher.setConnectionStopped(true); + } + + // Reject messages on pre-receive queue + consumer.rollback(); + + // Reject messages on pre-dispatch queue + rejectMessagesForConsumerTag(consumer.getConsumerTag(), true); + + // closeConsumer + consumer.markClosed(); + + _dispatcher.setConnectionStopped(stopped); + + } + } + + public void rollback() + { + + synchronized (_lock) + { + boolean isStopped = connectionStopped(); + + if (!isStopped) + { + setConnectionStopped(true); + } + + rejectAllMessages(true); + + _dispatcherLogger.debug("Session Pre Dispatch Queue cleared"); + + for (BasicMessageConsumer consumer : _consumers.values()) + { + if (!consumer.isNoConsume()) + { + consumer.rollback(); + } + else + { + // should perhaps clear the _SQ here. + // consumer._synchronousQueue.clear(); + consumer.clearReceiveQueue(); + } + + } + + setConnectionStopped(isStopped); + } + + } + + public void run() + { + if (_dispatcherLogger.isInfoEnabled()) + { + _dispatcherLogger.info(getName() + " started"); + } + + UnprocessedMessage message; + + // Allow disptacher to start stopped + synchronized (_lock) + { + while (connectionStopped()) + { + try + { + _lock.wait(); + } + catch (InterruptedException e) + { + // ignore + } + } + } + + try + { + while (!_closed.get() && ((message = (UnprocessedMessage) _queue.take()) != null)) + { + synchronized (_lock) + { + + while (connectionStopped()) + { + _lock.wait(); + } + + dispatchMessage(message); + + while (connectionStopped()) + { + _lock.wait(); + } + + } + + } + } + catch (InterruptedException e) + { + // ignore + } + + if (_dispatcherLogger.isInfoEnabled()) + { + _dispatcherLogger.info(getName() + " thread terminating for channel " + _channelId); + } + } + + // only call while holding lock + final boolean connectionStopped() + { + return _connectionStopped; + } + + boolean setConnectionStopped(boolean connectionStopped) + { + boolean currently; + synchronized (_lock) + { + currently = _connectionStopped; + _connectionStopped = connectionStopped; + _lock.notify(); + + if (_dispatcherLogger.isDebugEnabled()) + { + _dispatcherLogger.debug("Set Dispatcher Connection " + (connectionStopped ? "Stopped" : "Started") + + ": Currently " + (currently ? "Stopped" : "Started")); + } + } + + return currently; + } + + private void dispatchMessage(UnprocessedMessage message) + { + if (message.getDeliverBody() != null) + { + final BasicMessageConsumer consumer = + (BasicMessageConsumer) _consumers.get(message.getDeliverBody().consumerTag); + + if ((consumer == null) || consumer.isClosed()) + { + if (_dispatcherLogger.isInfoEnabled()) + { + if (consumer == null) + { + _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "[" + + message.getDeliverBody().deliveryTag + "] from queue " + + message.getDeliverBody().consumerTag + " )without a handler - rejecting(requeue)..."); + } + else + { + _dispatcherLogger.info("Received a message(" + System.identityHashCode(message) + ")" + "[" + + message.getDeliverBody().deliveryTag + "] from queue " + " consumer(" + + consumer.debugIdentity() + ") is closed rejecting(requeue)..."); + } + } + // Don't reject if we're already closing + if (!_closed.get()) + { + rejectMessage(message, true); + } + } + else + { + consumer.notifyMessage(message, _channelId); + } + } + } } + /*public void requestAccess(AMQShortString realm, boolean exclusive, boolean passive, boolean active, boolean write, + boolean read) throws AMQException + { + getProtocolHandler().writeCommandFrameAndWaitForReply(AccessRequestBody.createAMQFrame(getChannelId(), + getProtocolMajorVersion(), getProtocolMinorVersion(), active, exclusive, passive, read, realm, write), + new BlockingMethodFrameListener(_channelId) + { + + public boolean processMethod(int channelId, AMQMethodBody frame) // throws AMQException + { + if (frame instanceof AccessRequestOkBody) + { + setTicket(((AccessRequestOkBody) frame).getTicket()); + + return true; + } + else + { + return false; + } + } + }); + }*/ + + private class SuspenderRunner implements Runnable + { + private boolean _suspend; + + public SuspenderRunner(boolean suspend) + { + _suspend = suspend; + } + + public void run() + { + try + { + suspendChannel(_suspend); + } + catch (AMQException e) + { + _logger.warn("Unable to suspend channel"); + } + } + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java index 1c3cdbcb65..dfac0d45a8 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageConsumer.java @@ -20,22 +20,8 @@ */ package org.apache.qpid.client; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; - -import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; +import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.message.AbstractJMSMessage; import org.apache.qpid.client.message.MessageFactoryRegistry; import org.apache.qpid.client.message.UnprocessedMessage; @@ -48,9 +34,25 @@ import org.apache.qpid.framing.FieldTable; import org.apache.qpid.jms.MessageConsumer; import org.apache.qpid.jms.Session; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.Message; +import javax.jms.MessageListener; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + public class BasicMessageConsumer extends Closeable implements MessageConsumer { - private static final Logger _logger = Logger.getLogger(BasicMessageConsumer.class); + private static final Logger _logger = LoggerFactory.getLogger(BasicMessageConsumer.class); /** The connection being used by this consumer */ private AMQConnection _connection; @@ -140,9 +142,9 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer private List<StackTraceElement> _closedStack = null; protected BasicMessageConsumer(int channelId, AMQConnection connection, AMQDestination destination, - String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session, - AMQProtocolHandler protocolHandler, FieldTable rawSelectorFieldTable, int prefetchHigh, int prefetchLow, - boolean exclusive, int acknowledgeMode, boolean noConsume, boolean autoClose) + String messageSelector, boolean noLocal, MessageFactoryRegistry messageFactory, AMQSession session, + AMQProtocolHandler protocolHandler, FieldTable rawSelectorFieldTable, int prefetchHigh, int prefetchLow, + boolean exclusive, int acknowledgeMode, boolean noConsume, boolean autoClose) { _channelId = channelId; _connection = connection; @@ -219,7 +221,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer if (_logger.isDebugEnabled()) { _logger.debug("Session stopped : Message listener(" + messageListener + ") set for destination " - + _destination); + + _destination); } } else @@ -468,7 +470,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer if (_closedStack != null) { _logger.trace(_consumerTag + " close():" - + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6)); + + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 6)); _logger.trace(_consumerTag + " previously:" + _closedStack.toString()); } else @@ -481,9 +483,9 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer { // TODO: Be aware of possible changes to parameter order as versions change. final AMQFrame cancelFrame = - BasicCancelBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion(), _consumerTag, // consumerTag - false); // nowait + BasicCancelBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion(), _consumerTag, // consumerTag + false); // nowait try { @@ -497,10 +499,11 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer } catch (AMQException e) { - // _logger.error("Error closing consumer: " + e, e); - JMSException jmse = new JMSException("Error closing consumer: " + e); - jmse.setLinkedException(e); - throw jmse; + throw new JMSAMQException("Error closing consumer: " + e, e); + } + catch (FailoverException e) + { + throw new JMSAMQException("FailoverException interrupted basic cancel.", e); } } else @@ -540,7 +543,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer if (_closedStack != null) { _logger.trace(_consumerTag + " markClosed():" - + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 8)); + + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 8)); _logger.trace(_consumerTag + " previously:" + _closedStack.toString()); } else @@ -572,9 +575,9 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer try { AbstractJMSMessage jmsMessage = - _messageFactory.createMessage(messageFrame.getDeliverBody().deliveryTag, - messageFrame.getDeliverBody().redelivered, messageFrame.getDeliverBody().exchange, - messageFrame.getDeliverBody().routingKey, messageFrame.getContentHeader(), messageFrame.getBodies()); + _messageFactory.createMessage(messageFrame.getDeliverBody().deliveryTag, + messageFrame.getDeliverBody().redelivered, messageFrame.getDeliverBody().exchange, + messageFrame.getDeliverBody().routingKey, messageFrame.getContentHeader(), messageFrame.getBodies()); if (debug) { @@ -659,15 +662,15 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer switch (_acknowledgeMode) { - case Session.PRE_ACKNOWLEDGE: - _session.acknowledgeMessage(msg.getDeliveryTag(), false); - break; + case Session.PRE_ACKNOWLEDGE: + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + break; - case Session.CLIENT_ACKNOWLEDGE: - // we set the session so that when the user calls acknowledge() it can call the method on session - // to send out the appropriate frame - msg.setAMQSession(_session); - break; + case Session.CLIENT_ACKNOWLEDGE: + // we set the session so that when the user calls acknowledge() it can call the method on session + // to send out the appropriate frame + msg.setAMQSession(_session); + break; } } @@ -677,55 +680,55 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer switch (_acknowledgeMode) { - case Session.CLIENT_ACKNOWLEDGE: - if (isNoConsume()) - { - _session.acknowledgeMessage(msg.getDeliveryTag(), false); - } + case Session.CLIENT_ACKNOWLEDGE: + if (isNoConsume()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } - break; + break; - case Session.DUPS_OK_ACKNOWLEDGE: - if (++_outstanding >= _prefetchHigh) - { - _dups_ok_acknowledge_send = true; - } + case Session.DUPS_OK_ACKNOWLEDGE: + if (++_outstanding >= _prefetchHigh) + { + _dups_ok_acknowledge_send = true; + } - if (_outstanding <= _prefetchLow) - { - _dups_ok_acknowledge_send = false; - } + if (_outstanding <= _prefetchLow) + { + _dups_ok_acknowledge_send = false; + } - if (_dups_ok_acknowledge_send) + if (_dups_ok_acknowledge_send) + { + if (!_session.isInRecovery()) { - if (!_session.isInRecovery()) - { - _session.acknowledgeMessage(msg.getDeliveryTag(), true); - } + _session.acknowledgeMessage(msg.getDeliveryTag(), true); } + } - break; + break; - case Session.AUTO_ACKNOWLEDGE: - // we do not auto ack a message if the application code called recover() - if (!_session.isInRecovery()) - { - _session.acknowledgeMessage(msg.getDeliveryTag(), false); - } + case Session.AUTO_ACKNOWLEDGE: + // we do not auto ack a message if the application code called recover() + if (!_session.isInRecovery()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } - break; + break; - case Session.SESSION_TRANSACTED: - if (isNoConsume()) - { - _session.acknowledgeMessage(msg.getDeliveryTag(), false); - } - else - { - _receivedDeliveryTags.add(msg.getDeliveryTag()); - } + case Session.SESSION_TRANSACTED: + if (isNoConsume()) + { + _session.acknowledgeMessage(msg.getDeliveryTag(), false); + } + else + { + _receivedDeliveryTags.add(msg.getDeliveryTag()); + } - break; + break; } } @@ -757,7 +760,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer if (_closedStack != null) { _logger.trace(_consumerTag + " notifyError():" - + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 8)); + + Arrays.asList(Thread.currentThread().getStackTrace()).subList(3, 8)); _logger.trace(_consumerTag + " previously" + _closedStack.toString()); } else @@ -817,7 +820,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer } } - public void acknowledge() throws JMSException + public void acknowledge() // throws JMSException { if (!isClosed()) { @@ -877,7 +880,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer if (_logger.isDebugEnabled()) { _logger.debug("Rejecting the messages(" + _receivedDeliveryTags.size() + ") in _receivedDTs (RQ)" - + "for consumer with tag:" + _consumerTag); + + "for consumer with tag:" + _consumerTag); } Long tag = _receivedDeliveryTags.poll(); @@ -907,7 +910,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer if (_logger.isDebugEnabled()) { _logger.debug("Rejecting the messages(" + _synchronousQueue.size() + ") in _syncQueue (PRQ)" - + "for consumer with tag:" + _consumerTag); + + "for consumer with tag:" + _consumerTag); } Iterator iterator = _synchronousQueue.iterator(); @@ -931,7 +934,7 @@ public class BasicMessageConsumer extends Closeable implements MessageConsumer else { _logger.error("Queue contained a :" + o.getClass() - + " unable to reject as it is not an AbstractJMSMessage. Will be cleared"); + + " unable to reject as it is not an AbstractJMSMessage. Will be cleared"); iterator.remove(); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java index bd7cc94582..0ee4882ec2 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/BasicMessageProducer.java @@ -20,8 +20,23 @@ */ package org.apache.qpid.client; -import java.io.UnsupportedEncodingException; -import java.util.UUID; +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.client.message.AbstractJMSMessage; +import org.apache.qpid.client.message.MessageConverter; +import org.apache.qpid.client.protocol.AMQProtocolHandler; +import org.apache.qpid.framing.AMQFrame; +import org.apache.qpid.framing.BasicConsumeBody; +import org.apache.qpid.framing.BasicContentHeaderProperties; +import org.apache.qpid.framing.BasicPublishBody; +import org.apache.qpid.framing.CompositeAMQDataBlock; +import org.apache.qpid.framing.ContentBody; +import org.apache.qpid.framing.ContentHeaderBody; +import org.apache.qpid.framing.ExchangeDeclareBody; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.jms.BytesMessage; import javax.jms.DeliveryMode; @@ -36,24 +51,12 @@ import javax.jms.StreamMessage; import javax.jms.TextMessage; import javax.jms.Topic; -import org.apache.log4j.Logger; -import org.apache.mina.common.ByteBuffer; -import org.apache.qpid.AMQException; -import org.apache.qpid.client.message.AbstractJMSMessage; -import org.apache.qpid.client.message.MessageConverter; -import org.apache.qpid.client.protocol.AMQProtocolHandler; -import org.apache.qpid.framing.AMQFrame; -import org.apache.qpid.framing.BasicConsumeBody; -import org.apache.qpid.framing.BasicContentHeaderProperties; -import org.apache.qpid.framing.BasicPublishBody; -import org.apache.qpid.framing.CompositeAMQDataBlock; -import org.apache.qpid.framing.ContentBody; -import org.apache.qpid.framing.ContentHeaderBody; -import org.apache.qpid.framing.ExchangeDeclareBody; +import java.io.UnsupportedEncodingException; +import java.util.UUID; public class BasicMessageProducer extends Closeable implements org.apache.qpid.jms.MessageProducer { - protected final Logger _logger = Logger.getLogger(getClass()); + protected final Logger _logger = LoggerFactory.getLogger(getClass()); private AMQConnection _connection; @@ -119,14 +122,14 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j private final boolean _mandatory; private final boolean _waitUntilSent; - + private boolean _disableMessageId; - + private static final ContentBody[] NO_CONTENT_BODIES = new ContentBody[0]; protected BasicMessageProducer(AMQConnection connection, AMQDestination destination, boolean transacted, int channelId, - AMQSession session, AMQProtocolHandler protocolHandler, long producerId, - boolean immediate, boolean mandatory, boolean waitUntilSent) + AMQSession session, AMQProtocolHandler protocolHandler, long producerId, boolean immediate, boolean mandatory, + boolean waitUntilSent) { _connection = connection; _destination = destination; @@ -160,15 +163,15 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j // TODO: Be aware of possible changes to parameter order as versions change. AMQFrame declare = ExchangeDeclareBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion(), null, // arguments - false, // autoDelete - false, // durable - destination.getExchangeName(), // exchange - false, // internal - true, // nowait - false, // passive - _session.getTicket(), // ticket - destination.getExchangeClass()); // type + _protocolHandler.getProtocolMinorVersion(), null, // arguments + false, // autoDelete + false, // durable + destination.getExchangeName(), // exchange + false, // internal + true, // nowait + false, // passive + _session.getTicket(), // ticket + destination.getExchangeClass()); // type _protocolHandler.writeFrame(declare); } @@ -205,7 +208,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j if ((i != DeliveryMode.NON_PERSISTENT) && (i != DeliveryMode.PERSISTENT)) { throw new JMSException("DeliveryMode must be either NON_PERSISTENT or PERSISTENT. Value of " + i - + " is illegal"); + + " is illegal"); } _deliveryMode = i; @@ -317,12 +320,12 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j { validateDestination(destination); sendImpl((AMQDestination) destination, message, _deliveryMode, _messagePriority, _timeToLive, _mandatory, - _immediate); + _immediate); } } public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive) - throws JMSException + throws JMSException { checkPreConditions(); checkDestination(destination); @@ -334,7 +337,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j } public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory) throws JMSException + boolean mandatory) throws JMSException { checkPreConditions(); checkDestination(destination); @@ -346,7 +349,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j } public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate) throws JMSException + boolean mandatory, boolean immediate) throws JMSException { checkPreConditions(); checkDestination(destination); @@ -358,7 +361,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j } public void send(Destination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException + boolean mandatory, boolean immediate, boolean waitUntilSent) throws JMSException { checkPreConditions(); checkDestination(destination); @@ -366,7 +369,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j { validateDestination(destination); sendImpl((AMQDestination) destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, - waitUntilSent); + waitUntilSent); } } @@ -412,7 +415,7 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j else { throw new JMSException("Unable to send message, due to class conversion error: " - + message.getClass().getName()); + + message.getClass().getName()); } } } @@ -422,14 +425,14 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j if (!(destination instanceof AMQDestination)) { throw new JMSException("Unsupported destination class: " - + ((destination != null) ? destination.getClass() : null)); + + ((destination != null) ? destination.getClass() : null)); } declareDestination((AMQDestination) destination); } protected void sendImpl(AMQDestination destination, Message message, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate) throws JMSException + boolean mandatory, boolean immediate) throws JMSException { sendImpl(destination, message, deliveryMode, priority, timeToLive, mandatory, immediate, _waitUntilSent); } @@ -447,23 +450,23 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j * @throws JMSException */ protected void sendImpl(AMQDestination destination, Message origMessage, int deliveryMode, int priority, long timeToLive, - boolean mandatory, boolean immediate, boolean wait) throws JMSException + boolean mandatory, boolean immediate, boolean wait) throws JMSException { checkTemporaryDestination(destination); origMessage.setJMSDestination(destination); AbstractJMSMessage message = convertToNativeMessage(origMessage); - - if(_disableMessageId) + + if (_disableMessageId) { - message.setJMSMessageID(null); + message.setJMSMessageID(null); } else { - if (message.getJMSMessageID() == null) - { - message.setJMSMessageID(UUID.randomUUID().toString()); - } + if (message.getJMSMessageID() == null) + { + message.setJMSMessageID(UUID.randomUUID().toString()); + } } int type; @@ -486,9 +489,8 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. // Be aware of possible changes to parameter order as versions change. AMQFrame publishFrame = - BasicPublishBody.createAMQFrame( - _channelId, _protocolHandler.getProtocolMajorVersion(), _protocolHandler.getProtocolMinorVersion(), - destination.getExchangeName(), // exchange + BasicPublishBody.createAMQFrame(_channelId, _protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion(), destination.getExchangeName(), // exchange immediate, // immediate mandatory, // mandatory destination.getRoutingKey(), // routingKey @@ -535,9 +537,8 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. AMQFrame contentHeaderFrame = ContentHeaderBody.createAMQFrame(_channelId, - BasicConsumeBody.getClazz(_protocolHandler.getProtocolMajorVersion(), - _protocolHandler.getProtocolMinorVersion()), 0, - contentHeaderProperties, size); + BasicConsumeBody.getClazz(_protocolHandler.getProtocolMajorVersion(), + _protocolHandler.getProtocolMinorVersion()), 0, contentHeaderProperties, size); if (_logger.isDebugEnabled()) { _logger.debug("Sending content header frame to " + destination); @@ -685,6 +686,6 @@ public class BasicMessageProducer extends Closeable implements org.apache.qpid.j public boolean isBound(AMQDestination destination) throws JMSException { - return _session.isQueueBound(destination.getExchangeName(),null,destination.getRoutingKey()); + return _session.isQueueBound(destination.getExchangeName(), null, destination.getRoutingKey()); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/Closeable.java b/qpid/java/client/src/main/java/org/apache/qpid/client/Closeable.java index d246dc3931..7e119343a1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/Closeable.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/Closeable.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,12 +20,29 @@ */ package org.apache.qpid.client; -import java.util.concurrent.atomic.AtomicBoolean; - import javax.jms.IllegalStateException; import javax.jms.JMSException; -/** Provides support for orderly shutdown of an object. */ +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Captures the 'closed' state of an object, that is initially open, can be tested to see if it is closed, and provides + * a 'close' method to close it. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Mark an object as closed. + * <tr><td> Check if an object is closed. + * <tr><td> Raise a JMS exception if an object is closed. + * </table> + * + * @todo Might be better to make this an interface. This whole class doesn't really encapsulate a terribly neat + * piece of re-usable functionality. A simple interface defining a close method would suffice. + * + * @todo The convenience method {@link #checkNotClosed} is not that helpfull, what if the caller wants to do something + * other than throw an exception? It doesn't really represent a very usefull re-usable piece of code. Consider + * inlining it and dropping the method. + */ public abstract class Closeable { /** @@ -34,6 +51,11 @@ public abstract class Closeable */ protected final AtomicBoolean _closed = new AtomicBoolean(false); + /** + * Checks if this is closed, and raises a JMSException if it is. + * + * @throws JMSException If this is closed. + */ protected void checkNotClosed() throws JMSException { if (isClosed()) @@ -42,13 +64,20 @@ public abstract class Closeable } } + /** + * Checks if this is closed. + * + * @return <tt>true</tt> if this is closed, <tt>false</tt> otherwise. + */ public boolean isClosed() { -// synchronized (_closed) - { - return _closed.get(); - } + return _closed.get(); } + /** + * Closes this object. + * + * @throws JMSException If this cannot be closed for any reason. + */ public abstract void close() throws JMSException; } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java b/qpid/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java index d1237cff49..0927ca3625 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/JMSAMQException.java @@ -18,29 +18,12 @@ * under the License. * */ -/* - * - * Copyright (c) 2006 The Apache Software Foundation - * - * 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. - * - */ package org.apache.qpid.client; -import javax.jms.JMSException; - import org.apache.qpid.AMQException; +import javax.jms.JMSException; + /** * JMSException does not accept wrapped exceptions in its constructor. Presumably this is because it is a relatively old * Java exception class, before this was added as a default to Throwable. This exception class accepts wrapped exceptions @@ -50,8 +33,6 @@ import org.apache.qpid.AMQException; * <tr><th> Responsibilities <th> Collaborations * <tr><td> Accept wrapped exceptions as a JMSException. * </table> - * - * @author Apache Software Foundation */ public class JMSAMQException extends JMSException { @@ -71,6 +52,11 @@ public class JMSAMQException extends JMSException } } + /** + * @param s The underlying exception. + * + * @deprecated Use the other constructor and write a helpfull message. This one will be deleted. + */ public JMSAMQException(AMQException s) { super(s.getMessage(), String.valueOf(s.getErrorCode())); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java index a219f7d655..9eb826ba62 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/QueueSenderAdapter.java @@ -2,12 +2,12 @@ package org.apache.qpid.client; import javax.jms.Destination; import javax.jms.IllegalStateException; +import javax.jms.InvalidDestinationException; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageProducer; import javax.jms.Queue; import javax.jms.QueueSender; -import javax.jms.InvalidDestinationException; public class QueueSenderAdapter implements QueueSender { @@ -25,6 +25,7 @@ public class QueueSenderAdapter implements QueueSender public Queue getQueue() throws JMSException { checkPreConditions(); + return _queue; } @@ -40,15 +41,13 @@ public class QueueSenderAdapter implements QueueSender _delegate.send(queue, msg); } - public void publish(Message msg, int deliveryMode, int priority, long timeToLive) - throws JMSException + public void publish(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { checkPreConditions(); _delegate.send(msg, deliveryMode, priority, timeToLive); } - public void send(Queue queue, Message msg, int deliveryMode, int priority, long timeToLive) - throws JMSException + public void send(Queue queue, Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { checkPreConditions(queue); _delegate.send(queue, msg, deliveryMode, priority, timeToLive); @@ -63,36 +62,42 @@ public class QueueSenderAdapter implements QueueSender public int getDeliveryMode() throws JMSException { checkPreConditions(); + return _delegate.getDeliveryMode(); } public Destination getDestination() throws JMSException { checkPreConditions(); + return _delegate.getDestination(); } public boolean getDisableMessageID() throws JMSException { checkPreConditions(); + return _delegate.getDisableMessageID(); } public boolean getDisableMessageTimestamp() throws JMSException { checkPreConditions(); + return _delegate.getDisableMessageTimestamp(); } public int getPriority() throws JMSException { checkPreConditions(); + return _delegate.getPriority(); } public long getTimeToLive() throws JMSException { checkPreConditions(); + return _delegate.getTimeToLive(); } @@ -102,8 +107,7 @@ public class QueueSenderAdapter implements QueueSender _delegate.send(dest, msg); } - public void send(Message msg, int deliveryMode, int priority, long timeToLive) - throws JMSException + public void send(Message msg, int deliveryMode, int priority, long timeToLive) throws JMSException { checkPreConditions(); _delegate.send(msg, deliveryMode, priority, timeToLive); @@ -159,15 +163,21 @@ public class QueueSenderAdapter implements QueueSender AMQSession session = ((BasicMessageProducer) _delegate).getSession(); - if (session == null || session.isClosed()) + if ((session == null) || session.isClosed()) { throw new javax.jms.IllegalStateException("Invalid Session"); } + if (queue == null) + { + throw new UnsupportedOperationException("Queue is null."); + } + if (!(queue instanceof AMQDestination)) { throw new InvalidDestinationException("Queue: " + queue + " is not a valid Qpid queue"); } + AMQDestination destination = (AMQDestination) queue; if (!destination.isValidated() && checkQueueBeforePublish()) { @@ -185,7 +195,8 @@ public class QueueSenderAdapter implements QueueSender } else { - throw new InvalidDestinationException("Queue: " + queue + " is not a valid destination (no bindings on server"); + throw new InvalidDestinationException("Queue: " + queue + + " is not a valid destination (no bindings on server"); } } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java index 95899d533a..037b0dc2d1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverException.java @@ -21,14 +21,26 @@ package org.apache.qpid.client.failover; /** - * This exception is thrown when failover is taking place and otherparts of the client need to know about this. + * FailoverException is used to indicate that a synchronous request has failed to receive the reply that it is waiting + * for because the fail-over process has been started whilst it was waiting for its reply. Synchronous methods generally + * raise this exception to indicate that they must be re-tried once the fail-over process has completed. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Used to indicate failure of a synchronous request due to fail-over. + * </table> * * @todo This exception is created and passed as an argument to a method, rather than thrown. The exception is being - * used to represent a signal, passed out to other threads. Use of exceptions as arguments rather than as - * exceptions is extremly confusing. Eliminate. Use a Condition or set a flag and check it instead. Also - * FailoverException is Runtime but handled and should only use Runtimes for non-handleable conditions. + * used to represent an event, passed out to other threads. Use of exceptions as arguments rather than as + * exceptions is extremly confusing. Ideally use a condition or set a flag and check it instead. + * This exceptions-as-events pattern seems to be in a similar style to Mina code, which is not pretty, but + * potentially acceptable for that reason. We have the option of extending the mina model to add more events + * to it, that is, anything that is interested in handling failover as an event occurs below the main + * amq event handler, which knows the specific interface of the qpid handlers, which can pass this down as + * an explicit event, without it being an exception. Add failover method to BlockingMethodFrameListener, + * have it set a flag or interrupt the waiting thread, which then creates and raises this exception. */ -public class FailoverException extends RuntimeException +public class FailoverException extends Exception { public FailoverException(String message) { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java index 00eac7f2af..8e66aec0d6 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,59 +20,108 @@ */ package org.apache.qpid.client.failover; -import java.util.concurrent.CountDownLatch; - import org.apache.log4j.Logger; + import org.apache.mina.common.IoSession; + import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.client.state.AMQStateManager; +import java.util.concurrent.CountDownLatch; + /** - * When failover is required, we need a separate thread to handle the establishment of the new connection and - * the transfer of subscriptions. - * </p> - * The reason this needs to be a separate thread is because you cannot do this work inside the MINA IO processor - * thread. One significant task is the connection setup which involves a protocol exchange until a particular state - * is achieved. However if you do this in the MINA thread, you have to block until the state is achieved which means - * the IO processor is not able to do anything at all. + * FailoverHandler is a continuation that performs the failover procedure on a protocol session. As described in the + * class level comment for {@link AMQProtocolHandler}, a protocol connection can span many physical transport + * connections, failing over to a new connection if the transport connection fails. The procedure to establish a new + * connection is expressed as a continuation, in order that it may be run in a seperate thread to the i/o thread that + * detected the failure and is used to handle the communication to establish a new connection. + * + * </p>The reason this needs to be a separate thread is because this work cannot be done inside the i/o processor + * thread. The significant task is the connection setup which involves a protocol exchange until a particular state + * is achieved. This procedure waits until the state is achieved which would prevent the i/o thread doing the work + * it needs to do to achieve the new state. + * + * <p/>The failover procedure does the following: + * + * <ol> + * <li>Sets the failing over condition to true.</li> + * <li>Creates a {@link FailoverException} and gets the protocol connection handler to propagate this event to all + * interested parties.</li> + * <li>Takes the failover mutex on the protocol connection handler.</li> + * <li>Abandons the fail over if any of the interested parties vetoes it. The mutex is released and the condition + * reset.</li> + * <li>Creates a new {@link AMQStateManager} and re-established the connection through it.</li> + * <li>Informs the AMQConnection if the connection cannot be re-established.</li> + * <li>Recreates all sessions from the old connection to the new.</li> + * <li>Resets the failing over condition and releases the mutex.</li> + * </ol> + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Update fail-over state <td> {@link AMQProtocolHandler} + * </table> + * + * @todo The failover latch and mutex are used like a lock and condition. If the retrotranlator supports lock/condition + * then could change over to using them. 1.4 support still needed. + * + * @todo If the condition is set to null on a vetoes fail-over and there are already other threads waiting on the + * condition, they will never be released. It might be an idea to reset the condition in a finally block. + * + * @todo Creates a {@link AMQDisconnectedException} and passes it to the AMQConnection. No need to use an + * exception-as-argument here, could just as easily call a specific method for this purpose on AMQConnection. + * + * @todo Creates a {@link FailoverException} and propagates it to the MethodHandlers. No need to use an + * exception-as-argument here, could just as easily call a specific method for this purpose on + * {@link org.apache.qpid.protocol.AMQMethodListener}. */ public class FailoverHandler implements Runnable { + /** Used for debugging. */ private static final Logger _logger = Logger.getLogger(FailoverHandler.class); + /** Holds the MINA session for the connection that has failed, not the connection that is being failed onto. */ private final IoSession _session; + + /** Holds the protocol handler for the failed connection, upon which the new connection is to be set up. */ private AMQProtocolHandler _amqProtocolHandler; - /** - * Used where forcing the failover host - */ + /** Used to hold the host to fail over to. This is optional and if not set a reconnect to the previous host is tried. */ private String _host; - /** - * Used where forcing the failover port - */ + /** Used to hold the port to fail over to. */ private int _port; + /** + * Creates a failover handler on a protocol session, for a particular MINA session (network connection). + * + * @param amqProtocolHandler The protocol handler that spans the failover. + * @param session The MINA session, for the failing connection. + */ public FailoverHandler(AMQProtocolHandler amqProtocolHandler, IoSession session) { _amqProtocolHandler = amqProtocolHandler; _session = session; } + /** + * Performs the failover procedure. See the class level comment, {@link FailoverHandler}, for a description of the + * failover procedure. + */ public void run() { if (Thread.currentThread().isDaemon()) { throw new IllegalStateException("FailoverHandler must run on a non-daemon thread."); } - //Thread.currentThread().setName("Failover Thread"); + // Create a latch, upon which tasks that must not run in parallel with a failover can wait for completion of + // the fail over. _amqProtocolHandler.setFailoverLatch(new CountDownLatch(1)); // We wake up listeners. If they can handle failover, they will extend the - // FailoverSupport class and will in turn block on the latch until failover - // has completed before retrying the operation + // FailoverRetrySupport class and will in turn block on the latch until failover + // has completed before retrying the operation. _amqProtocolHandler.propagateExceptionToWaiters(new FailoverException("Failing over about to start")); // Since failover impacts several structures we protect them all with a single mutex. These structures @@ -99,8 +148,10 @@ public class FailoverHandler implements Runnable { _amqProtocolHandler.getConnection().exceptionReceived(new AMQDisconnectedException("Failover was vetoed by client", null)); } + _amqProtocolHandler.getFailoverLatch().countDown(); _amqProtocolHandler.setFailoverLatch(null); + return; } @@ -119,6 +170,7 @@ public class FailoverHandler implements Runnable { failoverSucceeded = _amqProtocolHandler.getConnection().attemptReconnection(); } + if (!failoverSucceeded) { _amqProtocolHandler.setStateManager(existingStateManager); @@ -140,6 +192,7 @@ public class FailoverHandler implements Runnable { _logger.info("Client vetoed automatic resubscription"); } + _amqProtocolHandler.getConnection().fireFailoverComplete(); _amqProtocolHandler.setFailoverState(FailoverState.NOT_STARTED); _logger.info("Connection failover completed successfully"); @@ -148,35 +201,36 @@ public class FailoverHandler implements Runnable { _logger.info("Failover process failed - exception being propagated by protocol handler"); _amqProtocolHandler.setFailoverState(FailoverState.FAILED); - try - { - _amqProtocolHandler.exceptionCaught(_session, e); - } + /*try + {*/ + _amqProtocolHandler.exceptionCaught(_session, e); + /*} catch (Exception ex) { _logger.error("Error notifying protocol session of error: " + ex, ex); - } + }*/ } } } - _amqProtocolHandler.getFailoverLatch().countDown(); - } - public String getHost() - { - return _host; + _amqProtocolHandler.getFailoverLatch().countDown(); } + /** + * Sets the host name to fail over to. This is optional and if not set a reconnect to the previous host is tried. + * + * @param host The host name to fail over to. + */ public void setHost(String host) { _host = host; } - public int getPort() - { - return _port; - } - + /** + * Sets the port to fail over to. + * + * @param port The port to fail over to. + */ public void setPort(int port) { _port = port; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java new file mode 100644 index 0000000000..8c05db3232 --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverNoopSupport.java @@ -0,0 +1,54 @@ +package org.apache.qpid.client.failover;
+
+import org.apache.qpid.client.AMQConnection;
+
+/**
+ * FailoverNoopSupport is a {@link FailoverSupport} implementation that does not really provide any failover support
+ * at all. It wraps a {@link FailoverProtectedOperation} but should that operation throw {@link FailoverException} this
+ * support class simply re-raises that exception as an IllegalStateException. This support wrapper should only be
+ * used where the caller can be certain that the failover protected operation cannot acutally throw a failover exception,
+ * for example, because the caller already holds a lock preventing that condition from arising.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Perform a fail-over protected operation raising providing no handling of fail-over conditions.
+ * </table>
+ */
+public class FailoverNoopSupport<T, E extends Exception> implements FailoverSupport<T, E>
+{
+ /** The protected operation that is to be retried in the event of fail-over. */
+ FailoverProtectedOperation<T, E> operation;
+
+ /** The connection on which the fail-over protected operation is to be performed. */
+ AMQConnection connection;
+
+ /**
+ * Creates an automatic retrying fail-over handler for the specified operation.
+ *
+ * @param operation The fail-over protected operation to wrap in this handler.
+ */
+ public FailoverNoopSupport(FailoverProtectedOperation<T, E> operation, AMQConnection con)
+ {
+ this.operation = operation;
+ this.connection = con;
+ }
+
+ /**
+ * Delegates to another continuation which is to be provided with fail-over handling.
+ *
+ * @return The return value from the delegated to continuation.
+ * @throws E Any exception that the delegated to continuation may raise.
+ */
+ public T execute() throws E
+ {
+ try
+ {
+ return operation.execute();
+ }
+ catch (FailoverException e)
+ {
+ throw new IllegalStateException("Fail-over interupted no-op failover support. "
+ + "No-op support should only be used where the caller is certain fail-over cannot occur.", e);
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java new file mode 100644 index 0000000000..054add07f7 --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverProtectedOperation.java @@ -0,0 +1,28 @@ +package org.apache.qpid.client.failover;
+
+/**
+ * FailoverProtectedOperation is a continuation for an operation that may throw a {@link FailoverException} because
+ * it has been interrupted by the fail-over process. The {@link FailoverRetrySupport} class defines support wrappers
+ * for failover protected operations, in order to provide different handling schemes when failovers occurr.
+ *
+ * <p/>The type of checked exception that the operation may perform has been generified, in order that fail over
+ * protected operations can be defined that raise arbitrary exceptions. The actuall exception types used should not
+ * be sub-classes of FailoverException, or else catching FailoverException in the {@link FailoverRetrySupport} classes
+ * will mask the exception.
+ *
+ * <p><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities
+ * <tr><td> Perform an operation that may be interrupted by fail-over.
+ * </table>
+ */
+public interface FailoverProtectedOperation<T, E extends Exception>
+{
+ /**
+ * Performs the continuations work.
+ *
+ * @return Provdes scope for the continuation to return an arbitrary value.
+ *
+ * @throws FailoverException If the operation is interrupted by a fail-over notification.
+ */
+ public abstract T execute() throws E, FailoverException;
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java new file mode 100644 index 0000000000..120a07f0fc --- /dev/null +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverRetrySupport.java @@ -0,0 +1,128 @@ +/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ *
+ */
+package org.apache.qpid.client.failover;
+
+import org.apache.qpid.client.AMQConnection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * FailoverRetrySupport is a continuation that wraps another continuation, delaying its execution until it is notified
+ * that a blocking condition has been met, and executing the continuation within a mutex. If the continuation fails, due
+ * to the original condition being broken, whilst the continuation is waiting for a reponse to a synchronous request,
+ * FailoverRetrySupport automatcally rechecks the condition and re-acquires the mutex and re-runs the continution. This
+ * automatic retrying is continued until the continuation succeeds, or throws an exception (different to
+ * FailoverException, which is used to signal the failure of the original condition).
+ *
+ * <p/>The blocking condition used is that the connection is not currently failing over, and the mutex used is the
+ * connection failover mutex, which guards against the fail-over process being run during fail-over vulnerable methods.
+ * These are used like a lock and condition variable.
+ *
+ * <p/>The wrapped operation may throw a FailoverException, this is an exception that can be raised by a
+ * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener}, in response to it being notified that a
+ * fail-over wants to start whilst it was waiting. Methods that are vulnerable to fail-over are those that are
+ * synchronous, where a failure will prevent them from getting the reply they are waiting for and asynchronous
+ * methods that should not be attempted when a fail-over is in progress.
+ *
+ * <p/>Wrapping a synchronous method in a FailoverRetrySupport will have the effect that the operation will not be
+ * started during fail-over, but be delayed until any current fail-over has completed. Should a fail-over process want
+ * to start whilst waiting for the synchrnous reply, the FailoverRetrySupport will detect this and rety the operation
+ * until it succeeds. Synchronous methods are usually coordinated with a
+ * {@link org.apache.qpid.client.protocol.BlockingMethodFrameListener} which is notified when a fail-over process wants
+ * to start and throws a FailoverException in response to this.
+ *
+ * <p/>Wrapping an asynchronous method in a FailoverRetrySupport will have the effect that the operation will not be
+ * started during fail-over, but be delayed until any current fail-over has completed.
+ *
+ * <p/><table id="crc"><caption>CRC Card</caption>
+ * <tr><th> Responsibilities <th> Collaborations
+ * <tr><td> Provide a continuation synchronized on a fail-over lock and condition.
+ * <tr><td> Automatically retry the continuation accross fail-overs until it succeeds, or raises an exception.
+ * </table>
+ *
+ * @todo Another continuation. Could use an interface Continuation (as described in other todos, for example, see
+ * {@link org.apache.qpid.pool.Job}). Then have a wrapping continuation (this), which blocks on an arbitrary
+ * Condition or Latch (specified in constructor call), that this blocks on before calling the wrapped Continuation.
+ * Must work on Java 1.4, so check retrotranslator works on Lock/Condition or latch first. Argument and return type
+ * to match wrapped condition as type parameters. Rename to AsyncConditionalContinuation or something like that.
+ *
+ * @todo InterruptedException not handled well.
+ */
+public class FailoverRetrySupport<T, E extends Exception> implements FailoverSupport<T, E>
+{
+ /** Used for debugging. */
+ private static final Logger _log = LoggerFactory.getLogger(FailoverRetrySupport.class);
+
+ /** The protected operation that is to be retried in the event of fail-over. */
+ FailoverProtectedOperation<T, E> operation;
+
+ /** The connection on which the fail-over protected operation is to be performed. */
+ AMQConnection connection;
+
+ /**
+ * Creates an automatic retrying fail-over handler for the specified operation.
+ *
+ * @param operation The fail-over protected operation to wrap in this handler.
+ */
+ public FailoverRetrySupport(FailoverProtectedOperation<T, E> operation, AMQConnection con)
+ {
+ this.operation = operation;
+ this.connection = con;
+ }
+
+ /**
+ * Delays a continuation until the "not failing over" condition is met on the specified connection. Repeats
+ * until the operation throws AMQException or succeeds without being interrupted by fail-over.
+ *
+ * @return The result of executing the continuation.
+ *
+ * @throws E Any underlying exception is allowed to fall through.
+ */
+ public T execute() throws E
+ {
+ while (true)
+ {
+ try
+ {
+ connection.blockUntilNotFailingOver();
+ }
+ catch (InterruptedException e)
+ {
+ _log.debug("Interrupted: " + e, e);
+
+ return null;
+ }
+
+ synchronized (connection.getFailoverMutex())
+ {
+ try
+ {
+ return operation.execute();
+ }
+ catch (FailoverException e)
+ {
+ _log.debug("Failover exception caught during operation: " + e, e);
+ }
+ }
+ }
+ }
+}
diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java index e5787439ec..807a5f7d13 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverState.java @@ -7,9 +7,9 @@ * to you 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 @@ -21,27 +21,42 @@ package org.apache.qpid.client.failover; /** - * Enumeration of failover states. Used to handle failover from within AMQProtocolHandler where MINA events need to be - * dealt with and can happen during failover. + * Defines the possible states of the failover process; not started, in progress, failed. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Represent a one of the states of the fail-over process. + * </table> */ public final class FailoverState { + /** The string description on this state. */ private final String _state; - /** Failover has not yet been attempted on this connection */ + /** Failover has not yet been attempted on this connection. */ public static final FailoverState NOT_STARTED = new FailoverState("NOT STARTED"); - /** Failover has been requested on this connection but has not completed */ + /** Failover has been requested on this connection but has not completed. */ public static final FailoverState IN_PROGRESS = new FailoverState("IN PROGRESS"); - /** Failover has been attempted and failed */ + /** Failover has been attempted and failed. */ public static final FailoverState FAILED = new FailoverState("FAILED"); + /** + * Creates a type safe enumeration of a fail-over state. + * + * @param state The fail-over state description string. + */ private FailoverState(String state) { _state = state; } + /** + * Prints this state, mainly for debugging purposes. + * + * @return The string description of this state. + */ public String toString() { return "FailoverState: " + _state; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java index a005bc5fdf..478482741c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/failover/FailoverSupport.java @@ -1,65 +1,26 @@ -/* +package org.apache.qpid.client.failover; + +/** + * FailoverSupport defines an interface for different types of fail-over handlers, that provide different types of + * behaviour for handling fail-overs during operations that can be interrupted by the fail-over process. For example, + * the support could automatically retry once the fail-over process completes, could prevent an operation from being + * started whilst fail-over is running, or could quietly abandon the operation or raise an exception, and so on. * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. + * <p><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities + * <tr><td> Perform a fail-over protected operation with handling for fail-over conditions. + * </table> * + * @todo Continuation, extend some sort of re-usable Continuation interface, which might look very like this one. */ -package org.apache.qpid.client.failover; - -import javax.jms.JMSException; - -import org.apache.log4j.Logger; -import org.apache.qpid.client.AMQConnection; - -public abstract class FailoverSupport +public interface FailoverSupport<T, E extends Exception> { - private static final Logger _log = Logger.getLogger(FailoverSupport.class); - - public Object execute(AMQConnection con) throws JMSException - { - // We wait until we are not in the middle of failover before acquiring the mutex and then proceeding. - // Any method that can potentially block for any reason should use this class so that deadlock will not - // occur. The FailoverException is propagated by the AMQProtocolHandler to any listeners (e.g. frame listeners) - // that might be causing a block. When that happens, the exception is caught here and the mutex is released - // before waiting for the failover to complete (either successfully or unsuccessfully). - while (true) - { - try - { - con.blockUntilNotFailingOver(); - } - catch (InterruptedException e) - { - _log.info("Interrupted: " + e, e); - return null; - } - synchronized (con.getFailoverMutex()) - { - try - { - return operation(); - } - catch (FailoverException e) - { - _log.info("Failover exception caught during operation: " + e, e); - } - } - } - } - - protected abstract Object operation() throws JMSException; + /** + * Delegates to another continuation which is to be provided with fail-over handling. + * + * @return The return value from the delegated to continuation. + * + * @throws E Any exception that the delegated to continuation may raise. + */ + public T execute() throws E; } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java index bd8177feb6..8f0ee05b3e 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicCancelOkMethodHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,7 +20,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; @@ -28,9 +27,13 @@ import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.BasicCancelOkBody; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class BasicCancelOkMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(BasicCancelOkMethodHandler.class); + private static final Logger _logger = LoggerFactory.getLogger(BasicCancelOkMethodHandler.class); + private static final BasicCancelOkMethodHandler _instance = new BasicCancelOkMethodHandler(); public static BasicCancelOkMethodHandler getInstance() @@ -39,10 +42,10 @@ public class BasicCancelOkMethodHandler implements StateAwareMethodListener } private BasicCancelOkMethodHandler() - { - } + { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException { BasicCancelOkBody body = (BasicCancelOkBody) evt.getMethod(); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java index d34d6688c1..51120da55c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicDeliverMethodHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,7 +20,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.message.UnprocessedMessage; import org.apache.qpid.client.protocol.AMQProtocolSession; @@ -29,9 +28,12 @@ import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.BasicDeliverBody; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class BasicDeliverMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(BasicDeliverMethodHandler.class); + private static final Logger _logger = LoggerFactory.getLogger(BasicDeliverMethodHandler.class); private static final BasicDeliverMethodHandler _instance = new BasicDeliverMethodHandler(); @@ -40,7 +42,8 @@ public class BasicDeliverMethodHandler implements StateAwareMethodListener return _instance; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException { final UnprocessedMessage msg = new UnprocessedMessage(evt.getChannelId(), (BasicDeliverBody) evt.getMethod()); _logger.debug("New JmsDeliver method received"); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java index 02573c5d00..0f00c6a26e 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/BasicReturnMethodHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,7 +20,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.message.UnprocessedMessage; import org.apache.qpid.client.protocol.AMQProtocolSession; @@ -29,9 +28,12 @@ import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.BasicReturnBody; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class BasicReturnMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(BasicReturnMethodHandler.class); + private static final Logger _logger = LoggerFactory.getLogger(BasicReturnMethodHandler.class); private static final BasicReturnMethodHandler _instance = new BasicReturnMethodHandler(); @@ -40,10 +42,11 @@ public class BasicReturnMethodHandler implements StateAwareMethodListener return _instance; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException { _logger.debug("New JmsBounce method received"); - final UnprocessedMessage msg = new UnprocessedMessage(evt.getChannelId(),(BasicReturnBody)evt.getMethod()); + final UnprocessedMessage msg = new UnprocessedMessage(evt.getChannelId(), (BasicReturnBody) evt.getMethod()); protocolSession.unprocessedMessageReceived(msg); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java index fbf4d96647..9ed3ef7a60 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseMethodHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,7 +20,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQChannelClosedException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQInvalidRoutingKeyException; @@ -36,9 +35,12 @@ import org.apache.qpid.framing.ChannelCloseOkBody; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class ChannelCloseMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(ChannelCloseMethodHandler.class); + private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseMethodHandler.class); private static ChannelCloseMethodHandler _handler = new ChannelCloseMethodHandler(); @@ -47,7 +49,8 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener return _handler; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException { _logger.debug("ChannelClose method received"); ChannelCloseBody method = (ChannelCloseBody) evt.getMethod(); @@ -68,6 +71,7 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener { _logger.debug("Channel close received with errorCode " + errorCode + ", and reason " + reason); } + if (errorCode == AMQConstant.NO_CONSUMERS) { throw new AMQNoConsumersException("Error: " + reason, null, null); @@ -94,7 +98,7 @@ public class ChannelCloseMethodHandler implements StateAwareMethodListener } } - //fixme why is this only done when the close is expected... + // fixme why is this only done when the close is expected... // should the above forced closes not also cause a close? protocolSession.channelClosed(evt.getChannelId(), errorCode, String.valueOf(reason)); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java index 0826deb2f4..e1fe2697e5 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelCloseOkMethodHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,16 +20,18 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class ChannelCloseOkMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(ChannelCloseOkMethodHandler.class); + private static final Logger _logger = LoggerFactory.getLogger(ChannelCloseOkMethodHandler.class); private static final ChannelCloseOkMethodHandler _instance = new ChannelCloseOkMethodHandler(); @@ -38,10 +40,11 @@ public class ChannelCloseOkMethodHandler implements StateAwareMethodListener return _instance; } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException { _logger.info("Received channel-close-ok for channel-id " + evt.getChannelId()); - //todo this should do the local closure + // todo this should do the local closure } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java index 1f003649c0..ca3f46d08b 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ChannelFlowOkMethodHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,7 +20,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; @@ -28,23 +27,26 @@ import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.ChannelFlowOkBody; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class ChannelFlowOkMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(ChannelFlowOkMethodHandler.class); - private static final ChannelFlowOkMethodHandler _instance = new ChannelFlowOkMethodHandler(); + private static final Logger _logger = LoggerFactory.getLogger(ChannelFlowOkMethodHandler.class); + private static final ChannelFlowOkMethodHandler _instance = new ChannelFlowOkMethodHandler(); - public static ChannelFlowOkMethodHandler getInstance() - { - return _instance; - } + public static ChannelFlowOkMethodHandler getInstance() + { + return _instance; + } - private ChannelFlowOkMethodHandler() - { - } + private ChannelFlowOkMethodHandler() + { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException - { - ChannelFlowOkBody method = (ChannelFlowOkBody) evt.getMethod(); - _logger.debug("Received Channel.Flow-Ok message, active = " + method.active); - } + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + ChannelFlowOkBody method = (ChannelFlowOkBody) evt.getMethod(); + _logger.debug("Received Channel.Flow-Ok message, active = " + method.active); + } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java index d8153f9c97..752f44237d 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionCloseMethodHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,7 +20,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQConnectionClosedException; import org.apache.qpid.AMQException; import org.apache.qpid.client.AMQAuthenticationException; @@ -34,9 +33,12 @@ import org.apache.qpid.framing.ConnectionCloseOkBody; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class ConnectionCloseMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(ConnectionCloseMethodHandler.class); + private static final Logger _logger = LoggerFactory.getLogger(ConnectionCloseMethodHandler.class); private static ConnectionCloseMethodHandler _handler = new ConnectionCloseMethodHandler(); @@ -46,16 +48,16 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener } private ConnectionCloseMethodHandler() - { - } + { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException { _logger.info("ConnectionClose frame received"); ConnectionCloseBody method = (ConnectionCloseBody) evt.getMethod(); // does it matter - //stateManager.changeState(AMQState.CONNECTION_CLOSING); + // stateManager.changeState(AMQState.CONNECTION_CLOSING); AMQConstant errorCode = AMQConstant.getConstant(method.replyCode); AMQShortString reason = method.replyText; @@ -64,7 +66,8 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener { // TODO: check whether channel id of zero is appropriate // Be aware of possible changes to parameter order as versions change. - protocolSession.writeFrame(ConnectionCloseOkBody.createAMQFrame((short) 0, method.getMajor(), method.getMinor())); + protocolSession.writeFrame(ConnectionCloseOkBody.createAMQFrame((short) 0, method.getMajor(), + method.getMinor())); if (errorCode != AMQConstant.REPLY_SUCCESS) { @@ -74,7 +77,7 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener protocolSession.closeProtocolSession(); - //todo this is a bit of a fudge (could be conssidered such as each new connection needs a new state manager or at least a fresh state. + // todo this is a bit of a fudge (could be conssidered such as each new connection needs a new state manager or at least a fresh state. stateManager.changeState(AMQState.CONNECTION_NOT_STARTED); throw new AMQAuthenticationException(errorCode, reason == null ? null : reason.toString(), null); @@ -83,7 +86,6 @@ public class ConnectionCloseMethodHandler implements StateAwareMethodListener { _logger.info("Connection close received with error code " + errorCode); - throw new AMQConnectionClosedException(errorCode, "Error: " + reason, null); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java index 866f65b384..213c0eba6e 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionRedirectMethodHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,7 +20,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; @@ -28,9 +27,12 @@ import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.ConnectionRedirectBody; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class ConnectionRedirectMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(ConnectionRedirectMethodHandler.class); + private static final Logger _logger = LoggerFactory.getLogger(ConnectionRedirectMethodHandler.class); private static final int DEFAULT_REDIRECT_PORT = 5672; @@ -42,10 +44,10 @@ public class ConnectionRedirectMethodHandler implements StateAwareMethodListener } private ConnectionRedirectMethodHandler() - { - } + { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException { _logger.info("ConnectionRedirect frame received"); ConnectionRedirectBody method = (ConnectionRedirectBody) evt.getMethod(); @@ -65,6 +67,7 @@ public class ConnectionRedirectMethodHandler implements StateAwareMethodListener host = host.substring(0, portIndex); } + protocolSession.failover(host, port); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java index 157128aebc..59b493a6f7 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionStartMethodHandler.java @@ -20,16 +20,6 @@ */ package org.apache.qpid.client.handler; -import java.io.UnsupportedEncodingException; -import java.util.HashSet; -import java.util.StringTokenizer; - -import javax.security.sasl.Sasl; -import javax.security.sasl.SaslClient; -import javax.security.sasl.SaslException; - -import org.apache.log4j.Logger; - import org.apache.qpid.AMQException; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.security.AMQCallbackHandler; @@ -47,9 +37,20 @@ import org.apache.qpid.framing.FieldTableFactory; import org.apache.qpid.framing.ProtocolVersion; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslClient; +import javax.security.sasl.SaslException; + +import java.io.UnsupportedEncodingException; +import java.util.HashSet; +import java.util.StringTokenizer; + public class ConnectionStartMethodHandler implements StateAwareMethodListener { - private static final Logger _log = Logger.getLogger(ConnectionStartMethodHandler.class); + private static final Logger _log = LoggerFactory.getLogger(ConnectionStartMethodHandler.class); private static final ConnectionStartMethodHandler _instance = new ConnectionStartMethodHandler(); @@ -62,15 +63,14 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener { } public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) - throws AMQException + throws AMQException { _log.debug("public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, " - + "AMQMethodEvent evt): called"); + + "AMQMethodEvent evt): called"); ConnectionStartBody body = (ConnectionStartBody) evt.getMethod(); - ProtocolVersion pv = new ProtocolVersion((byte) body.versionMajor,(byte) body.versionMinor); - + ProtocolVersion pv = new ProtocolVersion((byte) body.versionMajor, (byte) body.versionMinor); // For the purposes of interop, we can make the client accept the broker's version string. // If it does, it then internally records the version as being the latest one that it understands. @@ -110,7 +110,7 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener { SaslClient sc = Sasl.createSaslClient(new String[] { mechanism }, null, "AMQP", "localhost", null, - createCallbackHandler(mechanism, protocolSession)); + createCallbackHandler(mechanism, protocolSession)); if (sc == null) { throw new AMQException(null, "Client SASL configuration error: no SaslClient could be created for mechanism " + mechanism @@ -148,23 +148,22 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener FieldTable clientProperties = FieldTableFactory.newFieldTable(); clientProperties.setString(new AMQShortString(ClientProperties.instance.toString()), - protocolSession.getClientID()); + protocolSession.getClientID()); clientProperties.setString(new AMQShortString(ClientProperties.product.toString()), - QpidProperties.getProductName()); + QpidProperties.getProductName()); clientProperties.setString(new AMQShortString(ClientProperties.version.toString()), - QpidProperties.getReleaseVersion()); + QpidProperties.getReleaseVersion()); clientProperties.setString(new AMQShortString(ClientProperties.platform.toString()), getFullSystemInfo()); // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. // Be aware of possible changes to parameter order as versions change. protocolSession.writeFrame(ConnectionStartOkBody.createAMQFrame(evt.getChannelId(), - protocolSession.getProtocolMajorVersion(), - protocolSession.getProtocolMinorVersion(), - clientProperties, // clientProperties - new AMQShortString(selectedLocale), // locale - new AMQShortString(mechanism), // mechanism - saslResponse)); // response + protocolSession.getProtocolMajorVersion(), protocolSession.getProtocolMinorVersion(), + clientProperties, // clientProperties + new AMQShortString(selectedLocale), // locale + new AMQShortString(mechanism), // mechanism + saslResponse)); // response } catch (UnsupportedEncodingException e) @@ -175,13 +174,12 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener else { _log.error("Broker requested Protocol [" + body.versionMajor + "-" + body.versionMinor - + "] which is not supported by this version of the client library"); + + "] which is not supported by this version of the client library"); protocolSession.closeProtocolSession(); } } - private String getFullSystemInfo() { StringBuffer fullSystemInfo = new StringBuffer(); @@ -221,7 +219,7 @@ public class ConnectionStartMethodHandler implements StateAwareMethodListener } private AMQCallbackHandler createCallbackHandler(String mechanism, AMQProtocolSession protocolSession) - throws AMQException + throws AMQException { Class mechanismClass = CallbackHandlerRegistry.getInstance().getCallbackHandlerClass(mechanism); try diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java index 67f1a6519f..68556991d7 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ConnectionTuneMethodHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,7 +20,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.ConnectionTuneParameters; import org.apache.qpid.client.protocol.AMQProtocolSession; @@ -34,9 +33,12 @@ import org.apache.qpid.framing.ConnectionTuneBody; import org.apache.qpid.framing.ConnectionTuneOkBody; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class ConnectionTuneMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(ConnectionTuneMethodHandler.class); + private static final Logger _logger = LoggerFactory.getLogger(ConnectionTuneMethodHandler.class); private static final ConnectionTuneMethodHandler _instance = new ConnectionTuneMethodHandler(); @@ -46,10 +48,10 @@ public class ConnectionTuneMethodHandler implements StateAwareMethodListener } protected ConnectionTuneMethodHandler() - { - } + { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException { _logger.debug("ConnectionTune frame received"); ConnectionTuneBody frame = (ConnectionTuneBody) evt.getMethod(); @@ -60,38 +62,36 @@ public class ConnectionTuneMethodHandler implements StateAwareMethodListener params = new ConnectionTuneParameters(); } - params.setFrameMax(frame.frameMax); + params.setFrameMax(frame.frameMax); params.setChannelMax(frame.channelMax); params.setHeartbeat(Integer.getInteger("amqj.heartbeat.delay", frame.heartbeat)); protocolSession.setConnectionTuneParameters(params); stateManager.changeState(AMQState.CONNECTION_NOT_OPENED); - protocolSession.writeFrame(createTuneOkFrame(evt.getChannelId(), params,frame.getMajor(), frame.getMinor())); + protocolSession.writeFrame(createTuneOkFrame(evt.getChannelId(), params, frame.getMajor(), frame.getMinor())); String host = protocolSession.getAMQConnection().getVirtualHost(); AMQShortString virtualHost = new AMQShortString("/" + host); - - protocolSession.writeFrame(createConnectionOpenFrame(evt.getChannelId(), virtualHost, null, true,frame.getMajor(), frame.getMinor())); + protocolSession.writeFrame(createConnectionOpenFrame(evt.getChannelId(), virtualHost, null, true, frame.getMajor(), + frame.getMinor())); } - protected AMQFrame createConnectionOpenFrame(int channel, AMQShortString path, AMQShortString capabilities, boolean insist, byte major, byte minor) + protected AMQFrame createConnectionOpenFrame(int channel, AMQShortString path, AMQShortString capabilities, + boolean insist, byte major, byte minor) { // Be aware of possible changes to parameter order as versions change. - return ConnectionOpenBody.createAMQFrame(channel, - major, minor, // AMQP version (major, minor) - capabilities, // capabilities - insist, // insist - path); // virtualHost + return ConnectionOpenBody.createAMQFrame(channel, major, minor, // AMQP version (major, minor) + capabilities, // capabilities + insist, // insist + path); // virtualHost } protected AMQFrame createTuneOkFrame(int channel, ConnectionTuneParameters params, byte major, byte minor) { // Be aware of possible changes to parameter order as versions change. - return ConnectionTuneOkBody.createAMQFrame(channel, - major, minor, - params.getChannelMax(), // channelMax - params.getFrameMax(), // frameMax - params.getHeartbeat()); // heartbeat + return ConnectionTuneOkBody.createAMQFrame(channel, major, minor, params.getChannelMax(), // channelMax + params.getFrameMax(), // frameMax + params.getHeartbeat()); // heartbeat } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java index 146c705c00..8f9a84a3a6 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/ExchangeBoundOkMethodHandler.java @@ -17,7 +17,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; @@ -25,32 +24,33 @@ import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.ExchangeBoundOkBody; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * @author Apache Software Foundation */ public class ExchangeBoundOkMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(ExchangeBoundOkMethodHandler.class); - private static final ExchangeBoundOkMethodHandler _instance = new ExchangeBoundOkMethodHandler(); + private static final Logger _logger = LoggerFactory.getLogger(ExchangeBoundOkMethodHandler.class); + private static final ExchangeBoundOkMethodHandler _instance = new ExchangeBoundOkMethodHandler(); - public static ExchangeBoundOkMethodHandler getInstance() - { - return _instance; - } + public static ExchangeBoundOkMethodHandler getInstance() + { + return _instance; + } - private ExchangeBoundOkMethodHandler() - { - } + private ExchangeBoundOkMethodHandler() + { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException - { - if (_logger.isDebugEnabled()) - { + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + if (_logger.isDebugEnabled()) + { ExchangeBoundOkBody body = (ExchangeBoundOkBody) evt.getMethod(); - _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.replyCode + " text: " + - body.replyText); - } - } + _logger.debug("Received Exchange.Bound-Ok message, response code: " + body.replyCode + " text: " + + body.replyText); + } + } } - - diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java index eaf4721445..81228b4cdc 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/handler/QueueDeleteOkMethodHandler.java @@ -17,7 +17,6 @@ */ package org.apache.qpid.client.handler; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.protocol.AMQProtocolSession; import org.apache.qpid.client.state.AMQStateManager; @@ -25,31 +24,32 @@ import org.apache.qpid.client.state.StateAwareMethodListener; import org.apache.qpid.framing.QueueDeleteOkBody; import org.apache.qpid.protocol.AMQMethodEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * @author Apache Software Foundation */ public class QueueDeleteOkMethodHandler implements StateAwareMethodListener { - private static final Logger _logger = Logger.getLogger(QueueDeleteOkMethodHandler.class); - private static final QueueDeleteOkMethodHandler _instance = new QueueDeleteOkMethodHandler(); + private static final Logger _logger = LoggerFactory.getLogger(QueueDeleteOkMethodHandler.class); + private static final QueueDeleteOkMethodHandler _instance = new QueueDeleteOkMethodHandler(); - public static QueueDeleteOkMethodHandler getInstance() - { - return _instance; - } + public static QueueDeleteOkMethodHandler getInstance() + { + return _instance; + } - private QueueDeleteOkMethodHandler() - { - } + private QueueDeleteOkMethodHandler() + { } - public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) throws AMQException - { - if (_logger.isDebugEnabled()) - { + public void methodReceived(AMQStateManager stateManager, AMQProtocolSession protocolSession, AMQMethodEvent evt) + throws AMQException + { + if (_logger.isDebugEnabled()) + { QueueDeleteOkBody body = (QueueDeleteOkBody) evt.getMethod(); _logger.debug("Received Queue.Delete-Ok message, message count: " + body.messageCount); - } - } + } + } } - - diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java index 13f544516a..2dfeb19268 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessage.java @@ -20,18 +20,10 @@ */ package org.apache.qpid.client.message; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Map; -import java.util.UUID; - -import javax.jms.Destination; -import javax.jms.JMSException; -import javax.jms.MessageNotReadableException; -import javax.jms.MessageNotWriteableException; - import org.apache.commons.collections.map.ReferenceMap; + import org.apache.mina.common.ByteBuffer; + import org.apache.qpid.AMQException; import org.apache.qpid.client.*; import org.apache.qpid.framing.AMQShortString; @@ -41,6 +33,16 @@ import org.apache.qpid.url.AMQBindingURL; import org.apache.qpid.url.BindingURL; import org.apache.qpid.url.URLSyntaxException; +import javax.jms.Destination; +import javax.jms.JMSException; +import javax.jms.MessageNotReadableException; +import javax.jms.MessageNotWriteableException; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; +import java.util.UUID; + public abstract class AbstractJMSMessage extends AMQMessage implements org.apache.qpid.jms.Message { private static final Map _destinationCache = Collections.synchronizedMap(new ReferenceMap()); @@ -70,35 +72,32 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach _changedData = (data == null); _headerAdapter = new JMSHeaderAdapter(((BasicContentHeaderProperties) _contentHeaderProperties).getHeaders()); - _strictAMQP = Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT)); + _strictAMQP = + Boolean.parseBoolean(System.getProperties().getProperty(AMQSession.STRICT_AMQP, AMQSession.STRICT_AMQP_DEFAULT)); } protected AbstractJMSMessage(long deliveryTag, BasicContentHeaderProperties contentHeader, AMQShortString exchange, - AMQShortString routingKey, ByteBuffer data) throws AMQException + AMQShortString routingKey, ByteBuffer data) throws AMQException { this(contentHeader, deliveryTag); Integer type = contentHeader.getHeaders().getInteger(CustomJMSXProperty.JMS_QPID_DESTTYPE.getShortStringName()); - int contentType = (type == null) ? AMQDestination.UNKNOWN_TYPE : type.intValue(); AMQDestination dest; - switch (contentType) + if (AMQDestination.QUEUE_TYPE.equals(type)) { - - case AMQDestination.QUEUE_TYPE: - dest = new AMQQueue(exchange, routingKey, routingKey); - break; - - case AMQDestination.TOPIC_TYPE: - dest = new AMQTopic(exchange, routingKey, null); - break; - - default: - dest = new AMQUndefinedDestination(exchange, routingKey, null); - break; + dest = new AMQQueue(exchange, routingKey, routingKey); + } + else if (AMQDestination.TOPIC_TYPE.equals(type)) + { + dest = new AMQTopic(exchange, routingKey, null); } - //Destination dest = AMQDestination.createDestination(url); + else + { + dest = new AMQUndefinedDestination(exchange, routingKey, null); + } + // Destination dest = AMQDestination.createDestination(url); setJMSDestination(dest); _data = data; @@ -202,7 +201,7 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach if (!(destination instanceof AMQDestination)) { throw new IllegalArgumentException( - "ReplyTo destination may only be an AMQDestination - passed argument was type " + destination.getClass()); + "ReplyTo destination may only be an AMQDestination - passed argument was type " + destination.getClass()); } final AMQDestination amqd = (AMQDestination) destination; @@ -614,7 +613,6 @@ public abstract class AbstractJMSMessage extends AMQMessage implements org.apach getContentHeaderProperties().setHeaders(messageProperties); } - public JMSHeaderAdapter getJmsHeaders() { return _headerAdapter; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java index 5deaec4c17..87df7e1337 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/AbstractJMSMessageFactory.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,51 +20,53 @@ */ package org.apache.qpid.client.message; -import java.util.Iterator; -import java.util.List; - -import javax.jms.JMSException; - -import org.apache.log4j.Logger; import org.apache.mina.common.ByteBuffer; + import org.apache.qpid.AMQException; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.framing.ContentBody; import org.apache.qpid.framing.ContentHeaderBody; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; + +import java.util.Iterator; +import java.util.List; + public abstract class AbstractJMSMessageFactory implements MessageFactory { - private static final Logger _logger = Logger.getLogger(AbstractJMSMessageFactory.class); - + private static final Logger _logger = LoggerFactory.getLogger(AbstractJMSMessageFactory.class); - protected abstract AbstractJMSMessage createMessage(long messageNbr, ByteBuffer data, - AMQShortString exchange, AMQShortString routingKey, - ContentHeaderBody contentHeader) throws AMQException; + protected abstract AbstractJMSMessage createMessage(long messageNbr, ByteBuffer data, AMQShortString exchange, + AMQShortString routingKey, ContentHeaderBody contentHeader) throws AMQException; - protected AbstractJMSMessage createMessageWithBody(long messageNbr, - ContentHeaderBody contentHeader, - AMQShortString exchange, AMQShortString routingKey, - List bodies) throws AMQException + protected AbstractJMSMessage createMessageWithBody(long messageNbr, ContentHeaderBody contentHeader, + AMQShortString exchange, AMQShortString routingKey, List bodies) throws AMQException { ByteBuffer data; final boolean debug = _logger.isDebugEnabled(); // we optimise the non-fragmented case to avoid copying - if (bodies != null && bodies.size() == 1) + if ((bodies != null) && (bodies.size() == 1)) { - if(debug) + if (debug) { - _logger.debug("Non-fragmented message body (bodySize=" + contentHeader.bodySize +")"); + _logger.debug("Non-fragmented message body (bodySize=" + contentHeader.bodySize + ")"); } - data = ((ContentBody)bodies.get(0)).payload; + + data = ((ContentBody) bodies.get(0)).payload; } else if (bodies != null) { - if(debug) + if (debug) { - _logger.debug("Fragmented message body (" + bodies.size() + " frames, bodySize=" + contentHeader.bodySize + ")"); + _logger.debug("Fragmented message body (" + bodies.size() + " frames, bodySize=" + contentHeader.bodySize + + ")"); } - data = ByteBuffer.allocate((int)contentHeader.bodySize); // XXX: Is cast a problem? + + data = ByteBuffer.allocate((int) contentHeader.bodySize); // XXX: Is cast a problem? final Iterator it = bodies.iterator(); while (it.hasNext()) { @@ -72,27 +74,29 @@ public abstract class AbstractJMSMessageFactory implements MessageFactory data.put(cb.payload); cb.payload.release(); } + data.flip(); } else // bodies == null { data = ByteBuffer.allocate(0); } - if(debug) + + if (debug) { - _logger.debug("Creating message from buffer with position=" + data.position() + " and remaining=" + data.remaining()); + _logger.debug("Creating message from buffer with position=" + data.position() + " and remaining=" + + data.remaining()); } return createMessage(messageNbr, data, exchange, routingKey, contentHeader); } - public AbstractJMSMessage createMessage(long messageNbr, boolean redelivered, - ContentHeaderBody contentHeader, - AMQShortString exchange, AMQShortString routingKey, - List bodies) throws JMSException, AMQException + public AbstractJMSMessage createMessage(long messageNbr, boolean redelivered, ContentHeaderBody contentHeader, + AMQShortString exchange, AMQShortString routingKey, List bodies) throws JMSException, AMQException { final AbstractJMSMessage msg = createMessageWithBody(messageNbr, contentHeader, exchange, routingKey, bodies); msg.setJMSRedelivered(redelivered); + return msg; } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java index ae64ac987e..495f09e8fd 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/JMSMapMessage.java @@ -14,36 +14,38 @@ * "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. + * under the License. + * * - * */ package org.apache.qpid.client.message; +import org.apache.mina.common.ByteBuffer; + +import org.apache.qpid.AMQException; +import org.apache.qpid.framing.AMQShortString; +import org.apache.qpid.framing.ContentHeaderBody; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.jms.MessageFormatException; + import java.nio.charset.CharacterCodingException; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; -import javax.jms.JMSException; -import javax.jms.MessageFormatException; - -import org.apache.log4j.Logger; -import org.apache.mina.common.ByteBuffer; -import org.apache.qpid.AMQException; -import org.apache.qpid.framing.AMQShortString; -import org.apache.qpid.framing.ContentHeaderBody; - public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jms.MapMessage { - private static final Logger _logger = Logger.getLogger(JMSMapMessage.class); - + private static final Logger _logger = LoggerFactory.getLogger(JMSMapMessage.class); public static final String MIME_TYPE = "jms/map-message"; private static final AMQShortString MIME_TYPE_SHORT_STRING = new AMQShortString(MIME_TYPE); - private Map<String,Object> _map = new HashMap<String, Object>(); + private Map<String, Object> _map = new HashMap<String, Object>(); public JMSMapMessage() throws JMSException { @@ -56,24 +58,22 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm populateMapFromData(); } - - JMSMapMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, - AMQShortString routingKey, ByteBuffer data) throws AMQException + JMSMapMessage(long messageNbr, ContentHeaderBody contentHeader, AMQShortString exchange, AMQShortString routingKey, + ByteBuffer data) throws AMQException { super(messageNbr, contentHeader, exchange, routingKey, data); try { populateMapFromData(); - } + } catch (JMSException je) { throw new AMQException(null, "Error populating MapMessage from ByteBuffer", je); - + } } - public String toBodyString() throws JMSException { return _map.toString(); @@ -84,16 +84,14 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm return MIME_TYPE_SHORT_STRING; } - public ByteBuffer getData() { - //What if _data is null? + // What if _data is null? writeMapToData(); + return super.getData(); } - - @Override public void clearBodyImpl() throws JMSException { @@ -105,18 +103,18 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm { Object value = _map.get(propName); - if(value instanceof Boolean) + if (value instanceof Boolean) { - return ((Boolean)value).booleanValue(); + return ((Boolean) value).booleanValue(); } - else if((value instanceof String) || (value == null)) + else if ((value instanceof String) || (value == null)) { - return Boolean.valueOf((String)value); + return Boolean.valueOf((String) value); } else { - throw new MessageFormatException("Property " + propName + " of type " + - value.getClass().getName() + " cannot be converted to boolean."); + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to boolean."); } } @@ -125,18 +123,18 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm { Object value = _map.get(propName); - if(value instanceof Byte) + if (value instanceof Byte) { - return ((Byte)value).byteValue(); + return ((Byte) value).byteValue(); } - else if((value instanceof String) || (value==null)) + else if ((value instanceof String) || (value == null)) { - return Byte.valueOf((String)value).byteValue(); + return Byte.valueOf((String) value).byteValue(); } else { - throw new MessageFormatException("Property " + propName + " of type " + - value.getClass().getName() + " cannot be converted to byte."); + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to byte."); } } @@ -144,51 +142,50 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm { Object value = _map.get(propName); - if(value instanceof Short) + if (value instanceof Short) { - return ((Short)value).shortValue(); + return ((Short) value).shortValue(); } - else if(value instanceof Byte) + else if (value instanceof Byte) { - return ((Byte)value).shortValue(); + return ((Byte) value).shortValue(); } - else if((value instanceof String) || (value==null)) + else if ((value instanceof String) || (value == null)) { - return Short.valueOf((String)value).shortValue(); + return Short.valueOf((String) value).shortValue(); } else { - throw new MessageFormatException("Property " + propName + " of type " + - value.getClass().getName() + " cannot be converted to short."); + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to short."); } } - public int getInt(String propName) throws JMSException { Object value = _map.get(propName); - if(value instanceof Integer) + if (value instanceof Integer) { - return ((Integer)value).intValue(); + return ((Integer) value).intValue(); } - else if(value instanceof Short) + else if (value instanceof Short) { - return ((Short)value).intValue(); + return ((Short) value).intValue(); } - else if(value instanceof Byte) + else if (value instanceof Byte) { - return ((Byte)value).intValue(); + return ((Byte) value).intValue(); } - else if((value instanceof String) || (value==null)) + else if ((value instanceof String) || (value == null)) { - return Integer.valueOf((String)value).intValue(); + return Integer.valueOf((String) value).intValue(); } else { - throw new MessageFormatException("Property " + propName + " of type " + - value.getClass().getName() + " cannot be converted to int."); + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to int."); } } @@ -197,30 +194,32 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm { Object value = _map.get(propName); - if(value instanceof Long) + if (value instanceof Long) { - return ((Long)value).longValue(); + return ((Long) value).longValue(); } - else if(value instanceof Integer) + else if (value instanceof Integer) { - return ((Integer)value).longValue(); + return ((Integer) value).longValue(); } - if(value instanceof Short) + + if (value instanceof Short) { - return ((Short)value).longValue(); + return ((Short) value).longValue(); } - if(value instanceof Byte) + + if (value instanceof Byte) { - return ((Byte)value).longValue(); + return ((Byte) value).longValue(); } - else if((value instanceof String) || (value==null)) + else if ((value instanceof String) || (value == null)) { - return Long.valueOf((String)value).longValue(); + return Long.valueOf((String) value).longValue(); } else { - throw new MessageFormatException("Property " + propName + " of type " + - value.getClass().getName() + " cannot be converted to long."); + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to long."); } } @@ -229,45 +228,43 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm { Object value = _map.get(propName); - if(!_map.containsKey(propName)) + if (!_map.containsKey(propName)) { throw new MessageFormatException("Property " + propName + " not present"); } - else if(value instanceof Character) + else if (value instanceof Character) { - return ((Character)value).charValue(); + return ((Character) value).charValue(); } else if (value == null) { - throw new NullPointerException("Property " + propName + " has null value and therefore cannot " + - "be converted to char."); + throw new NullPointerException("Property " + propName + " has null value and therefore cannot " + + "be converted to char."); } else { - throw new MessageFormatException("Property " + propName + " of type " + - value.getClass().getName() + " cannot be converted to boolan."); + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to boolan."); } } - - public float getFloat(String propName) throws JMSException { Object value = _map.get(propName); - if(value instanceof Float) + if (value instanceof Float) { - return ((Float)value).floatValue(); + return ((Float) value).floatValue(); } - else if((value instanceof String) || (value==null)) + else if ((value instanceof String) || (value == null)) { - return Float.valueOf((String)value).floatValue(); + return Float.valueOf((String) value).floatValue(); } else { - throw new MessageFormatException("Property " + propName + " of type " + - value.getClass().getName() + " cannot be converted to float."); + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to float."); } } @@ -275,22 +272,22 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm { Object value = _map.get(propName); - if(value instanceof Double) + if (value instanceof Double) { - return ((Double)value).doubleValue(); + return ((Double) value).doubleValue(); } - else if(value instanceof Float) + else if (value instanceof Float) { - return ((Float)value).doubleValue(); + return ((Float) value).doubleValue(); } - else if((value instanceof String) || (value==null)) + else if ((value instanceof String) || (value == null)) { - return Double.valueOf((String)value).doubleValue(); + return Double.valueOf((String) value).doubleValue(); } else { - throw new MessageFormatException("Property " + propName + " of type " + - value.getClass().getName() + " cannot be converted to double."); + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to double."); } } @@ -298,14 +295,13 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm { Object value = _map.get(propName); - if((value instanceof String) || (value == null)) + if ((value instanceof String) || (value == null)) { return (String) value; } - else if(value instanceof byte[]) + else if (value instanceof byte[]) { - throw new MessageFormatException("Property " + propName + " of type byte[] " + - "cannot be converted to String."); + throw new MessageFormatException("Property " + propName + " of type byte[] " + "cannot be converted to String."); } else { @@ -318,18 +314,18 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm { Object value = _map.get(propName); - if(!_map.containsKey(propName)) + if (!_map.containsKey(propName)) { - throw new MessageFormatException("Property " + propName + " not present"); + throw new MessageFormatException("Property " + propName + " not present"); } - else if((value instanceof byte[]) || (value == null)) + else if ((value instanceof byte[]) || (value == null)) { - return (byte[])value; + return (byte[]) value; } else { - throw new MessageFormatException("Property " + propName + " of type " + - value.getClass().getName() + " cannot be converted to byte[]."); + throw new MessageFormatException("Property " + propName + " of type " + value.getClass().getName() + + " cannot be converted to byte[]."); } } @@ -343,7 +339,6 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm return Collections.enumeration(_map.keySet()); } - public void setBoolean(String propName, boolean b) throws JMSException { checkWritable(); @@ -416,46 +411,38 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm public void setBytes(String propName, byte[] bytes, int offset, int length) throws JMSException { - if((offset == 0) && (length == bytes.length)) + if ((offset == 0) && (length == bytes.length)) { - setBytes(propName,bytes); + setBytes(propName, bytes); } else { byte[] newBytes = new byte[length]; - System.arraycopy(bytes,offset,newBytes,0,length); - setBytes(propName,newBytes); + System.arraycopy(bytes, offset, newBytes, 0, length); + setBytes(propName, newBytes); } } public void setObject(String propName, Object value) throws JMSException - { + { checkWritable(); checkPropertyName(propName); - if(value instanceof Boolean - || value instanceof Byte - || value instanceof Short - || value instanceof Integer - || value instanceof Long - || value instanceof Character - || value instanceof Float - || value instanceof Double - || value instanceof String - || value instanceof byte[] - || value == null) + if ((value instanceof Boolean) || (value instanceof Byte) || (value instanceof Short) || (value instanceof Integer) + || (value instanceof Long) || (value instanceof Character) || (value instanceof Float) + || (value instanceof Double) || (value instanceof String) || (value instanceof byte[]) || (value == null)) { _map.put(propName, value); } else { - throw new MessageFormatException("Cannot set property " + propName + " to value " + value + - "of type " + value.getClass().getName() + "."); + throw new MessageFormatException("Cannot set property " + propName + " to value " + value + "of type " + + value.getClass().getName() + "."); } } private void checkPropertyName(String propName) { - if(propName == null || propName.equals("")) + if ((propName == null) || propName.equals("")) { throw new IllegalArgumentException("Property name cannot be null, or the empty String."); } @@ -466,19 +453,18 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm return _map.containsKey(propName); } - private void populateMapFromData() throws JMSException { - if(_data != null) + if (_data != null) { _data.rewind(); final int entries = readIntImpl(); - for(int i = 0; i < entries; i++) + for (int i = 0; i < entries; i++) { String propName = readStringImpl(); Object value = readObject(); - _map.put(propName,value); + _map.put(propName, value); } } else @@ -492,7 +478,7 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm allocateInitialBuffer(); final int size = _map.size(); writeIntImpl(size); - for(Map.Entry<String, Object> entry : _map.entrySet()) + for (Map.Entry<String, Object> entry : _map.entrySet()) { try { @@ -500,10 +486,10 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm } catch (CharacterCodingException e) { - throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey(),e); - + throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey(), e); } + try { writeObject(entry.getValue()); @@ -511,14 +497,11 @@ public class JMSMapMessage extends AbstractBytesTypedMessage implements javax.jm catch (JMSException e) { Object value = entry.getValue(); - throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey() + - " value : " + value + " (type: " + value.getClass().getName() + ").",e); + throw new IllegalArgumentException("Cannot encode property key name " + entry.getKey() + " value : " + value + + " (type: " + value.getClass().getName() + ").", e); } } } - - - } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java index 0afc8177fc..f6b11c6f6c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/message/MessageConverter.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,7 +20,8 @@ */ package org.apache.qpid.client.message; -import java.util.Enumeration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.jms.BytesMessage; import javax.jms.JMSException; @@ -31,7 +32,7 @@ import javax.jms.ObjectMessage; import javax.jms.StreamMessage; import javax.jms.TextMessage; -import org.apache.log4j.Logger; +import java.util.Enumeration; public class MessageConverter { @@ -39,7 +40,7 @@ public class MessageConverter /** * Log4J logger */ - protected final Logger _logger = Logger.getLogger(getClass()); + protected final Logger _logger = LoggerFactory.getLogger(getClass()); /** * AbstractJMSMessage which will hold the converted message @@ -81,6 +82,7 @@ public class MessageConverter String name = (String) mapNames.nextElement(); nativeMessage.setObject(name, message.getObject(name)); } + _newMessage = (AbstractJMSMessage) nativeMessage; setMessageProperties(message); } @@ -121,15 +123,16 @@ public class MessageConverter } catch (MessageEOFException e) { - //we're at the end so don't mind the exception + // we're at the end so don't mind the exception } + _newMessage = (AbstractJMSMessage) nativeMessage; setMessageProperties(message); } public MessageConverter(Message message) throws JMSException { - //Send a message with just properties. + // Send a message with just properties. // Throwing away content BytesMessage nativeMessage = new JMSBytesMessage(); @@ -160,7 +163,7 @@ public class MessageConverter while (propertyNames.hasMoreElements()) { String propertyName = String.valueOf(propertyNames.nextElement()); - //TODO: Shouldn't need to check for JMS properties here as don't think getPropertyNames() should return them + // TODO: Shouldn't need to check for JMS properties here as don't think getPropertyNames() should return them if (!propertyName.startsWith("JMSX_")) { Object value = message.getObjectProperty(propertyName); @@ -190,6 +193,7 @@ public class MessageConverter { _newMessage.setJMSReplyTo(message.getJMSReplyTo()); } + _newMessage.setJMSType(message.getJMSType()); _newMessage.setJMSCorrelationID(message.getJMSCorrelationID()); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java index 5687ad2658..d2d29039ea 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolHandler.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,10 +20,6 @@ */ package org.apache.qpid.client.protocol; -import java.util.Iterator; -import java.util.concurrent.CopyOnWriteArraySet; -import java.util.concurrent.CountDownLatch; - import org.apache.log4j.Logger; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoHandlerAdapter; @@ -34,10 +30,10 @@ import org.apache.qpid.AMQConnectionClosedException; import org.apache.qpid.AMQDisconnectedException; import org.apache.qpid.AMQException; import org.apache.qpid.AMQTimeoutException; -import org.apache.qpid.AMQChannelClosedException; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.client.AMQSession; import org.apache.qpid.client.SSLConfiguration; +import org.apache.qpid.client.failover.FailoverException; import org.apache.qpid.client.failover.FailoverHandler; import org.apache.qpid.client.failover.FailoverState; import org.apache.qpid.client.state.AMQState; @@ -60,9 +56,67 @@ import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQMethodListener; import org.apache.qpid.ssl.SSLContextFactory; +import java.util.Iterator; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.CountDownLatch; +/** + * AMQProtocolHandler is the client side protocol handler for AMQP, it handles all protocol events received from the + * network by MINA. The primary purpose of AMQProtocolHandler is to translate the generic event model of MINA into the + * specific event model of AMQP, by revealing the type of the received events (from decoded data), and passing the event + * on to more specific handlers for the type. In this sense, it channels the richer event model of AMQP, expressed in + * terms of methods and so on, through the cruder, general purpose event model of MINA, expressed in terms of "message + * received" and so on. + * + * <p/>There is a 1:1 mapping between an AMQProtocolHandler and an {@link AMQConnection}. The connection class is + * exposed to the end user of the AMQP client API, and also implements the JMS Connection API, so provides the public + * API calls through which an individual connection can be manipulated. This protocol handler talks to the network + * through MINA, in a behind the scenes role; it is not an exposed part of the client API. + * + * <p/>There is a 1:many mapping between an AMQProtocolHandler and a set of {@link AMQSession}s. At the MINA level, + * there is one session per connection. At the AMQP level there can be many channels which are also called sessions in + * JMS parlance. The {@link AMQSession}s are managed through an {@link AMQProtocolSession} instance. The protocol + * session is similar to the MINA per-connection session, except that it can span the lifecycle of multiple MINA + * sessions in the event of failover. See below for more information about this. + * + * <p/>Mina provides a session container that can be used to store/retrieve arbitrary objects as String named + * attributes. A more convenient, type-safe, container for session data is provided in the form of {@link + * AMQProtocolSession}. + * + * <p/>A common way to use MINA is to have a single instance of the event handler, and for MINA to pass in its session + * object with every event, and for per-connection data to be held in the MINA session (perhaps using a type-safe + * wrapper as described above). This event handler is different, because dealing with failover complicates things. To + * the end client of an AMQConnection, a failed over connection is still handled through the same connection instance, + * but behind the scenes a new transport connection, and MINA session will have been created. The MINA session object + * cannot be used to track the state of the fail-over process, because it is destroyed and a new one is created, as the + * old connection is shutdown and a new one created. For this reason, an AMQProtocolHandler is created per AMQConnection + * and the protocol session data is held outside of the MINA IOSession. + * + * <p/>This handler is responsibile for setting up the filter chain to filter all events for this handler through. The + * filter chain is set up as a stack of event handers that perform the following functions (working upwards from the + * network traffic at the bottom), handing off incoming events to an asynchronous thread pool to do the work, optionally + * handling secure sockets encoding/decoding, encoding/decoding the AMQP format itself. + * + * <p/><table id="crc"><caption>CRC Card</caption> <tr><th> Responsibilities <th> Collaborations <tr><td> Create the + * filter chain to filter this handlers events. <td> {@link ProtocolCodecFilter}, {@link SSLContextFactory}, {@link + * SSLFilter}, {@link ReadWriteThreadModel}. + * + * <tr><td> Maintain fail-over state. <tr><td> </table> + * + * @todo Explain the system property: amqj.shared_read_write_pool. How does putting the protocol codec filter before the + * async write filter make it a shared pool? The pooling filter uses the same thread pool for reading and writing + * anyway, see {@link org.apache.qpid.pool.PoolingFilter}, docs for comments. Will putting the protocol codec filter + * before it mean not doing the read/write asynchronously but in the main filter thread? + * @todo Use a single handler instance, by shifting everything to do with the 'protocol session' state, including + * failover state, into AMQProtocolSession, and tracking that from AMQConnection? The lifecycles of AMQProtocolSesssion + * and AMQConnection will be the same, so if there is high cohesion between them, they could be merged, although there + * is sense in keeping the session model seperate. Will clarify things by having data held per protocol handler, per + * protocol session, per network connection, per channel, in seperate classes, so that lifecycles of the fields match + * lifecycles of their containing objects. + */ public class AMQProtocolHandler extends IoHandlerAdapter { + /** Used for debugging. */ private static final Logger _logger = Logger.getLogger(AMQProtocolHandler.class); /** @@ -74,8 +128,10 @@ public class AMQProtocolHandler extends IoHandlerAdapter /** Our wrapper for a protocol session that provides access to session values in a typesafe manner. */ private volatile AMQProtocolSession _protocolSession; + /** Holds the state of the protocol session. */ private AMQStateManager _stateManager = new AMQStateManager(); + /** Holds the method listeners, */ private final CopyOnWriteArraySet _frameListeners = new CopyOnWriteArraySet(); /** @@ -91,15 +147,31 @@ public class AMQProtocolHandler extends IoHandlerAdapter */ private FailoverState _failoverState = FailoverState.NOT_STARTED; + /** Used to provide a condition to wait upon for operations that are required to wait for failover to complete. */ private CountDownLatch _failoverLatch; + /** Defines the default timeout to use for synchronous protocol commands. */ private final long DEFAULT_SYNC_TIMEOUT = 1000 * 30; + /** + * Creates a new protocol handler, associated with the specified client connection instance. + * + * @param con The client connection that this is the event handler for. + */ public AMQProtocolHandler(AMQConnection con) { _connection = con; } + /** + * Invoked by MINA when a MINA session for a new connection is created. This method sets up the filter chain on the + * session, which filters the events handled by this handler. The filter chain consists of, handing off events to an + * asynchronous thread pool, optionally encoding/decoding ssl, encoding/decoding AMQP. + * + * @param session The MINA session. + * + * @throws Exception Any underlying exceptions are allowed to fall through to MINA. + */ public void sessionCreated(IoSession session) throws Exception { _logger.debug("Protocol session created for session " + System.identityHashCode(session)); @@ -119,16 +191,15 @@ public class AMQProtocolHandler extends IoHandlerAdapter if (_connection.getSSLConfiguration() != null) { SSLConfiguration sslConfig = _connection.getSSLConfiguration(); - SSLContextFactory sslFactory = new SSLContextFactory(sslConfig.getKeystorePath(), sslConfig.getKeystorePassword(), sslConfig.getCertType()); + SSLContextFactory sslFactory = + new SSLContextFactory(sslConfig.getKeystorePath(), sslConfig.getKeystorePassword(), sslConfig.getCertType()); SSLFilter sslFilter = new SSLFilter(sslFactory.buildClientContext()); sslFilter.setUseClientMode(true); session.getFilterChain().addBefore("protocolFilter", "ssl", sslFilter); } - try { - ReadWriteThreadModel threadModel = ReadWriteThreadModel.getInstance(); threadModel.getAsynchronousReadFilter().createNewJobForSession(session); threadModel.getAsynchronousWriteFilter().createNewJobForSession(session); @@ -142,35 +213,38 @@ public class AMQProtocolHandler extends IoHandlerAdapter _protocolSession.init(); } - public void sessionOpened(IoSession session) throws Exception - { - //System.setProperty("foo", "bar"); - } - /** - * When the broker connection dies we can either get sessionClosed() called or exceptionCaught() followed by - * sessionClosed() depending on whether we were trying to send data at the time of failure. + * Called when the network connection is closed. This can happen, either because the client explicitly requested + * that the connection be closed, in which case nothing is done, or because the connection died. In the case where + * the connection died, an attempt to failover automatically to a new connection may be started. The failover + * process will be started, provided that it is the clients policy to allow failover, and provided that a failover + * has not already been started or failed. + * + * <p/>It is important to note that when the connection dies this method may be called or {@link #exceptionCaught} + * may be called first followed by this method. This depends on whether the client was trying to send data at the + * time of the failure. * - * @param session + * @param session The MINA session. * - * @throws Exception + * @todo Clarify: presumably exceptionCaught is called when the client is sending during a connection failure and + * not otherwise? The above comment doesn't make that clear. */ - public void sessionClosed(IoSession session) throws Exception + public void sessionClosed(IoSession session) { if (_connection.isClosed()) { - _logger.info("Session closed called by client"); + _logger.debug("Session closed called by client"); } else { - _logger.info("Session closed called with failover state currently " + _failoverState); + _logger.debug("Session closed called with failover state currently " + _failoverState); - //reconnetablility was introduced here so as not to disturb the client as they have made their intentions + // reconnetablility was introduced here so as not to disturb the client as they have made their intentions // known through the policy settings. if ((_failoverState != FailoverState.IN_PROGRESS) && _connection.failoverAllowed()) { - _logger.info("FAILOVER STARTING"); + _logger.debug("FAILOVER STARTING"); if (_failoverState == FailoverState.NOT_STARTED) { _failoverState = FailoverState.IN_PROGRESS; @@ -178,12 +252,12 @@ public class AMQProtocolHandler extends IoHandlerAdapter } else { - _logger.info("Not starting failover as state currently " + _failoverState); + _logger.debug("Not starting failover as state currently " + _failoverState); } } else { - _logger.info("Failover not allowed by policy."); + _logger.debug("Failover not allowed by policy."); // or already in progress? if (_logger.isDebugEnabled()) { @@ -199,12 +273,12 @@ public class AMQProtocolHandler extends IoHandlerAdapter } else { - _logger.info("sessionClose() failover in progress"); + _logger.debug("sessionClose() failover in progress"); } } } - _logger.info("Protocol Session [" + this + "] closed"); + _logger.debug("Protocol Session [" + this + "] closed"); } /** See {@link FailoverHandler} to see rationale for separate thread. */ @@ -223,25 +297,32 @@ public class AMQProtocolHandler extends IoHandlerAdapter _logger.debug("Protocol Session [" + this + ":" + session + "] idle: " + status); if (IdleStatus.WRITER_IDLE.equals(status)) { - //write heartbeat frame: + // write heartbeat frame: _logger.debug("Sent heartbeat"); session.write(HeartbeatBody.FRAME); HeartbeatDiagnostics.sent(); } else if (IdleStatus.READER_IDLE.equals(status)) { - //failover: + // failover: HeartbeatDiagnostics.timeout(); _logger.warn("Timed out while waiting for heartbeat from peer."); session.close(); } } - public void exceptionCaught(IoSession session, Throwable cause) throws Exception + /** + * Invoked when any exception is thrown by a user IoHandler implementation or by MINA. If the cause is an + * IOException, MINA will close the connection automatically. + * + * @param session The MINA session. + * @param cause The exception that triggered this event. + */ + public void exceptionCaught(IoSession session, Throwable cause) { if (_failoverState == FailoverState.NOT_STARTED) { - //if (!(cause instanceof AMQUndeliveredException) && (!(cause instanceof AMQAuthenticationException))) + // if (!(cause instanceof AMQUndeliveredException) && (!(cause instanceof AMQAuthenticationException))) if (cause instanceof AMQConnectionClosedException) { _logger.info("Exception caught therefore going to attempt failover: " + cause, cause); @@ -250,8 +331,8 @@ public class AMQProtocolHandler extends IoHandlerAdapter sessionClosed(session); } - //FIXME Need to correctly handle other exceptions. Things like ... -// if (cause instanceof AMQChannelClosedException) + // FIXME Need to correctly handle other exceptions. Things like ... + // if (cause instanceof AMQChannelClosedException) // which will cause the JMSSession to end due to a channel close and so that Session needs // to be removed from the map so we can correctly still call close without an exception when trying to close // the server closed session. See also CloseChannelMethodHandler as the sessionClose is never called on exception @@ -261,6 +342,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter else if (_failoverState == FailoverState.FAILED) { _logger.error("Exception caught by protocol handler: " + cause, cause); + // we notify the state manager of the error in case we have any clients waiting on a state // change. Those "waiters" will be interrupted and can handle the exception AMQException amqe = new AMQException(null, "Protocol handler error: " + cause, cause); @@ -297,7 +379,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter final boolean debug = _logger.isDebugEnabled(); final long msgNumber = ++_messageReceivedCount; - if (debug && (msgNumber % 1000 == 0)) + if (debug && ((msgNumber % 1000) == 0)) { _logger.debug("Received " + _messageReceivedCount + " protocol messages"); } @@ -317,7 +399,8 @@ public class AMQProtocolHandler extends IoHandlerAdapter _logger.debug("(" + System.identityHashCode(this) + ")Method frame received: " + frame); } - final AMQMethodEvent<AMQMethodBody> evt = new AMQMethodEvent<AMQMethodBody>(frame.getChannel(), (AMQMethodBody) bodyFrame); + final AMQMethodEvent<AMQMethodBody> evt = + new AMQMethodEvent<AMQMethodBody>(frame.getChannel(), (AMQMethodBody) bodyFrame); try { @@ -331,10 +414,16 @@ public class AMQProtocolHandler extends IoHandlerAdapter final AMQMethodListener listener = (AMQMethodListener) it.next(); wasAnyoneInterested = listener.methodReceived(evt) || wasAnyoneInterested; } + if (!wasAnyoneInterested) + { + throw new AMQException(null, "AMQMethodEvent " + evt + " was not processed by any listener. Listeners:" + _frameListeners, null); + } } + if (!wasAnyoneInterested) { - throw new AMQException(null, "AMQMethodEvent " + evt + " was not processed by any listener. Listeners:" + _frameListeners, null); + throw new AMQException(null, "AMQMethodEvent " + evt + " was not processed by any listener. Listeners:" + + _frameListeners, null); } } catch (AMQException e) @@ -349,20 +438,20 @@ public class AMQProtocolHandler extends IoHandlerAdapter listener.error(e); } } + exceptionCaught(session, e); } + break; case ContentHeaderBody.TYPE: - _protocolSession.messageContentHeaderReceived(frame.getChannel(), - (ContentHeaderBody) bodyFrame); + _protocolSession.messageContentHeaderReceived(frame.getChannel(), (ContentHeaderBody) bodyFrame); break; case ContentBody.TYPE: - _protocolSession.messageContentBodyReceived(frame.getChannel(), - (ContentBody) bodyFrame); + _protocolSession.messageContentBodyReceived(frame.getChannel(), (ContentBody) bodyFrame); break; case HeartbeatBody.TYPE: @@ -371,11 +460,13 @@ public class AMQProtocolHandler extends IoHandlerAdapter { _logger.debug("Received heartbeat"); } + break; default: } + _connection.bytesReceived(_protocolSession.getIoSession().getReadBytes()); } @@ -387,10 +478,11 @@ public class AMQProtocolHandler extends IoHandlerAdapter final boolean debug = _logger.isDebugEnabled(); - if (debug && (sentMessages % 1000 == 0)) + if (debug && ((sentMessages % 1000) == 0)) { _logger.debug("Sent " + _messagesOut + " protocol messages"); } + _connection.bytesSent(session.getWrittenBytes()); if (debug) { @@ -408,7 +500,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter { _frameListeners.remove(listener); } - */ + */ public void attainState(AMQState s) throws AMQException { getStateManager().attainState(s); @@ -437,9 +529,8 @@ public class AMQProtocolHandler extends IoHandlerAdapter * @param frame * @param listener the blocking listener. Note the calling thread will block. */ - public AMQMethodEvent writeCommandFrameAndWaitForReply(AMQFrame frame, - BlockingMethodFrameListener listener) - throws AMQException + public AMQMethodEvent writeCommandFrameAndWaitForReply(AMQFrame frame, BlockingMethodFrameListener listener) + throws AMQException, FailoverException { return writeCommandFrameAndWaitForReply(frame, listener, DEFAULT_SYNC_TIMEOUT); } @@ -451,9 +542,8 @@ public class AMQProtocolHandler extends IoHandlerAdapter * @param frame * @param listener the blocking listener. Note the calling thread will block. */ - public AMQMethodEvent writeCommandFrameAndWaitForReply(AMQFrame frame, - BlockingMethodFrameListener listener, long timeout) - throws AMQException + public AMQMethodEvent writeCommandFrameAndWaitForReply(AMQFrame frame, BlockingMethodFrameListener listener, + long timeout) throws AMQException, FailoverException { try { @@ -461,6 +551,7 @@ public class AMQProtocolHandler extends IoHandlerAdapter _protocolSession.writeFrame(frame); AMQMethodEvent e = listener.blockForFrame(timeout); + return e; // When control resumes before this line, a reply will have been received // that matches the criteria defined in the blocking listener @@ -478,25 +569,33 @@ public class AMQProtocolHandler extends IoHandlerAdapter } /** More convenient method to write a frame and wait for it's response. */ - public AMQMethodEvent syncWrite(AMQFrame frame, Class responseClass) throws AMQException + public AMQMethodEvent syncWrite(AMQFrame frame, Class responseClass) throws AMQException, FailoverException { return syncWrite(frame, responseClass, DEFAULT_SYNC_TIMEOUT); } /** More convenient method to write a frame and wait for it's response. */ - public AMQMethodEvent syncWrite(AMQFrame frame, Class responseClass, long timeout) throws AMQException + public AMQMethodEvent syncWrite(AMQFrame frame, Class responseClass, long timeout) throws AMQException, FailoverException { - return writeCommandFrameAndWaitForReply(frame, - new SpecificMethodFrameListener(frame.getChannel(), responseClass), timeout); + return writeCommandFrameAndWaitForReply(frame, new SpecificMethodFrameListener(frame.getChannel(), responseClass), + timeout); } - - public void closeSession(AMQSession session) throws AMQException { _protocolSession.closeSession(session); } + /** + * Closes the connection. + * + * <p/>If a failover exception occurs whilst closing the connection it is ignored, as the connection is closed + * anyway. + * + * @param timeout The timeout to wait for an acknowledgement to the close request. + * + * @throws AMQException If the close fails for any reason. + */ public void closeConnection(long timeout) throws AMQException { getStateManager().changeState(AMQState.CONNECTION_CLOSING); @@ -504,13 +603,13 @@ public class AMQProtocolHandler extends IoHandlerAdapter // AMQP version change: Hardwire the version to 0-8 (major=8, minor=0) // TODO: Connect this to the session version obtained from ProtocolInitiation for this session. // Be aware of possible changes to parameter order as versions change. - final AMQFrame frame = ConnectionCloseBody.createAMQFrame(0, - _protocolSession.getProtocolMajorVersion(), - _protocolSession.getProtocolMinorVersion(), // AMQP version (major, minor) - 0, // classId - 0, // methodId - AMQConstant.REPLY_SUCCESS.getCode(), // replyCode - new AMQShortString("JMS client is closing the connection.")); // replyText + final AMQFrame frame = + ConnectionCloseBody.createAMQFrame(0, _protocolSession.getProtocolMajorVersion(), + _protocolSession.getProtocolMinorVersion(), // AMQP version (major, minor) + 0, // classId + 0, // methodId + AMQConstant.REPLY_SUCCESS.getCode(), // replyCode + new AMQShortString("JMS client is closing the connection.")); // replyText try { @@ -521,8 +620,10 @@ public class AMQProtocolHandler extends IoHandlerAdapter { _protocolSession.closeProtocolSession(false); } - - + catch (FailoverException e) + { + _logger.debug("FailoverException interrupted connection close, ignoring as connection close anyway."); + } } /** @return the number of bytes read from this protocol session */ @@ -604,7 +705,6 @@ public class AMQProtocolHandler extends IoHandlerAdapter return _protocolSession.getProtocolMajorVersion(); } - public byte getProtocolMinorVersion() { return _protocolSession.getProtocolMinorVersion(); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java index f691637cdc..dcab89e9bb 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/AMQProtocolSession.java @@ -20,16 +20,8 @@ */ package org.apache.qpid.client.protocol; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import javax.jms.JMSException; -import javax.security.sasl.SaslClient; - import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Logger; - import org.apache.mina.common.CloseFuture; import org.apache.mina.common.IdleStatus; import org.apache.mina.common.IoSession; @@ -53,16 +45,24 @@ import org.apache.qpid.framing.VersionSpecificRegistry; import org.apache.qpid.protocol.AMQConstant; import org.apache.qpid.protocol.AMQVersionAwareProtocolSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.JMSException; +import javax.security.sasl.SaslClient; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + /** * Wrapper for protocol session that provides type-safe access to session attributes. <p/> The underlying protocol * session is still available but clients should not use it to obtain session attributes. */ public class AMQProtocolSession implements AMQVersionAwareProtocolSession { - protected static final int LAST_WRITE_FUTURE_JOIN_TIMEOUT = 1000 * 60 * 2; - protected static final Logger _logger = Logger.getLogger(AMQProtocolSession.class); + protected static final Logger _logger = LoggerFactory.getLogger(AMQProtocolSession.class); public static final String PROTOCOL_INITIATION_RECEIVED = "ProtocolInitiatiionReceived"; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java index 4691d48f29..0ab2e07340 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/BlockingMethodFrameListener.java @@ -7,9 +7,9 @@ * to you 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 @@ -27,71 +27,137 @@ import org.apache.qpid.framing.AMQMethodBody; import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQMethodListener; +/** + * BlockingMethodFrameListener is a 'rendezvous' which acts as a {@link AMQMethodListener} that delegates handling of + * incoming methods to a method listener implemented as a sub-class of this and hands off the processed method or + * error to a consumer. The producer of the event does not have to wait for the consumer to take the event, so this + * differs from a 'rendezvous' in that sense. + * + * <p/>BlockingMethodFrameListeners are used to coordinate waiting for replies to method calls that expect a response. + * They are always used in a 'one-shot' manner, that is, to recieve just one response. Usually the caller has to register + * them as method listeners with an event dispatcher and remember to de-register them (in a finally block) once they + * have been completed. + * + * <p/>The {@link #processMethod} must return <tt>true</tt> on any incoming method that it handles. This indicates to + * this listeners that the method it is waiting for has arrived. Incoming methods are also filtered by channel prior to + * being passed to the {@link #processMethod} method, so responses are only received for a particular channel. The + * channel id must be passed to the constructor. + * + * <p/>Errors from the producer are rethrown to the consumer. + * + * <p/><table id="crc"><caption>CRC Card</caption> + * <tr><th> Responsibilities <th> Collaborations + * <tr><td> Accept notification of AMQP method events. <td> {@link AMQMethodEvent} + * <tr><td> Delegate handling of the method to another method listener. <td> {@link AMQMethodBody} + * <tr><td> Block until a method is handled by the delegated to handler. + * <tr><td> Propagate the most recent exception to the consumer. + * </table> + * + * @todo Might be neater if this method listener simply wrapped another that provided the method handling using a + * methodRecevied method. The processMethod takes an additional channelId, however none of the implementations + * seem to use it. So wrapping the listeners is possible. + * + * @todo What is to stop a blocking method listener, receiving a second method whilst it is registered as a listener, + * overwriting the first one before the caller of the block method has had a chance to examine it? If one-shot + * behaviour is to be intended it should be enforced, perhaps by always returning false once the blocked for + * method has been received. + * + * @todo Interuption is caught but not handled. This could be allowed to fall through. This might actually be usefull + * for fail-over where a thread is blocking when failure happens, it could be interrupted to abandon or retry + * when this happens. At the very least, restore the interrupted status flag. + * + * @todo If the retrotranslator can handle it, could use a SynchronousQueue to implement this rendezvous. Need to + * check that SynchronousQueue has a non-blocking put method available. + */ public abstract class BlockingMethodFrameListener implements AMQMethodListener { + /** This flag is used to indicate that the blocked for method has been received. */ private volatile boolean _ready = false; - public abstract boolean processMethod(int channelId, AMQMethodBody frame) throws AMQException; - + /** Used to protect the shared event and ready flag between the producer and consumer. */ private final Object _lock = new Object(); - /** - * This is set if there is an exception thrown from processCommandFrame and the - * exception is rethrown to the caller of blockForFrame() - */ + /** Used to hold the most recent exception that is passed to the {@link #error(Exception)} method. */ private volatile Exception _error; + /** Holds the channel id for the channel upon which this listener is waiting for a response. */ protected int _channelId; + /** Holds the incoming method. */ protected AMQMethodEvent _doneEvt = null; + /** + * Creates a new method listener, that filters incoming method to just those that match the specified channel id. + * + * @param channelId The channel id to filter incoming methods with. + */ public BlockingMethodFrameListener(int channelId) { _channelId = channelId; } /** - * This method is called by the MINA dispatching thread. Note that it could - * be called before blockForFrame() has been called. + * Delegates any additional handling of the incoming methods to another handler. * - * @param evt the frame event - * @return true if the listener has dealt with this frame - * @throws AMQException + * @param channelId The channel id of the incoming method. + * @param frame The method body. + * + * @return <tt>true</tt> if the method was handled, <tt>false</tt> otherwise. */ - public boolean methodReceived(AMQMethodEvent evt) throws AMQException + public abstract boolean processMethod(int channelId, AMQMethodBody frame); // throws AMQException; + + /** + * Informs this listener that an AMQP method has been received. + * + * @param evt The AMQP method. + * + * @return <tt>true</tt> if this listener has handled the method, <tt>false</tt> otherwise. + */ + public boolean methodReceived(AMQMethodEvent evt) // throws AMQException { AMQMethodBody method = evt.getMethod(); - try + /*try + {*/ + boolean ready = (evt.getChannelId() == _channelId) && processMethod(evt.getChannelId(), method); + + if (ready) { - boolean ready = (evt.getChannelId() == _channelId) && processMethod(evt.getChannelId(), method); - if (ready) + // we only update the flag from inside the synchronized block + // so that the blockForFrame method cannot "miss" an update - it + // will only ever read the flag from within the synchronized block + synchronized (_lock) { - // we only update the flag from inside the synchronized block - // so that the blockForFrame method cannot "miss" an update - it - // will only ever read the flag from within the synchronized block - synchronized (_lock) - { - _doneEvt = evt; - _ready = ready; - _lock.notify(); - } + _doneEvt = evt; + _ready = ready; + _lock.notify(); } - return ready; } + + return ready; + + /*} catch (AMQException e) { error(e); // we rethrow the error here, and the code in the frame dispatcher will go round // each listener informing them that an exception has been thrown throw e; - } + }*/ } /** - * This method is called by the thread that wants to wait for a frame. + * Blocks until a method is received that is handled by the delegated to method listener, or the specified timeout + * has passed. + * + * @param timeout The timeout in milliseconds. + * + * @return The AMQP method that was received. + * + * @throws AMQException + * @throws FailoverException */ - public AMQMethodEvent blockForFrame(long timeout) throws AMQException + public AMQMethodEvent blockForFrame(long timeout) throws AMQException, FailoverException { synchronized (_lock) { @@ -117,24 +183,25 @@ public abstract class BlockingMethodFrameListener implements AMQMethodListener catch (InterruptedException e) { // IGNORE -- //fixme this isn't ideal as being interrupted isn't equivellant to sucess -// if (!_ready && timeout != -1) -// { -// _error = new AMQException("Server did not respond timely"); -// _ready = true; -// } + // if (!_ready && timeout != -1) + // { + // _error = new AMQException("Server did not respond timely"); + // _ready = true; + // } } } } + if (_error != null) { if (_error instanceof AMQException) { - throw(AMQException) _error; + throw (AMQException) _error; } else if (_error instanceof FailoverException) { - // This should ensure that FailoverException is not wrapped and can be caught. - throw(FailoverException) _error; // needed to expose FailoverException. + // This should ensure that FailoverException is not wrapped and can be caught. + throw (FailoverException) _error; // needed to expose FailoverException. } else { @@ -156,6 +223,7 @@ public abstract class BlockingMethodFrameListener implements AMQMethodListener // set the error so that the thread that is blocking (against blockForFrame()) // can pick up the exception and rethrow to the caller _error = e; + synchronized (_lock) { _ready = true; diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java index 6a7462cd0f..35ea44a331 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/HeartbeatConfig.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,11 +20,12 @@ */ package org.apache.qpid.client.protocol; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class HeartbeatConfig { - private static final Logger _logger = Logger.getLogger(HeartbeatConfig.class); + private static final Logger _logger = LoggerFactory.getLogger(HeartbeatConfig.class); static final HeartbeatConfig CONFIG = new HeartbeatConfig(); /** @@ -35,13 +36,13 @@ class HeartbeatConfig HeartbeatConfig() { String property = System.getProperty("amqj.heartbeat.timeoutFactor"); - if(property != null) + if (property != null) { try { timeoutFactor = Float.parseFloat(property); } - catch(NumberFormatException e) + catch (NumberFormatException e) { _logger.warn("Invalid timeout factor (amqj.heartbeat.timeoutFactor): " + property); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java index 652468d45d..93cc5e7ec3 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/protocol/ProtocolBufferMonitorFilter.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,10 +20,12 @@ */ package org.apache.qpid.client.protocol; -import org.apache.log4j.Logger; import org.apache.mina.common.IoFilterAdapter; import org.apache.mina.common.IoSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * A MINA filter that monitors the numbers of messages pending to be sent by MINA. It outputs a message * when a threshold has been exceeded, and has a frequency configuration so that messages are not output @@ -32,13 +34,13 @@ import org.apache.mina.common.IoSession; */ public class ProtocolBufferMonitorFilter extends IoFilterAdapter { - private static final Logger _logger = Logger.getLogger(ProtocolBufferMonitorFilter.class); + private static final Logger _logger = LoggerFactory.getLogger(ProtocolBufferMonitorFilter.class); public static long DEFAULT_FREQUENCY = 5000; public static int DEFAULT_THRESHOLD = 3000; - private int _bufferedMessages = 0; + private int _bufferedMessages = 0; private int _threshold; @@ -58,7 +60,7 @@ public class ProtocolBufferMonitorFilter extends IoFilterAdapter _outputFrequencyInMillis = frequency; } - public void messageReceived( NextFilter nextFilter, IoSession session, Object message ) throws Exception + public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception { _bufferedMessages++; if (_bufferedMessages > _threshold) @@ -66,8 +68,8 @@ public class ProtocolBufferMonitorFilter extends IoFilterAdapter long now = System.currentTimeMillis(); if ((now - _lastMessageOutputTime) > _outputFrequencyInMillis) { - _logger.warn("Protocol message buffer exceeded threshold of " + _threshold + ". Current backlog: " + - _bufferedMessages); + _logger.warn("Protocol message buffer exceeded threshold of " + _threshold + ". Current backlog: " + + _bufferedMessages); _lastMessageOutputTime = now; } } @@ -75,7 +77,7 @@ public class ProtocolBufferMonitorFilter extends IoFilterAdapter nextFilter.messageReceived(session, message); } - public void messageSent( NextFilter nextFilter, IoSession session, Object message ) throws Exception + public void messageSent(NextFilter nextFilter, IoSession session, Object message) throws Exception { _bufferedMessages--; nextFilter.messageSent(session, message); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java index 5c0f1de5bb..140cbdeb75 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/CallbackHandlerRegistry.java @@ -20,10 +20,11 @@ */ package org.apache.qpid.client.security; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; +import org.apache.qpid.util.FileUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; @@ -31,10 +32,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; -import org.apache.log4j.Logger; - -import org.apache.qpid.util.FileUtils; - /** * CallbackHandlerRegistry is a registry for call back handlers for user authentication and interaction during user * authentication. It is capable of reading its configuration from a properties file containing call back handler @@ -60,7 +57,7 @@ import org.apache.qpid.util.FileUtils; */ public class CallbackHandlerRegistry { - private static final Logger _logger = Logger.getLogger(CallbackHandlerRegistry.class); + private static final Logger _logger = LoggerFactory.getLogger(CallbackHandlerRegistry.class); /** The name of the system property that holds the name of the callback handler properties file. */ private static final String FILE_PROPERTY = "amq.callbackhandler.properties"; @@ -121,7 +118,7 @@ public class CallbackHandlerRegistry String filename = System.getProperty(FILE_PROPERTY); InputStream is = FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME, - CallbackHandlerRegistry.class.getClassLoader()); + CallbackHandlerRegistry.class.getClassLoader()); try { @@ -167,12 +164,12 @@ public class CallbackHandlerRegistry _logger.error("Unable to read from file " + filename + ": " + e, e); } } - + if (useDefault) { is = CallbackHandlerRegistry.class.getResourceAsStream(DEFAULT_RESOURCE_NAME); } - + return is; }*/ @@ -207,7 +204,7 @@ public class CallbackHandlerRegistry if (!AMQCallbackHandler.class.isAssignableFrom(clazz)) { _logger.warn("SASL provider " + clazz + " does not implement " + AMQCallbackHandler.class - + ". Skipping"); + + ". Skipping"); continue; } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java index 04db8044de..803b34b7fa 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/DynamicSaslRegistrar.java @@ -20,6 +20,13 @@ */ package org.apache.qpid.client.security; +import org.apache.qpid.util.FileUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.sasl.SaslClientFactory; + import java.io.IOException; import java.io.InputStream; import java.security.Security; @@ -28,13 +35,6 @@ import java.util.Map; import java.util.Properties; import java.util.TreeMap; -import javax.security.sasl.SaslClientFactory; - - -import org.apache.log4j.Logger; - -import org.apache.qpid.util.FileUtils; - /** * DynamicSaslRegistrar provides a collection of helper methods for reading a configuration file that contains a mapping * from SASL mechanism names to implementing client factory class names and registering a security provider with the @@ -55,7 +55,7 @@ import org.apache.qpid.util.FileUtils; */ public class DynamicSaslRegistrar { - private static final Logger _logger = Logger.getLogger(DynamicSaslRegistrar.class); + private static final Logger _logger = LoggerFactory.getLogger(DynamicSaslRegistrar.class); /** The name of the system property that holds the name of the SASL configuration properties. */ private static final String FILE_PROPERTY = "amq.dynamicsaslregistrar.properties"; @@ -71,8 +71,8 @@ public class DynamicSaslRegistrar // Open the SASL properties file, using the default name is one is not specified. String filename = System.getProperty(FILE_PROPERTY); InputStream is = - FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME, - DynamicSaslRegistrar.class.getClassLoader()); + FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME, + DynamicSaslRegistrar.class.getClassLoader()); try { @@ -169,7 +169,7 @@ public class DynamicSaslRegistrar Enumeration e = props.propertyNames(); TreeMap<String, Class<? extends SaslClientFactory>> factoriesToRegister = - new TreeMap<String, Class<? extends SaslClientFactory>>(); + new TreeMap<String, Class<? extends SaslClientFactory>>(); while (e.hasMoreElements()) { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java index 5bf120454e..5a2c5ac5c1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/JCAProvider.java @@ -20,13 +20,13 @@ */ package org.apache.qpid.client.security; -import java.security.Provider; -import java.security.Security; -import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.security.sasl.SaslClientFactory; -import org.apache.log4j.Logger; +import java.security.Provider; +import java.util.Map; /** * JCAProvider is a security provider for SASL client factories that is configured from a map of SASL mechanism names @@ -40,7 +40,7 @@ import org.apache.log4j.Logger; */ public class JCAProvider extends Provider { - private static final Logger log = Logger.getLogger(JCAProvider.class); + private static final Logger log = LoggerFactory.getLogger(JCAProvider.class); /** * Creates the security provider with a map from SASL mechanisms to implementing factories. @@ -50,9 +50,9 @@ public class JCAProvider extends Provider public JCAProvider(Map<String, Class<? extends SaslClientFactory>> providerMap) { super("AMQSASLProvider", 1.0, "A JCA provider that registers all " - + "AMQ SASL providers that want to be registered"); + + "AMQ SASL providers that want to be registered"); register(providerMap); -// Security.addProvider(this); + // Security.addProvider(this); } /** diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java b/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java index 46323e8c09..66176dac3c 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/security/UsernameHashedPasswordCallbackHandler.java @@ -20,26 +20,24 @@ */ package org.apache.qpid.client.security; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; +import org.apache.qpid.client.protocol.AMQProtocolSession; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.security.auth.callback.Callback; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.sasl.RealmCallback; - -import com.sun.crypto.provider.HmacMD5; -import org.apache.log4j.Logger; - -import org.apache.qpid.client.protocol.AMQProtocolSession; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; public class UsernameHashedPasswordCallbackHandler implements AMQCallbackHandler { - private static final Logger _logger = Logger.getLogger(UsernameHashedPasswordCallbackHandler.class); + private static final Logger _logger = LoggerFactory.getLogger(UsernameHashedPasswordCallbackHandler.class); private AMQProtocolSession _protocolSession; @@ -91,11 +89,11 @@ public class UsernameHashedPasswordCallbackHandler implements AMQCallbackHandler byte[] digest = md.digest(); - char[] hash = new char[digest.length ]; + char[] hash = new char[digest.length]; int index = 0; for (byte b : digest) - { + { hash[index++] = (char) b; } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java b/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java index c995bf40da..bef3180041 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/state/AMQStateManager.java @@ -20,12 +20,6 @@ */ package org.apache.qpid.client.state; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArraySet; - -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; import org.apache.qpid.client.handler.BasicCancelOkMethodHandler; import org.apache.qpid.client.handler.BasicDeliverMethodHandler; @@ -58,13 +52,22 @@ import org.apache.qpid.framing.QueueDeleteOkBody; import org.apache.qpid.protocol.AMQMethodEvent; import org.apache.qpid.protocol.AMQMethodListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArraySet; + /** * The state manager is responsible for managing the state of the protocol session. <p/> For each AMQProtocolHandler * there is a separate state manager. */ public class AMQStateManager implements AMQMethodListener { - private static final Logger _logger = Logger.getLogger(AMQStateManager.class); + private static final Logger _logger = LoggerFactory.getLogger(AMQStateManager.class); + private AMQProtocolSession _protocolSession; /** The current state */ @@ -78,14 +81,13 @@ public class AMQStateManager implements AMQMethodListener private final CopyOnWriteArraySet _stateListeners = new CopyOnWriteArraySet(); private final Object _stateLock = new Object(); - private static final long MAXIMUM_STATE_WAIT_TIME = 30000l; + private static final long MAXIMUM_STATE_WAIT_TIME = 30000L; public AMQStateManager() { this(null); } - public AMQStateManager(AMQProtocolSession protocolSession) { this(AMQState.CONNECTION_NOT_STARTED, true, protocolSession); @@ -179,20 +181,22 @@ public class AMQStateManager implements AMQMethodListener if (handler != null) { handler.methodReceived(this, _protocolSession, evt); + return true; } + return false; } - protected StateAwareMethodListener findStateTransitionHandler(AMQState currentState, - AMQMethodBody frame) - // throws IllegalStateTransitionException + protected StateAwareMethodListener findStateTransitionHandler(AMQState currentState, AMQMethodBody frame) + // throws IllegalStateTransitionException { final Class clazz = frame.getClass(); if (_logger.isDebugEnabled()) { _logger.debug("Looking for state[" + currentState + "] transition handler for frame " + clazz); } + final Map classToHandlerMap = (Map) _state2HandlersMap.get(currentState); if (classToHandlerMap == null) @@ -201,12 +205,14 @@ public class AMQStateManager implements AMQMethodListener // handler registered for "all" states return findStateTransitionHandler(null, frame); } + final StateAwareMethodListener handler = (StateAwareMethodListener) classToHandlerMap.get(clazz); if (handler == null) { if (currentState == null) { _logger.debug("No state transition handler defined for receiving frame " + frame); + return null; } else @@ -222,7 +228,6 @@ public class AMQStateManager implements AMQMethodListener } } - public void attainState(final AMQState s) throws AMQException { synchronized (_stateLock) @@ -230,7 +235,7 @@ public class AMQStateManager implements AMQMethodListener final long waitUntilTime = System.currentTimeMillis() + MAXIMUM_STATE_WAIT_TIME; long waitTime = MAXIMUM_STATE_WAIT_TIME; - while (_currentState != s && waitTime > 0) + while ((_currentState != s) && (waitTime > 0)) { try { @@ -240,11 +245,13 @@ public class AMQStateManager implements AMQMethodListener { _logger.warn("Thread interrupted"); } + if (_currentState != s) { waitTime = waitUntilTime - System.currentTimeMillis(); } } + if (_currentState != s) { _logger.warn("State not achieved within permitted time. Current state " + _currentState + ", desired state: " + s); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java b/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java index 1fd657c5fb..8b8453a1b0 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/state/StateWaiter.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,15 +20,17 @@ */ package org.apache.qpid.client.state; -import org.apache.log4j.Logger; import org.apache.qpid.AMQException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Waits for a particular state to be reached. */ public class StateWaiter implements StateListener { - private static final Logger _logger = Logger.getLogger(StateWaiter.class); + private static final Logger _logger = LoggerFactory.getLogger(StateWaiter.class); private final AMQState _state; @@ -52,13 +54,13 @@ public class StateWaiter implements StateListener // The guard is required in case we are woken up by a spurious // notify(). // - while (!_newStateAchieved && _throwable == null) + while (!_newStateAchieved && (_throwable == null)) { try { _logger.debug("State " + _state + " not achieved so waiting..."); _monitor.wait(TIME_OUT); - //fixme this won't cause the timeout to exit the loop. need to set _throwable + // fixme this won't cause the timeout to exit the loop. need to set _throwable } catch (InterruptedException e) { @@ -72,7 +74,7 @@ public class StateWaiter implements StateListener _logger.debug("Throwable reached state waiter: " + _throwable); if (_throwable instanceof AMQException) { - throw(AMQException) _throwable; + throw (AMQException) _throwable; } else { @@ -89,6 +91,7 @@ public class StateWaiter implements StateListener { _logger.debug("stateChanged called changing from :" + oldState + " to :" + newState); } + if (_state == newState) { _newStateAchieved = true; @@ -97,6 +100,7 @@ public class StateWaiter implements StateListener { _logger.debug("New state reached so notifying monitor"); } + _monitor.notifyAll(); } } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java b/qpid/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java index 1c70ded62a..623591e0b6 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/state/listener/SpecificMethodFrameListener.java @@ -34,7 +34,7 @@ public class SpecificMethodFrameListener extends BlockingMethodFrameListener _expectedClass = expectedClass; } - public boolean processMethod(int channelId, AMQMethodBody frame) throws AMQException + public boolean processMethod(int channelId, AMQMethodBody frame) //throws AMQException { return _expectedClass.isInstance(frame); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java index 04e7e40564..5482e48699 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/SocketTransportConnection.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,28 +20,32 @@ */ package org.apache.qpid.client.transport; -import java.io.IOException; -import java.net.InetSocketAddress; - -import org.apache.log4j.Logger; import org.apache.mina.common.ByteBuffer; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoConnector; import org.apache.mina.common.SimpleByteBufferAllocator; import org.apache.mina.transport.socket.nio.SocketConnectorConfig; import org.apache.mina.transport.socket.nio.SocketSessionConfig; + import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.pool.ReadWriteThreadModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetSocketAddress; + public class SocketTransportConnection implements ITransportConnection { - private static final Logger _logger = Logger.getLogger(SocketTransportConnection.class); + private static final Logger _logger = LoggerFactory.getLogger(SocketTransportConnection.class); private static final int DEFAULT_BUFFER_SIZE = 32 * 1024; private SocketConnectorFactory _socketConnectorFactory; - static interface SocketConnectorFactory { + static interface SocketConnectorFactory + { IoConnector newSocketConnector(); } @@ -50,8 +54,7 @@ public class SocketTransportConnection implements ITransportConnection _socketConnectorFactory = socketConnectorFactory; } - public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) - throws IOException + public void connect(AMQProtocolHandler protocolHandler, BrokerDetails brokerDetail) throws IOException { ByteBuffer.setUseDirectBuffers(Boolean.getBoolean("amqj.enableDirectBuffers")); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java index b9193ce14e..459579d920 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/TransportConnection.java @@ -20,13 +20,6 @@ */ package org.apache.qpid.client.transport; -import java.io.IOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.apache.log4j.Logger; - import org.apache.mina.common.IoConnector; import org.apache.mina.common.IoHandlerAdapter; import org.apache.mina.common.IoServiceConfig; @@ -34,11 +27,18 @@ import org.apache.mina.transport.socket.nio.SocketConnector; import org.apache.mina.transport.vmpipe.VmPipeAcceptor; import org.apache.mina.transport.vmpipe.VmPipeAddress; -import org.apache.qpid.client.AMQBrokerDetails; import org.apache.qpid.client.vmbroker.AMQVMBrokerCreationException; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.pool.ReadWriteThreadModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + /** * The TransportConnection is a helper class responsible for connecting to an AMQ server. It sets up the underlying * connector, which currently always uses TCP/IP sockets. It creates the "protocol handler" which deals with MINA @@ -57,7 +57,7 @@ public class TransportConnection private static final int TCP = 0; private static final int VM = 1; - private static Logger _logger = Logger.getLogger(TransportConnection.class); + private static Logger _logger = LoggerFactory.getLogger(TransportConnection.class); private static final String DEFAULT_QPID_SERVER = "org.apache.qpid.server.protocol.AMQPFastProtocolHandler"; @@ -99,7 +99,7 @@ public class TransportConnection // FIXME - this needs to be sorted to use the new Mina MultiThread SA. if (Boolean.getBoolean("qpidnio")) { - _logger.fatal("Using Qpid NIO - sysproperty 'qpidnio' is set."); + _logger.error("Using Qpid NIO - sysproperty 'qpidnio' is set."); // result = new org.apache.qpid.nio.SocketConnector(); // non-blocking connector } // else @@ -193,7 +193,7 @@ public class TransportConnection } catch (IOException e) { - _logger.error(e); + _logger.error("Got IOException.", e); // Try and unbind provider try @@ -262,7 +262,6 @@ public class TransportConnection catch (Exception e) { _logger.info("Unable to create InVM Qpid.AMQP on port " + port + ". Because: " + e.getCause()); - _logger.error(e); String because; if (e.getCause() == null) { diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java index 104c4b43d0..d9137dc8b1 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/transport/VmPipeTransportConnection.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,21 +20,25 @@ */ package org.apache.qpid.client.transport; -import java.io.IOException; - -import org.apache.log4j.Logger; import org.apache.mina.common.ConnectFuture; import org.apache.mina.common.IoServiceConfig; import org.apache.mina.transport.vmpipe.VmPipeAddress; import org.apache.mina.transport.vmpipe.VmPipeConnector; + import org.apache.qpid.client.protocol.AMQProtocolHandler; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.pool.PoolingFilter; import org.apache.qpid.pool.ReferenceCountingExecutorService; +import org.apache.qpid.pool.ReadWriteThreadModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; public class VmPipeTransportConnection implements ITransportConnection { - private static final Logger _logger = Logger.getLogger(VmPipeTransportConnection.class); + private static final Logger _logger = LoggerFactory.getLogger(VmPipeTransportConnection.class); private static int _port; @@ -47,14 +51,9 @@ public class VmPipeTransportConnection implements ITransportConnection { final VmPipeConnector ioConnector = new VmPipeConnector(); final IoServiceConfig cfg = ioConnector.getDefaultConfig(); - ReferenceCountingExecutorService executorService = ReferenceCountingExecutorService.getInstance(); - PoolingFilter asyncRead = PoolingFilter.createAynschReadPoolingFilter(executorService, - "AsynchronousReadFilter"); - cfg.getFilterChain().addFirst("AsynchronousReadFilter", asyncRead); - PoolingFilter asyncWrite = PoolingFilter.createAynschWritePoolingFilter(executorService, - "AsynchronousWriteFilter"); - cfg.getFilterChain().addLast("AsynchronousWriteFilter", asyncWrite); - + + cfg.setThreadModel(ReadWriteThreadModel.getInstance()); + final VmPipeAddress address = new VmPipeAddress(_port); _logger.info("Attempting connection to " + address); ConnectFuture future = ioConnector.connect(address, protocolHandler); diff --git a/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java b/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java index 642b928d81..0fc39a9318 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/client/util/FlowControllingBlockingQueue.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,16 +20,17 @@ */ package org.apache.qpid.client.util; - +import java.util.Iterator; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -import java.util.Iterator; /** * A blocking queue that emits events above a user specified threshold allowing the caller to take action (e.g. flow * control) to try to prevent the queue growing (much) further. The underlying queue itself is not bounded therefore the * caller is not obliged to react to the events. <p/> This implementation is <b>only</b> safe where we have a single * thread adding items and a single (different) thread removing items. + * + * @todo Make this implement java.util.Queue and hide the implementation. Then different queue types can be substituted. */ public class FlowControllingBlockingQueue { @@ -81,6 +82,7 @@ public class FlowControllingBlockingQueue } } } + return o; } @@ -104,4 +106,3 @@ public class FlowControllingBlockingQueue return _queue.iterator(); } } - diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java b/qpid/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java index 6ad3fb4bae..6ec883ff0b 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/jms/FailoverPolicy.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,22 +20,23 @@ */ package org.apache.qpid.jms; -import org.apache.log4j.Logger; import org.apache.qpid.jms.failover.FailoverMethod; import org.apache.qpid.jms.failover.FailoverRoundRobinServers; import org.apache.qpid.jms.failover.FailoverSingleServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class FailoverPolicy { - private static final Logger _logger = Logger.getLogger(FailoverPolicy.class); + private static final Logger _logger = LoggerFactory.getLogger(FailoverPolicy.class); private static final long MINUTE = 60000L; private static final long DEFAULT_METHOD_TIMEOUT = 1 * MINUTE; private static final long DEFAULT_FAILOVER_TIMEOUT = 4 * MINUTE; - private FailoverMethod _methods[] = new FailoverMethod[1]; + private FailoverMethod[] _methods = new FailoverMethod[1]; private int _currentMethod; @@ -52,7 +53,7 @@ public class FailoverPolicy { FailoverMethod method; - //todo This should be integrated in to the connection url when it supports + // todo This should be integrated in to the connection url when it supports // multiple strategies. _methodsRetries = 0; @@ -72,12 +73,12 @@ public class FailoverPolicy { String failoverMethod = connectionDetails.getFailoverMethod(); -/* - if (failoverMethod.equals(FailoverMethod.RANDOM)) - { - //todo write a random connection Failover - } -*/ + /* + if (failoverMethod.equals(FailoverMethod.RANDOM)) + { + //todo write a random connection Failover + } + */ if (failoverMethod.equals(FailoverMethod.SINGLE_BROKER)) { method = new FailoverRoundRobinServers(connectionDetails); @@ -92,12 +93,12 @@ public class FailoverPolicy { try { - Class[] constructorSpec = {ConnectionURL.class}; - Object [] params = {connectionDetails}; + Class[] constructorSpec = { ConnectionURL.class }; + Object[] params = { connectionDetails }; - method = (FailoverMethod) ClassLoader.getSystemClassLoader(). - loadClass(failoverMethod). - getConstructor(constructorSpec).newInstance(params); + method = + (FailoverMethod) ClassLoader.getSystemClassLoader().loadClass(failoverMethod) + .getConstructor(constructorSpec).newInstance(params); } catch (Exception cnfe) { @@ -157,13 +158,13 @@ public class FailoverPolicy return false; } - } else { if ((now - _lastFailTime) >= DEFAULT_FAILOVER_TIMEOUT) { _logger.info("Failover timeout"); + return false; } else @@ -179,7 +180,6 @@ public class FailoverPolicy _lastFailTime = _lastMethodTime; } - if (_methods[_currentMethod].failoverAllowed()) { failoverAllowed = true; @@ -190,6 +190,7 @@ public class FailoverPolicy { nextMethod(); _logger.info("Changing method to " + _methods[_currentMethod].methodName()); + return failoverAllowed(); } else @@ -207,6 +208,7 @@ public class FailoverPolicy { _currentMethod++; _methods[_currentMethod].reset(); + return true; } else @@ -225,11 +227,13 @@ public class FailoverPolicy _logger.info("Retrying methods starting with " + _methods[_currentMethod].methodName()); _methods[_currentMethod].reset(); + return failoverAllowed(); } else { _logger.debug("All failover methods exhausted"); + return false; } } @@ -278,7 +282,7 @@ public class FailoverPolicy public FailoverMethod getCurrentMethod() { - if (_currentMethod >= 0 && _currentMethod < (_methods.length - 1)) + if ((_currentMethod >= 0) && (_currentMethod < (_methods.length - 1))) { return _methods[_currentMethod]; } @@ -311,6 +315,7 @@ public class FailoverPolicy { sb.append(">"); } + sb.append(_methods[i].toString()); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java b/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java index f8325c35ef..4e0d0b79b5 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/jms/failover/FailoverRoundRobinServers.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,13 +20,15 @@ */ package org.apache.qpid.jms.failover; -import org.apache.log4j.Logger; import org.apache.qpid.jms.BrokerDetails; import org.apache.qpid.jms.ConnectionURL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class FailoverRoundRobinServers implements FailoverMethod { - private static final Logger _logger = Logger.getLogger(FailoverRoundRobinServers.class); + private static final Logger _logger = LoggerFactory.getLogger(FailoverRoundRobinServers.class); /** The default number of times to cycle through all servers */ public static final int DEFAULT_CYCLE_RETRIES = 0; @@ -72,7 +74,7 @@ public class FailoverRoundRobinServers implements FailoverMethod _connectionDetails = connectionDetails; - //There is no current broker at startup so set it to -1. + // There is no current broker at startup so set it to -1. _currentBrokerIndex = -1; String cycleRetries = _connectionDetails.getFailoverOption(ConnectionURL.OPTIONS_FAILOVER_CYCLE); @@ -104,9 +106,8 @@ public class FailoverRoundRobinServers implements FailoverMethod public boolean failoverAllowed() { - return ((_currentCycleRetries < _cycleRetries) - || (_currentServerRetry < _serverRetries) - || (_currentBrokerIndex < (_connectionDetails.getBrokerCount() - 1))); + return ((_currentCycleRetries < _cycleRetries) || (_currentServerRetry < _serverRetries) + || (_currentBrokerIndex < (_connectionDetails.getBrokerCount() - 1))); } public void attainedConnection() @@ -125,8 +126,6 @@ public class FailoverRoundRobinServers implements FailoverMethod return _connectionDetails.getBrokerDetails(_currentBrokerIndex); } - - public BrokerDetails getNextBrokerDetails() { if (_currentBrokerIndex == (_connectionDetails.getBrokerCount() - 1)) @@ -137,7 +136,7 @@ public class FailoverRoundRobinServers implements FailoverMethod { _currentBrokerIndex = 0; - setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex )); + setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex)); _logger.info("First run using " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); } @@ -151,15 +150,15 @@ public class FailoverRoundRobinServers implements FailoverMethod else { _currentCycleRetries++; - //failed to connect to first broker + // failed to connect to first broker _currentBrokerIndex = 0; - setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex )); + setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex)); // This is zero rather than -1 as we are already retrieving the details. _currentServerRetry = 0; } - //else - should force client to stop as max retries has been reached. + // else - should force client to stop as max retries has been reached. } else { @@ -169,7 +168,7 @@ public class FailoverRoundRobinServers implements FailoverMethod { _currentBrokerIndex = 0; - setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex )); + setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex)); _logger.info("First run using " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); } @@ -177,13 +176,14 @@ public class FailoverRoundRobinServers implements FailoverMethod { _logger.info("Retrying " + _connectionDetails.getBrokerDetails(_currentBrokerIndex)); } + _currentServerRetry++; } else { _currentBrokerIndex++; - setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex )); + setBroker(_connectionDetails.getBrokerDetails(_currentBrokerIndex)); // This is zero rather than -1 as we are already retrieving the details. _currentServerRetry = 0; } @@ -192,7 +192,6 @@ public class FailoverRoundRobinServers implements FailoverMethod return _connectionDetails.getBrokerDetails(_currentBrokerIndex); } - public void setBroker(BrokerDetails broker) { @@ -246,12 +245,13 @@ public class FailoverRoundRobinServers implements FailoverMethod sb.append(_currentBrokerIndex); sb.append("\n"); - for(int i=0; i < _connectionDetails.getBrokerCount() ; i++) + for (int i = 0; i < _connectionDetails.getBrokerCount(); i++) { if (i == _currentBrokerIndex) { sb.append(">"); } + sb.append(_connectionDetails.getBrokerDetails(i)); sb.append("\n"); } diff --git a/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java b/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java index 4f6f1561b6..a46c7f3cd5 100644 --- a/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java +++ b/qpid/java/client/src/main/java/org/apache/qpid/jndi/PropertiesFileInitialContextFactory.java @@ -7,9 +7,9 @@ * to you 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 @@ -20,38 +20,40 @@ */ package org.apache.qpid.jndi; -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ConcurrentHashMap; - -import javax.jms.ConnectionFactory; -import javax.jms.Destination; -import javax.jms.Queue; -import javax.jms.Topic; -import javax.naming.Context; -import javax.naming.NamingException; -import javax.naming.spi.InitialContextFactory; - -import org.apache.log4j.Logger; import org.apache.qpid.client.AMQConnectionFactory; import org.apache.qpid.client.AMQDestination; import org.apache.qpid.client.AMQHeadersExchange; import org.apache.qpid.client.AMQQueue; import org.apache.qpid.client.AMQTopic; +import org.apache.qpid.exchange.ExchangeDefaults; import org.apache.qpid.framing.AMQShortString; import org.apache.qpid.url.AMQBindingURL; import org.apache.qpid.url.BindingURL; import org.apache.qpid.url.URLSyntaxException; -import org.apache.qpid.exchange.ExchangeDefaults; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jms.ConnectionFactory; +import javax.jms.Destination; +import javax.jms.Queue; +import javax.jms.Topic; +import javax.naming.Context; +import javax.naming.NamingException; +import javax.naming.spi.InitialContextFactory; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; public class PropertiesFileInitialContextFactory implements InitialContextFactory { - protected final Logger _logger = Logger.getLogger(PropertiesFileInitialContextFactory.class); + protected final Logger _logger = LoggerFactory.getLogger(PropertiesFileInitialContextFactory.class); private String CONNECTION_FACTORY_PREFIX = "connectionfactory."; private String DESTINATION_PREFIX = "destination."; @@ -78,7 +80,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor if (file != null) { _logger.info("Loading Properties from:" + file); - //Load the properties specified + // Load the properties specified Properties p = new Properties(); p.load(new BufferedInputStream(new FileInputStream(file))); @@ -93,8 +95,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor } catch (IOException ioe) { - _logger.warn("Unable to load property file specified in Provider_URL:" + - environment.get(Context.PROVIDER_URL)); + _logger.warn("Unable to load property file specified in Provider_URL:" + environment.get(Context.PROVIDER_URL)); } createConnectionFactories(data, environment); @@ -109,7 +110,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor } // Implementation methods - //------------------------------------------------------------------------- + // ------------------------------------------------------------------------- protected ReadOnlyContext createContext(Map data, Hashtable environment) { return new ReadOnlyContext(environment, data); @@ -200,6 +201,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor { _logger.warn("Unable to createFactories:" + urlse); } + return null; } @@ -216,6 +218,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor catch (URLSyntaxException urlse) { _logger.warn("Unable to destination:" + urlse); + return null; } @@ -226,6 +229,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor catch (IllegalArgumentException iaw) { _logger.warn("Binding: '" + binding + "' not supported"); + return null; } } @@ -235,17 +239,15 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor */ protected Queue createQueue(Object value) { - if(value instanceof AMQShortString) + if (value instanceof AMQShortString) { return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, (AMQShortString) value); } else if (value instanceof String) - { return new AMQQueue(ExchangeDefaults.DIRECT_EXCHANGE_NAME, new AMQShortString((String) value)); } else if (value instanceof BindingURL) - { return new AMQQueue((BindingURL) value); } @@ -258,16 +260,15 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor */ protected Topic createTopic(Object value) { - if(value instanceof AMQShortString) + if (value instanceof AMQShortString) { - return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, (AMQShortString)value); + return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, (AMQShortString) value); } else if (value instanceof String) { return new AMQTopic(ExchangeDefaults.TOPIC_EXCHANGE_NAME, new AMQShortString((String) value)); } else if (value instanceof BindingURL) - { return new AMQTopic((BindingURL) value); } @@ -293,7 +294,7 @@ public class PropertiesFileInitialContextFactory implements InitialContextFactor } // Properties - //------------------------------------------------------------------------- + // ------------------------------------------------------------------------- public String getConnectionPrefix() { return CONNECTION_FACTORY_PREFIX; |
