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