<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4775403822715180937</id><updated>2011-09-14T04:59:42.228-07:00</updated><category term='custom classloader'/><category term='java'/><category term='JavaCompiler'/><title type='text'>Technology excruciation</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://atamur.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://atamur.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>atamur</name><uri>http://www.blogger.com/profile/03950407766693025318</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yg02DF9XetM/SN9kfinEuLI/AAAAAAAAChE/niDOQmP7djI/S220/me3.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>6</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4775403822715180937.post-4761918810050501202</id><published>2009-10-25T06:02:00.000-07:00</published><updated>2009-10-27T13:56:38.152-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='custom classloader'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaCompiler'/><title type='text'>Using built-in JavaCompiler with a custom classloader</title><content type='html'>Java 6 has a great feature built into the JVM (when it comes with JDK apparently): a programmatical access to javac functionality. There's a great article on &lt;a href="http://www.ibm.com/developerworks/java/library/j-jcomp/index.html"&gt;developer works&lt;/a&gt; that helped me a lot when I was implementing a feature in my application that was using this functionality. In fact I just took the code from that article, applied it to my project and everything worked like magic when I was running locally from my IDE =)&lt;br /&gt;&lt;br /&gt;So it was fine until I deployed the app to a real-life server. There the code stopped working completely. Which was not really a big surprise to me but quite frustrating. Looking into details I was able to find out the reason. The application utilising this code was loaded on the server by a custom classloader. I.e. the jvm was started with a single jar in classpath that was capable of loading different versions of the application in separate classloaders (don't ask me for reasons pls).  So the compiler was failing unable to resolve dependencies.&lt;br /&gt;&lt;br /&gt;Ok, let's do a simple example.&lt;br /&gt;Imagine that this loading jar is called &lt;i&gt;loader.jar&lt;/i&gt; and the application itself is contained in a jar &lt;i&gt;app.jar&lt;/i&gt;. Among others there is an interface ru.atamur.Compiled there. And finally I'm trying to compile a source of:&lt;br /&gt;&lt;pre class="brush: java"&gt;&lt;br /&gt;package ru.atamur.compiled;&lt;br /&gt;&lt;br /&gt;import ru.atamur.*;&lt;br /&gt;&lt;br /&gt;public class Generated implements Compiled {&lt;br /&gt;  @Overrides public int sum(int a, int b) { return a+b; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Apparently I need the interface to access my compiled methods, so I have this dependency on classes inside the &lt;i&gt;app.jar&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;So why was it running locally?&lt;br /&gt;if you check out the code from the article it contains this line:&lt;br /&gt;&lt;pre class="brush: java"&gt;&lt;br /&gt;JavaFileManager fileManager &lt;br /&gt;  = compiler.getStandardFileManager(diagnostics, null, null);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Basically &lt;i&gt;JavaFileManager&lt;/i&gt; is something that allows javac to ask for classes it can't find in the source code - the dependencies.&lt;br /&gt;&lt;i&gt;compiler.getStandarfFileManager&lt;/i&gt; matching its name returns you a standard manager. Probably the one that's used by command line javac. The runtime version uses the jvm classpath to resolve all the FQNs encountered. When I was running locally - my whole app (the &lt;i&gt;app.jar&lt;/i&gt;) was on my classpath, so this standard file manager was doing just fine, but when running on the server the only jar on the classpath was &lt;i&gt;loader.jar&lt;/i&gt; that didn't contain &lt;i&gt;ru.atamur.Compiled&lt;/i&gt; causing a compile error.&lt;br /&gt;&lt;br /&gt;This might seem a bit artificial but the same problem will occur in any user code that's run inside a container (app server, web server, ioc container etc).&lt;br /&gt;&lt;br /&gt;The most irritating detail was: all the classes the compiler needs are here in current thread's classloader, but there's no way to tell it about this fact!&lt;br /&gt;&lt;br /&gt;Browsing google I wasn't able to find a proper solution so here's what I finally came up with: use my very own implementation of &lt;i&gt;JavaFileManager&lt;/i&gt; that resolves the user class dependencies using a given classloader and resolves system dependencies (like java.lang) using standard file manager.&lt;br /&gt;&lt;br /&gt;The solution might be not enough for every possible application but was just fine for my app.&lt;br /&gt;&lt;br /&gt;First we modify that line from CharSequenceCompiler seen above:&lt;br /&gt;&lt;pre class="brush: java"&gt;&lt;br /&gt;    StandardJavaFileManager standardJavaFileManager &lt;br /&gt;      = compiler.getStandardFileManager(diagnostics, null, null);&lt;br /&gt;    final JavaFileManager fileManager&lt;br /&gt;      = new CustomClassloaderJavaFileManager(loader, standardJavaFileManager);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So this introduces a new class called &lt;i&gt;CustomClassloaderJavaFileManager&lt;/i&gt; which takes as a parameter a classloader that knows where application classes reside and the standard file manager that can resolve dependencies to standard java classes.&lt;br /&gt;&lt;br /&gt;Here is the definition of &lt;i&gt;CustomClassloaderJavaFileManager&lt;/i&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre  class="brush: java"&gt;&lt;br /&gt;package ru.atamur.compilation;&lt;br /&gt;&lt;br /&gt;import java.io.*;&lt;br /&gt;import java.util.*;&lt;br /&gt;import javax.tools.*;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * @author atamur&lt;br /&gt; * @since 15-Oct-2009&lt;br /&gt; */&lt;br /&gt;public class CustomClassloaderJavaFileManager implements JavaFileManager {&lt;br /&gt; private final ClassLoader classLoader;&lt;br /&gt; private final StandardJavaFileManager standardFileManager;&lt;br /&gt; private final PackageInternalsFinder finder;&lt;br /&gt;&lt;br /&gt; public CustomClassloaderJavaFileManager(ClassLoader classLoader, StandardJavaFileManager standardFileManager) {&lt;br /&gt;  this.classLoader = classLoader;&lt;br /&gt;  this.standardFileManager = standardFileManager;&lt;br /&gt;  finder = new PackageInternalsFinder(classLoader);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public ClassLoader getClassLoader(Location location) {&lt;br /&gt;  return classLoader;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public String inferBinaryName(Location location, JavaFileObject file) {&lt;br /&gt;  if (file instanceof CustomJavaFileObject) {&lt;br /&gt;   return ((CustomJavaFileObject) file).binaryName();&lt;br /&gt;  } else { // if it's not CustomJavaFileObject, then it's coming from standard file manager - let it handle the file&lt;br /&gt;   return standardFileManager.inferBinaryName(location, file);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public boolean isSameFile(FileObject a, FileObject b) {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public boolean handleOption(String current, Iterator&amp;lt;String&amp;gt; remaining) {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public boolean hasLocation(Location location) {&lt;br /&gt;  return location == StandardLocation.CLASS_PATH || location == StandardLocation.PLATFORM_CLASS_PATH; // we don't care about source and other location types - not needed for compilation&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public void flush() throws IOException {&lt;br /&gt;  // do nothing&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public void close() throws IOException {&lt;br /&gt;  // do nothing&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public Iterable&amp;lt;JavaFileObject&amp;gt; list(Location location, String packageName, Set&amp;lt;JavaFileObject.Kind&amp;gt; kinds, boolean recurse) throws IOException {&lt;br /&gt;  if (location == StandardLocation.PLATFORM_CLASS_PATH) { // let standard manager hanfle&lt;br /&gt;   return standardFileManager.list(location, packageName, kinds, recurse);&lt;br /&gt;  } else if (location == StandardLocation.CLASS_PATH &amp;&amp; kinds.contains(JavaFileObject.Kind.CLASS)) {&lt;br /&gt;   if (packageName.startsWith(&amp;quot;java&amp;quot;)) { // a hack to let standard manager handle locations like &amp;quot;java.lang&amp;quot; or &amp;quot;java.util&amp;quot;. Prob would make sense to join results of standard manager with those of my finder here&lt;br /&gt;    return standardFileManager.list(location, packageName, kinds, recurse);&lt;br /&gt;   } else { // app specific classes are here&lt;br /&gt;    return finder.find(packageName);&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;  return Collections.emptyList();&lt;br /&gt;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public int isSupportedOption(String option) {&lt;br /&gt;  return -1;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So this class implements only a subset of functionality defined by JavaFileManager interface but that appears to be enough to handle the compilation (at least in my case). It also has some not-that-pretty code to handle both custom classloader classes and standard java classes.&lt;br /&gt;&lt;br /&gt;There are 2 new classes used here &lt;i&gt;PackageInternalsFinder&lt;/i&gt; and &lt;i&gt;CustomJavaFileObject&lt;/i&gt;. FIrst one is a utility class that translates JavaFileManager requests to classloader requests and the second one is a custom implementation of JavaFileObject interface. JDK already contains an implementation of JavaFileObject: SimpleJavaFileObject based on URI that seemed suitable for my needs but it heavily depends on the URI having non null path which is not true for JAR URIs, so I had to develop my own version:&lt;br /&gt;&lt;pre class="brush: java"&gt;&lt;br /&gt;package ru.atamur.compilation;&lt;br /&gt;&lt;br /&gt;import java.net.URI;&lt;br /&gt;import java.io.*;&lt;br /&gt;import javax.tools.JavaFileObject;&lt;br /&gt;import javax.lang.model.element.NestingKind;&lt;br /&gt;import javax.lang.model.element.Modifier;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* @author atamur&lt;br /&gt;* @since 15-Oct-2009&lt;br /&gt;*/&lt;br /&gt;class CustomJavaFileObject implements JavaFileObject {&lt;br /&gt; private final String binaryName;&lt;br /&gt; private final URI uri;&lt;br /&gt; private final String name;&lt;br /&gt;&lt;br /&gt; public CustomJavaFileObject(String binaryName, URI uri) {&lt;br /&gt;  this.uri = uri;&lt;br /&gt;  this.binaryName = binaryName;&lt;br /&gt;  name = uri.getPath() == null ? uri.getSchemeSpecificPart() : uri.getPath(); // for FS based URI the path is not null, for JAR URI the scheme specific part is not null&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public URI toUri() {&lt;br /&gt;  return uri;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public InputStream openInputStream() throws IOException {&lt;br /&gt;  return uri.toURL().openStream(); // easy way to handle any URI!&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public OutputStream openOutputStream() throws IOException {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public String getName() {&lt;br /&gt;  return name;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public Reader openReader(boolean ignoreEncodingErrors) throws IOException {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public Writer openWriter() throws IOException {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public long getLastModified() {&lt;br /&gt;  return 0;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public boolean delete() {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public Kind getKind() {&lt;br /&gt;  return Kind.CLASS;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override // copied from SImpleJavaFileManager&lt;br /&gt; public boolean isNameCompatible(String simpleName, Kind kind) {&lt;br /&gt;  String baseName = simpleName + kind.extension;&lt;br /&gt;  return kind.equals(getKind())&lt;br /&gt;    &amp;&amp; (baseName.equals(getName())&lt;br /&gt;    || getName().endsWith(&amp;quot;/&amp;quot; + baseName));&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public NestingKind getNestingKind() {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public Modifier getAccessLevel() {&lt;br /&gt;  throw new UnsupportedOperationException();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String binaryName() {&lt;br /&gt;  return binaryName;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt; public String toString() {&lt;br /&gt;  return &amp;quot;CustomJavaFileObject{&amp;quot; +&lt;br /&gt;    &amp;quot;uri=&amp;quot; + uri +&lt;br /&gt;    '}';&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;again I didn't implement methods that are not absolutely required for doing compilation - and that's fine in my case.&lt;br /&gt;&lt;br /&gt;Last bit to actually work out URIs for classes needed by the compiler:&lt;br /&gt;&lt;pre class="brush: java"&gt;&lt;br /&gt;package ru.atamur.compilation;&lt;br /&gt;&lt;br /&gt;import java.util.List;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.Enumeration;&lt;br /&gt;import java.util.Collection;&lt;br /&gt;import java.util.jar.JarEntry;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.File;&lt;br /&gt;import java.net.URL;&lt;br /&gt;import java.net.JarURLConnection;&lt;br /&gt;import java.net.URI;&lt;br /&gt;import javax.tools.JavaFileObject;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* @author atamur&lt;br /&gt;* @since 15-Oct-2009&lt;br /&gt;*/&lt;br /&gt;class PackageInternalsFinder {&lt;br /&gt; private ClassLoader classLoader;&lt;br /&gt; private static final String CLASS_FILE_EXTENSION = &amp;quot;.class&amp;quot;;&lt;br /&gt;&lt;br /&gt; public PackageInternalsFinder(ClassLoader classLoader) {&lt;br /&gt;  this.classLoader = classLoader;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public List&amp;lt;JavaFileObject&amp;gt; find(String packageName) throws IOException {&lt;br /&gt;  String javaPackageName = packageName.replaceAll(&amp;quot;\\.&amp;quot;, &amp;quot;/&amp;quot;);&lt;br /&gt;&lt;br /&gt;  List&amp;lt;JavaFileObject&amp;gt; result = new ArrayList&amp;lt;JavaFileObject&amp;gt;();&lt;br /&gt;&lt;br /&gt;  Enumeration&amp;lt;URL&amp;gt; urlEnumeration = classLoader.getResources(javaPackageName);&lt;br /&gt;  while (urlEnumeration.hasMoreElements()) { // one URL for each jar on the classpath that has the given package&lt;br /&gt;   URL packageFolderURL = urlEnumeration.nextElement();&lt;br /&gt;   result.addAll(listUnder(packageName, packageFolderURL));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private Collection&amp;lt;JavaFileObject&amp;gt; listUnder(String packageName, URL packageFolderURL) {&lt;br /&gt;  File directory = new File(packageFolderURL.getFile());&lt;br /&gt;  if (directory.isDirectory()) { // browse local .class files - useful for local execution&lt;br /&gt;   return processDir(packageName, directory);&lt;br /&gt;  } else { // browse a jar file&lt;br /&gt;   return processJar(packageFolderURL);&lt;br /&gt;  } // maybe there can be something else for more involved class loaders&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private List&amp;lt;JavaFileObject&amp;gt; processJar(URL packageFolderURL) {&lt;br /&gt;  List&amp;lt;JavaFileObject&amp;gt; result = new ArrayList&amp;lt;JavaFileObject&amp;gt;();&lt;br /&gt;  try {&lt;br /&gt;   String jarUri = packageFolderURL.toExternalForm().split(&amp;quot;!&amp;quot;)[0];&lt;br /&gt;&lt;br /&gt;   JarURLConnection jarConn = (JarURLConnection) packageFolderURL.openConnection();&lt;br /&gt;   String rootEntryName = jarConn.getEntryName();&lt;br /&gt;   int rootEnd = rootEntryName.length()+1;&lt;br /&gt;&lt;br /&gt;   Enumeration&amp;lt;JarEntry&amp;gt; entryEnum = jarConn.getJarFile().entries();&lt;br /&gt;   while (entryEnum.hasMoreElements()) {&lt;br /&gt;    JarEntry jarEntry = entryEnum.nextElement();&lt;br /&gt;    String name = jarEntry.getName();&lt;br /&gt;    if (name.startsWith(rootEntryName) &amp;&amp; name.indexOf('/', rootEnd) == -1 &amp;&amp; name.endsWith(CLASS_FILE_EXTENSION)) {&lt;br /&gt;     URI uri = URI.create(jarUri + &amp;quot;!/&amp;quot; + name);&lt;br /&gt;     String binaryName = name.replaceAll(&amp;quot;/&amp;quot;, &amp;quot;.&amp;quot;);&lt;br /&gt;     binaryName = binaryName.replaceAll(CLASS_FILE_EXTENSION + &amp;quot;$&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;&lt;br /&gt;     result.add(new CustomJavaFileObject(binaryName, uri));&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  } catch (Exception e) {&lt;br /&gt;   throw new RuntimeException(&amp;quot;Wasn't able to open &amp;quot; + packageFolderURL + &amp;quot; as a jar file&amp;quot;, e);&lt;br /&gt;  }&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private List&amp;lt;JavaFileObject&amp;gt; processDir(String packageName, File directory) {&lt;br /&gt;  List&amp;lt;JavaFileObject&amp;gt; result = new ArrayList&amp;lt;JavaFileObject&amp;gt;();&lt;br /&gt;&lt;br /&gt;  File[] childFiles = directory.listFiles();&lt;br /&gt;  for (File childFile : childFiles) {&lt;br /&gt;   if (childFile.isFile()) {&lt;br /&gt;    // We only want the .class files.&lt;br /&gt;    if (childFile.getName().endsWith(CLASS_FILE_EXTENSION)) {&lt;br /&gt;     String binaryName = packageName + &amp;quot;.&amp;quot; + childFile.getName();&lt;br /&gt;     binaryName = binaryName.replaceAll(CLASS_FILE_EXTENSION + &amp;quot;$&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;&lt;br /&gt;     result.add(new CustomJavaFileObject(binaryName, childFile.toURI()));&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This code is tested under both windows and linux OS and both from command line and IDE.&lt;br /&gt;&lt;br /&gt;Hope someone will find this in time of need =)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4775403822715180937-4761918810050501202?l=atamur.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://atamur.blogspot.com/feeds/4761918810050501202/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4775403822715180937&amp;postID=4761918810050501202' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/4761918810050501202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/4761918810050501202'/><link rel='alternate' type='text/html' href='http://atamur.blogspot.com/2009/10/using-built-in-javacompiler-with-custom.html' title='Using built-in JavaCompiler with a custom classloader'/><author><name>atamur</name><uri>http://www.blogger.com/profile/03950407766693025318</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yg02DF9XetM/SN9kfinEuLI/AAAAAAAAChE/niDOQmP7djI/S220/me3.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4775403822715180937.post-7989293849397254099</id><published>2008-12-16T11:32:00.000-08:00</published><updated>2008-12-16T11:33:06.455-08:00</updated><title type='text'></title><content type='html'>&lt;a href='http://4.bp.blogspot.com/_yg02DF9XetM/SUgCcMDh6xI/AAAAAAAACk8/WUwMBnyPYes/s1600-h/IMG_1585.JPG'&gt;&lt;img src='http://4.bp.blogspot.com/_yg02DF9XetM/SUgCcMDh6xI/AAAAAAAACk8/WUwMBnyPYes/s320/IMG_1585.JPG' border='0' alt=''style='clear:both;float:left; margin:0px 10px 10px 0;' /&gt;&lt;/a&gt;&amp;nbsp; Just a nice image of my fiancee feeding ostriches ...&lt;div style='clear:both; text-align:LEFT'&gt;&lt;a href='http://picasa.google.com/blogger/' target='ext'&gt;&lt;img src='http://photos1.blogger.com/pbp.gif' alt='Posted by Picasa' style='border: 0px none ; padding: 0px; background: transparent none repeat scroll 0% 50%; -moz-background-clip: initial; -moz-background-origin: initial; -moz-background-inline-policy: initial;' align='middle' border='0' /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4775403822715180937-7989293849397254099?l=atamur.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://atamur.blogspot.com/feeds/7989293849397254099/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4775403822715180937&amp;postID=7989293849397254099' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/7989293849397254099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/7989293849397254099'/><link rel='alternate' type='text/html' href='http://atamur.blogspot.com/2008/12/just-nice-image-of-my-fiancee-feeding.html' title=''/><author><name>atamur</name><uri>http://www.blogger.com/profile/03950407766693025318</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yg02DF9XetM/SN9kfinEuLI/AAAAAAAAChE/niDOQmP7djI/S220/me3.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_yg02DF9XetM/SUgCcMDh6xI/AAAAAAAACk8/WUwMBnyPYes/s72-c/IMG_1585.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4775403822715180937.post-4959439522102575885</id><published>2008-11-01T00:32:00.001-07:00</published><updated>2009-10-27T13:59:48.514-07:00</updated><title type='text'>Seam 2.1.0GA and JBoss 5.0.0CR2</title><content type='html'>As seam and jboss are developed by the same team, I would expect them to be compatible. But in reality they are not =)&lt;br /&gt;&lt;br /&gt;So if you download the versions I mentioned in the subject and try to generate a default project by seam-gen (including db access) then there wil be a surprise for you. Deploying this project to jboss won't actually work. When it wants to connect to the database it will suddenly go: &lt;pre&gt; EntityManagerFactory not found in JNDI &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Googling that string was quite painful, but after couple of hours I finally got a result!&lt;br /&gt;Someone commented out something from jboss codebase and seam doesn't know about it =)&lt;br /&gt;&lt;br /&gt;So if you're looking for a solution, you should be using another way of binding your entity manager, not jndi. To do that in j2ee enviroment (which i suppose you would have running jboss) you need to create a small helper class that will translate the entitymanager from jboss world to seam world:&lt;pre class="brush: java"&gt;&lt;br /&gt;@Stateless&lt;br /&gt;@Name("entityManagerFactory")&lt;br /&gt;public class EntityManagerFactoryHackBean implements EntityManagerFactoryHackLocal {&lt;br /&gt;&lt;br /&gt; @PersistenceUnit(name="main")&lt;br /&gt; EntityManagerFactory factory;&lt;br /&gt; &lt;br /&gt; @Unwrap&lt;br /&gt; public EntityManagerFactory getEntityMangagerFactory()&lt;br /&gt; {&lt;br /&gt;  return factory;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt;} &lt;/pre&gt;&lt;br /&gt;see, having both &lt;i&gt;stateless&lt;/i&gt; and &lt;i&gt;name&lt;/i&gt; annotations it serves like a bridge between two worlds.&lt;br /&gt;&lt;br /&gt;then you have to use it in your components.xml instead of jndi name:&lt;pre class="brush: xml"&gt;&amp;lt;persistence:managed-persistence-context name="entityManager"&lt;br /&gt;  entity-manager-factory="#{entityManagerFactory}"&lt;br /&gt;  scope="conversation" auto-create="true" /&amp;gt; &lt;/pre&gt;&lt;br /&gt;Thanks for this &lt;a href="http://www.jboss.com/index.html?module=bb&amp;op=viewtopic&amp;t=142681"&gt;thread on jboss forum&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;There is &lt;a href="http://www.jboss.com/index.html?module=bb&amp;op=viewtopic&amp;p=4185829"&gt;another thread&lt;/a&gt; which has a solution without ugly hacks, &lt;deL&gt;but it doesn't work in j2ee env&lt;/del&gt; and a small amendment that should make it work even in j2ee env. Btw this second thread tells us that this should be fixed in a consequent release of jboss.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4775403822715180937-4959439522102575885?l=atamur.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://atamur.blogspot.com/feeds/4959439522102575885/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4775403822715180937&amp;postID=4959439522102575885' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/4959439522102575885'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/4959439522102575885'/><link rel='alternate' type='text/html' href='http://atamur.blogspot.com/2008/11/seam-210ga-and-jboss-500cr2.html' title='Seam 2.1.0GA and JBoss 5.0.0CR2'/><author><name>atamur</name><uri>http://www.blogger.com/profile/03950407766693025318</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yg02DF9XetM/SN9kfinEuLI/AAAAAAAAChE/niDOQmP7djI/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4775403822715180937.post-910124805646798473</id><published>2008-10-04T01:59:00.000-07:00</published><updated>2008-10-04T02:26:27.676-07:00</updated><title type='text'>Display a message when GWT app is loading</title><content type='html'>Quick search through the internet revealed a way to achieve the subject:&lt;br /&gt;&lt;a href="http://developerlife.com/theblog/?p=156"&gt;developerlife&lt;/a&gt; and &lt;a href="http://groups.google.com/group/Google-Web-Toolkit/browse_thread/thread/10df12f04999e221"&gt;google groups&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you're to lazy to read those, here is an extremely short tutorial:&lt;br /&gt;&lt;br /&gt;1. Place this div on your html (tip: you can generate your own loading gif &lt;a href="http://www.ajaxload.info/"&gt;here&lt;/a&gt;):&lt;br /&gt;&lt;pre&gt;&amp;lt;div id="initialLoading"&amp;gt;&lt;br /&gt;    &amp;lt;img src="images/ajax-loader.gif" alt="loading ..." /&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;2.  In your app's entry point add this line to remove the div when the app is actually loaded:&lt;br /&gt;&lt;pre&gt;DOM.removeChild(RootPanel.getBodyElement(), DOM.getElementById("initialLoading"));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;that's all&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4775403822715180937-910124805646798473?l=atamur.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://atamur.blogspot.com/feeds/910124805646798473/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4775403822715180937&amp;postID=910124805646798473' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/910124805646798473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/910124805646798473'/><link rel='alternate' type='text/html' href='http://atamur.blogspot.com/2008/10/display-message-when-gwt-app-is-loading.html' title='Display a message when GWT app is loading'/><author><name>atamur</name><uri>http://www.blogger.com/profile/03950407766693025318</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yg02DF9XetM/SN9kfinEuLI/AAAAAAAAChE/niDOQmP7djI/S220/me3.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4775403822715180937.post-240243291396598933</id><published>2008-09-28T09:27:00.001-07:00</published><updated>2009-10-27T14:00:30.889-07:00</updated><title type='text'>Adding custom styles to odd/even rows in a GWT table</title><content type='html'>For the sake of better UI we decided to implement a table like gmail one that has differnt colors for even/odd rows. This seems to be quite a simple task, but I had to try several approaches before I managed to implement it.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;1. Use custom RowFormatter &lt;/div&gt;&lt;div&gt;RowFormatter interface defines a getElement method which is supposed to be called for every row. I believed that if you override it you can attach special style to a row object based on it's number.&lt;/div&gt;&lt;div&gt;Wrong guess =)&lt;/div&gt;&lt;div&gt;The specific table implementation - FixedWidthGrid - I was using from the &lt;a href="http://code.google.com/p/google-web-toolkit-incubator/"&gt;gwt incubator&lt;/a&gt; project is using some custom/native logic inside prepareCell method so it doesn't call the getElement method =(&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2. Directly override prepareCell method - doesn't seem correct, cuz would be called much more often then you need it&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;3. Finally I got the idea that I'm not able to find any OO way of introducing this functionality and went with a brute force approach: implement a method that applies some special formatting to all the rows and call it every time the table content is updated. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now I faced a new problem: there is no explicit "onContentUpdate" callback method I could use.&lt;/div&gt;&lt;div&gt;The only way my application now allows the content to be updated is by sorting it, so I implemented an appropriate callback. Here is the final source code packed into a class:&lt;/div&gt;&lt;pre class="brush: java"&gt;    public static class StripedTable extends FixedWidthGrid {&lt;br /&gt;        private static final String EVEN_STYLE = "even_row";&lt;br /&gt;        private static final String ODD_STYLE = "odd_row";&lt;br /&gt;&lt;br /&gt;        public StripedTable() {&lt;br /&gt;            super();&lt;br /&gt;            addSortableColumnsListener(new SortableColumnsListener() {&lt;br /&gt;                public void onColumnSorted(TableModel.ColumnSortList sortList) {&lt;br /&gt;                    applyStripes();&lt;br /&gt;                }&lt;br /&gt;            });            &lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public StripedTable(int rows, int columns) {&lt;br /&gt;            this();&lt;br /&gt;            resize(rows, columns);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private void applyStripes() {&lt;br /&gt;            for (int i = 0; i &lt; getRowCount(); i++) {&lt;br /&gt;                this.getRowFormatter().setStyleName(i, getRowStyle(i));&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        private static String getRowStyle(int row) {&lt;br /&gt;            return row % 2 == 0 ? EVEN_STYLE : ODD_STYLE;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4775403822715180937-240243291396598933?l=atamur.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://atamur.blogspot.com/feeds/240243291396598933/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4775403822715180937&amp;postID=240243291396598933' title='Комментарии: 1'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/240243291396598933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/240243291396598933'/><link rel='alternate' type='text/html' href='http://atamur.blogspot.com/2008/09/adding-custom-styles-to-oddeven-rows-in.html' title='Adding custom styles to odd/even rows in a GWT table'/><author><name>atamur</name><uri>http://www.blogger.com/profile/03950407766693025318</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yg02DF9XetM/SN9kfinEuLI/AAAAAAAAChE/niDOQmP7djI/S220/me3.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4775403822715180937.post-8701357202105075156</id><published>2008-09-28T02:32:00.000-07:00</published><updated>2008-09-28T04:01:04.285-07:00</updated><title type='text'>hibernate4gwt and processing all class fields</title><content type='html'>Today I was stuck working on my project which employed both hibernate and GWT technologies and thus also needed to use &lt;a href="http://hibernate4gwt.sourceforge.net/hibernate_gwt_problem.html"&gt;hibernate4gwt&lt;/a&gt; lib.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is a great library that makes it possible to integrate two other great framewroks into one project. But as usual it has some caveats =)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It worked great for my model classes passing them from server side to JS client side. With one exception. All model object's ids were dropped. At first I thought this might have to do with @Id or @GeneratedValue annotations ... but that even sounds silly - why would you expect a cloning lib to drop a plain simple Long value because it has an @Id annotation? =)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Half an hour and 15Mb source code downloads later I found out the real cause. The library that hibernate4gwt uses for actual cloning of hibernate-enchanced objects to POJO is called &lt;a href="http://beanlib.sourceforge.net/"&gt;beanlib&lt;/a&gt; (specifically beanlib-hibernate) and deep inside, in a method called net.sf.beanlib.provider.BeanPopulator#populate it uses all defined setters on a given class to populate it. Apparently I haven't defined a setter for id field - which caused the beanutil lib to drop it when converting to POJO.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So the moral of this story is: always create both getters and setters for all you fields on a class that you expect to pass to some 3rd party bean handling libs =)&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4775403822715180937-8701357202105075156?l=atamur.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://atamur.blogspot.com/feeds/8701357202105075156/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4775403822715180937&amp;postID=8701357202105075156' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/8701357202105075156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4775403822715180937/posts/default/8701357202105075156'/><link rel='alternate' type='text/html' href='http://atamur.blogspot.com/2008/09/hibernate4gwt-and-processing-all-class.html' title='hibernate4gwt and processing all class fields'/><author><name>atamur</name><uri>http://www.blogger.com/profile/03950407766693025318</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='21' height='32' src='http://4.bp.blogspot.com/_yg02DF9XetM/SN9kfinEuLI/AAAAAAAAChE/niDOQmP7djI/S220/me3.jpg'/></author><thr:total>0</thr:total></entry></feed>
