| /* |
| * 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.netbeans.modules.payara.jakartaee.ide; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import java.util.regex.Pattern; |
| import static java.util.stream.Collectors.toList; |
| import javax.enterprise.deploy.shared.CommandType; |
| import javax.enterprise.deploy.spi.Target; |
| import javax.enterprise.deploy.spi.TargetModuleID; |
| import javax.enterprise.deploy.spi.status.ProgressEvent; |
| import javax.enterprise.deploy.spi.status.ProgressListener; |
| import javax.enterprise.deploy.spi.status.ProgressObject; |
| import org.netbeans.modules.payara.tooling.TaskEvent; |
| import org.netbeans.modules.payara.tooling.TaskState; |
| import org.netbeans.api.project.FileOwnerQuery; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.modules.payara.eecommon.api.HttpMonitorHelper; |
| import org.netbeans.modules.payara.eecommon.api.Utils; |
| import org.netbeans.modules.payara.eecommon.api.XmlFileCreator; |
| import org.netbeans.modules.payara.jakartaee.Hk2DeploymentManager; |
| import org.netbeans.modules.payara.jakartaee.ModuleConfigurationImpl; |
| import org.netbeans.modules.payara.jakartaee.ResourceRegistrationHelper; |
| import org.netbeans.modules.payara.spi.PayaraModule; |
| import org.netbeans.modules.payara.spi.PayaraModule2; |
| import org.netbeans.modules.j2ee.deployment.common.api.ConfigurationException; |
| import org.netbeans.modules.j2ee.deployment.devmodules.api.J2eeApplication; |
| import org.netbeans.modules.j2ee.deployment.devmodules.api.J2eeModule; |
| import org.netbeans.modules.j2ee.deployment.plugins.api.AppChangeDescriptor; |
| import org.netbeans.modules.j2ee.deployment.plugins.api.DeploymentChangeDescriptor; |
| import org.netbeans.modules.j2ee.deployment.plugins.spi.DeploymentContext; |
| import org.netbeans.modules.j2ee.deployment.plugins.spi.IncrementalDeployment; |
| import org.netbeans.modules.j2ee.deployment.plugins.spi.IncrementalDeployment2; |
| import org.netbeans.modules.j2ee.deployment.plugins.spi.config.ModuleConfiguration; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileSystem; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.util.NbBundle; |
| import org.openide.util.Utilities; |
| import org.xml.sax.SAXException; |
| |
| /** |
| * |
| * @author Ludovic Champenois |
| * @author Peter Williams |
| */ |
| public class FastDeploy extends IncrementalDeployment implements IncrementalDeployment2 { |
| |
| private static volatile long lastDeployHack = System.currentTimeMillis(); |
| |
| private static final String GFDEPLOY = "gfdeploy"; // NOI18N |
| |
| private Hk2DeploymentManager dm; |
| |
| /** |
| * Creates a new instance of FastDeploy |
| * |
| * @param dm The deployment manager for the server instance this object |
| * deploys to. |
| */ |
| public FastDeploy(Hk2DeploymentManager dm) { |
| this.dm = dm; |
| } |
| |
| /** |
| * |
| * @param target |
| * @param app |
| * @param configuration |
| * @param file |
| * @return |
| */ |
| @Override |
| public ProgressObject initialDeploy(Target target, final J2eeModule module, ModuleConfiguration configuration, final File dir) { |
| return initialDeploy(target, module, dir, new File[0]); |
| } |
| |
| @Override |
| public ProgressObject initialDeploy(Target target, DeploymentContext context) { |
| return initialDeploy(target, context.getModule(), context.getModuleFile(), context.getRequiredLibraries()); |
| } |
| |
| |
| /** |
| * Get web application context root from module configuration. |
| * @param module Java EE module containing context root (WAR archive). |
| * @param dir Web application root. |
| * @return |
| */ |
| private String getContextRoot(final J2eeModule module, final File dir) { |
| ModuleConfigurationImpl mci = ModuleConfigurationImpl.get(module); |
| if (null != mci) { |
| try { |
| return mci.getContextRoot(); |
| } catch (ConfigurationException ex) { |
| Logger.getLogger("payara").log(Level.WARNING, |
| "could not getContextRoot() for {0}", dir); |
| return null; |
| } |
| } else { |
| return null; |
| } |
| } |
| |
| private ProgressObject initialDeploy(Target target, J2eeModule module, final File dir, final File[] requiredLibraries) { |
| final PayaraModule commonSupport = dm.getCommonServerSupport(); |
| String url = commonSupport.getInstanceProperties().get(PayaraModule.URL_ATTR); |
| String targ = Hk2DeploymentManager.getTargetFromUri(url); |
| String nameSuffix = ""; // NOI18N |
| if (null != targ) |
| nameSuffix = "_"+targ; // NOI18N |
| final String moduleName = org.netbeans.modules.payara.spi.Utils.sanitizeName(Utils.computeModuleID(module, dir, Integer.toString(hashCode()))) + |
| nameSuffix; |
| String contextRoot = null; |
| J2eeModule.Type type = module.getType(); |
| if (type == J2eeModule.Type.WAR) { |
| contextRoot = getContextRoot(module, dir); |
| // drop a glassfish-web.xml file in here.. if necessary |
| FileObject rootOfWebApp = FileUtil.toFileObject(FileUtil.normalizeFile(dir)); |
| if (null != rootOfWebApp) { |
| String fileName = null; |
| String SUNWEB = "WEB-INF/sun-web.xml"; // NOI18N |
| if (url.contains("pfv3") |
| || url.contains("pfv4") |
| || url.contains("pfv5")) { // NOI18N |
| String GFWEB = "WEB-INF/glassfish-web.xml"; // NOI18N |
| String PYWEB = "WEB-INF/payara-web.xml"; // NOI18N |
| if (null != rootOfWebApp.getFileObject(GFWEB)) { |
| fileName = PYWEB; |
| } else if (null == rootOfWebApp.getFileObject(GFWEB) |
| && null == rootOfWebApp.getFileObject(SUNWEB)) { |
| fileName = GFWEB; |
| } |
| } else { |
| if (null == rootOfWebApp.getFileObject(SUNWEB)) { |
| // add sun-web to deployed app |
| fileName = SUNWEB; |
| } |
| } |
| if (null != fileName) { |
| File ddFile = new File(dir, fileName); |
| addDescriptorToDeployedDirectory(module, ddFile); |
| } |
| } |
| // Context root is in encapsulated WAR |
| } else if (type == J2eeModule.Type.EAR) { |
| if (module instanceof J2eeApplication) { |
| for (J2eeModule child : ((J2eeApplication)module).getModules()) { |
| if (child.getType() == J2eeModule.Type.WAR) { |
| contextRoot = getContextRoot(child, dir); |
| break; |
| } |
| } |
| } |
| } |
| // XXX fix cast -- need error instance for ProgressObject to return errors |
| Hk2TargetModuleID moduleId = Hk2TargetModuleID.get((Hk2Target) target, |
| moduleName, contextRoot, dir.getAbsolutePath()); |
| |
| // prevent issues by protecting against triggering |
| ProgressObject po = checkAgainstGF15690(dir,moduleId); |
| if (null != po) { |
| return po; |
| } |
| final MonitorProgressObject deployProgress = new MonitorProgressObject(dm, moduleId); |
| final MonitorProgressObject updateCRProgress = new MonitorProgressObject(dm, moduleId); |
| deployProgress.addProgressListener(new UpdateContextRoot(updateCRProgress,moduleId, dm.getServerInstance(), J2eeModule.Type.WAR.equals(module.getType()))); |
| MonitorProgressObject restartProgress = new MonitorProgressObject(dm, moduleId); |
| |
| final PayaraModule2 commonSupport2 = (commonSupport instanceof PayaraModule2 ? |
| (PayaraModule2)commonSupport : null); |
| boolean restart = false; |
| try { |
| restart = HttpMonitorHelper.synchronizeMonitor(commonSupport.getInstanceProperties().get(PayaraModule.DOMAINS_FOLDER_ATTR), |
| commonSupport.getInstanceProperties().get(PayaraModule.DOMAIN_NAME_ATTR), |
| Boolean.parseBoolean(commonSupport.getInstanceProperties().get(PayaraModule.HTTP_MONITOR_FLAG)), |
| "modules/org-netbeans-modules-schema2beans.jar"); |
| } catch (IOException | SAXException ex) { |
| Logger.getLogger("payara-jakartaee").log(Level.WARNING, "http monitor state", ex); |
| } |
| ResourceRegistrationHelper.deployResources(dir,dm); |
| if (restart) { |
| final String cr = contextRoot; |
| restartProgress.addProgressListener(new ProgressListener() { |
| @Override |
| public void handleProgressEvent(ProgressEvent event) { |
| if (event.getDeploymentStatus().isCompleted()) { |
| if (commonSupport2 != null && requiredLibraries.length > 0) { |
| commonSupport2.deploy(deployProgress, dir, moduleName, cr, Collections.<String, String>emptyMap(), requiredLibraries); |
| } else { |
| commonSupport.deploy(deployProgress, dir, moduleName, cr); |
| } |
| } else { |
| deployProgress.fireHandleProgressEvent(event.getDeploymentStatus()); |
| } |
| } |
| }); |
| commonSupport.restartServer(restartProgress); |
| return updateCRProgress; |
| } else { |
| if (commonSupport2 != null && requiredLibraries.length > 0) { |
| commonSupport2.deploy(deployProgress, dir, moduleName, contextRoot, Collections.<String, String>emptyMap(), requiredLibraries); |
| } else { |
| commonSupport.deploy(deployProgress, dir, moduleName, contextRoot); |
| } |
| return updateCRProgress; |
| } |
| } |
| |
| private void addDescriptorToDeployedDirectory(J2eeModule module, File sunDDFile) { |
| FileObject sunDDTemplate = Utils.getSunDDFromProjectsModuleVersion(module, sunDDFile.getName()); //FileUtil.getConfigFile(resource); |
| if (sunDDTemplate != null) { |
| try { |
| FileObject configFolder = FileUtil.createFolder(sunDDFile.getParentFile()); |
| FileSystem fs = configFolder.getFileSystem(); |
| XmlFileCreator creator = new XmlFileCreator(sunDDTemplate, configFolder, sunDDTemplate.getName(), sunDDTemplate.getExt()); |
| fs.runAtomicAction(creator); |
| } catch (IOException ioe) { |
| Logger.getLogger("payara").log(Level.WARNING, "could not create {0}", sunDDTemplate.getPath()); |
| } |
| } |
| } |
| |
| private static Pattern badName = Pattern.compile(".*\\s.*_[jwrc]ar"); // NOI18N |
| private static Pattern badPath = Pattern.compile(".*[\\\\/].*\\s.*_[jwrc]ar[\\\\/].*"); // NOI18N |
| |
| private ProgressObject checkAgainstGF15690(final File dir, Hk2TargetModuleID moduleId) { |
| File parent = dir.getParentFile(); |
| if (null != parent) { |
| if (GFDEPLOY.equals(parent.getName())) { |
| File modules[] = dir.listFiles(); |
| for (File f : modules) { |
| if (f.isDirectory()) { |
| String fname = f.getName(); |
| if (badName.matcher(fname).matches()) { |
| MonitorProgressObject po = new MonitorProgressObject(dm, moduleId); |
| po.operationStateChanged(TaskState.FAILED, TaskEvent.CMD_FAILED, |
| NbBundle.getMessage(FastDeploy.class, "ERR_SPACE_IN_JAR_NAMES", fname)); // NOI18N |
| return po; |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * |
| * @param targetModuleID |
| * @param appChangeDescriptor |
| * @return |
| */ |
| @Override |
| public ProgressObject incrementalDeploy(final TargetModuleID targetModuleID, AppChangeDescriptor appChangeDescriptor) { |
| return incrementalDeploy(targetModuleID, appChangeDescriptor, new File[0]); |
| } |
| |
| @Override |
| public ProgressObject incrementalDeploy(final TargetModuleID targetModuleID, DeploymentContext context) { |
| return incrementalDeploy(targetModuleID, context.getChanges(), context.getRequiredLibraries()); |
| } |
| |
| private ProgressObject incrementalDeploy(final TargetModuleID targetModuleID, AppChangeDescriptor appChangeDescriptor, final File[] requiredLibraries) { |
| final Hk2TargetModuleID hk2TargetModuleID = (Hk2TargetModuleID) targetModuleID; |
| final MonitorProgressObject progressObject = new MonitorProgressObject( |
| dm, hk2TargetModuleID, CommandType.REDEPLOY); |
| // prevent issues by protecting against triggering |
| // http://java.net/jira/browse/GLASSFISH-15690 |
| for (File f : appChangeDescriptor.getChangedFiles()) { |
| String fname = f.getAbsolutePath(); |
| if (badPath.matcher(fname).matches()) { // NOI18N |
| progressObject.operationStateChanged(TaskState.FAILED, TaskEvent.CMD_FAILED, |
| NbBundle.getMessage(FastDeploy.class, "ERR_SPACE_IN_JAR_NAMES", fname)); // NOI18N |
| return progressObject; |
| } |
| } |
| MonitorProgressObject restartObject = new MonitorProgressObject(dm, hk2TargetModuleID, |
| CommandType.REDEPLOY); |
| final MonitorProgressObject updateCRObject = new MonitorProgressObject(dm, |
| hk2TargetModuleID, CommandType.REDEPLOY); |
| progressObject.addProgressListener(new UpdateContextRoot(updateCRObject,hk2TargetModuleID, dm.getServerInstance(), ! (null == targetModuleID.getWebURL()))); |
| final PayaraModule commonSupport = dm.getCommonServerSupport(); |
| final PayaraModule2 commonSupport2 = (commonSupport instanceof PayaraModule2 ? |
| (PayaraModule2)commonSupport : null); |
| boolean restart = false; |
| try { |
| restart = HttpMonitorHelper.synchronizeMonitor( |
| commonSupport.getInstanceProperties().get(PayaraModule.DOMAINS_FOLDER_ATTR), |
| commonSupport.getInstanceProperties().get(PayaraModule.DOMAIN_NAME_ATTR), |
| Boolean.parseBoolean(commonSupport.getInstanceProperties().get(PayaraModule.HTTP_MONITOR_FLAG)), |
| "modules/org-netbeans-modules-schema2beans.jar"); |
| } catch (IOException | SAXException ex) { |
| Logger.getLogger("payara-jakartaee").log(Level.WARNING,"http monitor state", |
| ex); |
| } |
| long currentTime = System.currentTimeMillis(); |
| long sinceLast = currentTime - lastDeployHack; |
| final boolean resourcesChanged = containsFileWithName("payara-resources.xml",appChangeDescriptor.getChangedFiles()) // NOI18N |
| || containsFileWithName("glassfish-resources.xml",appChangeDescriptor.getChangedFiles()); // NOI18N |
| |
| final boolean hasChanges = appChangeDescriptor.classesChanged() || |
| appChangeDescriptor.descriptorChanged() || |
| appChangeDescriptor.ejbsChanged() || |
| appChangeDescriptor.manifestChanged() || |
| appChangeDescriptor.serverDescriptorChanged() || |
| appChangeDescriptor.getRemovedFiles().length > 0 || |
| resourcesChanged || |
| // |
| // this accounts for a feature/bug of Payara. |
| // if a static resource is modified there is a window where later |
| // modifications might be missed... so old content gets served... |
| // even when new content is available to be served. |
| // That window is about 5 seconds. |
| // |
| // If the user is doing pathological things like adding a character |
| // and hitting save, the auto-refresh will show the right thing. |
| // |
| sinceLast < 5000; |
| |
| final boolean metadataChanged |
| = appChangeDescriptor.descriptorChanged() |
| || appChangeDescriptor.serverDescriptorChanged() |
| || appChangeDescriptor.manifestChanged() |
| || appChangeDescriptor.getRemovedFiles().length > 0 |
| || resourcesChanged; |
| |
| File targetDir = getDirectoryForModule(targetModuleID); |
| URI targetURI = Utilities.toURI(targetDir); |
| final List<String> sourcesChanged = Arrays.stream(appChangeDescriptor.getChangedFiles()) |
| .map(Utilities::toURI) |
| .map(targetURI::relativize) |
| .map(URI::getPath) |
| .collect(toList()); |
| |
| lastDeployHack = currentTime; |
| |
| if(appChangeDescriptor instanceof DeploymentChangeDescriptor) { |
| DeploymentChangeDescriptor dcd = (DeploymentChangeDescriptor)appChangeDescriptor; |
| if (dcd.serverResourcesChanged()) { |
| ResourceRegistrationHelper.deployResources(targetDir, dm); |
| } |
| } |
| |
| if (restart) { |
| restartObject.addProgressListener(event -> { |
| if (event.getDeploymentStatus().isCompleted()) { |
| if (hasChanges) { |
| if (commonSupport2 != null && requiredLibraries.length > 0) { |
| commonSupport2.redeploy( |
| progressObject, |
| hk2TargetModuleID.getModuleID(), |
| hk2TargetModuleID.getContextRoot(), |
| requiredLibraries, |
| resourcesChanged, |
| metadataChanged, |
| sourcesChanged |
| ); |
| } else { |
| commonSupport.redeploy( |
| progressObject, |
| hk2TargetModuleID.getModuleID(), |
| hk2TargetModuleID.getContextRoot(), |
| resourcesChanged, |
| metadataChanged, |
| sourcesChanged |
| ); |
| } |
| } else { |
| progressObject.fireHandleProgressEvent(event.getDeploymentStatus()); |
| } |
| } else { |
| progressObject.fireHandleProgressEvent(event.getDeploymentStatus()); |
| } |
| }); |
| commonSupport.restartServer(restartObject); |
| return updateCRObject; |
| } else { |
| if (hasChanges) { |
| if (commonSupport2 != null && requiredLibraries.length > 0) { |
| commonSupport2.redeploy( |
| progressObject, |
| hk2TargetModuleID.getModuleID(), |
| hk2TargetModuleID.getContextRoot(), |
| requiredLibraries, |
| resourcesChanged, |
| metadataChanged, |
| sourcesChanged |
| ); |
| } else { |
| commonSupport.redeploy( |
| progressObject, |
| hk2TargetModuleID.getModuleID(), |
| hk2TargetModuleID.getContextRoot(), |
| resourcesChanged, |
| metadataChanged, |
| sourcesChanged |
| ); |
| } |
| } else { |
| progressObject.operationStateChanged(TaskState.COMPLETED, TaskEvent.CMD_COMPLETED, |
| NbBundle.getMessage(FastDeploy.class, "MSG_RedeployUnneeded")); |
| } |
| return updateCRObject; |
| } |
| } |
| |
| /** |
| * |
| * @param target |
| * @param deployable |
| * @return |
| */ |
| @Override |
| public boolean canFileDeploy(Target target, J2eeModule deployable) { |
| if (null == deployable){ |
| return false; |
| } |
| |
| if (deployable.getType() == J2eeModule.Type.CAR) { |
| return false; |
| } |
| |
| final PayaraModule commonSupport = dm.getCommonServerSupport(); |
| String url = commonSupport.getInstanceProperties().get(PayaraModule.URL_ATTR); |
| |
| if (!url.trim().matches(".*:[0-9]+$")) // NOI18N |
| return url.trim().endsWith("server"); |
| |
| return true; |
| } |
| |
| /** |
| * @return Absolute path root directory for the specified app or null if |
| * server can accept the deployment from an arbitrary directory. |
| */ |
| @Override |
| public File getDirectoryForNewApplication(Target target, J2eeModule app, ModuleConfiguration configuration) { |
| File dest = null; |
| if (app.getType() == J2eeModule.Type.EAR) { |
| File tmp = getProjectDir(app); |
| if (null == tmp) { |
| return dest; |
| } |
| dest = new File(tmp, "target"); // NOI18N |
| if (!dest.exists()) { |
| // the app wasn't a maven project |
| dest = new File(tmp, "dist"); // NOI18N |
| } |
| if (dest.isFile() || (dest.isDirectory() && !dest.canWrite())) { |
| throw new IllegalStateException(); |
| } |
| String moduleName = org.netbeans.modules.payara.spi.Utils.sanitizeName(Utils.computeModuleID(app, null, null)); |
| String dirName = GFDEPLOY; |
| if (null != moduleName) { |
| dirName += "/"+moduleName; // NOI18N |
| } |
| dest = new File(dest, dirName); |
| boolean retval = true; |
| if (!dest.exists()) { |
| retval = dest.mkdirs(); |
| } |
| if (!retval || !dest.isDirectory()) { |
| dest = null; |
| } |
| } |
| return dest; |
| } |
| |
| /** |
| * |
| * @param file |
| * @param string |
| * @param app |
| * @param configuration |
| * @return |
| */ |
| @Override |
| public File getDirectoryForNewModule(File file, String string, J2eeModule app, ModuleConfiguration configuration) { |
| return new File(file, transform(removeLeadSlash(string))); |
| } |
| |
| private String removeLeadSlash(String s) { |
| if (null == s) { |
| return s; |
| } |
| if (s.length() < 1) { |
| return s; |
| } |
| if (!s.startsWith("/")) { |
| return s; |
| } |
| return s.substring(1); |
| } |
| |
| static String transform(String s) { |
| int len = s.length(); |
| if (len > 4) { |
| StringBuilder sb = new StringBuilder(s); |
| char tmp = sb.charAt(len - 4); |
| if (tmp == '.') { |
| sb.setCharAt(len-4, '_'); |
| return sb.toString(); |
| } |
| } |
| return s; |
| } |
| |
| /** |
| * |
| * @param targetModuleID |
| * @return |
| */ |
| @Override |
| public File getDirectoryForModule(TargetModuleID targetModuleID) { |
| File retVal |
| = new File(((Hk2TargetModuleID) targetModuleID).getLocation()); |
| if (retVal.getPath().contains("${")) { |
| throw new IllegalStateException(NbBundle.getMessage(FastDeploy.class, |
| "ERR_UndeployAndRedeploy")); |
| } |
| return retVal; |
| } |
| |
| @Override |
| public ProgressObject deployOnSave(TargetModuleID module, DeploymentChangeDescriptor desc) { |
| return incrementalDeploy(module, desc); |
| } |
| |
| @Override |
| public boolean isDeployOnSaveSupported() { |
| final PayaraModule commonSupport = dm.getCommonServerSupport(); |
| String url = commonSupport.getInstanceProperties().get(PayaraModule.URL_ATTR); |
| |
| if (!url.trim().matches(".*:[0-9]+$")) // NOI18N |
| return url.trim().endsWith("server"); |
| return !"false".equals(System.getProperty("glassfish.javaee.deployonsave")); |
| } |
| |
| // try to get the Project Directory as a File |
| // use a couple different stratgies, since the resource.dir is in a user- |
| // editable file -- but it is quicker to access, if it is there.... |
| // |
| private File getProjectDir(J2eeModule app) { |
| try { |
| FileObject fo = app.getContentDirectory(); |
| Project p = FileOwnerQuery.getOwner(fo); |
| if (null != p) { |
| fo = p.getProjectDirectory(); |
| return FileUtil.toFile(fo); |
| } |
| } catch (IOException ex) { |
| Logger.getLogger("payara-jakartaee").log(Level.FINER, // NOI18N |
| null,ex); |
| } |
| java.io.File tmp = app.getResourceDirectory(); |
| |
| if (tmp != null) { |
| return tmp.getParentFile(); |
| } |
| return null; |
| } |
| |
| @Override |
| public String getModuleUrl(TargetModuleID module) { |
| assert null != module; |
| // if (null == module) { |
| // return "/bogusModule"; |
| // } |
| Hk2TargetModuleID self = (Hk2TargetModuleID) module; |
| String retVal = self.getModuleID(); |
| return retVal.startsWith("/") ? retVal : "/"+retVal; |
| } |
| |
| private boolean containsFileWithName(String name, File[] changedFiles) { |
| if (null == changedFiles || null == name) |
| return false; |
| for (File f : changedFiles) { |
| String fp = null != f ? f.getAbsolutePath() : null; |
| if (null != fp && fp.contains(name)) |
| return true; |
| } |
| return false; |
| } |
| } |