{"id":211937,"date":"2014-02-09T14:03:03","date_gmt":"2014-02-09T10:03:03","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=211937"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=211937","title":{"rendered":"<span class=\"post_title\">\u041f\u0440\u043e\u0441\u0442\u043e\u0439 \u0432\u044b\u0437\u043e\u0432 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0432 \u043e\u0434\u043d\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445<\/span>"},"content":{"rendered":"<div class=\"content html_format\">   \t\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435, \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u0438\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c \u0432 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438<br \/>  \u043a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u0432 \u043e\u0434\u043d\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 <br \/>  \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u044e \u043d\u0430 Java.<\/p>\n<p>  \u0421\u043e\u043a\u0440\u0430\u0449\u0451\u043d\u043d\u043e, \u044f \u043d\u0430\u0437\u044b\u0432\u0430\u044e \u044d\u0442\u043e\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 \u00abJson Remote Service Procedure Call\u00bb \u2014 JRSPC.<br \/>  (\u041d\u0435 \u043e\u0447\u0435\u043d\u044c \u0431\u043b\u0430\u0433\u043e\u0437\u0432\u0443\u0447\u043d\u043e, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043d\u043e \u0438\u0437 \u043f\u0435\u0441\u043d\u0438 \u0441\u043b\u043e\u0432\u0430 \u043d\u0435 \u0432\u044b\u043a\u0438\u043d\u0435\u0448\u044c.)<\/p>\n<p>  \u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 jrspc \u2014 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043b\u043e\u0451\u0432<br \/>  \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0447\u0442\u043e \u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u0434\u0430,<br \/>  \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0435\u0433\u043e \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433, \u0438 \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043e\u043a.<\/p>\n<p>  \u0426\u0435\u043d\u0430 \u0437\u0430 \u044d\u0442\u043e \u2014 \u0437\u0430\u043c\u0435\u043d\u0430 \u043d\u0430\u0431\u043e\u0440\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430\u0445, <br \/>  \u043d\u0430 \u043e\u0434\u0438\u043d \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u2014 \u043e\u0431\u044a\u0435\u043a\u0442 Json, \u0447\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u0442 \u043a\u043e\u0434 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430\u0445.<\/p>\n<p>  \u0422.\u0435, \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0432\u043c\u0435\u0441\u0442\u043e: <code>int plus(int a, int, b){return a + b;};<\/code>, <br \/>  \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u0443\u0434\u0435\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c: <code>int plus(JSONObject p){return p.optInt(&quot;a&quot;) +  p.optInt(&quot;b&quot;, &quot;4&quot;);};<\/code>,<\/p>\n<p>  \u0430 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435, \u0432\u043c\u0435\u0441\u0442\u043e: <code>PlusService.plus(1, 2, callbacks);<\/code>,<br \/>  \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u0443\u0434\u0435\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c: <code>Server.call(&quot;plusService&quot;, &quot;plus&quot;, {b: 2, a: 1}, callbacks);<\/code>.<\/p>\n<p>  \u041e\u0434\u043d\u0430\u043a\u043e, \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u0432 \u044d\u0442\u0443 \u0446\u0435\u043d\u0443, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438\u0437 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438<br \/>  \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0438\u0445 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435, <br \/>  \u0430 \u0442\u0430\u043a\u0436\u0435, \u0441\u043c\u043e\u0436\u0435\u043c \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043e\u043a, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u043c\u0435\u0441\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432,<br \/>  \u0438 \u0441\u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e ( p.optInt(\u00abb\u00bb, \u00ab4\u00bb) ). <\/p>\n<p>  <a name=\"habracut\"><\/a>  <\/p>\n<h3>\u041a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442<\/h3>\n<p>  \u041d\u0430 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442\u043d\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435, jrspc \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 json-rpc, \u0441 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c <br \/>  \u0432 \u0432\u044b\u0437\u043e\u0432\u0435 \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u0435\u0442\u043e\u0434, \u043d\u043e \u0438 \u0441\u0435\u0440\u0432\u0438\u0441. <br \/>  \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u0442\u0430\u043a\u043e\u0439 json-rpc \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043d\u0430\u0437\u0432\u0430\u0442\u044c json-rspc (s-service).<\/p>\n<p>  \u0415\u0441\u043b\u0438 \u0431\u044b \u043d\u0430 \u043d\u0435\u0433\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043b\u0430 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f, \u0442\u043e \u043e\u043d\u0430 \u0431\u044b\u043b\u0430 \u0431\u044b \u043f\u043e\u0445\u043e\u0436\u0430 \u043d\u0430<br \/>  \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044e <a href=\"http:\/\/www.jsonrpc.org\/specification\">json-rpc 2.0<\/a>, \u0437\u0430 \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u0430<br \/>  \u0431\u044b\u043b\u043e \u0431\u044b \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u043f\u043e\u043b\u0435 \u00abservice\u00bb, \u0430 \u043f\u043e\u043b\u0435 \u00abid\u00bb \u2014 \u0431\u044b\u043b\u043e \u0431\u044b \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c, \u0438 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u2014 \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u0435\u043d errorCode.<\/p>\n<p>  \u0414\u043b\u044f \u0434\u0435\u043c\u043e\u043d\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u044f \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u043f\u0440\u043e\u0441\u0442\u043e\u0435 <a href=\"http:\/\/94.127.68.13\/jrspc\/demo\/application.html\">\u0434\u0435\u043c\u043e-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435<\/a>, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f<br \/>  \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u043b\u043e\u0433\u0438\u043d\u0430, \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043f\u0440\u0430\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<h3>\u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/h3>\n<p>  \u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u2014 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u043d\u0430 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0435 <a href=\"http:\/\/angularjs.org\/\">AngularJS<\/a>. <\/p>\n<p>  (\u0421\u0447\u0438\u0442\u0430\u044e \u0441\u0432\u043e\u0438\u043c \u0434\u043e\u043b\u0433\u043e\u043c \u2014 \u043f\u0440\u0435\u0434\u0443\u043f\u0440\u0435\u0434\u0438\u0442\u044c \u0442\u0435\u0445, \u043a\u0442\u043e \u0435\u0449\u0451 \u043d\u0435 \u043f\u0440\u043e\u0431\u043e\u0432\u0430\u043b \u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0430 \u043d\u0451\u043c:<br \/>   {{user.name}}, \u0410\u043d\u0433\u0443\u043b\u044f\u0440 \u2014 \u0442\u044f\u0436\u0451\u043b\u044b\u0439 \u043d\u0430\u0440\u043a\u043e\u0442\u0438\u043a! <br \/>   \u0414\u043b\u044f \u043f\u043e\u043f\u0430\u0434\u0435\u043d\u0438\u044f \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u044c \u043e\u0442 \u043d\u0435\u0433\u043e \u2014 \u0434\u043e\u0441\u0442\u0430\u0442\u0447\u043d\u043e \u0441\u043b\u043e\u0432\u0438\u0442\u044c \u043a\u0430\u0439\u0444 \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u0438\u043d \u0440\u0430\u0437.)<\/p>\n<p>  \u0414\u043b\u044f \u043e\u0444\u043e\u0440\u043c\u043b\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <a href=\"http:\/\/getbootstrap.com\/\">Bootstrap<\/a>.<\/p>\n<p>  \u0412 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u0438 \u2014 <a href=\"http:\/\/projects.spring.io\/spring-framework\/\">Spring<\/a><\/p>\n<p>  \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 json, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <code>JSONObject<\/code><br \/>  \u0438\u0437 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 <a href=\"http:\/\/json-lib.sourceforge.net\/\">json-lib<\/a>.<\/p>\n<p>  \u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0442\u0440\u0451\u0445 \u0444\u0430\u0439\u043b\u043e\u0432:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">ajax-connector.js<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">var Server = {url: &quot;http:\/\/&quot;+ document.location.host +&quot;\/jrspc\/ajax-request&quot;};  (function() { \t \tfunction getXMLHttpRequest() { \t\tif (window.XMLHttpRequest) { \t\t\treturn new XMLHttpRequest(); \t\t} else if (window.ActiveXObject) { \t\t\treturn new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;); \t\t} \t\tif(confirm(&quot;This browser not support AJAX!\\nDownload modern browser?&quot;)){ \t\t\tdocument.location = &quot;http:\/\/www.mozilla.org\/ru\/firefox\/new\/&quot;; \t\t}else{ \t\t\talert(&quot;Download modern browser for work with this application!&quot;); \t\t    throw &quot;This browser not suport Ajax!&quot;; \t\t} \t} \t \tServer.call = function(service, method, params, successCallback, errorCallback, control) { \t\tvar data = { \t\t\tservice : service, \t\t\tmethod : method, \t\t\tparams : params ? params : {} \t\t}; \t\tif (control) {control.disabled = true;} \t\tvar requestData = JSON.stringify(data); \t\tvar request = getXMLHttpRequest();  \t\trequest.onreadystatechange = function() {\t\t\t \t\t\t\/\/log(&quot;request.status=&quot;+request.status+&quot;, request.readyState=&quot;+request.readyState); \t\t\tif ((request.readyState == 4 && request.status != 200)) { \t\t\t\tprocessError(&quot;network error!&quot;, errorCallback); \t\t\t\tif (control) {control.disabled = false;} \t\t\t\treturn; \t\t\t}\t \t\t\tif (!(request.readyState == 4 && request.status == 200)) {return;}\t\t\t \t\t\t\/\/log(&quot;request.responseText=&quot;+request.responseText); \t\t\ttry { \t\t\t\tvar response = JSON.parse(request.responseText); \t\t\t\tif (response.error) { \t\t\t\t\tprocessError(response.error, errorCallback); \t\t\t\t} else { \t\t\t\t\tif (successCallback) { \t\t\t\t\t\ttry { \t\t\t\t\t\t\t\/\/log(&quot;response=&quot;+JSON.stringify(response));\t\t\t\t\t\t\t \t\t\t\t\t\t\tsuccessCallback(response.result); \t\t\t\t\t\t} catch (ex) { \t\t\t\t\t\t\terror(&quot;in ajax successCallback: &quot; + ex + &quot;, data=&quot; + data); \t\t\t\t\t\t} \t\t\t\t\t} \t\t\t\t} \t\t\t} catch (conectionError) { \t\t\t\terror(&quot;in process ajax request: &quot; + conectionError); \t\t\t}\t\t\t \t\t\tif (control) {control.disabled = false;} \t\t} \t\trequest.open(&quot;POST&quot;, Server.url, true); \t\trequest.send(requestData); \t} \t \tfunction processError(error, errorCallback){ \t\tif (errorCallback) { \t\t\ttry { \t\t\t\terrorCallback(error); \t\t\t} catch (ex) { \t\t\t\terror(&quot;in ajax errorCallback: &quot; + ex); \t\t\t} \t\t} else { \t\t\talert(error); \t\t} \t\t \t}\t })();  function error(s){if(window.console){console.error(s);}}; function log(s){if(window.console){console.log(s);}}; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443, \u0438\u043d\u043a\u0430\u043f\u0441\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u0435 <code>Server<\/code>.<br \/>  (\u041f\u0440\u0435\u0444\u0438\u043a\u0441 ajax \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c \u0435\u0433\u043e \u043e\u0442 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u043d\u043e\u0433\u043e ws-connector.js,<br \/>  \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0437\u0430\u043c\u0435\u043d\u0451\u043d, \u0431\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430 user-controller.js.)<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">user-controller.js<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"javascript\">function userController($scope){ \t\t \tvar self = $scope;\t  \tself.user = {login: &quot;&quot;, password: &quot;&quot;}; \t \tself.error = &quot;&quot;; \tself.result = &quot;\u0414\u043b\u044f \u0432\u0445\u043e\u0434\u0430 \u0438\u043b\u0438 \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 - \u0432\u0432\u0435\u0434\u0438\u0442\u0435 \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c.&quot;; \tself.loged = false;  \t \t\/** This method will called at application initialization (see last string in this file). *\/ \t \tself.trySetSessionUser = function(control){ \t\tServer.call(&quot;testUserService&quot;, &quot;getSessionUser&quot;, null,  \t\t   function(user){ \t\t\tlog(&quot;checkUser: user=&quot;+JSON.stringify(user)); \t\t\tif(!user.id){return;} \t\t\tself.user = user; \t\t\tself.loged = true; \t\t\tself.$digest();\t\t\t \t\t}, self.onError, control);\t\t \t}\t \t \t \t\/** common user methods *\/ \t \tself.registerUser = function(control){ \t\tServer.call(&quot;testUserService&quot;, &quot;registerUser&quot;, self.user,  \t\t   function(id){ \t\t\tself.user.id = id;\t\t\t \t\t\tself.onSuccess(&quot;you registered with id: &quot;+id);\t\t \t\t\tsetTimeout(function(){control.disabled = true;}, 20); \t\t}, self.onError, control);\t\t \t} \t \tself.logIn = function(control){ \t\tself.loginControl = control; \t\tServer.call(&quot;testUserService&quot;, &quot;logIn&quot;, self.user, function(user){ \t\t\tself.user = user; \t\t\tself.loged = true; \t\t\tself.onSuccess(&quot;you loged in with role: &quot;+user.role);\t \t\t\tsetTimeout(function(){control.disabled = true;}, 20); \t\t}, self.onError, control);\t\t \t} \t \t \tself.logOut = function(control){\t\t \t\tServer.call(&quot;testUserService&quot;, &quot;logOut&quot;, {}, function(){ \t\t\tself.user.role = &quot;&quot;; \t\t\tself.user.city = &quot;&quot;; \t\t\tself.loged = false; \t\t\tself.onSuccess(&quot;you loged out&quot;); \t\t\tsetTimeout(function(){ \t\t\t\tcontrol.disabled = true; \t\t\t\tif(self.loginControl){self.loginControl.disabled = false;} \t\t\t}, 20); \t\t}, self.onError, control);\t \t}\t\t \t \tself.getUsersCount = function(control){ \t\tServer.call(&quot;testAdminService&quot;, &quot;getUsersCount&quot;, null, function(count){ \t\t\tself.onSuccess(&quot;users count: &quot;+count);\t\t\t \t\t}, self.onError, control);\t\t\t \t}\t \t \tself.changeCity = function(control){ \t\tServer.call(&quot;testUserService&quot;, &quot;changeCity&quot;, {city: self.user.city}, function(){ \t\t\tself.onSuccess(&quot;users city changed to: &quot;+self.user.city);\t\t\t \t\t}, self.onError, control);\t\t\t \t}\t\t \t \t \t\/** admin methods *\/ \t \tself.grantRole = function(control){\t\t \t\tServer.call(&quot;testAdminService&quot;, &quot;grantRole&quot;, {role: self.role, userId: self.userId}, function(result){      \t\tself.onSuccess(result);\t\t \t\t}, self.onError, control);\t\t \t}\t \t\t \tself.removeUser = function(control){ \t\tServer.call(&quot;testAdminService&quot;, &quot;removeUser&quot;, {userId: self.userId}, self.onSuccess, self.onError, control);\t\t \t}\t\t \t \t\/** common callbacks *\/ \t \tself.onError = function(error){ \t\tself.error = error;\t\t \t\tself.$digest();\t\t \t} \t \tself.onSuccess = function(result){\t \t\tself.result = result; \t\tself.error = &quot;&quot;; \t\tself.$digest();\t\t \t}\t\t \t \t\/** initialization *\/ \tself.trySetSessionUser(); } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>\u0417\u0434\u0435\u0441\u044c \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0431\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0438\u043d\u043a\u0430\u043f\u0441\u0443\u043b\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 <code>userController<\/code>.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">application.html<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"html\">&lt;html x-ng-app&gt;&lt;head&gt; &lt;meta http-equiv=&quot;content-type&quot; content=&quot;text\/html; charset=UTF-8&quot; \/&gt; &lt;title&gt;JRSPC Demo application&lt;\/title&gt; &lt;link href=&quot;http:\/\/getbootstrap.com\/dist\/css\/bootstrap.css&quot; rel=&quot;stylesheet&quot;&gt; &lt;script src=&quot;http:\/\/ajax.googleapis.com\/ajax\/libs\/angularjs\/1.2.9\/angular.min.js&quot;&gt;&lt;\/script&gt; &lt;script src=&quot;ajax-connector.js&quot;&gt;&lt;\/script&gt; &lt;script src=&quot;user-controller.js&quot;&gt;&lt;\/script&gt; &lt;\/head&gt;&lt;body style=&quot;padding-left: 42px; padding-top: 12px; padding-right: 12px;&quot;               x-ng-app=&quot;jrspcTest&quot;&gt; &lt;table&gt;&lt;tr&gt;&lt;td&gt; &lt;h1 title=&quot;JSON Remote Service Procedure Call&quot; style=&quot;cursor: help;&quot;&gt; JRSPC Demo application &lt;\/h1&gt;&lt;\/td&gt;&lt;td style=&quot;padding-left: 120px;&quot; valign=&quot;middle&quot;&gt;      &lt;a href=&quot;aga&quot;&gt;related article on habrahabr.ru&lt;\/a&gt;&lt;\/td&gt;&lt;\/tr&gt;&lt;\/table&gt; &lt;div  x-ng-controller=&quot;userPanelController&quot;&gt; &lt;pre&gt;   User:         id: {{user.id}}               login: &lt;input type=&quot;text&quot; x-ng-model=&quot;user.login&quot; x-ng-disabled=&quot;loged&quot;\/&gt;                  password: &lt;input type=&quot;password&quot; x-ng-model=&quot;user.password&quot; x-ng-disabled=&quot;loged&quot;\/&gt;     from city: &lt;input type=&quot;text&quot; x-ng-model=&quot;user.city&quot;\/&gt;  &lt;input value=&quot;save&quot;                     x-ng-disabled=&quot;!loged&quot; type=&quot;button&quot;                     x-ng-click=&quot;changeCity($event.target)&quot; class=&quot;btn btn-success&quot;\/&gt;                          role: {{user.role}}                            &lt;input value=&quot;register&quot; x-ng-disabled=&quot;loged || user.id &gt; 0  || user.login=='' || user.password==''&quot;              type=&quot;button&quot; x-ng-click=&quot;registerUser($event.target)&quot; class=&quot;btn btn-primary&quot;\/&gt; &lt;input                                          value=&quot;log in&quot; x-ng-disabled=&quot;loged || user.login=='' || user.password==''&quot;              type=&quot;button&quot; x-ng-click=&quot;logIn($event.target)&quot; class=&quot;btn btn-success &quot;\/&gt; &lt;input                             value=&quot;log out&quot; x-ng-disabled=&quot;!loged&quot;              type=&quot;button&quot; x-ng-click=&quot;logOut($event.target)&quot; class=&quot;btn btn-warning&quot;\/&gt;                   If you are is admin, you also can:             &lt;input value=&quot;grant role:&quot; type=&quot;button&quot; x-ng-click=&quot;grantRole($event.target)&quot;              class=&quot;btn btn-success&quot;\/&gt; &lt;input type=&quot;text&quot; style=&quot;width: 50px;&quot;              x-ng-model=&quot;role&quot;\/&gt; to user: &lt;input type=&quot;text&quot; x-ng-model=&quot;userId&quot;               style=&quot;width: 40px;&quot;\/&gt; or &lt;input value=&quot;remove this user&quot;               type=&quot;button&quot; x-ng-click=&quot;removeUser($event.target)&quot; class=&quot;btn btn-warning&quot;\/&gt;                                           &lt;\/pre&gt; &lt;div class=&quot;alert alert-{{error == '' ? 'info':'warning'}}&quot;&gt;{{error == '' ? result : error}}&lt;\/div&gt;                     &lt;input value=&quot;get users count&quot;               type=&quot;button&quot; x-ng-click=&quot;getUsersCount($event.target)&quot; class=&quot;btn&quot;\/&gt;    &lt;\/div&gt; &lt;\/body&gt;&lt;\/html&gt; <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>\u0413\u0440\u0430\u0444\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0441 \u043b\u043e\u0433\u0438\u043a\u043e\u0439 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u043a\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432.<\/p>\n<p>  \u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, \u0432 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u043e\u0433\u043e \u043a\u043e\u0434\u0430, \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440 \u2014 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043a\u0430\u043a<br \/>  \u043e\u0431\u044a\u0435\u043a\u0442 Server, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043f\u0440\u043e\u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d url&#8217;\u043e\u043c.<\/p>\n<p>  \u0427\u0435\u0440\u0435\u0437 \u044d\u0442\u043e\u0442 \u043e\u0431\u044a\u0435\u043a\u0442, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u043a \u043b\u044e\u0431\u043e\u043c\u0443 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0443 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435<br \/>  \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043b\u044e\u0431\u044b\u0435 \u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434\u044b, \u0442\u0430\u043a\u0438\u043c \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c:<\/p>\n<p>  <code>Server.call(serviceName, mathodName, params, successCallBack, errorCallback, control);<\/code><\/p>\n<p>  \u041e\u0442\u0432\u0435\u0442\u044b \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0438 \u2014 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u043a\u043e\u043b\u043b\u0431\u044d\u043a\u0438. <\/p>\n<p>  \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0438\u0441\u0430 \u0438\u043b\u0438 \u043c\u0435\u0442\u043e\u0434\u0430 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u2014 \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u0437\u0430\u0442\u0440\u0430\u0433\u0438\u0432\u0430\u0435\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0439 \u043a\u043e\u0434,<br \/>  \u0438 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u044d\u0442\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0438 \u043c\u0435\u0442\u043e\u0434\u044b \u0441\u0440\u0430\u0437\u0443, \u043f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u043e\u043d\u0438 \u043f\u043e\u044f\u0432\u0438\u043b\u0438\u0441\u044c \u0432 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u043c \u043a\u043e\u0434\u0435.<\/p>\n<p>  \u0415\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e, \u0441\u043a\u0430\u0437\u0430\u0432 \u00ab\u043b\u044e\u0431\u043e\u043c\u0443 \u0438 \u043b\u044e\u0431\u044b\u0435\u00bb \u2014 \u044f \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0442\u043e\u0448\u0451\u043b \u043e\u0442 \u0438\u0441\u0442\u0438\u043d\u044b. <br \/>  \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u043a\u0430\u043a \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u043e\u0433\u0443\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043a\u043b\u0430\u0441\u0441\u044b, \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u044b\u0435 \u043e\u0442 <br \/>  <code>AbstractService<\/code>, \u0430 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u044b\u0435 \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e \u043c\u0435\u0442\u043e\u0434\u044b, \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0430\u043d\u043d\u043e\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u044b <code>@Remote<\/code>. <\/p>\n<p>  \u0414\u043b\u044f \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u043c\u0435\u0442\u043e\u0434\u0430\u043c \u2014 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u044f <code>@Secured(roleName)<\/code>.<br \/>  \u0422\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u0435\u0442\u043e\u0434, \u0430\u043d\u043d\u043e\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 <code>@Secured(&quot;Admin&quot;)<\/code> \u2014 \u043d\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0432\u044b\u0437\u0432\u0430\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c<br \/>  \u0441 \u0440\u043e\u043b\u044c\u044e \u00abUser\u00bb. <\/p>\n<h3>C\u0435\u0440\u0432\u0435\u0440\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c<\/h3>\n<p>  \u0412\u0435\u0441\u044c \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u044b\u0439 \u00ab\u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u00bb, \u0435\u0441\u043b\u0438 \u043c\u043e\u0436\u043d\u043e \u0442\u0430\u043a \u0432\u044b\u0440\u0430\u0437\u0438\u0442\u044c\u0441\u044f, \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043c\u0435\u043d\u044c\u0448\u0435 9 \u043a\u0431.,<br \/>  \u0438 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 \u0448\u0435\u0441\u0442\u0438 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u0434\u0432\u0430 \u0438\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u2014 \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b\u0435 \u043d\u0430\u043c \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0438   <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Remote<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">package habr.metalfire.jrspc;  import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  \/** If method NOT annotated as Remote MethodInvoker throw exception,   *  when user try to call this method from browser  **\/  @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Remote {} <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Secured<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">package habr.metalfire.jrspc;  import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  \/** If method annotated as Secured  MethodInvoker throw exception,  *  if User not in declared role.  **\/  @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Secured {     String[] value();     } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0430 \u0442\u0430\u043a\u0436\u0435<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">AbstractService<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">package habr.metalfire.jrspc;  import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;  \/** parent class for all services *\/  public abstract class AbstractService {      protected  Log log = LogFactory.getLog(this.getClass());          private User user;                      public void setUser(User user) {                    this.user = user;     }            public User getUser() {                   return user;     }        }<\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>\u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441, \u043e\u0442 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0434\u043e\u043b\u0436\u043d\u044b \u043d\u0430\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u0441\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b, \u0438<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">CommonServiceController(controller)<\/b><\/p>\n<div class=\"spoiler_text\">.  <\/p>\n<pre><code class=\"java\">package habr.metalfire.jrspc;  import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays;  import javax.servlet.http.HttpSession;  import net.sf.json.JSONObject;  import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Controller; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody;  @Controller public class CommonServiceController {      final static Log log = LogFactory.getLog(CommonServiceController.class);      @Autowired     private ApplicationContext applicationContext;          @Autowired     private HttpSession session;          @RequestMapping(value = &quot;\/ajax-request&quot;, method = RequestMethod.POST)     @ResponseBody     private String processAjaxRequest(@RequestBody String requestJson) {         \/\/log.debug(&quot;requestJson=&quot;+requestJson);             JSONObject request = JSONObject.fromObject(requestJson);         String serviceName = request.optString(&quot;service&quot;);         String methodName = request.optString(&quot;method&quot;);         JSONObject params = request.optJSONObject(&quot;params&quot;);           log.debug(&quot;request =&quot;+request);         JSONObject response = callServiceMethod(serviceName, methodName, params);         log.debug(&quot;response=&quot;+response);         return response.toString();            }          private JSONObject callServiceMethod(String serviceName, String methodName, JSONObject params) {         JSONObject response =  new JSONObject();          try {             Object serviceObject = applicationContext.getBean(serviceName);             if (serviceObject == null) {                 throw new RuntimeException(&quot;AbstractService bean with name &quot; + serviceName + &quot; not found!&quot;);             }             if (!(serviceObject instanceof AbstractService)) {                 throw new RuntimeException(&quot;Collable service \\&quot;&quot;+serviceName+&quot;\\&quot; MUST be instance of AbstractService, but not of: &quot;                         + serviceObject.getClass().getName());             }             AbstractService service = (AbstractService) serviceObject;                          User user = (User) session.getAttribute(&quot;user&quot;);             service.setUser(user);             Object result = invokeMethod(service, methodName, params);                       if(result != null){                 response.put(&quot;result&quot;, result);             } else{                 response.put(&quot;result&quot;, new JSONObject());             }                                     } catch (Throwable th) {                    response.put(&quot;error&quot;, th.getMessage());                }         return response;     }        private Object invokeMethod(AbstractService service, String methodName, JSONObject methodParams) throws Throwable {         try {                                             User user = service.getUser();             log.debug(&quot;user=&quot;+ JSONObject.fromObject(user));                         Class&lt;?&gt; ownerClass = service.getClass();             Class&lt;?&gt;[] parameterTypes = new Class[] { JSONObject.class };             Object[] arguments = new Object[] { methodParams };             Method actionMethod = ownerClass.getMethod(methodName, parameterTypes);             checkAccess(actionMethod, methodParams, user);                                Object result = actionMethod.invoke(service, arguments);                       return result == null ? new Object() : result;                     } catch (Throwable th) {             if (th instanceof InvocationTargetException) {                 th = ((InvocationTargetException) th).getTargetException();             }              if (th instanceof NoSuchMethodException) {                 th = new RuntimeException(&quot;Method \\&quot;&quot;+methodName+&quot;\\&quot; not found on class \\&quot;&quot;+service.getClass().getName()+&quot;\\&quot;!&quot;);             }                      throw th;         }     }      private void checkAccess(Method method, Object methodParams, User user) {         if (!method.isAnnotationPresent(Remote.class)) {             throw new RuntimeException(&quot;Remotely invoked method MUST be annotated as Remote!&quot;);         }                         if (method.isAnnotationPresent(Secured.class)) {             String[] roles = method.getAnnotation(Secured.class).value();                         if ( user == null || ( !Arrays.asList(roles).contains(user.getRole()) && !&quot;Admin&quot;.equals(user.getRole()) ) ) {                 String message = &quot;User not in role: &quot;                             + StringUtils.arrayToDelimitedString(roles, &quot; or &quot;)                                                  + &quot;, required for invocation of \\&quot;&quot;                             + method.getName() + &quot;\\&quot; method !&quot;;                                throw new RuntimeException(message);             }         }              }       }<\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>\u0412 \u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434 <code>processAjaxRequest<\/code> \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0438\u0437 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432\u043e\u0433\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>Service<\/code>. <br \/>  \u0414\u0430\u043b\u0435\u0435, \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0442\u0441\u044f \u0432 <code>JSONObject<\/code>, \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442, \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u0441\u0435\u0440\u0432\u0438\u0441\u0430,<br \/>  \u0438 \u043d\u0430 \u043d\u0451\u043c, \u043f\u043e\u0441\u043b\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430, \u0440\u0435\u0444\u043b\u0435\u043a\u0442\u0438\u0432\u043d\u043e, \u0432\u044b\u0437\u0432\u0430\u0435\u0442\u0441\u044f \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434.<br \/>  \u0412 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u043e\u043c \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e \u043c\u0435\u0442\u043e\u0434\u0435 \u2014 \u0432\u0441\u0435\u0433\u0434\u0430 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440, \u0442\u0438\u043f\u0430 <code>JSONObject<\/code>. <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">User (entity)<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">package habr.metalfire.jrspc;  public class User{                  public static enum Role { User, Admin, Supervisor }          private Long id;     private String login;         private String password;     private String city;           private String role;          public User() { }          public Long getId() {return id;}     public void setId(Long id) {this.id = id;}          public String getLogin() {return login;}     public void setLogin(String login) {this.login = login;}          public String getPassword() { return password;}     public void setPassword(String password) {this.password = password; }          public String getRole() {return role;}     public void setRole(String role) {this.role = role;}      public String getCity() { return city;}     public void setCity(String city) {this.city = city;}         }<\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>\u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435, \u0438<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">UserManager(component)<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">package habr.metalfire.jrspc;  import java.util.HashMap; import java.util.concurrent.atomic.AtomicLong;  import org.springframework.stereotype.Component;   @Component public class UserManager {              private static HashMap&lt;Long, User&gt; idUsersMap = new HashMap&lt;Long, User&gt;();          private static HashMap&lt;String, Long&gt; loginIdMap = new HashMap&lt;String, Long&gt;();            private AtomicLong nextId = new AtomicLong(0);              public User findById(Long id) {                return idUsersMap.get(id);     }       public User findByLogin(String login) {         Long id = loginIdMap.get(login);         if(id == null){return null;}         return  findById(id);     }            public boolean saveUser(User user) {         user.setId(nextId.addAndGet(1));         idUsersMap.put(user.getId(), user);         loginIdMap.put(user.getLogin(), user.getId());         return false;     }      public void updateUser(User user) {        idUsersMap.put(user.getId(), user);            }      public void deleteUser(User user) {        idUsersMap.remove(user.getId());               loginIdMap.remove(user.getLogin());                }              public Integer getUsersCount() {        return idUsersMap.size();       }          }<\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>\u0434\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u0441 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u043c <code>User<\/code> (\u0442\u0435\u0441\u0442\u043e\u0432\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0441 \u044d\u043c\u0443\u043b\u044f\u0446\u0438\u0435\u0439 \u043f\u0435\u0440\u0441\u0438\u0441\u0442\u0435\u043d\u0442\u043d\u043e\u0441\u0442\u0438). <\/p>\n<p>  \u0411\u0438\u0437\u043d\u0435\u0441-\u043b\u043e\u0433\u0438\u043a\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u0430 \u0432 \u0434\u0432\u0443\u0445 \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u0445:   <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">TestUserService(component)<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">package habr.metalfire.jrspc;  import javax.servlet.http.HttpSession;  import net.sf.json.JSONObject;  import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component;  @Component @Scope(&quot;session&quot;) public class TestUserService extends AbstractService{      @Autowired     UserManager userManager;              @Autowired     private HttpSession session;                     @Remote     public Long registerUser(JSONObject userJson){                 User user = (User) JSONObject.toBean(userJson, User.class);                 if(userManager.findByLogin(user.getLogin()) != null){           throw new RuntimeException(&quot;User with login &quot;+user.getLogin()+&quot; already registered!&quot;);         }                    if(userManager.getUsersCount() == 0){           user.setRole(User.Role.Admin.name());         }else{           user.setRole(User.Role.User.name());         }          userManager.saveUser(user);          return user.getId();     }              @Remote     public User logIn(JSONObject params){                String error = &quot;Unknown combination of login and password!&quot;;          User user = userManager.findByLogin(params.optString(&quot;login&quot;));          if(user == null){ throw new RuntimeException(error);}          if(!user.getPassword().equals(params.optString(&quot;password&quot;))){ throw new RuntimeException(error);}           session.setAttribute(&quot;user&quot;, user);          return user;     }               @Secured(&quot;User&quot;)      @Remote     public void logOut(JSONObject params){                 session.removeAttribute(&quot;user&quot;);     }                     @Secured(&quot;User&quot;)        @Remote     public void changeCity(JSONObject params){            String city = params.optString(&quot;city&quot;);                         User user = getUser();         user.setCity(city);                         userManager.updateUser(user);     }                  @Remote     public User getSessionUser(JSONObject params){                    try{            return (User) session.getAttribute(&quot;user&quot;);         }catch(Throwable th){log.debug(&quot;in checkUser: &quot;+th);}         return null;     }          }<\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>\u0441\u0435\u0440\u0432\u0438\u0441 \u0441 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438, \u043b\u043e\u0433\u0438\u043d\u0430, \u0438 \u0440\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445, \u0438<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">TestAdminService(component)<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">package habr.metalfire.jrspc;  import net.sf.json.JSONObject;  import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component;  @Component @Scope(&quot;session&quot;) public class TestAdminService extends AbstractService{      @Autowired     UserManager userManager;                         private User checkUser(Long userId){         User user = userManager.findById(userId);         if(user == null){throw new RuntimeException(&quot;User with id &quot;+userId+&quot; not found!&quot;);}         return user;             }         @Secured(&quot;Admin&quot;)        @Remote     public String grantRole(JSONObject params){             Long userId = params.optLong(&quot;userId&quot;);           User user = userManager.findById(userId);         String role = params.optString(&quot;role&quot;);                      if(user.getId().equals(getUser().getId())){throw new RuntimeException(&quot;Admin role cannot be revoked!&quot;);}         user.setRole(role);          userManager.updateUser(user);         return &quot;role &quot;+role+&quot; granted to user &quot;+userId;             }               @Secured(&quot;Admin&quot;)        @Remote     public String removeUser(JSONObject params){          User user = checkUser(params.optLong(&quot;userId&quot;));         if(&quot;Admin&quot;.equals(user.getRole())){throw new RuntimeException(&quot;Admin cannot be removed!&quot;);}         userManager.deleteUser(user);         return &quot;User &quot;+user.getId()+&quot; removed.&quot;;             }               @Remote     public Integer getUsersCount(JSONObject params){                 return userManager.getUsersCount();     }         }<\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>\u0441\u0435\u0440\u0432\u0438\u0441 \u0441 \u043c\u0435\u0442\u043e\u0434\u0430\u043c\u0438 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f \u044e\u0437\u0435\u0440\u0430, \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0435\u0433\u043e \u0440\u043e\u043b\u0438.<\/p>\n<p>  \u041a\u043e\u0434 \u043d\u0430\u043f\u0438\u0441\u0430\u043d \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e self-explanatory, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u043d\u0451\u043c \u0431\u0443\u0434\u0435\u0442 \u043b\u0435\u0433\u043a\u043e.<\/p>\n<p>  <a href=\"https:\/\/github.com\/janson13\/demo-jrspc\">\u041a\u043e\u0434 \u0434\u0435\u043c\u043e-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u0430 \u0413\u0438\u0442\u0445\u0430\u0431\u0435<\/a><\/p>\n<h3>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435?<\/h3>\n<p>  \u0412 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0442\u0430\u0442\u044c\u0435, \u044f \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u044e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c, \u043a\u0430\u043a, \u043d\u0430 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u043e\u0433\u043e \u043f\u043e\u0434\u0445\u043e\u0434\u0430,<br \/>  \u043c\u043e\u0436\u043d\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0435 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u044b, <br \/>  \u0438 \u043a\u0430\u043a, \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0438\u0437 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u043d\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u0434\u043e\u0441\u0442\u0430\u0442\u044c \u0441\u0435\u0441\u0441\u0438\u044e http.<\/p>\n<div class=\"clear\"><\/div>\n<\/p><\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"http:\/\/habrahabr.ru\/post\/211937\/\"> http:\/\/habrahabr.ru\/post\/211937\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div class=\"content html_format\">   \t\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435, \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u0441\u0432\u043e\u0438\u043c \u043f\u043e\u0434\u0445\u043e\u0434\u043e\u043c \u0432 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438<br \/>  \u043a\u043b\u0438\u0435\u043d\u0442-\u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0433\u043e \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u0432 \u043e\u0434\u043d\u043e\u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u044b\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u043d\u044b\u0445 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u0445 <br \/>  \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043d\u043e\u0439 \u0447\u0430\u0441\u0442\u044c\u044e \u043d\u0430 Java.<\/p>\n<p>  \u0421\u043e\u043a\u0440\u0430\u0449\u0451\u043d\u043d\u043e, \u044f \u043d\u0430\u0437\u044b\u0432\u0430\u044e \u044d\u0442\u043e\u0442 \u043f\u043e\u0434\u0445\u043e\u0434 \u00abJson Remote Service Procedure Call\u00bb \u2014 JRSPC.<br \/>  (\u041d\u0435 \u043e\u0447\u0435\u043d\u044c \u0431\u043b\u0430\u0433\u043e\u0437\u0432\u0443\u0447\u043d\u043e, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043d\u043e \u0438\u0437 \u043f\u0435\u0441\u043d\u0438 \u0441\u043b\u043e\u0432\u0430 \u043d\u0435 \u0432\u044b\u043a\u0438\u043d\u0435\u0448\u044c.)<\/p>\n<p>  \u041f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u0435 jrspc \u2014 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043b\u043e\u0451\u0432<br \/>  \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0447\u0442\u043e \u0441\u043e\u043a\u0440\u0430\u0449\u0430\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u0434\u0430,<br \/>  \u0443\u043f\u0440\u043e\u0449\u0430\u0435\u0442 \u0435\u0433\u043e \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433, \u0438 \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u043e\u0448\u0438\u0431\u043e\u043a.<\/p>\n<p>  \u0426\u0435\u043d\u0430 \u0437\u0430 \u044d\u0442\u043e \u2014 \u0437\u0430\u043c\u0435\u043d\u0430 \u043d\u0430\u0431\u043e\u0440\u0430 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430\u0445, <br \/>  \u043d\u0430 \u043e\u0434\u0438\u043d \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u2014 \u043e\u0431\u044a\u0435\u043a\u0442 Json, \u0447\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u044f\u0435\u0442 \u043a\u043e\u0434 \u0432 \u0441\u0435\u0440\u0432\u0438\u0441\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u0430\u0445.<\/p>\n<p>  \u0422.\u0435, \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435, \u0432\u043c\u0435\u0441\u0442\u043e: <code>int plus(int a, int, b){return a + b;};<\/code>, <br \/>  \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u0443\u0434\u0435\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c: <code>int plus(JSONObject p){return p.optInt(&quot;a&quot;) +  p.optInt(&quot;b&quot;, &quot;4&quot;);};<\/code>,<\/p>\n<p>  \u0430 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435, \u0432\u043c\u0435\u0441\u0442\u043e: <code>PlusService.plus(1, 2, callbacks);<\/code>,<br \/>  \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u0443\u0434\u0435\u043c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c: <code>Server.call(&quot;plusService&quot;, &quot;plus&quot;, {b: 2, a: 1}, callbacks);<\/code>.<\/p>\n<p>  \u041e\u0434\u043d\u0430\u043a\u043e, \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u0432 \u044d\u0442\u0443 \u0446\u0435\u043d\u0443, \u043c\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0438\u0437 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438<br \/>  \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0438\u0445 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435, <br \/>  \u0430 \u0442\u0430\u043a\u0436\u0435, \u0441\u043c\u043e\u0436\u0435\u043c \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043e\u043a, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0445 \u0441 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u043c\u0435\u0441\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432,<br \/>  \u0438 \u0441\u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0432 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e ( p.optInt(\u00abb\u00bb, \u00ab4\u00bb) ). <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-211937","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/211937","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=211937"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/211937\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=211937"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=211937"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=211937"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}