{"id":913,"date":"2012-09-18T23:20:59","date_gmt":"2012-09-18T21:20:59","guid":{"rendered":"http:\/\/hasselba.ch\/blog\/?p=913"},"modified":"2012-09-18T23:20:59","modified_gmt":"2012-09-18T21:20:59","slug":"xpages-high-performance-applications","status":"publish","type":"post","link":"https:\/\/hasselba.ch\/blog\/?p=913","title":{"rendered":"XPages: High Performance Applications"},"content":{"rendered":"<p>During the last months I worked on a high performance XPages application used by a lot of end users.\u00a0 To get a better data throughput, I decided to use a reverse proxy for load balancing, caching of ressources, SSL connections etc.<\/p>\n<p title=\"yahoo.com: Best Practices for Speeding Up Your Web Site\">For static resources I am using some &#8222;special&#8220; domains: This means that the browser is allowed to do more HTTP requests at once to the same NSF. If you have for example some images in your database which are reachable from outside via <em>http:\/\/www.example.com\/mydb.nsf\/image.gif<\/em>, this can be changed to <em>http:\/\/static.example.com\/mydb.nsf\/image.gif<\/em> (<a title=\"yahoo.com: Best Practices for Speeding Up Your Web Site\" href=\"http:\/\/developer.yahoo.com\/performance\/rules.html\" target=\"_blank\">Here<\/a> you can find a list of best practices).<\/p>\n<p>I solved the problem of multiple execution during the JSF lifecycle\u00a0 by checking if the request has already actual data\u00a0(details can be found <a title=\"Performance-Killer in der XPage\" href=\"https:\/\/hasselba.ch\/blog\/?p=572\" target=\"_blank\">here<\/a> &#8211; German only), but there was still a problem: Everytime a XPage wants some data to display, a query is sent to the domino server. This is nice if your application requires data in real-time, but not for a normal application &#8211; it kills the user experience.<\/p>\n<p>This is why I searched a way which easily allows to implement memory cached database queries for domino databases.\u00a0 The main idea is that the database query is no longer send directly to the backend . Instead, the request is made against a JSON wrapper, and the request to this wrapper (an agent residing in the same NSF) is done via a proxy. This allows a full control of the requested data.<\/p>\n<p><img decoding=\"async\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWMAAABsCAIAAAAwgUTXAAAZ90lEQVR4nO2dW2wb15mA+aCHADVQozCyRpECLpoCbuBivWmzSBun63SzmwvajYFk3TYQNkGArLNew3Far11LsjQmKVIkh+TwPryTEilSFCmREk1RoqKRzMq0TdNMTTlMxE2JWImZRkEZgwgGi3ngPozLMrzM8KohqfPhf5DImeGZI52P\/5xz5gwrDwAAAHSwmC4AAADoAoApAAAAPcAUAACAHmAKAABADzAFAACgB5gCAADQA0wBAADoAaYAAAD0AFMAAAB6gCkAAAA9wBQAAIAeYAoAAEAPMAUAAKAHmAIAANADTAEAAOgBpgAAAPQAUwAAAHqAKQAAAD3AFABAg2xsbES+zvLysrkJAoFAyQFjsRjTZ\/kAYAoAoJTPPvuMbKhkA1bIxiQwhwz2JaiAXDKiUw0Xx4ThIjY31HA4zRdLDqiWjUBFwEI2WQxEwiXLtry8HIlENjc3210n3WcKgiByuRzTpQD0CB9\/\/DFpBL1OJYE5Aj4EQdAYDyIbKtmAE1cG0zcfBHH3Qv5TxmIrPkAWI3VtkCzbhOGiTjUsEY1AEMTlQBKYo1GLzWbzyspKMplsYUV1nynG7WrPjIPpUgC6mGQyaTabZVIejwuJBBBphEhwKH1zcPvOAIMiaDLw9IX0zcE4NojNDU3oL8olI2w2JIE5ZrM5Eok0WWldZoqV1bnRsXOBBR\/TBQF0JX6\/X8CHJPAINjeUXB\/MpbrYC7UEcfdC+uYgNjekUw3zRqFJ+3jDVddNpti4c1Ohf1PjOIphGNNlAXQfVpPaZrjY1VlDM5FLDSzNDClk\/MZqr2tM8dmfP5Gqzk7feAiYAtAYAj4UxwYZb7EMxvadAT4PIgiigdrrDlPgOC5VDDnX987EWSpLfzweZ7pEgO4DgiCb9qRSOhAJDu2qzCKXGohjgzbDBaXkHARBjdVed5hCa2JbFx+dibOAKQANA0FQPnFg++rfB+2\/UUrOwYIhp\/lC+PJQ+uZg9oOeEgfZtRldHvLaLyDwICwY8FpfT4WeyicOdJkpcrnc+tVwjRt7fCqd+0lSEzNxlsJworXDP4BdAmmKQuSiP0gu\/mzZ8YpFfRoR\/R6CIFQ+YEEvkKOPyfXBzh8KydweKB4xdZovWNALEASN8YYs6tP+8f64\/9ns9UPFZ91lpli7gnF5Q3+8fYt2y+sxv8zwq4ImZuIsmeZMOp1ufxkBvUaJKcojE348vfIkNnUMmzrm1J+wqE+T6TqXM2xBLxRi2fu3uVJkSlISDQypkFlASUSCf\/ugoHuwUABYMEROxEJlv7OoT9u0J8kyJxd\/ll55kvocu8wUeiNiXXxUgZ7b2tqi2Cx9NwYr\/ssT6wOmADQPrSmqBfHe99IrTxYi7P4l2TKxqWNkSlISsGAAqhMyCyiJoP03hQ+KzLxYKEAu+oPGTqTLTIHj+JhwxBPrc13bo9Rwstlsxc2+vP+FRPE22YtZHEJ4qNouAAAFDZuil6KbTJFIJFSmN8hmb1\/dj2ol5cM2BEEodWfGQwdKNFG7KXAcb0\/xe4HNzc3CPUhffvkl08XZIYApuswU7hmbwXe40PInFo5Muawl24xPDehnf1yuiZk4a5Q3QmuB2K0bItHY7pTFxsbG2tqa2Ww2GI2wWExGSa4rEkuUGi0ZXO5o8Vvc0VFyFykiM5vNTqczEol8+umnTJ9WCwCmYMYUyWSSupehGgIh23VtT3HjN03\/x9oVrLBBABMoLL+oqImZOIv6VHO5nNmq5PIG\/QFPA2XrRpLJpMvlkitV\/DEBBEESRIbqjb7gsj+0en0jRcbdL\/+vxvjw07+Qu4RjCV9w2eWdV2q0PD4fgiARLEZRbSAQ6FJxAFMwY4rAgo\/NGfbOOer66s5kMhL5+fL2j5rPkWOft+5MidX9Jb2YNZoiduuGGBkxBw4KxcO9fb8pjuOBQEAmV7DZbAkic3nnV6\/HE3+6V7sRGojrG6kF7A\/miUkenz\/K46vU6rW1NaZrog6AKZgxRTKZVBhOGL0\/kSCXYrdu1LjXykoIdT5b3v49sT6lhhP7Y0Akf6sk4ygO5\/peWMItPyyZSqitr03feEjv+kXo3fmGz6vD2dzclMkVXO7ouMMVjiXSX3zVVjtUi\/fvfr50JaLUaLmjoxoU7YosA5iCGVPgOD7KG5mJs1zX9qitr2m08Pb2Nu1eeiNSsZ9yJs6ajvwdX\/Q7++r+apqoZgoylSAncbqu7RGJ2Y3NbO9wPv74Y5lcIUZk4ViCETtUu1oJrITHBAINin7xxRdMVxIVwBTMmCKfz8MSbmEU07byCCwdCC75KFppQS4NhyO8D5HxCgcsTiUeXMXYX6599mcXEY3FpDJ5RzmiONJffBVYCYulyEcffcR0VVWlmilYLFZdrzcQrCKaPE5XmqJkFGMmztJPPysSs6vNto7H4ypLfzOmGA8d0BsR8mjFqUTBI3IVr\/cSivWrEVRv+t8\/32fcCNQRT90VweKOlQWFKcpbYPOtulrzbuaw3WqKii3fdW2PwnDCbFWWz3ooN0tjpsBxfNpjLU4lyOjVW0KEIlG7eytbFfMhTKFQMl1hlWnSFBXzAvLnwovVNqB1R8UdK5ah2qfUmLMwY4pqAxkzcZZ18VEYGXp3ZaH4G57Hv1TStusNk\/+QXCGRIJdMc0+UvGVf3a9QCZo5nc4km82OCYSMK6DGuL6R4vHHmK6zylBffVRs\/7R5QXHjLPm5xiygmgsqblDNUxQH7AhT5CkbvyfWp516Xq7ipVKpfD6\/tbUlVZ1tRhNkTiHTnqg4OCLXnSI\/qPfg8cfqmhPBYLh8ly+x2UxXWGXaZIpqTbpGU9AWpi6tdK4pJuxac+AgRdt2hPfJdaecU6b5eV\/F8dGWhDlwcMKubfJcOhYIgtR6Uzx1l3ERUMfyehQ1WRv+X2w3tD2aJQ2vWnpfMXdo0hQlR67XFBXL1lmmCIfDmsnna7lqEMAD1COgzYREft7tdmMYFolE0rXRRbeZQRC0vnlPodG5fJc\/\/PQvjBuhPOKpu6aJSbPTE8981aumoG26DZuC9oKC1hS1yIhhU6TTaZnmTJvaf43hifWhriMax1GN46jG9pJMc6aWEMIPbvLncjkdPqETgqB45qvYJ\/c9S2sisXTad7lD8ov0F1+tXo+bJiYRpToQeS+e+QqYohZTUBy5Z01BEMQoD6KYfN2xMR46IJGfn7BrOz+5IE1BRuyT++6lNUSpFkmk077LV99L7vzoaeJP9969GrVOunh8vsE2VXBER5lie3u7ZB2TWuZT1PJzLTapZd+KW5Y3\/nq10qFXH\/l8XqEStO+yoh1hX90v153SG5FMJtP86e8AxaYoxPrmPffSGmqyjvL5MpXGMTO3dCVyfSPVDnEk\/nTv+kbKF1w2TUwKhCKJTGF2uP3hm7FP7pcXbCdNgeN4KpXCMAzDMLfbbbFYDAbDX1NFbsmQeTOmKDTjko6AZvYtf7fEQeXbUwiL4vidYorAgq94nctODuf6XpXpDYVK0F2jJBVNURyrG+nZd9et016VzjTK53M4XNRg0pvHfcFlMgo3lZJR3NkRT90tfmv1epzcZdp3GTWYZCoNBEESmUKlMznmlwKR96599Dl1YXbSFARBjI2NVVhCamys\/E8MZnPvkCkIgiD7AuPxOGlxvRHRGxFYwtXYXmLcAtThurYHtb8MS7iJRKKxmmIQWlOUROyT+6HYB4s3NhzzS2SodKbiGBOKCo0KUaqL3zLYpshd3EtrodgHqxvpuj56568+gsFguSYqZotVTcFiPYgee6Xdpshms9FoFMOwUChIukAgZEMQxOFAZF+gytJPdh+Ohw6Mhw6UL2DXUTF94yHt1PMiMbt77wep1xTMxk6aIpfLLS4uXrr0tweQIwhSreOpsik6p2HviDJaaYq1K9iY6LzGcRR1HSFdQHEbeCeHJ9an9xwRiaGSqaJdBzBFOdls1u\/3IwiCYZjNZiM1gaIoxTAWuPposSny+fysz2byPs14U28mDL7DQvGwP+DpgTXygCmKSafTTqcTQZBoNEp+AWQyGQiCLBYL9d8amKL1psjn8y6PyTL\/U8YbfANhDhyEpQPuGVuHz5KoHWAKkkQigaKoxWIpvw8QwzDatBGYoi2mIAjCapNNLD3GeMuvPeyr+6WqsxN2bS1r6nQRu9wUOI5HIhEEQdxudzMD28AUbTFFvpIsymHcDsVhmHmmfJnvbocgiF1rilwuFwwGEQQJBoPNT5ADpmiXKfL5PI7jqH6sMLGqXA2dJgul4eSd9283VhcdSzVTsFgsihfLtd5FpshkMl6vF4bhSCTSqp4mYIo2miL\/dVlQmIL8oTjRKE89inen+Lk8W6mYxZR\/4kycNX3jIURxsfMnaNcFRU5R0v6Lfy1Xw87IoklTJJNJi8WComjLn2jvdlmw6V8R732P8ebKVER9zxm0osZqr6aZV9lsVq2DnOt7qU1RrfEXt2pqU9C+SPGJhbCtPKI1wF09LFpCm0xRLd0ofrFihtIOUxAEEY\/HlUql0+ls03NnCYJYXpqDhZe84yeSiz8reQh4r0Yu+oNU6Cn\/xGuwYMTvm2w4Qat1jiYpC1YZ1GqgSD1o9VHjoSqGyfv0rM\/WWI10INT9FMWNmTqDoPBIxYOU+6KW3ITaFOWdzblcDsMwBEG8Xu8OdEXjOB6Px52TRkQyCgshp\/GdGp8S3i2RCT+eCj2FTR1zm\/8bgYdhIcdmVUej0SaHAuuYzZ3JZFgsVskKV7Tf\/HWZouLVRzU9UZhiJs5SW1+r\/SkkHQ5tj2bFr\/paXqlRN60yhdfrRVG08Gvx7ClGhrRzuVwymcQwzDlptBjlEATBQsiCnnMa3yafME4aZGvtCcbbf7kO0itPkkbApo55x09Y0HNK6UUIglCVyGZVYxiWSCRaeBle3x1iLBYLUZ0vvse85aaoPXegNoUjvI\/DgWj\/\/wiCSCQSbre7iTpshFwu5\/V6a1wfuGFTlFBtx+J3q5mC9lDUpiAIojCTkrx7qGT2VIeQy+XS6TSpj4JBDFpJYbY4qRIygpP9ZEMtRGTmxfTKkw1H1PdcyQGXnccLH0eK4MFsVJXIYpSTRsAwLB6Pp9PptmZkdZtidtYj153qZFM41\/eqLP3UN4MVBMHlcsmqb7om6yObzRZuZ6JVRquuPihSjLpyCuoor0wcxy0WS\/EdXBVnT3UFpEpIIpEI9nWCC3MWo7zh8M9NlxwwHA4XPo7ZWUI1mSKTyYRCQYVKwGKxVJb+4kds1GsKip+rvUUtnZI8gnREtW7zckEUaGmt0lMwRXH7qaaMVvVo1rJjy02Ry+VQFC052R6bGrcbqGoKHMcTiYR7xiYQsiXy86jriH11f3kWSvENX3EzalMU70V7qOKf7av7FYYTiIxHMbSWSCTKBdGBwDBcsrYC1GZTFF9QVHy9rmwFKjJFNptFEKT8HIPBYEv+fQE7Rqkptre3I5GIRguP8kZUpjcMvsOdfyOpJ9YngAej0Sjt2T7o93Y6y\/9321O9VSnPKbhcrtvtTiQS5dft1UxRscVWyw4qblnRBRVfL5Z1jTlFJpOBYbi8qlEUtdlsHdU9AaDlgSmSyaRvzgVLuLB0QGN7ybbyCOPtv66odw5FuTLaVsOVKZiCQhAFKHKKdkdd1x3FpsBx3Ov1khfbyWQynU53y1qETeKx2QyILBQMkr2MTBenZbDy+Xwmk+Hx2VLtrzt8TRrqsC78cNyurvf8C8poQ91SkcvlaAVRgMIUhWVLWvVKQQ217EWdU+xCluf91pd\/5T9w0Hbk58b+11VnfgtBkIzHtyhV3W6NBzkF+dBw1NbfjatsF8LgfmFh0cNshbaDaqZooSAqKoO0RrVtgClKeP\/2bd3JU+usb5TE0r7viP\/n3Pu3u\/t2pK\/1U1y7fkUqH+i6S4\/iUJnf\/OPtW0zVZptg8Oqjgdidpnj\/9m392++E+75ZboqpZ5677HIxXcBmqdCjqdHCOudLXZpceGJ9cvTs1tYWI7XZJoApOpz1FUx38lRFTcw\/9rhFrmC6gC2g8ijp6lqoe5OL3rudFJiiYyEIwmuzjx9\/tdwR66xv+H74xIRM3hujPFXnU3RscgHL3pFpzpDzO6pt4wjvU6GjPbCCJgkwRWeytbWl5PGnn\/7nipqYeuY5m1rTG5rI087R7MDkQiI\/H41GyTmj5KQP\/eyPy0dtJpYeM1nlO1OJ7QaYotMgCGLR51OcPbe07zvljgj3fdPw5ls90DdRDP1s7k5LLuS6U4UpjORE0sJMEPX4cXPgYKGchpln\/Jd74a8FTJGv9JxRpojH4wiXO\/nsixVTiaV935ENDN6+1Wvd6rXeIUYmF50w4UI9frzidMzt7e1oNDph13I4kFR1VuM4alt5RDt5\/A\/rKy2tMQbYtabIZrPxeHzKZBayOZyREatW26ojN0Y8Hhdzucb+15f3fruiJiaffVHJ4\/fkXS113Es6YddaLv8D46bQOI5iGEZd1K2tLQzDyDnpEAR9uHmnqUpiml1lCjJPnHNNy3h80dBFY\/\/rnkM\/Wt77bcngEFO91DiOR6NRakcEHvk+cv7Cos\/XMx0TJdRqip18\/Id9dT9F8mLwHXbP1LqeFY7jyWSy2+cR7wZTpFKpUDCoEgh5IyP6N96c+vFTxV0AtpdeWQ0stLxiaSEIApubh6FL5uOvVnNEcP93tSdOorC42\/\/NqKnJFAuLHoP7hR3LGuS6Uzw+W2E4UbEndTx0QG9E2l0vHUWvmmJrayscDhsQGQRBmlOnbUd+Htz\/3YpNUcMf27HvahzHw+EwLBRBEMQZHrE++8LaQ9+icIRKIOzStTbqgt4US8uzBtexnby+GOWNkLmARgvLNGeKl8OYibMc4X2IjLcDVdM59JIpyO4ku1bHu3RJcfbc+PO\/9B84WLEdFkLx2x2aSpfL5Rbsk2I2Z+o\/T67seZiiSLMHD2tOnd4ljiChMcUf1le0k8d3UhMlIkin02arUiI\/b\/AdJjfwxPo4nN4fhyumB0yRzWY9NpuQzZEMDJqPvzp78HC1L+qScP3kn7yWNj7tKZVKWdUa8r5e\/rnztiM\/pyjYyp6H7Uf\/VTR00a7Vlawh0vNQmeJmbF1jfWMnNTFTpRsik8m4Z2xCeEjvOeKJ9ZFJRzurpbPoDVNIoUu06UP5xISxwSEIgsbGxpp\/jFg6nXa73SWLZSj7X\/M9eoi2JIFHvm948y0hm7Pkv9xL039rp6op7rx\/W2k42T4juK7tKVnmmwyN7aVIJFKxSNls1h\/wCMXDbM5Ib\/celdADpsjn89lsVjPKq9gTQRtrD33L\/W8vi35\/oXxdnNpRnjrtOfSjGj9xZc\/D1mdf4P71E3UqFcWyrLuByqb4cPOO2vh2W6daieXnZOrB8hnZUhXNRWkul2vhE+i6gt4wRT6fx3FcNyZoTBY7E+G+b3oO\/cjw5luCkZFFrS732Z938g\/dyVQwRSaT4XAgRHNa4ziqcRy1Lj46HjrQ2jlXes8Rf8Czvb2tRgWmuScKr3tifaM8qFdHpBumZ0yRJx9eKRDOP\/Y441IgY3nvt8eP\/ovgnd+SuQOHzZ6edOzy9KEilXOKdDqdSqXIpc3MVqXeiMASLgRBo7wRmeaMTHOGlIjJf4g679BOPV9+ieGJ9QnFw+STOAiCmPZY1dbXyOPYV\/crVIKdrYEuoJdMkc\/nCYKYkMmpZeH9x6fJpjt6YUB37JV6OzgoYvbgYV3\/65zhYfL4Ej5\/eda7O7se6qK+533gOE4+eoCUiFanQF1HqvZNul+w2fUK3emS11H7y+tXw8WHjd26gajOO8L7dO4nAwu+lp5gL9Bjpsj\/VRaenx6t2JhX9jyMsDnkBSa5fKFJrW6mh6IYp9NZ46KEgGLqM0UJGIZpHEcraqKwqmXJOKvr2h6RmF3+d8pkMip0VIScafkTrnuA3jNFPp8nCMJtMleUheHNt0D+32m0xRT21f1qVFDQwazPZvI+Tb6lMJyoNlkFx\/HAgo+R51N2OD1pChK3yTz1zHPFmvA9esiq1rSvMgGN0ZQpotGoerx0Xpbr2h6FZrj4wo8gCJMFGQ8dsK08otHCTZd519HDpsjn85ddrslj\/14YepAOj4Begw6kKVPE43GVpb+kt7LiMpY4jsuUHJH0wq6aB9EqhGLp4o0NxhVQS6xupDUalP6Uvs7yrNf28q\/XWd+wvfTKFbobhQGM0GJTUCyNvb29vboWaubjdi18oUipM8Y+uc+4CKgj9sl9q8PVWBfDamBBc\/IUKhCCvsbOpClTpNNpmeZM8WBHTz5ug3HM1gnH\/KLaaF3fvMe4DqrF+uY9g2W8mQ7p2PpVkHJ2LC0zRWOP8ALUwqf3MqjBNLcWFYqlJoe703yxvnnPOuWRSJEOWb0O0A5aY4qSwQ5Ay8lmszK5wuL0TAdXhWKpTKObfXf9xsd\/YVAQ1z763PvuulylkUgRMLbd8zRlimw2K4SHygc7AG0iduuWWCpVoDr7bMA4OT3K4yt1Rsf80uKNjZ3pxbj20ef+8E37jF+F6oQieH7eD64XdgnNmoIvuNh7z+zqcLa2tubm\/WKpFJZIUdO4QmuUo3o2hyNVqNVGq2N+ybd6PRT7oHl3rG\/eC8U+cC+tOeaCGr0JlkiFInhqejoSiYC\/+G6jWVNAENR7zwHtFsilqzEMM5rMMoWCnK3MFwhEEkQAi9lsNvmKGFHINDqZWouoUINtyjG\/VB5qg1mtM2r0JtIID+6JkEqNJvNCMBgOh9PpNEgbdzNNmSKfz4NOrE6DfC4GCemR+fn52dnZYDAYiURCodDs7Oz8\/Dz2dVKpVGEvYARAOc2aAgAA7AaAKQAAAD3AFAAAgB5gCgAAQA8wBQAAoAeYAgAA0ANMAQAA6AGmAAAA9ABTAAAAev4f4irBN\/0sK0MAAAAASUVORK5CYII=\" alt=\"\" \/><\/p>\n<p>The browser sends the HTTP request to the XPage and receives the response. The XPage queries the MemCache for data; if the data is not in the cache, the proxy queries the data storage (the domino database) and caches the result. The XPage has to parse the JSON data only, and this boosts the performance in my test environment for about 250-300%.<\/p>\n<p>By &#8222;stealing&#8220; the session cookie, the database query to the backend database will be done in the context of the user; the security for domino databases is not influenced.<\/p>\n<p>In my solution, I am using Apache 2.2 as reverse proxy. The following modules are additionally enabled for this solution:<\/p>\n<ul>\n<li>cache_module<\/li>\n<li>deflate_module<\/li>\n<li>expires_module<\/li>\n<li>headers_module<\/li>\n<li>mem_cache_module<\/li>\n<li>proxy_module<\/li>\n<li>proxy_http_module<\/li>\n<li>rewrite_module<\/li>\n<li>setenvif_module<\/li>\n<\/ul>\n<p>The virtual host configuration looks like this:<\/p>\n<pre>&lt;VirtualHost *:8080&gt;\r\n\u00a0ServerName localhost\r\n\r\n\u00a0# Enable reverseproxy\r\n\u00a0ProxyRequests Off\r\n\u00a0ProxyPreserveHost On\r\n\u00a0&lt;Proxy *&gt;\r\n\u00a0 AddDefaultCharset off\r\n\u00a0 Order allow,deny\r\n\u00a0 Allow from all\r\n\u00a0&lt;\/Proxy&gt;\r\n\r\n\u00a0# Proxy config for Domino server\r\n\u00a0# \r\n\u00a0ProxyPass \/ http:\/\/localhost:80\/\r\n\u00a0ProxyPassReverse \/ http:\/\/localhost:80\/\r\n\r\n\u00a0 # prevent max-age calculation from Last-Modified\r\n\u00a0 # prevent If-Modified-Since requests\r\n\u00a0 # reduces the number of requests that hit the server\r\n\u00a0&lt;LocationMatch \"\/.*$\"&gt;\r\n\u00a0 Header unset Last-Modified\r\n\u00a0 Header unset ETag\r\n\u00a0 Header unset HTTP_CACHE_CONTROL\r\n\u00a0&lt;\/LocationMatch&gt;\r\n\r\n\u00a0# MemCache Config\r\n\u00a0# \r\n\u00a0CacheEnable mem \/\r\n\u00a0CacheEnable mem http:\/\/\r\n\r\n\u00a0# Cache-Size 80 MB\r\n\u00a0MCacheSize 81920\r\n\u00a0MCacheMaxObjectCount 8192\r\n\r\n\u00a0# Min Obj. Size 1 Byte\r\n\u00a0MCacheMinObjectSize 1\r\n\r\n\u00a0# Max Obj. Size 1 MB\r\n\u00a0MCacheMaxObjectSize 1000000\r\n\r\n\u00a0# cache for 60 seconds by default\r\n\u00a0CacheDefaultExpire 60\r\n\r\n\u00a0# FORCE caching for all documents (without Cache-Control: no-cache)\r\n\u00a0CacheIgnoreNoLastMod On\r\n\r\n\u00a0# force caching for all requests\r\n\u00a0# ignore client side Cache-Control header\r\n\u00a0CacheIgnoreCacheControl On\r\n\u00a0# don't add Set-Cookie header to cache\r\n\u00a0CacheIgnoreHeaders Set-Cookie\r\n\r\n\u00a0# Add expires headers for images, css &amp; js files\r\n\u00a0# reduces the number of requests that hit the server\r\n\u00a0ExpiresActive On\r\n\u00a0ExpiresByType domino\/json A600\r\n\r\n&lt;\/VirtualHost&gt;<\/pre>\n<p>As you can see, the proxy runs on port 8080, and I have added a special content type &#8222;<em>domino\/json<\/em>&#8222;. This makes it easier to identify the relevant data.<\/p>\n<p>This is the XPage the user accesses:<\/p>\n<pre>&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n&lt;xp:view xmlns:xp=\"http:\/\/www.ibm.com\/xsp\/core\"\r\n  xmlns:xc=\"http:\/\/www.ibm.com\/xsp\/custom\"&gt;\r\n\r\n\u00a0\u00a0 \u00a0&lt;xp:pager layout=\"Previous Group Next\" partialRefresh=\"true\"\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0id=\"pager1\" for=\"repeat1\"&gt;\r\n\u00a0\u00a0 \u00a0&lt;\/xp:pager&gt;\r\n\r\n\u00a0\u00a0 \u00a0&lt;xp:inputText id=\"inputSearch\" value=\"#{sessionScope.searchFor}\" \/&gt;\r\n\r\n\u00a0\u00a0 \u00a0&lt;xp:button value=\"Label\" id=\"button1\"&gt;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&lt;xp:eventHandler event=\"onclick\" submit=\"true\"\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0refreshMode=\"complete\"&gt;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&lt;\/xp:eventHandler&gt;\r\n\u00a0\u00a0 \u00a0&lt;\/xp:button&gt;\r\n\r\n\u00a0\u00a0 \u00a0&lt;xp:repeat id=\"repeat1\" rows=\"30\" var=\"rowData\"&gt;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&lt;xp:this.value&gt;&lt;![CDATA[#{javascript:\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0importPackage( ch.hasselba.xpages.util );\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0var sessionId = null;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0try{\r\n\u00a0\u00a0 \u00a0 \u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0sessionId = cookie.get(\"DomAuthSessId\").getValue();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}catch(e){}\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0var url = \"http:\/\/localhost:8080\/Data.nsf\/DoSearch?OpenAgent\";\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0url += \"&amp;sessionId=\" + sessionId;\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if( sessionScope.get(\"searchFor\") !== null ){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if( sessionScope.get(\"searchFor\") !== \"\" )\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0url += \"&amp;search=\"; \r\n                    url += java.net.URLEncoder.encode(sessionScope.get(\"searchFor\"),\"UTF-8\");\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0var data = ch.hasselba.xpages.util.URLReader.read( url, sessionId );\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0var parsed = null;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0try{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0 \u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0parsed = fromJson(data).data;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}catch(e){}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0parsed\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}]]&gt;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&lt;\/xp:this.value&gt;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&lt;xp:text escape=\"true\" id=\"computedField1\" value=\"#{javascript:rowData.LastName}\"&gt;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&lt;\/xp:text&gt;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&amp;#160;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&lt;xp:text escape=\"true\" id=\"computedField2\" value=\"#{javascript:rowData.FirstName}\"&gt;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&lt;\/xp:text&gt;\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0&lt;xp:br \/&gt;\r\n\u00a0\u00a0 \u00a0&lt;\/xp:repeat&gt;\r\n\r\n&lt;\/xp:view&gt;<\/pre>\n<p>The Java class used is really simple, I know there are better ways to do a Http request, but this is a proof of concept.<\/p>\n<pre>package ch.hasselba.xpages.util;\r\n\r\nimport java.io.BufferedReader;\r\nimport java.io.InputStreamReader;\r\nimport java.net.URL;\r\nimport java.net.URLConnection;\r\n\r\n\/**\r\n\u00a0* URLReader\r\n\u00a0* performs a HTTP request\r\n\u00a0* \r\n\u00a0* @author Sven Hasselbach\r\n\u00a0* @category URL\r\n\u00a0* @category Proxy\r\n\u00a0* @version 0.2\r\n\u00a0*\/\r\npublic class URLReader {\r\n\r\n\u00a0\u00a0 \u00a0\/\/ Constants\r\n\u00a0\u00a0 \u00a0private final static String PROPERTY_COOKIE_NAME = \"Cookie\";\r\n\u00a0\u00a0 \u00a0private final static String PROPERTY_DOMAUTHSESSID_VALUE = \"DomAuthSessId=\";\r\n\r\n\u00a0\u00a0 \u00a0\/**\r\n\u00a0\u00a0 \u00a0 * reads data from a given URL\r\n\u00a0\u00a0 \u00a0 * \r\n\u00a0\u00a0 \u00a0 * @param pURL URL to load data from\r\n\u00a0\u00a0 \u00a0 * @param pSessionId session data for doing a request in the current user context\r\n\u00a0\u00a0 \u00a0 * @return String containg the result of the http request\r\n\u00a0\u00a0 \u00a0 * @author Sven Hasselbach\r\n\u00a0\u00a0 \u00a0 * @category URL\r\n\u00a0\u00a0 \u00a0 * @category Proxy\r\n\u00a0\u00a0 \u00a0 * @version 0.2\r\n\u00a0\u00a0 \u00a0 *\/\r\n\u00a0\u00a0 \u00a0public static String read( final String pURL, final String pSessionId ){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0String data = null;\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0try{\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ init the URL connection\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0URL url = new URL( pURL );\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0URLConnection uc = url.openConnection();\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ \"steal\" the original user session cookie\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0if( !(\"\".equals(pSessionId)))\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0uc.setRequestProperty ( PROPERTY_COOKIE_NAME ,\r\n                      \u00a0PROPERTY_DOMAUTHSESSID_VALUE + pSessionId);\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ do the HTTP request\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0BufferedReader in = new BufferedReader( \r\n               new InputStreamReader( uc.getInputStream() ));\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\/\/ process the data returned \r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0StringBuffer strBuf = new StringBuffer();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0String tmpStr = \"\";\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0while((tmpStr = in.readLine()) != null ){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0strBuf.append( tmpStr );\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0data = strBuf.toString();\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}catch(Exception e){\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0e.printStackTrace();\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0}\r\n\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0return data;\r\n\u00a0\u00a0 \u00a0}\r\n}<\/pre>\n<p>And here comes the JSON handler, a simple Lotus Script agent:<\/p>\n<pre>Sub Initialize\r\n\u00a0\u00a0 \u00a0Dim session As New NotesSession\r\n\u00a0\u00a0 \u00a0Dim db As NotesDatabase\r\n\u00a0\u00a0 \u00a0Dim dc As NotesDocumentCollection\r\n\u00a0\u00a0 \u00a0Dim doc As NotesDocument\r\n\u00a0\u00a0 \u00a0Dim isFirst As Boolean\r\n\u00a0\u00a0 \u00a0Dim contextDoc As NotesDocument\r\n\u00a0\u00a0 \u00a0Dim hlp\r\n\u00a0\u00a0 \u00a0Set contextDoc = session.Documentcontext\r\n\u00a0\u00a0 \u00a0Set db = session.Currentdatabase\r\n\r\n\u00a0\u00a0 \u00a0' get the search string from the URL or use the default search\r\n\u00a0\u00a0 \u00a0hlp = Split( contextDoc.QUERY_STRING_DECODED(0), \"search=\" )\r\n\u00a0\u00a0 \u00a0If UBound( hlp ) = 0 Then\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0Set dc = db.Ftsearch(\"[FirstNAME] CONTAINS AARON\", 0)\r\n\u00a0\u00a0 \u00a0Else\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0Set dc = db.Ftsearch(hlp(1), 0)\r\n\u00a0\u00a0 \u00a0End If\r\n\r\n\u00a0\u00a0 \u00a0' create the JSON output\u00a0\u00a0 \u00a0\r\n\u00a0\u00a0 \u00a0isFirst = true\r\n\u00a0\u00a0 \u00a0Set doc = dc.Getfirstdocument()\r\n\r\n\u00a0\u00a0 \u00a0' special content type domino\/json\r\n\u00a0\u00a0 \u00a0Print |Content-type: domino\/json|\r\n\u00a0\u00a0 \u00a0Print\r\n\u00a0\u00a0 \u00a0Print |{\"data\":[|\r\n\u00a0\u00a0 \u00a0While Not doc Is Nothing\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0If Not isFirst Then Print \",\"\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0  Print |{\"LastName\":\"| &amp; doc.LastName(0) &amp; _\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0|\",\"FirstName\":\"| &amp; doc.FirstName(0) &amp; |\"}|\r\n\u00a0       isFirst = False\r\n\u00a0\u00a0 \u00a0\u00a0\u00a0 \u00a0Set doc = dc.Getnextdocument( doc )\r\n\u00a0\u00a0 \u00a0Wend\r\n\r\n\u00a0\u00a0 \u00a0Print |]}|\r\n\r\nEnd Sub<\/pre>\n<p>In the next days I will provide a sample database and add more details. A database with 300k test datasets will be added too.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>During the last months I worked on a high performance XPages application used by a lot of end users.\u00a0 To get a better data throughput, I decided to use a reverse proxy for load balancing, caching of ressources, SSL connections &hellip; <a href=\"https:\/\/hasselba.ch\/blog\/?p=913\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[89,9,20,27,76,81,74],"tags":[7,31,4,80,87,16,32,12,3],"class_list":["post-913","post","type-post","status-publish","format-standard","hentry","category-java","category-javascript","category-lotus-script","category-performance","category-ssjs","category-web","category-xpages","tag-domino","tag-java","tag-js","tag-lotus-script","tag-performance","tag-server","tag-tipp","tag-web","tag-xpages"],"_links":{"self":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/913","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=913"}],"version-history":[{"count":13,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/913\/revisions"}],"predecessor-version":[{"id":926,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=\/wp\/v2\/posts\/913\/revisions\/926"}],"wp:attachment":[{"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=913"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=913"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hasselba.ch\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=913"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}