/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.hibernate.eclipse.launch; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import org.eclipse.ant.internal.launching.launchConfigurations.AntLaunchDelegate; import org.eclipse.ant.launching.IAntLaunchConstants; import org.eclipse.core.externaltools.internal.IExternalToolConstants; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.filebuffers.manipulation.FileBufferOperationRunner; import org.eclipse.core.filebuffers.manipulation.MultiTextEditWithProgress; import org.eclipse.core.filebuffers.manipulation.TextFileBufferOperation; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.internal.core.LaunchConfiguration; import org.eclipse.debug.internal.core.LaunchConfigurationWorkingCopy; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.ToolFactory; import org.eclipse.jdt.core.formatter.CodeFormatter; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jface.text.DocumentRewriteSessionType; import org.eclipse.jface.text.IDocument; import org.eclipse.osgi.util.NLS; import org.eclipse.text.edits.TextEdit; import org.hibernate.console.ConsoleConfiguration; import org.hibernate.console.HibernateConsoleRuntimeException; import org.hibernate.console.KnownConfigurations; import org.hibernate.console.execution.ExecutionContext.Command; import org.hibernate.eclipse.console.HibernateConsoleMessages; import org.hibernate.eclipse.console.HibernateConsolePlugin; import org.hibernate.eclipse.console.common.ConsoleExtension; import org.hibernate.eclipse.console.model.impl.ExporterFactory; import org.hibernate.util.xpl.StringHelper; import org.jboss.tools.hibernate.runtime.spi.HibernateException; import org.jboss.tools.hibernate.runtime.spi.IArtifactCollector; import org.jboss.tools.hibernate.runtime.spi.IConfiguration; import org.jboss.tools.hibernate.runtime.spi.IExporter; import org.jboss.tools.hibernate.runtime.spi.IOverrideRepository; import org.jboss.tools.hibernate.runtime.spi.IReverseEngineeringSettings; import org.jboss.tools.hibernate.runtime.spi.IReverseEngineeringStrategy; import org.jboss.tools.hibernate.runtime.spi.IService; @SuppressWarnings("restriction") public class CodeGenerationLaunchDelegate extends AntLaunchDelegate { protected IPath path2GenBuildXml = null; private static final class FormatGeneratedCode extends TextFileBufferOperation { private FormatGeneratedCode(String name) { super( name ); } protected DocumentRewriteSessionType getDocumentRewriteSessionType() { return DocumentRewriteSessionType.SEQUENTIAL; } protected MultiTextEditWithProgress computeTextEdit( ITextFileBuffer textFileBuffer, IProgressMonitor progressMonitor) throws CoreException, OperationCanceledException { IResource bufferRes = ResourcesPlugin.getWorkspace().getRoot().findMember(textFileBuffer.getLocation()); Map<?, ?> options = null; if(bufferRes!=null) { IJavaProject project = JavaCore.create(bufferRes.getProject()); options = project.getOptions(true); } CodeFormatter codeFormatter = ToolFactory.createCodeFormatter(options); IDocument document = textFileBuffer.getDocument(); String string = document.get(); TextEdit edit = codeFormatter.format(CodeFormatter.K_COMPILATION_UNIT, string, 0, string.length(), 0, null); MultiTextEditWithProgress multiTextEditWithProgress = new MultiTextEditWithProgress(getOperationName()); if(edit==null) { //HibernateConsolePlugin.getDefault().log("empty format for " + textFileBuffer.getLocation().toOSString()); } else { multiTextEditWithProgress.addChild(edit); } return multiTextEditWithProgress; } } /** * Create file with file name fileName and content fileContent * * @param fileName - file name * @param fileContent - file content * @throws IOException */ protected void createFile(String fileName, String fileContent) throws IOException { FileOutputStream fos = null; try { File ff = new File(fileName); if (!ff.exists()) { ff.createNewFile(); } fos = new FileOutputStream(fileName); fos.write(fileContent.getBytes()); fos.flush(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) {} } } } /** * Create temporary build.xml and then erase it after code generation complete * * @param lc * @throws IOException * @throws UnsupportedEncodingException */ protected void createBuildXmlFile(ILaunchConfiguration lc, String fileName) throws UnsupportedEncodingException, IOException { CodeGenXMLFactory codeGenXMLFactory = new CodeGenXMLFactory(lc); String externalPropFileName = CodeGenXMLFactory.getExternalPropFileNameStandard(fileName); codeGenXMLFactory.setExternalPropFileName(externalPropFileName); IWorkspace workspace = ResourcesPlugin.getWorkspace(); if (workspace != null && workspace.getRoot() != null && workspace.getRoot().getLocation() != null) { codeGenXMLFactory.setWorkspacePath(workspace.getRoot().getLocation().toString()); } String buildXml = codeGenXMLFactory.createCodeGenXML(); createFile(fileName, buildXml); final String propFileContentPreSave = codeGenXMLFactory.getPropFileContentPreSave(); createFile(externalPropFileName, propFileContentPreSave); } public class MockLaunchConfigWorkingCopy extends LaunchConfigurationWorkingCopy { protected final Map<String, String> tmpAttr; public MockLaunchConfigWorkingCopy(LaunchConfiguration original, Map<String, String> tmpAttr) throws CoreException { super(original); this.tmpAttr = tmpAttr; } public MockLaunchConfigWorkingCopy(LaunchConfigurationWorkingCopy parent, Map<String, String> tmpAttr) throws CoreException { super(parent); this.tmpAttr = tmpAttr; } @Override public String getAttribute(String attributeName, String defaultValue) throws CoreException { String res = tmpAttr.get(attributeName); if (res == null) { res = super.getAttribute(attributeName, defaultValue); } return res; } } public class MockLaunchConfig extends LaunchConfiguration { protected final Map<String, String> tmpAttr; public MockLaunchConfig(ILaunchConfiguration original, Map<String, String> tmpAttr) throws CoreException { super(original.getMemento()); this.tmpAttr = tmpAttr; } @Override public String getAttribute(String attributeName, String defaultValue) throws CoreException { String res = tmpAttr.get(attributeName); if (res == null) { res = super.getAttribute(attributeName, defaultValue); } return res; } public ILaunchConfigurationWorkingCopy getWorkingCopy() throws CoreException { return new MockLaunchConfigWorkingCopy(this, tmpAttr); } } /** * Update launch configuration with attributes required for external process codegen. * * @param lc * @return * @throws CoreException */ public ILaunchConfiguration updateLaunchConfig(ILaunchConfiguration lc) throws CoreException { Map<String, String> tmpAttributes = new HashMap<String, String>(); String fileName = null; try { fileName = getPath2GenBuildXml().toString(); } catch (IOException e) { throw new CoreException(HibernateConsolePlugin.throwableToStatus(e, 666)); } // setup location of Ant build.xml file tmpAttributes.put(IExternalToolConstants.ATTR_LOCATION, fileName); // setup Ant runner main type tmpAttributes.put(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, IAntLaunchConstants.MAIN_TYPE_NAME); // setup ant remote process factory tmpAttributes.put(DebugPlugin.ATTR_PROCESS_FACTORY_ID, "org.eclipse.ant.ui.remoteAntProcessFactory"); //$NON-NLS-1$ // refresh whole workspace //tmpAttributes.put(RefreshUtil.ATTR_REFRESH_SCOPE, RefreshUtil.MEMENTO_WORKSPACE); ILaunchConfiguration mockedConfig = lc.isWorkingCopy() ? new MockLaunchConfigWorkingCopy((LaunchConfigurationWorkingCopy)lc, tmpAttributes) : new MockLaunchConfig(lc, tmpAttributes); return mockedConfig; } public ILaunch getLaunch(ILaunchConfiguration configuration, String mode) throws CoreException { configuration = updateLaunchConfig(configuration); return super.getLaunch(configuration, mode); } public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { Assert.isNotNull(configuration); Assert.isNotNull(monitor); ExporterAttributes attributes = new ExporterAttributes(configuration); ConsoleConfiguration cc = KnownConfigurations.getInstance().find(attributes.getConsoleConfigurationName()); ConsoleExtension consoleExtension = cc.getHibernateExtension().getConsoleExtension(); Map<String, File[]> generatedFiles = consoleExtension.launchExporters(configuration, mode, launch, monitor); // code formatting needs to happen *after* refresh to make sure eclipse will format the uptodate files! if(generatedFiles!=null) { formatGeneratedCode( monitor, generatedFiles ); } /* The code is moved to consoleExtension and delegated method is called instead. List<ExporterFactory> exporterFactories = attributes.getExporterFactories(); for (Iterator<ExporterFactory> iter = exporterFactories.iterator(); iter.hasNext();) { ExporterFactory exFactory = iter.next(); if (!exFactory.isEnabled(configuration)) { iter.remove(); } } if (attributes.isUseExternalProcess()) { // create temporary build.xml and then erase it after code generation complete String fileName = null; try { fileName = getPath2GenBuildXml().toString(); createBuildXmlFile(configuration, fileName); } catch (UnsupportedEncodingException e) { throw new CoreException(HibernateConsolePlugin.throwableToStatus(e, 666)); } catch (IOException e) { throw new CoreException(HibernateConsolePlugin.throwableToStatus(e, 666)); } configuration = updateLaunchConfig(configuration); super.launch(configuration, mode, launch, monitor); // final Properties props = new Properties(); props.put(CodeGenerationStrings.EJB3, "" + attributes.isEJB3Enabled()); //$NON-NLS-1$ props.put(CodeGenerationStrings.JDK5, "" + attributes.isJDK5Enabled()); //$NON-NLS-1$ Set<String> outputDirs = new HashSet<String>(); for (Iterator<ExporterFactory> iter = exporterFactories.iterator(); iter.hasNext();) { ExporterFactory exFactory = iter.next(); exFactory.collectOutputDirectories(attributes.getOutputPath(), props, outputDirs); } // final IProcess[] processes = launch.getProcesses(); // codegen listener to erase build.xml file after codegen process complete CodeGenerationProcessListener refresher = new CodeGenerationProcessListener( processes[0], fileName, outputDirs); refresher.startBackgroundRefresh(); return; } try { Set<String> outputDirectories = new HashSet<String>(); ExporterFactory[] exporters = exporterFactories.toArray( new ExporterFactory[exporterFactories.size()] ); ArtifactCollector collector = runExporters(attributes, exporters, outputDirectories, monitor); for (String path : outputDirectories) { CodeGenerationUtils.refreshOutputDir(path); } RefreshTab.refreshResources(configuration, monitor); // code formatting needs to happen *after* refresh to make sure eclipse will format the uptodate files! if(collector!=null) { formatGeneratedCode( monitor, collector ); } } catch(Exception e) { throw new CoreException(HibernateConsolePlugin.throwableToStatus(e, 666)); } catch(NoClassDefFoundError e) { throw new CoreException(HibernateConsolePlugin.throwableToStatus(new HibernateConsoleRuntimeException(HibernateConsoleMessages.CodeGenerationLaunchDelegate_received_noclassdeffounderror,e), 666)); } finally { monitor.done(); }*/ } private void formatGeneratedCode(IProgressMonitor monitor, Map<String, File[]> generatedFiles) { final TextFileBufferOperation operation = new FormatGeneratedCode( HibernateConsoleMessages.CodeGenerationLaunchDelegate_formate_generated_code ); if (generatedFiles != null){ File[] javaFiles = generatedFiles.get("java"); //$NON-NLS-1$ if(javaFiles != null && javaFiles.length>0) { IPath[] locations = new IPath[javaFiles.length]; for (int i = 0; i < javaFiles.length; i++) { File file = javaFiles[i]; locations[i] = new Path(file.getPath()); } FileBufferOperationRunner runner= new FileBufferOperationRunner(FileBuffers.getTextFileBufferManager(), HibernateConsolePlugin.getShell()); try { runner.execute(locations, operation, monitor); } catch (OperationCanceledException e) { HibernateConsolePlugin.getDefault().logErrorMessage(HibernateConsoleMessages.CodeGenerationLaunchDelegate_java_format_cancelled, e); } catch (CoreException e) { HibernateConsolePlugin.getDefault().logErrorMessage(HibernateConsoleMessages.CodeGenerationLaunchDelegate_exception_during_java_format, e); } catch (Throwable e) { // full guard since the above operation seem to be able to fail with IllegalArugmentException and SWT Invalid thread access while users are editing. HibernateConsolePlugin.getDefault().logErrorMessage(HibernateConsoleMessages.CodeGenerationLaunchDelegate_exception_during_java_format, e); } } } } private IArtifactCollector runExporters (final ExporterAttributes attributes, final ExporterFactory[] exporterFactories, final Set<String> outputDirectories, final IProgressMonitor monitor) throws CoreException { monitor.beginTask(HibernateConsoleMessages.CodeGenerationLaunchDelegate_generating_code_for + attributes.getConsoleConfigurationName(), exporterFactories.length + 1); if (monitor.isCanceled()) return null; ConsoleConfiguration cc = KnownConfigurations.getInstance().find(attributes.getConsoleConfigurationName()); if (attributes.isReverseEngineer()) { monitor.subTask(HibernateConsoleMessages.CodeGenerationLaunchDelegate_reading_jdbc_metadata); } final IConfiguration cfg = buildConfiguration(attributes, cc, ResourcesPlugin.getWorkspace().getRoot()); monitor.worked(1); if (monitor.isCanceled()) return null; final IService service = cc.getHibernateExtension().getHibernateService(); return (IArtifactCollector) cc.execute(new Command() { public Object execute() { IArtifactCollector artifactCollector = service.newArtifactCollector(); // Global properties Properties props = new Properties(); props.put(CodeGenerationStrings.EJB3, "" + attributes.isEJB3Enabled()); //$NON-NLS-1$ props.put(CodeGenerationStrings.JDK5, "" + attributes.isJDK5Enabled()); //$NON-NLS-1$ for (int i = 0; i < exporterFactories.length; i++) { monitor.subTask(exporterFactories[i].getExporterDefinition().getDescription()); Properties globalProperties = new Properties(); globalProperties.putAll(props); IExporter exporter; try { exporter = exporterFactories[i].createConfiguredExporter(cfg, attributes.getOutputPath(), attributes.getTemplatePath(), globalProperties, outputDirectories, artifactCollector, service); } catch (CoreException e) { throw new HibernateConsoleRuntimeException(HibernateConsoleMessages.CodeGenerationLaunchDelegate_error_while_setting_up + exporterFactories[i].getExporterDefinition(), e); } try { exporter.start(); } catch(HibernateException he) { throw new HibernateConsoleRuntimeException(HibernateConsoleMessages.CodeGenerationLaunchDelegate_error_while_running + exporterFactories[i].getExporterDefinition().getDescription(), he); } monitor.worked(1); } return artifactCollector; } }); } private IConfiguration buildConfiguration(final ExporterAttributes attributes, ConsoleConfiguration cc, IWorkspaceRoot root) { final boolean reveng = attributes.isReverseEngineer(); final String reverseEngineeringStrategy = attributes.getRevengStrategy(); final boolean preferBasicCompositeids = attributes.isPreferBasicCompositeIds(); final IResource revengres = PathHelper.findMember( root, attributes.getRevengSettings()); if(reveng) { IConfiguration configuration = null; if(cc.hasConfiguration()) { configuration = cc.getConfiguration(); } else { configuration = cc.buildWith( null, false ); } // final JDBCMetaDataConfiguration cfg = new JDBCMetaDataConfiguration(); final IService service = cc.getHibernateExtension().getHibernateService(); final IConfiguration cfg = service.newJDBCMetaDataConfiguration(); Properties properties = configuration.getProperties(); cfg.setProperties( properties ); cc.buildWith(cfg,false); cfg.setPreferBasicCompositeIds(preferBasicCompositeids); cc.execute(new Command() { // need to execute in the consoleconfiguration to let it handle classpath stuff! public Object execute() { //todo: factor this setup of revengstrategy to core IReverseEngineeringStrategy res = service.newDefaultReverseEngineeringStrategy(); IOverrideRepository repository = null; if(revengres!=null) { File file = PathHelper.getLocation( revengres ).toFile(); repository = service.newOverrideRepository(); repository.addFile(file); } if (repository != null){ res = repository.getReverseEngineeringStrategy(res); } if(reverseEngineeringStrategy!=null && reverseEngineeringStrategy.trim().length()>0) { res = service.newReverseEngineeringStrategy(reverseEngineeringStrategy, res); } IReverseEngineeringSettings qqsettings = service.newReverseEngineeringSettings(res) .setDefaultPackageName(attributes.getPackageName()) .setDetectManyToMany( attributes.detectManyToMany() ) .setDetectOneToOne( attributes.detectOneToOne() ) .setDetectOptimisticLock( attributes.detectOptimisticLock() ); res.setSettings(qqsettings); cfg.setReverseEngineeringStrategy( res ); cfg.readFromJDBC(); cfg.buildMappings(); return null; } }); return cfg; } else { cc.build(); cc.buildMappings(); return cc.getConfiguration(); } } public boolean preLaunchCheck(ILaunchConfiguration configuration, String mode, IProgressMonitor monitor) throws CoreException { ExporterAttributes attributes = new ExporterAttributes(configuration); String configName = attributes.getConsoleConfigurationName(); if(StringHelper.isEmpty( configName )) { abort(HibernateConsoleMessages.CodeGenerationLaunchDelegate_console_configuration_name_is_empty_in + configuration.getName(), null, ICodeGenerationLaunchConstants.ERR_UNSPECIFIED_CONSOLE_CONFIGURATION); } if(KnownConfigurations.getInstance().find( configName )==null) { String out = NLS.bind(HibernateConsoleMessages.CodeGenerationLaunchDelegate_console_configuration_not_found_in, configName, configuration.getName()); abort(out, null, ICodeGenerationLaunchConstants.ERR_CONSOLE_CONFIGURATION_NOTFOUND); } if(StringHelper.isEmpty(attributes.getOutputPath())) { abort(HibernateConsoleMessages.CodeGenerationLaunchDelegate_output_has_to_be_specified_in + configuration.getName(), null, ICodeGenerationLaunchConstants.ERR_OUTPUT_PATH_NOTFOUND); } List<ExporterFactory> exporterFactories = attributes.getExporterFactories(); for (Iterator<ExporterFactory> iter = exporterFactories.iterator(); iter.hasNext();) { ExporterFactory exFactory = iter.next(); if (exFactory.isEnabled(configuration) && exFactory.getExporterDefinitionId().equals("org.hibernate.tools.query")) { //$NON-NLS-1$ if (!exFactory.getProperties().containsKey("query_string")){ //$NON-NLS-1$ abort("Query property should be explicitly set for Query Exporter", null, ICodeGenerationLaunchConstants.ERR_OUTPUT_PATH_NOTFOUND); //$NON-NLS-1$ } } } return super.preLaunchCheck( configuration, mode, monitor ); } protected void abort(String message, Throwable exception, int code) throws CoreException { throw new CoreException(new Status(IStatus.ERROR, HibernateConsolePlugin.ID, code, message, exception)); } public IPath getPath2GenBuildXml() throws IOException { if (path2GenBuildXml != null) { return path2GenBuildXml; } path2GenBuildXml = new Path(File.createTempFile("build_", "xml").getAbsolutePath()); //$NON-NLS-1$ //$NON-NLS-2$ return path2GenBuildXml; } }