"," expects a list of products (actually a JavaScript array). But currently the type of products is a\n",[119,963,964],{},"java.util.List"," which doesn’t have the map method. Note the datatype of products in the image below.",[18,967,968],{},[216,969],{"alt":970,"src":971},"nashorn-debugging","https://media.synyx.de/uploads//2016/03/nashorn-debugging.png",[618,973,974],{},[18,975,976],{},"“Given a Java array or Collection, this function returns a JavaScript array with a shallow copy of its contents”",[18,978,979,980,982],{},"So our renderServer function defined in ",[119,981,132],{}," must be extended to:",[18,984,985],{},"Now we’re ready to go 🙂",[18,987,988,989,992,993,998],{},"Rebuild the frontend with ",[119,990,991],{},"npm run build",", restart the Spring Boot application, reload ",[362,994,995],{},[22,996,181],{"href":181,"rel":997},[26]," and\nadmire our awesome product list.",[18,1000,1001],{},[216,1002],{"alt":1003,"src":1004},"awesome-product-list-001","https://media.synyx.de/uploads//2016/03/awesome-product-list-001.png",[36,1006,387],{"id":386},[56,1008,1009,1012,1018,1026],{},[44,1010,1011],{},"using Nashorn is no rocket science",[44,1013,1014,1015,1017],{},"load js files via ",[119,1016,705],{}," to enable debugging (at least in IntelliJ)",[44,1019,1020,1022,1023],{},[362,1021,964],{}," must be converted to JavaScript array with ",[119,1024,1025],{},"Java.from",[44,1027,1028],{},"manually rebuilding and reloading the ReactJS app sucks (autoreload would be cool, right)",[18,1030,1031],{},[216,1032],{"alt":1033,"src":1034},"js-webpack-nashorn","https://media.synyx.de/uploads//2016/03/js-webpack-nashorn.png",[36,1036,421],{"id":420},[56,1038,1039,1042],{},[44,1040,1041],{},"using webpack to enhance developer experience",[44,1043,1044],{},"implementing the sorting feature",[18,1046,1047],{},[99,1048,433],{},[435,1050,437],{},{"title":189,"searchDepth":203,"depth":203,"links":1052},[1053,1054,1055,1056,1057],{"id":38,"depth":203,"text":39},{"id":582,"depth":203,"text":495},{"id":599,"depth":203,"text":501},{"id":386,"depth":203,"text":387},{"id":420,"depth":203,"text":421},[447],"2016-03-11T11:29:12","This is the first article of a series about server side rendering and progressive enhancement. We will implement a\\nproduct list that can be sorted by two parameters. Furthermore the app will be progressively enhanced, means the html\\ndocument is rendered on the server and javascript will just enhance the app on the client if possible.","https://synyx.de/blog/springboot-reactjs-server-side-rendering/",{},"/blog/springboot-reactjs-server-side-rendering",{"title":469,"description":478},"springboot-reactjs-server-side-rendering","blog/springboot-reactjs-server-side-rendering",[459,460,461,462,463,464],"This is the first article of a series about server side rendering and progressive enhancement. We will implement a product list that can be sorted by two parameters. Furthermore the…","NgbG5R1prPK9EUbjPI1FYf4uauo9uWfYO5zNB_kSUvk",{"id":1071,"title":1072,"author":1073,"body":1076,"category":1329,"date":1331,"description":1332,"extension":450,"link":1333,"meta":1334,"navigation":199,"path":1335,"seo":1336,"slug":1080,"stem":1338,"tags":1339,"teaser":1344,"__hash__":1345},"blog/blog/javascript-linting-tool-evaluation.md","Javascript Linting Tool Evaluation",[1074,1075],"mueller","schneider",{"type":11,"value":1077,"toc":1320},[1078,1081,1096,1121,1130,1133,1137,1205,1208,1211,1224,1239,1242,1259,1262,1265,1276,1280,1283,1286,1289,1300,1304,1307,1313,1317],[14,1079,1072],{"id":1080},"javascript-linting-tool-evaluation",[18,1082,1083,1084,1089,1090,1095],{},"In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\nlegendary ‘",[22,1085,1088],{"href":1086,"rel":1087},"http://www.javaposse.com",[26],"The Java Posse","‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate\nalternatives to our current JavaScript linting standart, JSHint. Although well established by now among different\ndevelopment teams across ",[22,1091,1094],{"href":1092,"rel":1093},"http://www.synyx.de",[26],"synyx",", using it never felt 100% comfortable. A quick Google search left\nus with three alternatives:",[56,1097,1098,1106,1113],{},[44,1099,1100,1105],{},[22,1101,1104],{"href":1102,"rel":1103},"http://jslint.com",[26],"JSLint"," by Doug Crockford himself",[44,1107,1108],{},[22,1109,1112],{"href":1110,"rel":1111},"https://developers.google.com/closure/utilities/",[26],"Closure Linter by Google",[44,1114,1115,1120],{},[22,1116,1119],{"href":1117,"rel":1118},"http://eslint.org",[26],"ESLint",", the new kid on the block",[18,1122,1123,1124,1129],{},"…as well as ",[22,1125,1128],{"href":1126,"rel":1127},"http://jshint.com/",[26],"JSHint"," itself, of course.",[18,1131,1132],{},"We drew up a quick spreadsheet for evaluating the tools and came up with the following.",[36,1134,1136],{"id":1135},"criteria","Criteria",[56,1138,1139,1145,1151,1157,1163,1169,1175,1181,1187,1193,1199],{},[44,1140,1141,1144],{},[99,1142,1143],{},"Performance"," How long does it take to run over our example project, a single page webapp with a couple of thousands\nof JavaScript LOC?",[44,1146,1147,1150],{},[99,1148,1149],{},"Licensing"," Does the license meet our requirements (and those of our customers, of course)?",[44,1152,1153,1156],{},[99,1154,1155],{},"Project health/adoption"," How healthy is the project? Is it on Github, and is it well maintained?",[44,1158,1159,1162],{},[99,1160,1161],{},"Completeness of configurations"," Does the tool cover all our use-cases for a linting tool?",[44,1164,1165,1168],{},[99,1166,1167],{},"Productivity (rule set creation / project setup)"," When creating a new project, is it difficult to create a matching\nruleset? Does the tool come with a reasonable default rule set, or do you need to set up all the checks yourself?",[44,1170,1171,1174],{},[99,1172,1173],{},"Productivity (active software development)"," During active development, does the tool assist the developer in\nwriting quality code, or does it bully you to the point where you’d rather abolish using a linting tool at all?",[44,1176,1177,1180],{},[99,1178,1179],{},"Quality of Documentation/Tutorials/Self Help"," How good is the project documentation? When the tool breaks the build\nwith a certain error message, how difficult is it to find reliable information on the error in question (why does it\noccur, why is it a bad practice, how to fix it)?",[44,1182,1183,1186],{},[99,1184,1185],{},"Ability to integrate with existing projects"," Is it possible to integrate the linting tool in an existing projects\nwithout making changes to the project to comply to the rules?",[44,1188,1189,1192],{},[99,1190,1191],{},"Integration with build tool"," Is it possible to integrate the linting tool into your build chain to receive direct\nfeedback?",[44,1194,1195,1198],{},[99,1196,1197],{},"ES6 support"," How well does the project support future versions of the language?",[44,1200,1201,1204],{},[99,1202,1203],{},"Pluggable"," Is it possible to extend the given rule set with custom checks?",[36,1206,1128],{"id":1207},"jshint",[18,1209,1210],{},"The first tool we looked at was the already-familiar JSHint. We already knew what was bothering us about it:",[56,1212,1213,1221],{},[44,1214,1215,1216,1220],{},"Its hard to find the documentation for a certain error message. While the error messages itself are mostly\nself-explanatory, it can be somewhat difficult to find out how to deactivate or customize a certain rule. For\nexample, ",[22,1217,1218],{"href":1218,"rel":1219},"http://jshint.com/docs/options/",[26]," JSHint has both ‘enforcing’ and ‘relaxing’ rules. While setting a ‘enforcing’\nrule to true turns it on, setting a ‘relaxing’ rule to true deactivates it.",[44,1222,1223],{},"More often than not, using JSHint can be frustrating. For example, we had ‘maxdepth’",[18,1225,1226,1227,1230,1231,1234,1235,1238],{},"set to 3, meaning a maximum of three nested blocks of code was allowed. In case one of those blocks was a ‘",[362,1228,1229],{},"for … in","‘\nstatement, JSHint would (correctly) complain that its body should be wrapped in an ‘",[362,1232,1233],{},"if(obj.hasOwnProperty(key))","\n‘-check to filter out unwanted properties. However, doing so meant introducing another nested block, and if that pushed\nthe total depth beyond ‘",[362,1236,1237],{},"maxdepth","‘, JSHint would fail the build. The solution was usually to introduce private helper\nfunctions, which can make otherwise trivial code difficult to read (since you have to skip blocks of code).",[18,1240,1241],{},"Of course, that is not really the fault of JSHint (seeing that it only did what it was told to do), but it was a rather\nbig annoyance that caused us to re-evaluate our JavaScript linting practices in the first place.",[56,1243,1244],{},[44,1245,1246,1247,1252,1253,1258],{},"Being a fork of JSLint, JSHint has the same license containing the\ninfamous ",[22,1248,1251],{"href":1249,"rel":1250,"title":1251},"http://en.wikipedia.org/wiki/JSLint#License",[26],"JSLint License"," ‘Good, not evil’ statement.\nWhile we understand its humorous intent (and being\na ",[22,1254,1257],{"href":1255,"rel":1256,"title":1257},"https://synyx.de/unternehmen/verantwortung_csr/",[26],"fairly social responsible company",",\nwe wholeheartedly support it), we were worried that some corporate lawyer might not approve of our use of a tool bound\nto such a license.",[36,1260,1104],{"id":1261},"jslint",[18,1263,1264],{},"After JSHint we decided to evaluate the old guy in the gang, JSLint. It was the first linting tool for JavaScript, and\nit feels like that. From our opinion JSLint has two major problems, besides the license (see JSHint):",[56,1266,1267,1270,1273],{},[44,1268,1269],{},"The website of JSLint is very old-school and does not contain any explanations of the ruleset or any information to\nget a link between the error messages provided by JSLint and the problem in the code. So you have to search through\nthe internet to find any third-party explanation that will help you to fix the problem.",[44,1271,1272],{},"Some of the rules of JSLint are, at least, strangely named. There is a rule to forbid (or allow, if deactivated)\n‘stupidity’. The project’s web page does not provide any explanation (again) – resorting to Google, we found out that\n‘stupidity’ referred to the usage of synchronous functions in Node.js’s file system module.",[44,1274,1275],{},"The rule set is very strict and, when you look through the GitHub issues and pull requests, it is very hard to\nparticipate in the JSLint project. That’s why the community is very small and there are a lot more people active in\nJSHint and other projects and bring in their ideas there. Maybe that is why JSLint does not provide a rule similar to\n‘latedef’ from JSHint or ‘no-use-before-define’ from ESLint. Without this rule it is very hard to structure your\ncode with private named-functions at the end without assigning the function to a variable at the start (and that\nwould not be what we want).",[36,1277,1279],{"id":1278},"closure-linter","Closure Linter",[18,1281,1282],{},"The Closure Linter is part of Google’s Closure tool set. It was designed for internal use and provides very little\noptions for customization. Since following the rules enforced by it seems to be mandatory within Google, that is\ncertainly an acceptable practice. However, since it is (for example) not possible to change the default maximum line\nlength of 80 characters, we quickly decided not to look into the tool any more.",[36,1284,1119],{"id":1285},"eslint",[18,1287,1288],{},"When looking at ESLint, we were quick to decide that we might be looking at a potential winner:",[56,1290,1291,1294,1297],{},[44,1292,1293],{},"While the project is (by far) the youngest (or as others might put it: the least mature) of the four tools we looked\nat, it is also the best maintained. 100+ contributors on Github and a roughly monthly release schedule speak for\nthemselves.",[44,1295,1296],{},"Applying it to our example web app, we were surprised to find out that it was sufficient to write about ten lines of\nconfiguration to perform the same amount of checks that required around a hundred lines in JSHint. Of course, that\nmight only mean that we have the same idea of quality JavaScript code as the tool’s authors, but nevertheless it meant\nthat the tool would be quite easy to adopt into our development process.",[44,1298,1299],{},"The output is quite handy: It prints both a one-line human readable error message as well as an error code for a\nquick lookup in the documentation.",[36,1301,1303],{"id":1302},"evaluation","Evaluation",[18,1305,1306],{},"The criteria from above has been weighted from five to 15, from not important to important, and the tools got a 0, 0.5\nor 1 if it does not, almost or absolute fulfill the criterion.",[18,1308,1309],{},[216,1310],{"alt":1311,"src":1312},"\"jsLinting\"","https://media.synyx.de/uploads//2015/02/jsLinting2.png",[36,1314,1316],{"id":1315},"conclusion","Conclusion",[18,1318,1319],{},"As you can see in the image above, ESLint proved to be the winner of our evaluation. We decided that its major flaw, the\npotential immaturity, was acceptable to us since by its nature, it would only be used during (internal) development. The\nease of use, both because of the robust and reasonable default rule set and the high-quality documentation, outweighed\nany concern by far. We are looking forward to adopt ESLint into our development tool chain over the coming weeks and\nmonths!",{"title":189,"searchDepth":203,"depth":203,"links":1321},[1322,1323,1324,1325,1326,1327,1328],{"id":1135,"depth":203,"text":1136},{"id":1207,"depth":203,"text":1128},{"id":1261,"depth":203,"text":1104},{"id":1278,"depth":203,"text":1279},{"id":1285,"depth":203,"text":1119},{"id":1302,"depth":203,"text":1303},{"id":1315,"depth":203,"text":1316},[447,1330],"open-source-blog","2015-02-03T09:45:38","In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\\nlegendary ‘The Java Posse‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate\\nalternatives to our current JavaScript linting standart, JSHint. Although well established by now among different\\ndevelopment teams across synyx, using it never felt 100% comfortable. A quick Google search left\\nus with three alternatives:","https://synyx.de/blog/javascript-linting-tool-evaluation/",{},"/blog/javascript-linting-tool-evaluation",{"title":1072,"description":1337},"In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the\nlegendary ‘The Java Posse‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate\nalternatives to our current JavaScript linting standart, JSHint. Although well established by now among different\ndevelopment teams across synyx, using it never felt 100% comfortable. A quick Google search left\nus with three alternatives:","blog/javascript-linting-tool-evaluation",[1340,1285,1302,460,1341,1207,1261,1342,1343],"development","js","linting","software-development","In our internal JavaScript ‘User Group’ (called JS-Posse in honour of the legendary ‘The Java Posse‘ by Dick Wall, Chet Haase et al.), we recently decided to evaluate alternatives to…","QcXdToJb-JTf6znqD4o0GbdwGfu2XcNA6y-tOvH75ck",{"id":1347,"title":1348,"author":1349,"body":1351,"category":2515,"date":2516,"description":189,"extension":450,"link":2517,"meta":2518,"navigation":199,"path":2519,"seo":2520,"slug":1355,"stem":2521,"tags":2522,"teaser":2529,"__hash__":2530},"blog/blog/code-gluse.md","Code gluse",[1350],"clausen",{"type":11,"value":1352,"toc":2509},[1353,1356,1360,1371,1374,1377,1380,1387,1398,1405,1409,1412,1422,1508,1514,1517,1547,1554,1612,1615,1817,1820,1823,1827,1830,1848,1855,1858,1869,1872,1875,1882,1889,1920,1923,1926,1959,1962,1965,2020,2028,2035,2038,2041,2143,2145,2149,2160,2165,2179,2182,2193,2200,2202,2225,2231,2259,2371,2374,2377,2380,2387,2390,2393,2396,2399,2506],[14,1354,1348],{"id":1355},"code-gluse",[1357,1358,1348],"h3",{"id":1359},"code-gluse-1",[18,1361,1362,1363,1366,1367,1370],{},"Today’s post targets an API, which has been released on Dec. 11, 2006; the ",[119,1364,1365],{},"javax.scripting"," package ",[193,1368,1369],{},"1"," and a lot of\ngood articles that have been written around it.",[18,1372,1373],{},"The intention for this post is not about ‘how to use the scripting packaging’, but about gluse. So what do I mean with\nthe phrase gluse? Gluse is a coinage",[18,1375,1376],{},"for glue and (re)usage. As many of the Java developer know about the plenty of good libraries from maven central /\ngithub and the integration process, a few of them",[18,1378,1379],{},"might ask how to integrate libraries from other languages as well. As many of the every day problems have already bean\naddressed, there is a good chance that someone else has done the job for you and is willing to share.",[18,1381,1382,1383,1386],{},"Sometimes it’s written in pure Java, sometimes in a different language. Let’s see how to integrate the latter\nlibraries. (StackOverflow lists a dozen ",[193,1384,1385],{},"2"," btw.)",[18,1388,1389,1390,1393,1394,1397],{},"The next parts will give you some information in form of three examples. The first and second example will address\nJavascript, as Javascript is getting more and more into the focus of developers and Oracle will ship their new engine\n‘Nashorn’ with the next Java 8 release, while the third example will target a more complex example using JRuby. ",[99,1391,1392],{},"All\nexamples"," can be downloaded from ",[193,1395,1396],{},"3",". So it’s up to you if you want to read the sources in parallel, afterwards, or by\nplaying with the code in your IDE instantly.",[18,1399,1400,1401,1404],{},"All code examples have been written to be compatible with Java 1.6. See the note in example two, when it comes to the\n",[119,1402,1403],{},"bind"," function.",[1357,1406,1408],{"id":1407},"proxy","Proxy",[18,1410,1411],{},"Lets discuss the first example: We want to replace parts of a string using a regular expression, but hook into the\nprocess of manipulating the matching elements, before the replacement eventually takes place – by adding some new\ncontent or returning something completely different.",[18,1413,1414,1415,1418,1419,335],{},"In Java you would probably end up using the ",[119,1416,1417],{},"java.util.regex.Pattern",", creating a matcher and iterating over the matched\ngroups and so on. There’s nothing wrong about it, but Javascript already defines that kind of behaviour ",[193,1420,1421],{},"4",[184,1423,1426],{"className":1424,"code":1425,"language":460,"meta":189,"style":189},"language-javascript shiki shiki-themes github-light github-dark","\"first 000 second\".replace(/[a-zA-Z]+/g, function (match) {\n return \"[\" + match.toUpperCase() + \"]\";\n});\n",[119,1427,1428,1475,1503],{"__ignoreMap":189},[193,1429,1430,1434,1437,1441,1444,1447,1451,1455,1457,1460,1462,1465,1468,1472],{"class":195,"line":196},[193,1431,1433],{"class":1432},"sZZnC","\"first 000 second\"",[193,1435,335],{"class":1436},"sVt8B",[193,1438,1440],{"class":1439},"sScJk","replace",[193,1442,1443],{"class":1436},"(",[193,1445,1446],{"class":1432},"/",[193,1448,1450],{"class":1449},"sj4cs","[a-zA-Z]",[193,1452,1454],{"class":1453},"szBVR","+",[193,1456,1446],{"class":1432},[193,1458,1459],{"class":1453},"g",[193,1461,266],{"class":1436},[193,1463,1464],{"class":1453},"function",[193,1466,1467],{"class":1436}," (",[193,1469,1471],{"class":1470},"s4XuR","match",[193,1473,1474],{"class":1436},") {\n",[193,1476,1477,1480,1483,1486,1489,1492,1495,1497,1500],{"class":195,"line":203},[193,1478,1479],{"class":1453}," return",[193,1481,1482],{"class":1432}," \"[\"",[193,1484,1485],{"class":1453}," +",[193,1487,1488],{"class":1436}," match.",[193,1490,1491],{"class":1439},"toUpperCase",[193,1493,1494],{"class":1436},"() ",[193,1496,1454],{"class":1453},[193,1498,1499],{"class":1432}," \"]\"",[193,1501,1502],{"class":1436},";\n",[193,1504,1505],{"class":195,"line":209},[193,1506,1507],{"class":1436},"});\n",[18,1509,1510,1511,1513],{},"As both, Rhino and Nashorn, support the javax.script.Invocable type, we will create an interface to address the\nproblem – you’ll find the whole documentation in the mentioned project ",[193,1512,1396],{},", but for the sake of completeness:",[18,1515,1516],{},"Apply the ‘pattern’ on the ‘sequence’ and call the ‘callback’ on each matched element. Either on ‘all’ matching\nelements, or on ‘any’ (first makes sense here).",[184,1518,1521],{"className":1519,"code":1520,"language":459,"meta":189,"style":189},"language-java shiki shiki-themes github-light github-dark","\n public interface Replacement {\n public abstract CharSequence any (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n public abstract CharSequence all (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n }\n\n",[119,1522,1523,1527,1532,1537,1542],{"__ignoreMap":189},[193,1524,1525],{"class":195,"line":196},[193,1526,200],{"emptyLinePlaceholder":199},[193,1528,1529],{"class":195,"line":203},[193,1530,1531],{}," public interface Replacement {\n",[193,1533,1534],{"class":195,"line":209},[193,1535,1536],{}," public abstract CharSequence any (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n",[193,1538,1539],{"class":195,"line":839},[193,1540,1541],{}," public abstract CharSequence all (Pattern pattern, CharSequence sequence, Function\u003CCharSequence, CharSequence> callback);\n",[193,1543,1544],{"class":195,"line":945},[193,1545,1546],{}," }\n",[18,1548,1549,1550,1553],{},"The final Java code would look like the following (Java 8 users will flavour the new lambda syntax:\n",[119,1551,1552],{},"(match) -> { return \"[\" + match + \"]\"; }","):",[184,1555,1557],{"className":1519,"code":1556,"language":459,"meta":189,"style":189},"\n Replacement replacement;\n replacement = replacement ();\n CharSequence enclosed = replacement.all (Pattern.compile (\"\\\\d+\"), \"could you please enclose 1234, 789, 345 with brackets?\", new Function\u003CCharSequence, CharSequence> () {\n @Override\n public CharSequence apply (CharSequence sequence) {\n return \"[\" + sequence + \"]\";\n }\n });\n /* replacement () returns a proxy of the type Replacement, using the shipped js scripting engine. the evaluated script returns an instance, which can be encapsulated using the Invocable#getInterface signature */\n\n",[119,1558,1559,1563,1568,1573,1578,1583,1588,1594,1600,1606],{"__ignoreMap":189},[193,1560,1561],{"class":195,"line":196},[193,1562,200],{"emptyLinePlaceholder":199},[193,1564,1565],{"class":195,"line":203},[193,1566,1567],{}," Replacement replacement;\n",[193,1569,1570],{"class":195,"line":209},[193,1571,1572],{}," replacement = replacement ();\n",[193,1574,1575],{"class":195,"line":839},[193,1576,1577],{}," CharSequence enclosed = replacement.all (Pattern.compile (\"\\\\d+\"), \"could you please enclose 1234, 789, 345 with brackets?\", new Function\u003CCharSequence, CharSequence> () {\n",[193,1579,1580],{"class":195,"line":945},[193,1581,1582],{}," @Override\n",[193,1584,1585],{"class":195,"line":951},[193,1586,1587],{}," public CharSequence apply (CharSequence sequence) {\n",[193,1589,1591],{"class":195,"line":1590},7,[193,1592,1593],{}," return \"[\" + sequence + \"]\";\n",[193,1595,1597],{"class":195,"line":1596},8,[193,1598,1599],{}," }\n",[193,1601,1603],{"class":195,"line":1602},9,[193,1604,1605],{}," });\n",[193,1607,1609],{"class":195,"line":1608},10,[193,1610,1611],{}," /* replacement () returns a proxy of the type Replacement, using the shipped js scripting engine. the evaluated script returns an instance, which can be encapsulated using the Invocable#getInterface signature */\n",[18,1613,1614],{},"The Javascript implementation would look like:",[184,1616,1618],{"className":1424,"code":1617,"language":460,"meta":189,"style":189},"\n(function () {\n function replace (regex, content, callback) {\n ...\n }\n var Replacement = function () {};\n Replacement.prototype.any = function (regex, content, callback) {\n return replace (new RegExp (regex), content, callback);\n };\n Replacement.prototype.all = function (regex, content, callback) {\n return replace (new RegExp (regex, 'g'), content, callback);\n };\n return new Replacement ();\n}) ();\n\n",[119,1619,1620,1624,1633,1657,1662,1666,1683,1716,1734,1739,1770,1792,1797,1811],{"__ignoreMap":189},[193,1621,1622],{"class":195,"line":196},[193,1623,200],{"emptyLinePlaceholder":199},[193,1625,1626,1628,1630],{"class":195,"line":203},[193,1627,1443],{"class":1436},[193,1629,1464],{"class":1453},[193,1631,1632],{"class":1436}," () {\n",[193,1634,1635,1638,1641,1643,1646,1648,1650,1652,1655],{"class":195,"line":209},[193,1636,1637],{"class":1453}," function",[193,1639,1640],{"class":1439}," replace",[193,1642,1467],{"class":1436},[193,1644,1645],{"class":1470},"regex",[193,1647,266],{"class":1436},[193,1649,759],{"class":1470},[193,1651,266],{"class":1436},[193,1653,1654],{"class":1470},"callback",[193,1656,1474],{"class":1436},[193,1658,1659],{"class":195,"line":839},[193,1660,1661],{"class":1453}," ...\n",[193,1663,1664],{"class":195,"line":945},[193,1665,1546],{"class":1436},[193,1667,1668,1671,1674,1677,1680],{"class":195,"line":951},[193,1669,1670],{"class":1453}," var",[193,1672,1673],{"class":1439}," Replacement",[193,1675,1676],{"class":1453}," =",[193,1678,1679],{"class":1453}," function",[193,1681,1682],{"class":1436}," () {};\n",[193,1684,1685,1688,1690,1693,1695,1698,1700,1702,1704,1706,1708,1710,1712,1714],{"class":195,"line":1590},[193,1686,1687],{"class":1449}," Replacement",[193,1689,335],{"class":1436},[193,1691,1692],{"class":1449},"prototype",[193,1694,335],{"class":1436},[193,1696,1697],{"class":1439},"any",[193,1699,1676],{"class":1453},[193,1701,1679],{"class":1453},[193,1703,1467],{"class":1436},[193,1705,1645],{"class":1470},[193,1707,266],{"class":1436},[193,1709,759],{"class":1470},[193,1711,266],{"class":1436},[193,1713,1654],{"class":1470},[193,1715,1474],{"class":1436},[193,1717,1718,1721,1723,1725,1728,1731],{"class":195,"line":1596},[193,1719,1720],{"class":1453}," return",[193,1722,1640],{"class":1439},[193,1724,1467],{"class":1436},[193,1726,1727],{"class":1453},"new",[193,1729,1730],{"class":1439}," RegExp",[193,1732,1733],{"class":1436}," (regex), content, callback);\n",[193,1735,1736],{"class":195,"line":1602},[193,1737,1738],{"class":1436}," };\n",[193,1740,1741,1743,1745,1747,1749,1752,1754,1756,1758,1760,1762,1764,1766,1768],{"class":195,"line":1608},[193,1742,1687],{"class":1449},[193,1744,335],{"class":1436},[193,1746,1692],{"class":1449},[193,1748,335],{"class":1436},[193,1750,1751],{"class":1439},"all",[193,1753,1676],{"class":1453},[193,1755,1679],{"class":1453},[193,1757,1467],{"class":1436},[193,1759,1645],{"class":1470},[193,1761,266],{"class":1436},[193,1763,759],{"class":1470},[193,1765,266],{"class":1436},[193,1767,1654],{"class":1470},[193,1769,1474],{"class":1436},[193,1771,1773,1775,1777,1779,1781,1783,1786,1789],{"class":195,"line":1772},11,[193,1774,1720],{"class":1453},[193,1776,1640],{"class":1439},[193,1778,1467],{"class":1436},[193,1780,1727],{"class":1453},[193,1782,1730],{"class":1439},[193,1784,1785],{"class":1436}," (regex, ",[193,1787,1788],{"class":1432},"'g'",[193,1790,1791],{"class":1436},"), content, callback);\n",[193,1793,1795],{"class":195,"line":1794},12,[193,1796,1738],{"class":1436},[193,1798,1800,1803,1806,1808],{"class":195,"line":1799},13,[193,1801,1802],{"class":1453}," return",[193,1804,1805],{"class":1453}," new",[193,1807,1673],{"class":1439},[193,1809,1810],{"class":1436}," ();\n",[193,1812,1814],{"class":195,"line":1813},14,[193,1815,1816],{"class":1436},"}) ();\n",[18,1818,1819],{},"The Java code for this example would probably be less – measured in LOC – but the basic steps needed for an integration\ncan be shown pretty good and two worlds might benefit from your certainly approved works 🙂",[18,1821,1822],{},"One nice feature about this kind of mechanism is, that you can quickly prototype your functionality, while still able to\nchange parts of the implementation using pure Java afterwards.",[1357,1824,1826],{"id":1825},"modularity","Modularity",[18,1828,1829],{},"Let’s come to the second example. You may have written a bunch of Javascript files in a modular way or just don’t want\nto put everything in a single file. While the first example showed how to proxy your implementation, the second example\nwill show you a basic approach for dynamically loading further resource and/or code files. The following signature\nshould be provided and accessible from all scripts.",[184,1831,1833],{"className":1424,"code":1832,"language":460,"meta":189,"style":189},"require(\"org.geonames.reverse\");\n",[119,1834,1835],{"__ignoreMap":189},[193,1836,1837,1840,1842,1845],{"class":195,"line":196},[193,1838,1839],{"class":1439},"require",[193,1841,1443],{"class":1436},[193,1843,1844],{"class":1432},"\"org.geonames.reverse\"",[193,1846,1847],{"class":1436},");\n",[18,1849,1850,1851,1854],{},"The similarity to requirejs ",[193,1852,1853],{},"5"," is intentional and you may want to extend the signature to be fully compliant, but this\nwill be left for your curiosity 🙂",[18,1856,1857],{},"Loading resources from ‘unknown’, or from ‘at runtime unknown’ sources is by nature critical, as the code is executed in\nthe same JVM which hosts your application as well. Therefore, you should only load resources you really trust.",[18,1859,1860,1861,1864,1865,1868],{},"You could achieve this by verifying the signature of the reviewed files using a PKI ",[193,1862,1863],{},"6"," infrastructure – ",[119,1866,1867],{},"javax.crypto","\nis your friend here and fortunately you can implement this in Java and/or use a security library to accomplish this\ntask.",[18,1870,1871],{},"Simply spoken: Always check the integrity if you provide a way for modifications.",[18,1873,1874],{},"If you are already familiar with the scripting engine API, you might have noticed that require is a function and not an\n‘object’.",[18,1876,1877,1878,1881],{},"Even when functions in Javascript are objects, there is no semantic way to say that ",[362,1879,1880],{},"“this object is a function and can\nbe invoked”"," if you share it between the environment.",[18,1883,1884,1885,1888],{},"There might be support for some engines, but not for the others and ",[119,1886,1887],{},"javax.script"," API is designed for general purpose –\ndepending on engine internal interfaces is not desired.",[184,1890,1892],{"className":1424,"code":1891,"language":460,"meta":189,"style":189},"obj.require(\n \"org.geonames.reverse\",\n); /* nah, okay but requires additional knowledge obj. */\n",[119,1893,1894,1904,1911],{"__ignoreMap":189},[193,1895,1896,1899,1901],{"class":195,"line":196},[193,1897,1898],{"class":1436},"obj.",[193,1900,1839],{"class":1439},[193,1902,1903],{"class":1436},"(\n",[193,1905,1906,1909],{"class":195,"line":203},[193,1907,1908],{"class":1432}," \"org.geonames.reverse\"",[193,1910,262],{"class":1436},[193,1912,1913,1916],{"class":195,"line":209},[193,1914,1915],{"class":1436},"); ",[193,1917,1919],{"class":1918},"sJ8bj","/* nah, okay but requires additional knowledge obj. */\n",[18,1921,1922],{},"Fortunately there is a solution. You can attach a script context to the evaluation process and reuse the context later\non, but you shouldn’t use the internal context as it could leak if your engine leaks.",[18,1924,1925],{},"Pseudo Algorithm:",[41,1927,1928,1947,1950,1953,1956],{},[44,1929,1930,1931,266,1934,266,1937,266,1940,1943,1944],{},"create a java function object which can load your resources from the ",[362,1932,1933],{},"classpath",[362,1935,1936],{},"internet",[362,1938,1939],{},"local filesystem",[362,1941,1942],{},"…","\nwith a method signature you know. (a function/SAM object) like: ",[119,1945,1946],{},"void apply (String)",[44,1948,1949],{},"create a script context and attach the object from 1. to it with a variable called ‘whatever’ (really whatever name\nyou like)",[44,1951,1952],{},"evaluate an inline require function before you evaluate your business code, which puts your require function into the\nscope of the context from 2.",[44,1954,1955],{},"evaluate your business codes which relies on the require function with the same scope from 2.",[44,1957,1958],{},"have fun",[18,1960,1961],{},"var require = function (library) { whatever.apply (library); }",[18,1963,1964],{},"The above code would be sufficient, but has some drawbacks as it only works if the ‘whatever’ object is in the correct\nexecution scope and if it provides the correct signature – someone could overwrite the binding or does something you\nsimply don’t want him/her to do. We need some slight improvements to correct this.",[184,1966,1968],{"className":1424,"code":1967,"language":460,"meta":189,"style":189},"var require = function (library) {\n this.apply(library);\n}.bind(whatever);\ndelete whatever;\n",[119,1969,1970,1989,2002,2012],{"__ignoreMap":189},[193,1971,1972,1975,1978,1980,1982,1984,1987],{"class":195,"line":196},[193,1973,1974],{"class":1453},"var",[193,1976,1977],{"class":1439}," require",[193,1979,1676],{"class":1453},[193,1981,1679],{"class":1453},[193,1983,1467],{"class":1436},[193,1985,1986],{"class":1470},"library",[193,1988,1474],{"class":1436},[193,1990,1991,1994,1996,1999],{"class":195,"line":203},[193,1992,1993],{"class":1449}," this",[193,1995,335],{"class":1436},[193,1997,1998],{"class":1439},"apply",[193,2000,2001],{"class":1436},"(library);\n",[193,2003,2004,2007,2009],{"class":195,"line":209},[193,2005,2006],{"class":1436},"}.",[193,2008,1403],{"class":1439},[193,2010,2011],{"class":1436},"(whatever);\n",[193,2013,2014,2017],{"class":195,"line":839},[193,2015,2016],{"class":1453},"delete",[193,2018,2019],{"class":1436}," whatever;\n",[618,2021,2022],{},[18,2023,2024,2025],{},"“The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a\ngiven sequence of arguments preceding any provided when the new function is called.” ",[193,2026,2027],{},"7",[18,2029,2030,2031,2034],{},"If the bind function is not available by your Rhino environment, you may want to look at the implementation from\nPrototype ",[193,2032,2033],{},"8"," et al. and add it manually with the same procedure.",[18,2036,2037],{},"We delete the ‘whatever’ object from the script context afterwards.",[18,2039,2040],{},"You need the following Java code as glue:",[184,2042,2044],{"className":1519,"code":2043,"language":459,"meta":189,"style":189},"\n// internal context for variables\nfinal Bindings bindings = scripting.newBindings ();\n bindings.put (Importer, new Function\u003CString, Void> () {\n // load a library 'argument' from the 'lib' directory\n @Override\n public Void apply (String argument) {\n // trusting returns either a valid stream object or throws an 'untrusted code' exception\n String resource;\n resource = String.format (\"/lib/%s.js\", argument);\n scripting.evaluate (trusting (resource), context);\n return null;\n }\n});\ncontext.setBindings (bindings, ScriptContext.ENGINE_SCOPE);\n// add require function to the scope before the application script is loaded\nscripting.evaluate (requirejs (Importer), context);\n// execute the script ultimately\nscripting.evaluate (applicationjs (), context);\n\n",[119,2045,2046,2050,2055,2060,2065,2070,2075,2080,2085,2090,2095,2100,2105,2109,2113,2119,2125,2131,2137],{"__ignoreMap":189},[193,2047,2048],{"class":195,"line":196},[193,2049,200],{"emptyLinePlaceholder":199},[193,2051,2052],{"class":195,"line":203},[193,2053,2054],{},"// internal context for variables\n",[193,2056,2057],{"class":195,"line":209},[193,2058,2059],{},"final Bindings bindings = scripting.newBindings ();\n",[193,2061,2062],{"class":195,"line":839},[193,2063,2064],{}," bindings.put (Importer, new Function\u003CString, Void> () {\n",[193,2066,2067],{"class":195,"line":945},[193,2068,2069],{}," // load a library 'argument' from the 'lib' directory\n",[193,2071,2072],{"class":195,"line":951},[193,2073,2074],{}," @Override\n",[193,2076,2077],{"class":195,"line":1590},[193,2078,2079],{}," public Void apply (String argument) {\n",[193,2081,2082],{"class":195,"line":1596},[193,2083,2084],{}," // trusting returns either a valid stream object or throws an 'untrusted code' exception\n",[193,2086,2087],{"class":195,"line":1602},[193,2088,2089],{}," String resource;\n",[193,2091,2092],{"class":195,"line":1608},[193,2093,2094],{}," resource = String.format (\"/lib/%s.js\", argument);\n",[193,2096,2097],{"class":195,"line":1772},[193,2098,2099],{}," scripting.evaluate (trusting (resource), context);\n",[193,2101,2102],{"class":195,"line":1794},[193,2103,2104],{}," return null;\n",[193,2106,2107],{"class":195,"line":1799},[193,2108,1546],{},[193,2110,2111],{"class":195,"line":1813},[193,2112,1507],{},[193,2114,2116],{"class":195,"line":2115},15,[193,2117,2118],{},"context.setBindings (bindings, ScriptContext.ENGINE_SCOPE);\n",[193,2120,2122],{"class":195,"line":2121},16,[193,2123,2124],{},"// add require function to the scope before the application script is loaded\n",[193,2126,2128],{"class":195,"line":2127},17,[193,2129,2130],{},"scripting.evaluate (requirejs (Importer), context);\n",[193,2132,2134],{"class":195,"line":2133},18,[193,2135,2136],{},"// execute the script ultimately\n",[193,2138,2140],{"class":195,"line":2139},19,[193,2141,2142],{},"scripting.evaluate (applicationjs (), context);\n",[18,2144,314],{},[1357,2146,2148],{"id":2147},"reporting","Reporting",[18,2150,2151,2152,2155,2156,2159],{},"In the last example I want to explain how to integrate a script engine, which is not shipped by default – JRuby ",[193,2153,2154],{},"9",".\nThe idea behind is to embed code of ruby gems into your application, especially PDFKit ",[193,2157,2158],{},"10"," for this example. PDFKit\ndescribes itself with",[618,2161,2162],{},[18,2163,2164],{},"“Create PDFs using plain old HTML+CSS. Uses wkhtmltopdf on the back-end which renders HTML using Webkit.”",[18,2166,2167,2168,2171,2172,2175,2176,335],{},"Mostly you don’t want to handle HTML content directly, as your data is often stored in form of a ‘model’. Our solution\nshould therefore target the transformation from: ",[119,2169,2170],{},"Model -> HTML -> PDF",", which can be achieved using e.g. the nice Jade\n",[193,2173,2174],{},"11"," language for the rendering process, especially Jade4j ",[193,2177,2178],{},"12",[18,2180,2181],{},"Instead of writing the integration code for wkhtmltopdf, we will base on the work of PDFKit and write some JRuby glue.",[18,2183,2184,2185,2188,2189,2192],{},"If you need some information about ‘gem bundling’ I would recommend the articles/examples from Sieger ",[193,2186,2187],{},"13"," and Harada\n",[193,2190,2191],{},"14"," as a starting point.",[18,2194,2195,2196,2199],{},"You will find a local file-based repository in the project, as I wanted Maven ",[193,2197,2198],{},"15"," to handle all the dependencies, but\nany other repository might work fine. It simply depends on your infrastructure and what suits you best.",[18,2201,1925],{},[41,2203,2204,2207,2210,2216,2219,2222],{},[44,2205,2206],{},"put jruby-complete on your classpath, as the library ships the jsr223 implementation",[44,2208,2209],{},"put the converted pdfkit bundle on your classpath",[44,2211,2212,2213],{},"put any other needed library on your classpath ",[193,2214,2215],{},"jade4j, guava, …",[44,2217,2218],{},"write some jruby code to instantiate a configured pdfkit object",[44,2220,2221],{},"proxy the returned jruby object from 4. with a java interface",[44,2223,2224],{},"convert a jade (or differently) generated html stream to pdf using the proxy from 5.",[18,2226,2227,2228,2230],{},"I’ll show you the glue for the proxy only. Please download the project under ",[193,2229,1396],{}," if you want to see the remaining\nparts.",[184,2232,2234],{"className":1519,"code":2233,"language":459,"meta":189,"style":189},"\npublic interface Pdfy {\n public boolean convert (InputStream streamin, OutputStream streamout);\n public boolean convert (InputStream streamin, OutputStream streamout, Map\u003CString, String> options);\n}\n\n",[119,2235,2236,2240,2245,2250,2255],{"__ignoreMap":189},[193,2237,2238],{"class":195,"line":196},[193,2239,200],{"emptyLinePlaceholder":199},[193,2241,2242],{"class":195,"line":203},[193,2243,2244],{},"public interface Pdfy {\n",[193,2246,2247],{"class":195,"line":209},[193,2248,2249],{}," public boolean convert (InputStream streamin, OutputStream streamout);\n",[193,2251,2252],{"class":195,"line":839},[193,2253,2254],{}," public boolean convert (InputStream streamin, OutputStream streamout, Map\u003CString, String> options);\n",[193,2256,2257],{"class":195,"line":945},[193,2258,842],{},[184,2260,2264],{"className":2261,"code":2262,"language":2263,"meta":189,"style":189},"language-ruby shiki shiki-themes github-light github-dark","\nclass Pdfy\n def initialize(stylesheet)\n @stylesheet = stylesheet\n end\n def convert(streamin, streamout, options = {})\n begin\n html = streamin.to_io.read\n kit = PDFKit.new(html, options)\n if @stylesheet\n kit.stylesheets \u003C\u003C @stylesheet\n end\n out = streamout.to_io\n out.binmode \u003C\u003C kit.to_pdf\n out.flush\n rescue\n return false\n end\n true\n end\nend\n\n","ruby",[119,2265,2266,2270,2275,2280,2285,2290,2295,2300,2305,2310,2315,2320,2325,2330,2335,2340,2345,2350,2355,2360,2365],{"__ignoreMap":189},[193,2267,2268],{"class":195,"line":196},[193,2269,200],{"emptyLinePlaceholder":199},[193,2271,2272],{"class":195,"line":203},[193,2273,2274],{},"class Pdfy\n",[193,2276,2277],{"class":195,"line":209},[193,2278,2279],{}," def initialize(stylesheet)\n",[193,2281,2282],{"class":195,"line":839},[193,2283,2284],{}," @stylesheet = stylesheet\n",[193,2286,2287],{"class":195,"line":945},[193,2288,2289],{}," end\n",[193,2291,2292],{"class":195,"line":951},[193,2293,2294],{}," def convert(streamin, streamout, options = {})\n",[193,2296,2297],{"class":195,"line":1590},[193,2298,2299],{}," begin\n",[193,2301,2302],{"class":195,"line":1596},[193,2303,2304],{}," html = streamin.to_io.read\n",[193,2306,2307],{"class":195,"line":1602},[193,2308,2309],{}," kit = PDFKit.new(html, options)\n",[193,2311,2312],{"class":195,"line":1608},[193,2313,2314],{}," if @stylesheet\n",[193,2316,2317],{"class":195,"line":1772},[193,2318,2319],{}," kit.stylesheets \u003C\u003C @stylesheet\n",[193,2321,2322],{"class":195,"line":1794},[193,2323,2324],{}," end\n",[193,2326,2327],{"class":195,"line":1799},[193,2328,2329],{}," out = streamout.to_io\n",[193,2331,2332],{"class":195,"line":1813},[193,2333,2334],{}," out.binmode \u003C\u003C kit.to_pdf\n",[193,2336,2337],{"class":195,"line":2115},[193,2338,2339],{}," out.flush\n",[193,2341,2342],{"class":195,"line":2121},[193,2343,2344],{}," rescue\n",[193,2346,2347],{"class":195,"line":2127},[193,2348,2349],{}," return false\n",[193,2351,2352],{"class":195,"line":2133},[193,2353,2354],{}," end\n",[193,2356,2357],{"class":195,"line":2139},[193,2358,2359],{}," true\n",[193,2361,2363],{"class":195,"line":2362},20,[193,2364,2289],{},[193,2366,2368],{"class":195,"line":2367},21,[193,2369,2370],{},"end\n",[18,2372,2373],{},"Maven will produce an assembly as zip file, which can be extracted elsewhere with a shell script for windows and *nix\nbased systems.",[18,2375,2376],{},"You need to provide the full qualified path for wkhtmltopdf as first argument and the full qualified path of the output\nfile with file extension as second argument.",[18,2378,2379],{},"I did not implement any special CLI handling for this prototype.",[18,2381,2382,2383,2386],{},"You need to install wkhtmltopdf ",[193,2384,2385],{},"16"," as a consequence. I installed wkhtmltopdf 0.11.0 rc2 on windows 7 x64 and\nwkhtmltopdf 0.9.9 on ubuntu 13.10 x64 (virtualization).",[18,2388,2389],{},"Even if writing some boilerplate is not that interesting, writing less boilerplate is! So instead of writing your own\nwheel, you might want to spend your energy on making another wheel feel more rounded.",[18,2391,2392],{},"Whether a script engine can be used in your production environment depends on your configuration, of course, but writing\nsome glue to reuse another solutions might be worth thinking about it.",[18,2394,2395],{},"The effort could be less in comparison to a full rewrite. Stick to a separation of interfaces and implementations and\nlet the environment decide.",[18,2397,2398],{},"The devs. from Rhino/Nashorn/JRuby did quite a good job! As well as the devs. from the mentioned libraries. You should\ncompile the project with Java 1.6(!), 1.7 and 1.8 and look at the results.",[18,2400,2401,2404,2408,2411,2415,2418,2422,2425,2429,2432,2436,2439,2443,2446,2450,2453,2457,2460,2464,2466,2470,2472,2476,2478,2482,2484,2488,2490,2494,2496,2500,2502],{},[193,2402,2403],{}," 1",[22,2405,2406],{"href":2406,"rel":2407},"http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform",[26],[193,2409,2410],{}," 2",[22,2412,2413],{"href":2413,"rel":2414},"http://stackoverflow.com/questions/11838369/where-can-i-find-a-list-of-available-jsr-223-scripting-languages",[26],[193,2416,2417],{}," 3",[22,2419,2420],{"href":2420,"rel":2421},"https://media.synyx.de/uploads//2014/01/synyx.sample.zip",[26],[193,2423,2424],{}," 4",[22,2426,2427],{"href":2427,"rel":2428},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace",[26],[193,2430,2431],{}," 5",[22,2433,2434],{"href":2434,"rel":2435},"http://requirejs.org/",[26],[193,2437,2438],{}," 6",[22,2440,2441],{"href":2441,"rel":2442},"http://de.wikipedia.org/wiki/Public-Key-Infrastruktur",[26],[193,2444,2445],{}," 7",[22,2447,2448],{"href":2448,"rel":2449},"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind",[26],[193,2451,2452],{}," 8",[22,2454,2455],{"href":2455,"rel":2456},"http://prototypejs.org/doc/latest/language/Function/prototype/bind/",[26],[193,2458,2459],{}," 9",[22,2461,2462],{"href":2462,"rel":2463},"http://www.jruby.org/",[26],[193,2465,2158],{},[22,2467,2468],{"href":2468,"rel":2469},"https://github.com/pdfkit/pdfkit",[26],[193,2471,2174],{},[22,2473,2474],{"href":2474,"rel":2475},"http://jade-lang.com/",[26],[193,2477,2178],{},[22,2479,2480],{"href":2480,"rel":2481},"https://github.com/neuland/jade4j",[26],[193,2483,2187],{},[22,2485,2486],{"href":2486,"rel":2487},"http://blog.nicksieger.com/articles/2009/01/10/jruby-1-1-6-gems-in-a-jar/",[26],[193,2489,2191],{},[22,2491,2492],{"href":2492,"rel":2493},"http://yokolet.blogspot.de/2010/10/gems-in-jar-with-redbridge.html",[26],[193,2495,2198],{},[22,2497,2498],{"href":2498,"rel":2499},"http://maven.apache.org/",[26],[193,2501,2385],{},[22,2503,2504],{"href":2504,"rel":2505},"https://code.google.com/p/wkhtmltopdf/",[26],[435,2507,2508],{},"html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":189,"searchDepth":203,"depth":203,"links":2510},[2511,2512,2513,2514],{"id":1359,"depth":209,"text":1348},{"id":1407,"depth":209,"text":1408},{"id":1825,"depth":209,"text":1826},{"id":2147,"depth":209,"text":2148},[447],"2014-01-22T10:47:14","https://synyx.de/blog/code-gluse/",{},"/blog/code-gluse",{"title":1348,"description":189},"blog/code-gluse",[459,460,2523,2524,2525,2526,2527,2528],"jruby","jsr-223","mozilla-rhino","oracle-nashorn","pdfkit","wkhtmltopdf","Code gluse Today’s post targets an API, which has been released on Dec. 11, 2006; the javax.scripting package [1] and a lot of good articles that have been written around…","M4C3LMe5gjOAJ7fTRdyMF5pjkdkf_FbgJp5K0AK4qnA",[2532,2535,2538,2541,2544,2547,2550,2553,2556,2559,2562,2565,2568,2570,2573,2576,2579,2582,2585,2588,2591,2594,2596,2599,2602,2605,2608,2610,2613,2616,2619,2622,2625,2628,2631,2634,2637,2640,2643,2646,2649,2652,2655,2658,2661,2664,2667,2670,2673,2676,2679,2682,2685,2688,2691,2694,2697,2700,2703,2706,2709,2712,2715,2718,2721,2724,2727,2730,2733,2736,2739,2742,2745,2748,2751,2754,2757,2760,2763,2766,2769,2772,2775,2778,2781,2784,2787,2790,2793,2795,2798,2801,2804,2807,2810,2813,2816,2819,2822,2824,2827,2830,2833,2836,2838,2841,2844,2847,2850,2853,2856,2858,2860,2863,2866,2869,2872,2875,2878,2881,2884,2887,2890,2893,2896,2899,2902,2905,2908,2911,2912,2915,2918,2921,2924,2927,2930,2933,2936,2939,2942,2945],{"slug":2533,"name":2534},"abel","Jennifer Abel",{"slug":2536,"name":2537},"allmendinger","Otto Allmendinger",{"slug":2539,"name":2540},"antony","Ben Antony",{"slug":2542,"name":2543},"arrasz","Joachim Arrasz",{"slug":2545,"name":2546},"bauer","David Bauer",{"slug":2548,"name":2549},"bechtold","Janine Bechtold",{"slug":2551,"name":2552},"boersig","Jasmin Börsig",{"slug":2554,"name":2555},"buch","Fabian Buch",{"slug":2557,"name":2558},"buchloh","Aljona Buchloh",{"slug":2560,"name":2561},"burgard","Julia Burgard",{"slug":2563,"name":2564},"caspar-schwedes","Caspar Schwedes",{"slug":2566,"name":2567},"christina-schmitt","Christina Schmitt",{"slug":1350,"name":2569},"Michael Clausen",{"slug":2571,"name":2572},"contargo_poetzsch","Thomas Pötzsch",{"slug":2574,"name":2575},"damrath","Sebastian Damrath",{"slug":2577,"name":2578},"daniel","Markus Daniel",{"slug":2580,"name":2581},"dasch","Julia Dasch",{"slug":2583,"name":2584},"denman","Joffrey Denman",{"slug":2586,"name":2587},"dfuchs","Daniel Fuchs",{"slug":2589,"name":2590},"dobler","Max Dobler",{"slug":2592,"name":2593},"dobriakov","Vladimir Dobriakov",{"slug":2595,"name":2595},"dreiqbik",{"slug":2597,"name":2598},"dschaefer","Denise Schäfer",{"slug":2600,"name":2601},"dschneider","Dominik Schneider",{"slug":2603,"name":2604},"duerlich","Isabell Duerlich",{"slug":2606,"name":2607},"dutkowski","Bernd Dutkowski",{"slug":2609,"name":2609},"eifler",{"slug":2611,"name":2612},"essig","Tim Essig",{"slug":2614,"name":2615},"ferstl","Maximilian Ferstl",{"slug":2617,"name":2618},"fey","Prisca Fey",{"slug":2620,"name":2621},"frank","Leonard Frank",{"slug":2623,"name":2624},"franke","Arnold Franke",{"slug":2626,"name":2627},"frischer","Nicolette Rudmann",{"slug":2629,"name":2630},"fuchs","Petra Fuchs",{"slug":2632,"name":2633},"gari","Sarah Gari",{"slug":2635,"name":2636},"gast","Gast",{"slug":2638,"name":2639},"graf","Johannes Graf",{"slug":2641,"name":2642},"grammlich","Daniela Grammlich",{"slug":2644,"name":2645},"guthardt","Sabrina Guthardt",{"slug":2647,"name":2648},"haeussler","Johannes Häussler",{"slug":2650,"name":2651},"hammann","Daniel Hammann",{"slug":2653,"name":2654},"heetel","Julian Heetel",{"slug":2656,"name":2657},"heft","Florian Heft",{"slug":2659,"name":2660},"heib","Sebastian Heib",{"slug":2662,"name":2663},"heisler","Ida Heisler",{"slug":2665,"name":2666},"helm","Patrick Helm",{"slug":2668,"name":2669},"herbold","Michael Herbold",{"slug":2671,"name":2672},"hofmann","Peter Hofmann",{"slug":2674,"name":2675},"hopf","Florian Hopf",{"slug":2677,"name":2678},"jaud","Alina Jaud",{"slug":2680,"name":2681},"jayasinghe","Robin De Silva Jayasinghe",{"slug":2683,"name":2684},"jbuch","Jonathan Buch",{"slug":2686,"name":2687},"junghanss","Gitta Junghanß",{"slug":2689,"name":2690},"kadyietska","Khrystyna Kadyietska",{"slug":2692,"name":2693},"kannegiesser","Marc Kannegiesser",{"slug":2695,"name":2696},"karoly","Robert Károly",{"slug":2698,"name":2699},"karrasz","Katja Arrasz-Schepanski",{"slug":2701,"name":2702},"kaufmann","Florian Kaufmann",{"slug":2704,"name":2705},"kesler","Mike Kesler",{"slug":2707,"name":2708},"kirchgaessner","Bettina Kirchgäßner",{"slug":2710,"name":2711},"klem","Yannic Klem",{"slug":2713,"name":2714},"klenk","Timo Klenk",{"slug":2716,"name":2717},"knell","Tobias Knell",{"slug":2719,"name":2720},"knoll","Anna-Lena Knoll",{"slug":2722,"name":2723},"knorre","Matthias Knorre",{"slug":2725,"name":2726},"koenig","Melanie König",{"slug":2728,"name":2729},"kraft","Thomas Kraft",{"slug":2731,"name":2732},"krupicka","Florian Krupicka",{"slug":2734,"name":2735},"kuehn","Christian Kühn",{"slug":2737,"name":2738},"lange","Christian Lange",{"slug":2740,"name":2741},"larrasz","Luca Arrasz",{"slug":2743,"name":2744},"leist","Sascha Leist",{"slug":2746,"name":2747},"lihs","Michael Lihs",{"slug":2749,"name":2750},"linsin","David Linsin",{"slug":2752,"name":2753},"maniyar","Christian Maniyar",{"slug":2755,"name":2756},"martin","Björnie",{"slug":2758,"name":2759},"martin-koch","Martin Koch",{"slug":2761,"name":2762},"matt","Tobias Matt",{"slug":2764,"name":2765},"mennerich","Christian Mennerich",{"slug":2767,"name":2768},"menz","Alexander Menz",{"slug":2770,"name":2771},"meseck","Frederick Meseck",{"slug":2773,"name":2774},"messner","Oliver Messner",{"slug":2776,"name":2777},"michael-ploed","Michael Plöd",{"slug":2779,"name":2780},"mies","Marius Mies",{"slug":2782,"name":2783},"mihai","Alina Mihai",{"slug":2785,"name":2786},"moeller","Jörg Möller",{"slug":2788,"name":2789},"mohr","Rebecca Mohr",{"slug":2791,"name":2792},"moretti","David Moretti",{"slug":1074,"name":2794},"Sven Müller",{"slug":2796,"name":2797},"muessig","Alexander Müssig",{"slug":2799,"name":2800},"neupokoev","Grigory Neupokoev",{"slug":2802,"name":2803},"nussbaecher","Carmen Nussbächer",{"slug":2805,"name":2806},"ochs","Pascal Ochs",{"slug":2808,"name":2809},"oelhoff","Jan Oelhoff",{"slug":2811,"name":2812},"oengel","Yasin Öngel",{"slug":2814,"name":2815},"oezsoy","Enis Özsoy",{"slug":2817,"name":2818},"posch","Maya Posch",{"slug":2820,"name":2821},"ralfmueller","Ralf Müller",{"slug":2823,"name":2823},"redakteur",{"slug":2825,"name":2826},"reich","Michael Reich",{"slug":2828,"name":2829},"reinhard","Karl-Ludwig Reinhard",{"slug":2831,"name":2832},"rmueller","Rebecca Müller",{"slug":2834,"name":2835},"rosum","Jan Rosum",{"slug":2837,"name":2837},"rueckert",{"slug":2839,"name":2840},"ruessel","Sascha Rüssel",{"slug":2842,"name":2843},"sauter","Moritz Sauter",{"slug":2845,"name":2846},"schaefer","Julian Schäfer",{"slug":2848,"name":2849},"scherer","Petra Scherer",{"slug":2851,"name":2852},"schlicht","Anne Schlicht",{"slug":2854,"name":2855},"schmidt","Jürgen Schmidt",{"slug":1075,"name":2857},"Tobias Schneider",{"slug":9,"name":2859},"Benjamin Seber",{"slug":2861,"name":2862},"sommer","Marc Sommer",{"slug":2864,"name":2865},"speaker-fels","Jakob Fels",{"slug":2867,"name":2868},"speaker-gierke","Oliver Gierke",{"slug":2870,"name":2871},"speaker-krupa","Malte Krupa",{"slug":2873,"name":2874},"speaker-mader","Jochen Mader",{"slug":2876,"name":2877},"speaker-meusel","Tim Meusel",{"slug":2879,"name":2880},"speaker-milke","Oliver Milke",{"slug":2882,"name":2883},"speaker-paluch","Mark Paluch",{"slug":2885,"name":2886},"speaker-schad","Jörg Schad",{"slug":2888,"name":2889},"speaker-schalanda","Jochen Schalanda",{"slug":2891,"name":2892},"speaker-schauder","Jens Schauder",{"slug":2894,"name":2895},"speaker-unterstein","Johannes Unterstein",{"slug":2897,"name":2898},"speaker-wolff","Eberhard Wolff",{"slug":2900,"name":2901},"speaker-zoerner","Stefan Zörner",{"slug":2903,"name":2904},"stefan-belger","Stefan Belger",{"slug":2906,"name":2907},"steinegger","Roland Steinegger",{"slug":2909,"name":2910},"stern","sternchen synyx",{"slug":1094,"name":1094},{"slug":2913,"name":2914},"szulc","Mateusz Szulc",{"slug":2916,"name":2917},"tamara","Tamara Tunczinger",{"slug":2919,"name":2920},"theuer","Tobias Theuer",{"slug":2922,"name":2923},"thieme","Sandra Thieme",{"slug":2925,"name":2926},"thies-clasen","Marudor",{"slug":2928,"name":2929},"toernstroem","Olle Törnström",{"slug":2931,"name":2932},"ullinger","Max Ullinger",{"slug":2934,"name":2935},"ulrich","Stephan Ulrich",{"slug":2937,"name":2938},"wagner","Stefan Wagner",{"slug":2940,"name":2941},"weigel","Andreas Weigel",{"slug":2943,"name":2944},"werner","Fabian Werner",{"slug":2946,"name":2947},"wolke","Sören Wolke",["Reactive",2949],{"$scookieConsent":2950,"$ssite-config":2952},{"functional":2951,"analytics":2951},false,{"_priority":2953,"env":2957,"name":2958,"url":2959},{"name":2954,"env":2955,"url":2956},-10,-15,0,"production","nuxt-app","https://synyx.de",["Set"],["ShallowReactive",2962],{"category-js":-1,"authors":-1},"/blog/tags/js"]