Author: pcl
Date: Mon May 21 15:54:26 2007
New Revision: 540340

URL: http://svn.apache.org/viewvc?view=rev&rev=540340
Log:
OPENJPA-243: initial revision of IDEA PCEnhancer plugin.

Added:
    incubator/openjpa/sandboxes/IDEA-plugin/
    incubator/openjpa/sandboxes/IDEA-plugin/src/
    incubator/openjpa/sandboxes/IDEA-plugin/src/main/
    incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/
    incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/
    incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/
    incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/
    
incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/
    
incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerExecutor.java
    
incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerProjectComponent.java
    incubator/openjpa/sandboxes/IDEA-plugin/src/main/resources/
    incubator/openjpa/sandboxes/IDEA-plugin/src/main/resources/META-INF/
    
incubator/openjpa/sandboxes/IDEA-plugin/src/main/resources/META-INF/plugin.xml

Added: 
incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerExecutor.java
URL: 
http://svn.apache.org/viewvc/incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerExecutor.java?view=auto&rev=540340
==============================================================================
--- 
incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerExecutor.java
 (added)
+++ 
incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerExecutor.java
 Mon May 21 15:54:26 2007
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.idea;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+import org.apache.openjpa.conf.OpenJPAConfigurationImpl;
+import org.apache.openjpa.enhance.PCEnhancer;
+import org.apache.openjpa.lib.log.Log;
+import org.apache.openjpa.lib.log.LogFactoryImpl;
+import org.apache.openjpa.lib.conf.ConfigurationProvider;
+import org.apache.openjpa.lib.conf.Configurations;
+import org.apache.openjpa.lib.conf.ProductDerivations;
+import org.apache.openjpa.persistence.PersistenceProductDerivation;
+
+/**
+ * Responsible for invoking the PCEnhancer. The project component invokes
+ * the enhancer indirectly via reflection through this class so that it can
+ * use the OpenJPA version in the module, rather than requiring OpenJPA to
+ * be bundled with the plugin itself.
+ */
+public class PCEnhancerExecutor {
+
+    public boolean enhance(Collection<String> args, String puName,
+        Object logAdapter, ClassLoader cl)
+        throws IOException, NoSuchMethodException {
+
+        PersistenceProductDerivation pd = new PersistenceProductDerivation();
+        ConfigurationProvider cp = pd.load("META-INF/persistence.xml",
+            puName, cl);
+        ProductDerivations.beforeConfigurationConstruct(cp);
+        
+        OpenJPAConfigurationImpl conf = new OpenJPAConfigurationImpl();
+        if (cp == null)
+            // if we can't load any configuration files for some reason,
+            // do some basic spec-related set-up. This should never happen,
+            // as we only exercise this code if the PU is a JPA one, which
+            // implies the presence of a persistence.xml file.
+            conf.setSpecification("jpa");
+        else
+            cp.setInto(conf);
+
+        LogFactoryImpl lf = new LogFactoryAdapter(logAdapter);
+        Configurations.configureInstance(lf, conf,
+            Configurations.getProperties(conf.getLog()));
+        
+        conf.setLogFactory(lf);
+
+        return PCEnhancer.run(conf, args.toArray(new String[args.size()]),
+            new PCEnhancer.Flags(), null, null, cl);
+    }
+
+    private class LogFactoryAdapter
+        extends LogFactoryImpl {
+
+        private final Method error;
+        private final Object o;
+        private final Method warn;
+        private final Method info;
+
+        public LogFactoryAdapter(Object adapter) throws NoSuchMethodException {
+            // adapter has methods error(String), warn(String), info(String)
+            o = adapter;
+            error = o.getClass().getMethod("error", String.class);
+            warn = o.getClass().getMethod("warn", String.class);
+            info = o.getClass().getMethod("info", String.class);
+        }
+
+        @Override
+        protected LogImpl newLogImpl() {
+            return new LogFactoryImpl.LogImpl() {
+                @Override
+                protected void log(short i, String s, Throwable t) {
+                    String msg = formatMessage(i, s, t);
+                    try {
+                        switch (i) {
+                            case Log.ERROR:
+                            case Log.FATAL:
+                                error.invoke(o, msg);
+                                break;
+                            case Log.WARN:
+                                warn.invoke(o, msg);
+                                break;
+                            case Log.INFO:
+                            case Log.TRACE:
+                            default:
+                                info.invoke(o, msg);
+                                break;
+                        }
+                    } catch (IllegalAccessException e) {
+                        // FIXME better logging
+                        e.printStackTrace();
+                    } catch (InvocationTargetException e) {
+                        // FIXME better logging
+                        e.printStackTrace();
+                    }
+                }
+            };
+        }
+    }
+}

