/******************************************************************************* * Copyright (c) 2015 Pivotal Software, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of 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. * * Contributors: * Pivotal Software, Inc. - initial API and implementation ********************************************************************************/ package org.cloudfoundry.ide.eclipse.server.ui.internal; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.cloudfoundry.ide.eclipse.server.core.internal.CloudFoundryPlugin; import org.cloudfoundry.ide.eclipse.server.core.internal.CloudFoundryProjectUtil; import org.cloudfoundry.ide.eclipse.server.core.internal.CloudServerEvent; import org.cloudfoundry.ide.eclipse.server.core.internal.ServerEventHandler; import org.cloudfoundry.ide.eclipse.server.core.internal.jrebel.CloudRebelAppHandler; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.wst.server.core.IModule; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class CloudRebelUIHandler extends CloudRebelAppHandler { private static CloudRebelAppHandler handler; public CloudRebelUIHandler() { } public void register() { if (handler == null && CloudRebelAppHandler.isJRebelIDEInstalled()) { handler = this; ServerEventHandler.getDefault().addServerListener(handler); } } @Override protected void handleRebelProject(CloudServerEvent event, IModule module, IProgressMonitor monitor) throws CoreException { IProject project = module.getProject(); // Only replace rebel xml file if a manual Remoting // update is performed on Spring boot applications if (event.getType() == CloudServerEvent.EVENT_JREBEL_REMOTING_UPDATE && CloudFoundryProjectUtil.isSpringBootCloudFoundryConfigured(project)) { updateRebelXML(project, monitor); } super.handleRebelProject(event, module, monitor); } protected void updateRebelXML(IProject project, IProgressMonitor monitor) { // rebel.xml is overwritten for Spring Boot Jar apps to skip the /lib // folder which // is only generated in the Spring Boot Jar but has no workspace // equivalent try { IFile file = getRebelXMLFile(project); if (file != null && file.isAccessible()) { String path = file.getRawLocation() != null ? file.getRawLocation().toString() : null; if (path != null) { List<String> outputPaths = getClasspathSourceOutputPaths(project); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder db = factory.newDocumentBuilder(); Document doc = db.parse(new File(path)); Element javaProjectOutputElement = null; NodeList nodeList = doc.getElementsByTagName("*"); //$NON-NLS-1$ if (nodeList != null) { String libFolderName = "lib/**"; //$NON-NLS-1$ String javaBuildpackFolderName = ".java-buildpack/**"; //$NON-NLS-1$ // Find the node element that corresponds to the // project's source output. Check all the resolved // output paths to find // the one that matches the "dir" element in the // rebel.xml for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if ((node instanceof Element) && node.getNodeName().equals("dir")) { //$NON-NLS-1$ Element element = (Element) node; String att = element.getAttribute("name"); //$NON-NLS-1$ if (att != null) { for (String outpath : outputPaths) { if (att.contains(outpath)) { javaProjectOutputElement = element; break; } } if (javaProjectOutputElement != null) { break; } } } } if (javaProjectOutputElement != null) { NodeList binChildren = javaProjectOutputElement.getChildNodes(); Element existingExcludeLib = null; Element existingExcludeJavabuildpack = null; if (binChildren != null) { for (int i = 0; i < binChildren.getLength(); i++) { Node node = binChildren.item(i); if (node instanceof Element && node.getNodeName().equals("exclude")) { //$NON-NLS-1$ Element excludeElement = (Element) node; Attr attr = excludeElement.getAttributeNode("name"); //$NON-NLS-1$ if (attr != null && attr.getNodeValue() != null) { if (attr.getNodeValue().equals(libFolderName)) { existingExcludeLib = excludeElement; } else if (attr.getNodeValue().equals(javaBuildpackFolderName)) { existingExcludeJavabuildpack = excludeElement; } } } } } Element updatedExcludeLib = null; if (existingExcludeLib == null) { updatedExcludeLib = doc.createElement("exclude"); //$NON-NLS-1$ updatedExcludeLib.setAttribute("name", libFolderName); //$NON-NLS-1$ javaProjectOutputElement.appendChild(updatedExcludeLib); } Element updatedExcludeJavabuildpack = null; if (existingExcludeJavabuildpack == null) { updatedExcludeJavabuildpack = doc.createElement("exclude"); //$NON-NLS-1$ updatedExcludeJavabuildpack.setAttribute("name", javaBuildpackFolderName); //$NON-NLS-1$ javaProjectOutputElement.appendChild(updatedExcludeJavabuildpack); } if (updatedExcludeLib != null || updatedExcludeJavabuildpack != null) { final boolean[] proceed = { false }; final List<String> toexclude = new ArrayList<String>(); if (updatedExcludeLib != null) { toexclude.add(libFolderName); } if (updatedExcludeJavabuildpack != null) { toexclude.add(javaBuildpackFolderName); } Display.getDefault().syncExec(new Runnable() { public void run() { Shell shell = CloudUiUtil.getShell(); proceed[0] = shell != null && !shell.isDisposed() && MessageDialog.openQuestion( shell, Messages.CloudRebelUIHandler_TEXT_REPLACE_REBEL_XML_TITLE, NLS.bind( Messages.CloudRebelUIHandler_TEXT_REPLACE_REBEL_XML_BODY, toexclude)); } }); if (proceed[0]) { // If replacing the exist rebel.xml file, be // sure to switch off automatic rebel.xml // generation project.setPersistentProperty(new QualifiedName( "org.zeroturnaround.eclipse.jrebel",//$NON-NLS-1$ "autoGenerateRebelXml"), "false"); //$NON-NLS-1$ //$NON-NLS-2$ Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); //$NON-NLS-1$ DOMSource source = new DOMSource(doc); StreamResult console = new StreamResult(new File(path)); transformer.transform(source, console); project.refreshLocal(IResource.DEPTH_INFINITE, monitor); } } } } } } } catch (ParserConfigurationException e) { CloudFoundryPlugin.logError(e); } catch (SAXException e) { CloudFoundryPlugin.logError(e); } catch (IOException e) { CloudFoundryPlugin.logError(e); } catch (TransformerException e) { CloudFoundryPlugin.logError(e); } catch (CoreException e) { CloudFoundryPlugin.logError(e); } } protected List<String> getClasspathSourceOutputPaths(IProject project) { IJavaProject javaProject = CloudFoundryProjectUtil.getJavaProject(project); List<String> outputPaths = new ArrayList<String>(); if (javaProject != null) { try { IClasspathEntry[] classpath = javaProject.getResolvedClasspath(true); if (classpath != null) { for (IClasspathEntry entry : classpath) { if (entry != null && entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { String outputPath = entry.getOutputLocation() != null ? entry.getOutputLocation() .toString() : null; if (outputPath != null && !outputPaths.contains(outputPath) && !outputPath.contains("target/test-classes")) {//$NON-NLS-1$ outputPaths.add(outputPath); } } } } String outputPath = javaProject.getOutputLocation() != null ? javaProject.getOutputLocation() .toString() : null; if (outputPath != null && !outputPaths.contains(outputPath)) { outputPaths.add(outputPath); } } catch (JavaModelException e) { CloudFoundryPlugin.logError(e); } } return outputPaths; } protected IFile getRebelXMLFile(IProject project) { if (project != null && project.isAccessible()) { try { return getFile(project, "rebel.xml"); //$NON-NLS-1$ } catch (CoreException e) { CloudFoundryPlugin.logError(e); } } return null; } protected IFile getFile(IResource resource, String fileName) throws CoreException { if (resource instanceof IFile && resource.getName().equals(fileName)) { return (IFile) resource; } else if (resource instanceof IContainer) { IContainer container = (IContainer) resource; IResource[] children = container.members(); if (children != null) { for (IResource child : children) { IFile file = getFile(child, fileName); if (file != null) { return file; } } } } return null; } }