{"id":28,"date":"2007-08-07T06:19:00","date_gmt":"2007-08-07T06:19:00","guid":{"rendered":"http:\/\/euve3303.vserver.de\/stefan\/blog\/?p=30"},"modified":"2007-08-07T06:19:00","modified_gmt":"2007-08-07T06:19:00","slug":"ableiten-von-final-klassen-mit-gluonj","status":"publish","type":"post","link":"https:\/\/cogito-ergo-blog.de\/blog\/2007\/08\/07\/ableiten-von-final-klassen-mit-gluonj\/","title":{"rendered":"Ableiten von Final-Klassen mit Gluonj"},"content":{"rendered":"<h3>\u00c4rgernis beim Testen<\/h3>\n<p>Beim Testen mit Unittests verwendet man meist Mock-Objecte, die die &#8220;Umgebung&#8221; der Klasse, die zu Testen ist &#8220;simulieren&#8221;. Dumm nur, dass diese Umgebung manchmal aus Klassen besteht, die kein Interface implementieren. Wenn dann auch noch einer dieser Klassen &#8220;final&#8221; ist (oder eine Methode), dann ist man selbst mit so sch\u00f6nen Mock &#8220;Generatoren&#8221; wie <a href=\"http:\/\/www.easymock.org\">EasyMock<\/a> am Ende.<\/p>\n<h3>EasyMock Classextensions<\/h3>\n<p>Zwar kann EasyMock mit den ClassExtensions auch Mock-Objecte von Klassen erzeugen, aber dazu d\u00fcrfen diese nicht &#8220;final&#8221; sein. Das hat damit zu tun, dass unter der Haube mit Hilfe der CGLIB eine Ableitung der betreffenden Klasse erzeugt wird, die als Mock-Object dient. Das aber kann nur funktionieren, wann Ableiten erlaubt ist. Hier kann <a href=\"http:\/\/www.csg.is.titech.ac.jp\/projects\/gluonj\/\">GLUONJ<\/a> Abhilfe schaffen &#8230;<\/p>\n<p><!--more--><\/p>\n<h3>AOP mit GLUONJ<\/h3>\n<p>Gluonj ist ein kleines aber feines AOP-Framework, das auf <a href=\"http:\/\/www.csg.is.titech.ac.jp\/~chiba\/javassist\/\">Javaassist<\/a> aufbaut. Mit Hilfe von Annotationen werden sogenannte &#8220;Glues&#8221; definiert, die bestimmen, wie der Bytecode ver\u00e4ndert werden soll.<br \/>\nEin Gluon ist eine Java-Klasse, die mit bestimmten Annotation &#8220;verziert&#8221; wird und so den Code-Weaver von Gluonj steuert. Normalerweise wird der Code Weaver als JavaAgent in die VM eingebunden, was aber nur geht, wenn man einen Startparameter der VM selbst angeben kann. Beim Testen ist sowas eher unpraktisch. Man kann jedoch ganz leicht auch einen ClassLoader schreiben, der eine mit Gluonj-gewobene Klasse zur Laufzeit laden kann.\n<\/p>\n<p>Ein solcher ClassLoader sieht in etwa so aus:<\/p>\n<blockquote style=\"border: 1px solid gray; overflow: auto; font-size: 100%; height: 300px; background-color: rgb(238, 238, 238);\"><p>\n<tt class=\"java\"><span class=\"java4\">package <\/span><span class=\"java10\">com.rinke.solutions.gluonj;<\/p>\n<p><\/span><span class=\"java4\">import <\/span><span class=\"java10\">java.io.ByteArrayOutputStream;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">java.io.IOException;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">java.io.InputStream;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">java.util.HashMap;<\/p>\n<p><\/span><span class=\"java4\">import <\/span><span class=\"java10\">javassist.gluonj.WeaveException;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">javassist.gluonj.weave.Weaver;<\/p>\n<p><\/span><span class=\"java14\">\/**<br \/>\n * a class loader that uses a gluonj weaver to transform classes.<br \/>\n * <\/span><span class=\"java11\">@author <\/span><span class=\"java14\">ster<br \/>\n *\/<br \/>\n<\/span><span class=\"java4\">public class <\/span><span class=\"java10\">GluonClassloader <\/span><span class=\"java4\">extends <\/span><span class=\"java10\">ClassLoader <\/span><span class=\"java8\">{<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java14\">\/**<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * the gluonj weaver used by this class loader.<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; *\/<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">private <\/span><span class=\"java10\">Weaver weaver = <\/span><span class=\"java4\">null<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java14\">\/**<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * the parent loader used by the weaver.<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; *\/<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">private <\/span><span class=\"java10\">ClassLoader loader;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java14\">\/**<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * the name of the gluon class.<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; *\/<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">private <\/span><span class=\"java10\">String glueName;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java14\">\/**<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * cache for all classes already loaded by this class loader.<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; *\/<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">private <\/span><span class=\"java10\">HashMap&lt;String,Class&gt; classes = <\/span><span class=\"java4\">new <\/span><span class=\"java10\">HashMap&lt;String,Class&gt;<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java14\">\/**<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * constructor<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * <\/span><span class=\"java11\">@param <\/span><span class=\"java14\">loader the class used by the weaver (normally the parent class loader \/ system class loader )<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * <\/span><span class=\"java11\">@param <\/span><span class=\"java14\">glueName the class name of the &#34;glue&#34; class (see gluonj documentation)<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; *\/<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public <\/span><span class=\"java10\">GluonClassloader<\/span><span class=\"java8\">(<\/span><span class=\"java10\">ClassLoader loader, String glueName<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">super<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">this<\/span><span class=\"java10\">.loader = loader;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">this<\/span><span class=\"java10\">.glueName = glueName;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java14\">\/**<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * load and transform a the given class.<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * <\/span><span class=\"java11\">@param <\/span><span class=\"java14\">classname the name of the class to load<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * <\/span><span class=\"java11\">@param <\/span><span class=\"java14\">resolveIt flag wether to call resolve or not<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; *\/<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java16\">@Override<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public synchronized <\/span><span class=\"java10\">Class&lt;?&gt; loadClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String classname, <\/span><span class=\"java9\">boolean <\/span><span class=\"java10\">resolveIt<\/span><span class=\"java8\">) <\/span><span class=\"java4\">throws <\/span><span class=\"java10\">ClassNotFoundException <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">try <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ init the code weaver, if null<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">weaver == <\/span><span class=\"java4\">null <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">weaver = <\/span><span class=\"java4\">new <\/span><span class=\"java10\">Weaver<\/span><span class=\"java8\">(<\/span><span class=\"java10\">glueName, loader<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ check cache<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">Class result = <\/span><span class=\"java8\">(<\/span><span class=\"java10\">Class<\/span><span class=\"java8\">)<\/span><span class=\"java10\">classes.get<\/span><span class=\"java8\">(<\/span><span class=\"java10\">classname<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">result != <\/span><span class=\"java4\">null <\/span><span class=\"java8\">) { <\/span><span class=\"java3\">\/\/ if cache hit, return it<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return <\/span><span class=\"java10\">result;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ exclude system classes (wont work)<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">classname.startsWith<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;java.&#34;<\/span><span class=\"java8\">) <\/span><span class=\"java10\">|| classname.startsWith<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;javax.&#34;<\/span><span class=\"java8\">)) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return super<\/span><span class=\"java10\">.loadClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">classname, resolveIt<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ load bytes from class path of parent class loader (normal classpath, set by VM)<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java9\">byte<\/span><span class=\"java8\">[] <\/span><span class=\"java10\">orgBytes = loadClassFromClassPath<\/span><span class=\"java8\">(<\/span><span class=\"java10\">classname<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ if found try to transform it<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">orgBytes != <\/span><span class=\"java4\">null <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java9\">byte<\/span><span class=\"java8\">[] <\/span><span class=\"java10\">classBytes = weaver.transform<\/span><span class=\"java8\">(<\/span><span class=\"java10\">classname, orgBytes<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ transformation needed?<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">classBytes == <\/span><span class=\"java4\">null <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ no, do it the normal way<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return super<\/span><span class=\"java10\">.loadClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">classname, resolveIt<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ generate class from the transformed class<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">result = defineClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">classname, classBytes, <\/span><span class=\"java7\">0<\/span><span class=\"java10\">, classBytes.length<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ if successful<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">result != <\/span><span class=\"java4\">null <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ resolve if, if requested<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">resolveIt <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">resolveClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">result<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ store in my cache<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">classes.put<\/span><span class=\"java8\">(<\/span><span class=\"java10\">classname, result<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ and return it<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return <\/span><span class=\"java10\">result;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; }<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ else there is not such class<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">throw new <\/span><span class=\"java10\">ClassNotFoundException<\/span><span class=\"java8\">(<\/span><span class=\"java10\">classname + <\/span><span class=\"java5\">&#34; not found&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">} <\/span><span class=\"java4\">catch <\/span><span class=\"java8\">(<\/span><span class=\"java10\">WeaveException e<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ the weaver has a problem ???<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">throw new <\/span><span class=\"java10\">ClassNotFoundException<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;weaving problems&#34;<\/span><span class=\"java10\">,e<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0; }<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java14\">\/**<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * load class bytes from system class path using getSystemResourceAsStream<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * <\/span><span class=\"java11\">@param <\/span><span class=\"java14\">name the name of the class<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * <\/span><span class=\"java11\">@return <\/span><span class=\"java14\">the classes bytes or null if not found<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; *\/<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">private <\/span><span class=\"java9\">byte<\/span><span class=\"java8\">[] <\/span><span class=\"java10\">loadClassFromClassPath<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String name<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">String filename = name.replace <\/span><span class=\"java8\">(<\/span><span class=\"java6\">'.'<\/span><span class=\"java10\">, <\/span><span class=\"java6\">'\/'<\/span><span class=\"java8\">) <\/span><span class=\"java10\">+ <\/span><span class=\"java5\">&#34;.class&#34;<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">try <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java9\">byte<\/span><span class=\"java8\">[] <\/span><span class=\"java10\">buffer = <\/span><span class=\"java4\">new <\/span><span class=\"java9\">byte<\/span><span class=\"java8\">[<\/span><span class=\"java7\">10000<\/span><span class=\"java8\">]<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; ByteArrayOutputStream bos = <\/span><span class=\"java4\">new <\/span><span class=\"java10\">ByteArrayOutputStream<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; InputStream i = getSystemResourceAsStream<\/span><span class=\"java8\">(<\/span><span class=\"java10\">filename<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">i != <\/span><span class=\"java4\">null <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">while<\/span><span class=\"java8\">(<\/span><span class=\"java10\">i.available<\/span><span class=\"java8\">() <\/span><span class=\"java10\">&gt; <\/span><span class=\"java7\">0 <\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java9\">int <\/span><span class=\"java10\">r = i.read<\/span><span class=\"java8\">(<\/span><span class=\"java10\">buffer<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">(&#xA0; <\/span><span class=\"java10\">r &lt; <\/span><span class=\"java7\">0<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">break<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">bos.write<\/span><span class=\"java8\">(<\/span><span class=\"java10\">buffer, <\/span><span class=\"java7\">0<\/span><span class=\"java10\">, r<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">i.close<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; bos.close<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return <\/span><span class=\"java10\">bos.toByteArray<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; } <\/span><span class=\"java4\">catch <\/span><span class=\"java8\">(<\/span><span class=\"java10\">IOException e<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; }<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return null<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java14\">\/**<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * convenience method<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; *\/<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java16\">@Override<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public <\/span><span class=\"java10\">Class&lt;?&gt; loadClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String classname<\/span><span class=\"java8\">) <\/span><span class=\"java4\">throws <\/span><span class=\"java10\">ClassNotFoundException <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return <\/span><span class=\"java10\">loadClass<\/span><span class=\"java8\">(<\/span><span class=\"java10\">classname, <\/span><span class=\"java4\">true<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n}<\/span><\/tt><\/p>\n<\/blockquote>\n<p>Hat man nun eine Final-Klasse, die f\u00fcr einen Test ver\u00e4ndert werden soll &#8230;<\/p>\n<blockquote style=\"border: 1px solid gray; overflow: auto; font-size: 100%; height: 120px; background-color: rgb(238, 238, 238);\"><p>\n<tt class=\"java\"><span class=\"java4\">package <\/span><span class=\"java10\">test;<\/p>\n<p><\/span><span class=\"java4\">public final class <\/span><span class=\"java10\">FinalTest <\/span><span class=\"java8\">{<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public <\/span><span class=\"java10\">String getHello<\/span><span class=\"java8\">() {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return <\/span><span class=\"java5\">&#34;this class is final&#34;<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>}<\/span><\/tt>\n<\/p><\/blockquote>\n<p>dann kann man mit Gluonj eine Gluon-Klasse definieren, die die FinalTest Klasse wie gew\u00fcnscht \u00e4ndert:<\/p>\n<blockquote style=\"border: 1px solid gray; overflow: auto; font-size: 100%; height: 120px; background-color: rgb(238, 238, 238);\"><p>\n<tt class=\"java\"><span class=\"java4\">package <\/span><span class=\"java10\">com.rinke.solutions.gluonj;<\/p>\n<p><\/span><span class=\"java4\">import <\/span><span class=\"java10\">javassist.gluonj.Glue;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">javassist.gluonj.Privileged;<br \/>\n<\/span><span class=\"java4\">import <\/span><span class=\"java10\">javassist.gluonj.Refine;<\/p>\n<p><\/span><span class=\"java14\">\/**<br \/>\n * just a test glue, that refines a final class<br \/>\n * <\/span><span class=\"java11\">@author <\/span><span class=\"java14\">ster<br \/>\n *\/<br \/>\n<\/span><span class=\"java16\">@Glue <\/span><span class=\"java4\">public class <\/span><span class=\"java10\">MyGlue <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java16\">@Privileged @Refine<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;test.FinalTest&#34;<\/span><span class=\"java8\">)<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public static class <\/span><span class=\"java10\">Diff&#xA0; <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public <\/span><span class=\"java10\">String getHello<\/span><span class=\"java8\">() {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">return <\/span><span class=\"java5\">&#34;hello&#34;<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0; }<\/p>\n<p>}<\/span><\/tt>\n<\/p><\/blockquote>\n<p>So wird zwar nicht von der finalen Klassen abgeleitet (das ist ja verboten \ud83d\ude09 ) aber der GluonClassloader \u00e4ndert die Klasse beim Laden wie gew\u00fcnscht ab.\n<\/p>\n<p>Jetzt braucht man nur noch ein Test-Programm, welches das Ganze aufruft und den GluonClassloader nutzt:<\/p>\n<blockquote style=\"border: 1px solid gray; overflow: auto; font-size: 100%; height: 300px; background-color: rgb(238, 238, 238);\">\n<p><tt class=\"java\"><span class=\"java4\">package <\/span><span class=\"java10\">com.rinke.solutions.gluonj;<\/p>\n<p><\/span><span class=\"java4\">import <\/span><span class=\"java10\">java.lang.reflect.Method;<\/p>\n<p><\/span><span class=\"java4\">public class <\/span><span class=\"java10\">Main <\/span><span class=\"java8\">{<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java16\">@SuppressWarnings<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;unused&#34;<\/span><span class=\"java8\">)<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">private <\/span><span class=\"java10\">String<\/span><span class=\"java8\">[] <\/span><span class=\"java10\">args;<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public <\/span><span class=\"java10\">Main<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String<\/span><span class=\"java8\">[] <\/span><span class=\"java10\">args<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">this<\/span><span class=\"java10\">.args = args;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java14\">\/**<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; * <\/span><span class=\"java11\">@param <\/span><span class=\"java14\">args<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0; *\/<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public static <\/span><span class=\"java9\">void <\/span><span class=\"java10\">main<\/span><span class=\"java8\">(<\/span><span class=\"java10\">String<\/span><span class=\"java8\">[] <\/span><span class=\"java10\">args<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">Main r = <\/span><span class=\"java4\">new <\/span><span class=\"java10\">Main<\/span><span class=\"java8\">(<\/span><span class=\"java10\">args<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; r.run<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">public <\/span><span class=\"java9\">void <\/span><span class=\"java10\">run<\/span><span class=\"java8\">() {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">GluonClassloader myLoader =<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">new <\/span><span class=\"java10\">GluonClassloader<\/span><span class=\"java8\">(<\/span><span class=\"java4\">this<\/span><span class=\"java10\">.getClass<\/span><span class=\"java8\">()<\/span><span class=\"java10\">.getClassLoader<\/span><span class=\"java8\">()<\/span><span class=\"java10\">,<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java5\">&#34;com.rinke.solutions.gluonj.MyGlue&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">try <\/span><span class=\"java8\">{<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">Class&lt;?&gt; c = myLoader.loadClass<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;test.FinalTest&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java3\">\/\/ do not cast to FinalTest, because its loaded by a different class loader<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">Object ft = c.newInstance<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; Method method = c.getMethod<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;getHello&#34;<\/span><span class=\"java10\">, <\/span><span class=\"java4\">new <\/span><span class=\"java10\">Class<\/span><span class=\"java8\">[] {})<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; Object result = method.invoke<\/span><span class=\"java8\">(<\/span><span class=\"java10\">ft, <\/span><span class=\"java4\">new <\/span><span class=\"java10\">Object<\/span><span class=\"java8\">[]{})<\/span><span class=\"java10\">;<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java4\">if<\/span><span class=\"java8\">( <\/span><span class=\"java10\">result.equals<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;hello&#34;<\/span><span class=\"java8\">) ) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">System.out.println<\/span><span class=\"java8\">(<\/span><span class=\"java5\">&#34;Test succeeded&#34;<\/span><span class=\"java8\">)<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<\/p>\n<p>&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; } <\/span><span class=\"java4\">catch <\/span><span class=\"java8\">(<\/span><span class=\"java10\">Exception e<\/span><span class=\"java8\">) {<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java10\">e.printStackTrace<\/span><span class=\"java8\">()<\/span><span class=\"java10\">;<br \/>\n&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0; <\/span><span class=\"java8\">}<br \/>\n&#xA0;&#xA0;&#xA0; }<\/p>\n<p>}<\/span><\/tt>\n<\/p><\/blockquote>\n<p>\nZu beachten ist hierbei, dass die FinalTest-Klassen nicht auch im Main benutzt wird, weil die Klasse, ja dann schon vom SystemClassLoader geladen w\u00fcrde. Ein<\/p>\n<blockquote><p>\n<span class=\"java10\">FinalTest ft = (FinalTest) c.newInstance();<\/span>\n<\/p><\/blockquote>\n<p>w\u00fcrde z.B. eine ClassCast-Exception ausl\u00f6sen, weil c vom GluonClassloader geladen wurde und &#8220;FinalTest&#8221; in Main aber vom SystemClassLoader.\n<\/p>\n<p>\nMit Gluonj lassen sich noch weitere &#8220;h\u00fcbsche&#8221; Dinge anstellen (siehe <a href=\"http:\/\/www.csg.is.titech.ac.jp\/projects\/gluonj\/documentation\/index.html\">Gluonj-Dokumentation<\/a>), die sich nicht nur f\u00fcr Tests nutzen lassen.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u00c4rgernis beim Testen Beim Testen mit Unittests verwendet man meist Mock-Objecte, die die &#8220;Umgebung&#8221; der Klasse, die zu Testen ist &#8220;simulieren&#8221;. Dumm nur, dass diese Umgebung manchmal aus Klassen besteht, die kein Interface implementieren. Wenn dann auch noch einer dieser &hellip; <a href=\"https:\/\/cogito-ergo-blog.de\/blog\/2007\/08\/07\/ableiten-von-final-klassen-mit-gluonj\/\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"_links":{"self":[{"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/posts\/28"}],"collection":[{"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/comments?post=28"}],"version-history":[{"count":0,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/posts\/28\/revisions"}],"wp:attachment":[{"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/media?parent=28"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/categories?post=28"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cogito-ergo-blog.de\/blog\/wp-json\/wp\/v2\/tags?post=28"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}