Added: 
incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerProjectComponent.java
URL: 
http://svn.apache.org/viewvc/incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerProjectComponent.java?view=auto&rev=540340
==============================================================================
--- 
incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerProjectComponent.java
 (added)
+++ 
incubator/openjpa/sandboxes/IDEA-plugin/src/main/java/org/apache/openjpa/idea/PCEnhancerProjectComponent.java
 Mon May 21 15:54:26 2007
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.idea;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ArrayList;
+import java.io.IOException;
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URLClassLoader;
+import java.net.URL;
+
+import com.intellij.openapi.components.ProjectComponent;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.compiler.CompilerManager;
+import com.intellij.openapi.compiler.CompileTask;
+import com.intellij.openapi.compiler.CompileContext;
+import com.intellij.openapi.compiler.CompilerMessageCategory;
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.openapi.roots.ProjectRootsTraversing;
+import com.intellij.jpa.facet.JpaFacet;
+import com.intellij.javaee.model.xml.persistence.PersistenceUnit;
+import com.intellij.javaee.model.common.persistence.mapping.EntityMappings;
+import com.intellij.javaee.model.common.persistence.mapping.PersistentObject;
+import com.intellij.psi.PsiClass;
+import com.intellij.util.PathsList;
+import com.intellij.util.lang.UrlClassLoader;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+public class PCEnhancerProjectComponent
+    implements ProjectComponent {
+
+    private final Project project;
+
+    public PCEnhancerProjectComponent(Project p) {
+        project = p;
+    }
+
+    public void projectOpened() {
+        CompilerManager.getInstance(project).addAfterTask(new CompileTask() {
+            public boolean execute(CompileContext context) {
+                return ApplicationManager.getApplication().runReadAction(
+                    new ComputableImpl(context)).booleanValue();
+            }
+        });
+    }
+
+    public void projectClosed() {
+    }
+
+    @NonNls
+    @NotNull
+    public String getComponentName() {
+        return "OpenJPA Enhancer";
+    }
+
+    public void initComponent() {
+    }
+
+    public void disposeComponent() {
+    }
+
+    private class ComputableImpl
+        implements Computable<Boolean> {
+
+        private final CompileContext ctx;
+
+        private ComputableImpl(CompileContext ctx) {
+            this.ctx = ctx;
+        }
+
+        public Boolean compute() {
+            try {
+                // ### We could do something clever with the results of
+                // ### ctx.isMake() to only process those files that
+                // ### were explicitly selected in cases where the user
+                // ### is compiling manually. It doesn't seem like there
+                // ### is a big performance win to doing this, but it would
+                // ### help us avoid any possible enhancer problems with
+                // ### classes that don't currently compile or don't
+                // ### currently enhance, since the user could selectively
+                // ### explicitly compile files that do work.
+                ctx.getProgressIndicator().pushState();
+                ctx.getProgressIndicator().setText("OpenJPA Enhancer running");
+                boolean status = true;
+                Module[] modules = ctx.getCompileScope().getAffectedModules();
+                for (Module m : modules) {
+                    JpaFacet jpa = JpaFacet.getInstance(m);
+                    if (jpa == null)
+                        continue;
+                    for (PersistenceUnit pu : jpa.getPersistenceUnits()) {
+                        if (isOpenJPA(pu))
+                            status &= processPersistenceUnit(jpa, pu);
+                    }
+                }
+                return status;
+            } catch (Throwable t) {
+                if (t instanceof InvocationTargetException)
+                    t = t.getCause();
+                
+                ctx.addMessage(CompilerMessageCategory.ERROR,
+                    "An unexpected error of type " + t.getClass().getName()
+                        + " occurred in the OpenJPA plugin. Message: "
+                        + t.getMessage(),
+                    null, -1, -1);
+                t.printStackTrace(); // FIXME need better logging
+                return false;
+            } finally {
+                ctx.getProgressIndicator().popState();
+            }
+        }
+
+        private boolean isOpenJPA(PersistenceUnit pu) {
+            PsiClass providerClass = pu.getProvider().getValue();
+            return providerClass == null || providerClass.getName().equals(
+                "org.apache.openjpa.persistence.PersistenceProviderImpl");
+        }
+
+        private boolean processPersistenceUnit(JpaFacet jpa,
+            PersistenceUnit pu)
+            throws IOException, ClassNotFoundException, IllegalAccessException,
+            InstantiationException, NoSuchMethodException,
+            InvocationTargetException {
+
+            EntityMappings ems = jpa.getEntityMappings(pu);
+            List<String> args = new ArrayList<String>();
+            addPersistentObjects(args, ems.getEmbeddables(), ctx);
+            addPersistentObjects(args, ems.getMappedSuperclasses(), ctx);
+            addPersistentObjects(args, ems.getEntities(), ctx);
+
+            if (args.size() == 0)
+                return true;
+            ClassLoader cl = newClassLoader(jpa.getModule());
+            ClassLoader oldLoader =
+                Thread.currentThread().getContextClassLoader();
+            Thread.currentThread().setContextClassLoader(cl);
+            try {
+                Class cls = Class.forName(
+                    "org.apache.openjpa.idea.PCEnhancerExecutor", true, cl);
+                Object o = cls.newInstance();
+
+                // assume that all the PersistentObjects in a given PU are
+                // accessible from the same classloader.
+                ctx.getProgressIndicator().setText2(
+                    "running enhancer on persistent types in '" + pu.getName()
+                        + "'");
+
+                boolean status = (Boolean) cls.getMethod("enhance",
+                    Collection.class, String.class, Object.class,
+                    ClassLoader.class)
+                    .invoke(o, args, pu.getName().getValue(),
+                        new LogAdapter(ctx), cl);
+
+                if (status)
+                    ctx.getProgressIndicator().setText2("done enhancing '"
+                        + pu.getName() + "'");
+                else
+                    ctx.getProgressIndicator().setText2("error while"
+                        + " enhancing '" + pu.getName() + "'");
+
+                return status;
+            } finally {
+                Thread.currentThread().setContextClassLoader(oldLoader);
+            }
+        }
+
+        /**
+         * Creates a new [EMAIL PROTECTED] ClassLoader} that includes all the 
module jars
+         * and output dirs in the current compile context, and all the jars
+         * in the current classloader. Since the returned classloader does
+         * not actually delegate to the classloader of this class,
+         * non-bootstrap instances cannot be shared between instances from
+         * the classloader returned by this call and the current instance.
+         */
+        private ClassLoader newClassLoader(Module module)
+            throws IOException {
+            Collection urls = new LinkedList();
+
+            UrlClassLoader loader =
+                (UrlClassLoader) getClass().getClassLoader();
+            urls.addAll(loader.getUrls());
+
+            for (VirtualFile vf : ctx.getAllOutputDirectories())
+                urls.add(new File(vf.getPath()).getCanonicalFile().toURL());
+
+            PathsList paths = ProjectRootsTraversing.collectRoots(module,
+                ProjectRootsTraversing.PROJECT_LIBRARIES);
+            for (VirtualFile vf : paths.getVirtualFiles()) {
+                File f = new File(vf.getPath());
+                urls.add(f.toURL());
+            }
+
+            return new URLClassLoader(
+                (URL[]) urls.toArray(new URL[urls.size()]));
+        }
+    }
+
+    private static void addPersistentObjects(List<String> args,
+        List<? extends PersistentObject> pos, CompileContext ctx) {
+        for (PersistentObject po : pos)
+            if (isUserClass(po) && inScope(po, ctx))
+                // don't just use po.getClazz().getStringValue() since it
+                // returns unqualified names for persistent objects defined
+                // in XML.
+                args.add(po.getClazz().getValue().getQualifiedName());
+    }
+
+    /**
+     * Determines whether or not <code>po</code> is a class in the
+     * user-defined area of the module (vs. in a pre-packaged library).
+     */
+    private static boolean isUserClass(PersistentObject po) {
+        return po.getContainingFile().getVirtualFile().isWritable();
+    }
+
+    /**
+     * Whether or not <code>po</code>'s source file is in the scope of this
+     * compilation context. If the file is not in scope, then we should not
+     * run the enhancer on the file, since it may not have been actually
+     * compiled yet.
+     */
+    private static boolean inScope(PersistentObject po, CompileContext ctx) {
+        return ctx.getCompileScope().belongs(
+            po.getContainingFile().getVirtualFile().getUrl());
+    }
+
+    /**
+     * Helper class for sending messages to a compile context. Since the
+     * classloader returned by [EMAIL PROTECTED] #newClassLoader} does not 
share the
+     * same IDEA classes as the enclosing class, we cannot directly use
+     * things like CompileContext from inside the PCEnhancerExecutor. So,
+     * this class simplifies the relatively-complex reflection that would
+     * be necessary to send messages to the compile context.
+     */
+    class LogAdapter {
+        private final CompileContext ctx;
+
+        public LogAdapter(CompileContext ctx) {
+            this.ctx = ctx;
+        }
+
+        public void error(String msg) {
+            ctx.addMessage(CompilerMessageCategory.ERROR, msg, null, -1, -1);
+        }
+
+        public void warn(String msg) {
+            ctx.addMessage(CompilerMessageCategory.WARNING, msg, null, -1, -1);
+        }
+
+        public void info(String msg) {
+            ctx.addMessage(CompilerMessageCategory.INFORMATION, msg, null, -1,
+                -1);
+        }
+    }
+}

Added: 
incubator/openjpa/sandboxes/IDEA-plugin/src/main/resources/META-INF/plugin.xml
URL: 
http://svn.apache.org/viewvc/incubator/openjpa/sandboxes/IDEA-plugin/src/main/resources/META-INF/plugin.xml?view=auto&rev=540340
==============================================================================
--- 
incubator/openjpa/sandboxes/IDEA-plugin/src/main/resources/META-INF/plugin.xml 
(added)
+++ 
incubator/openjpa/sandboxes/IDEA-plugin/src/main/resources/META-INF/plugin.xml 
Mon May 21 15:54:26 2007
@@ -0,0 +1,17 @@
+<!DOCTYPE idea-plugin PUBLIC "Plugin/DTD" 
"http://plugins.intellij.net/plugin.dtd";>
+<idea-plugin>
+    <name>OpenJPA Integration</name>
+    <description>Integration between OpenJPA and IDEA, including automatic 
class
+        post-processing during IDEA compilation</description>
+    <version>0.1</version>
+    <vendor>Apache OpenJPA</vendor>
+    <!-- JpaFacet is new in IntelliJ 7 -->
+    <idea-version since-build="6819"/>
+    <project-components>
+        <component>
+            <implementation-class>
+                org.apache.openjpa.idea.PCEnhancerProjectComponent
+            </implementation-class>
+        </component>
+    </project-components>
+</idea-plugin>
\ No newline at end of file


Reply via email to