Java tutorial
/** * Copyright 2012 Houghton Associates * * Licensed 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 com.houghtonassociates.bamboo.plugins; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.configuration.HierarchicalConfiguration; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.SystemUtils; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.http.auth.Credentials; import org.apache.log4j.Logger; import org.eclipse.jgit.api.MergeResult.MergeStatus; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.revwalk.RevCommit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.atlassian.bamboo.author.Author; import com.atlassian.bamboo.author.AuthorCachingFacade; import com.atlassian.bamboo.bandana.PlanAwareBandanaContext; import com.atlassian.bamboo.build.BuildDefinition; import com.atlassian.bamboo.build.BuildLoggerManager; import com.atlassian.bamboo.build.fileserver.BuildDirectoryManager; import com.atlassian.bamboo.build.logger.BuildLogger; import com.atlassian.bamboo.build.strategy.BuildStrategy; import com.atlassian.bamboo.build.strategy.TriggeredBuildStrategy; import com.atlassian.bamboo.chains.Chain; import com.atlassian.bamboo.commit.Commit; import com.atlassian.bamboo.commit.CommitContext; import com.atlassian.bamboo.commit.CommitContextImpl; import com.atlassian.bamboo.commit.CommitContextImpl.Builder; import com.atlassian.bamboo.commit.CommitFile; import com.atlassian.bamboo.commit.CommitFileImpl; import com.atlassian.bamboo.commit.CommitImpl; import com.atlassian.bamboo.plan.PlanExecutionManager; import com.atlassian.bamboo.plan.PlanKey; import com.atlassian.bamboo.plan.PlanKeys; import com.atlassian.bamboo.plan.PlanManager; import com.atlassian.bamboo.plan.TopLevelPlan; import com.atlassian.bamboo.plan.branch.ChainBranch; import com.atlassian.bamboo.plan.branch.ChainBranchManager; import com.atlassian.bamboo.plan.branch.VcsBranch; import com.atlassian.bamboo.plan.branch.VcsBranchImpl; import com.atlassian.bamboo.plan.cache.ImmutableChain; import com.atlassian.bamboo.project.Project; import com.atlassian.bamboo.repository.AbstractStandaloneRepository; import com.atlassian.bamboo.repository.AdvancedConfigurationAwareRepository; import com.atlassian.bamboo.repository.BranchInformationProvider; import com.atlassian.bamboo.repository.BranchMergingAwareRepository; import com.atlassian.bamboo.repository.BranchingAwareRepository; import com.atlassian.bamboo.repository.CustomVariableProviderRepository; import com.atlassian.bamboo.repository.PushCapableRepository; import com.atlassian.bamboo.repository.Repository; import com.atlassian.bamboo.repository.RepositoryDataEntity; import com.atlassian.bamboo.repository.RepositoryDefinition; import com.atlassian.bamboo.repository.RepositoryDefinitionManager; import com.atlassian.bamboo.repository.RepositoryException; import com.atlassian.bamboo.security.EncryptionService; import com.atlassian.bamboo.template.TemplateRenderer; import com.atlassian.bamboo.trigger.TriggerDefinition; import com.atlassian.bamboo.util.Narrow; import com.atlassian.bamboo.utils.SystemProperty; import com.atlassian.bamboo.utils.error.ErrorCollection; import com.atlassian.bamboo.utils.error.SimpleErrorCollection; import com.atlassian.bamboo.v2.build.BuildContext; import com.atlassian.bamboo.v2.build.BuildRepositoryChanges; import com.atlassian.bamboo.v2.build.BuildRepositoryChangesImpl; import com.atlassian.bamboo.v2.build.agent.capability.Requirement; import com.atlassian.bamboo.v2.build.agent.remote.RemoteBuildDirectoryManager; import com.atlassian.bamboo.v2.build.repository.CustomSourceDirectoryAwareRepository; import com.atlassian.bamboo.v2.build.repository.RequirementsAwareRepository; import com.atlassian.bamboo.v2.events.ChangeDetectionRequiredEvent; import com.atlassian.bamboo.variable.CustomVariableContext; import com.atlassian.bamboo.variable.VariableDefinitionManager; import com.atlassian.bamboo.ww2.actions.build.admin.create.BuildConfiguration; import com.atlassian.bandana.BandanaManager; import com.atlassian.bandana.DefaultBandanaManager; import com.atlassian.bandana.impl.MemoryBandanaPersister; import com.atlassian.event.api.EventPublisher; import com.atlassian.plugin.ModuleDescriptor; import com.houghtonassociates.bamboo.plugins.dao.GerritChangeVO; import com.houghtonassociates.bamboo.plugins.dao.GerritChangeVO.FileSet; import com.houghtonassociates.bamboo.plugins.dao.GerritChangeVO.PatchSet; import com.houghtonassociates.bamboo.plugins.dao.GerritConfig; import com.houghtonassociates.bamboo.plugins.dao.GerritProcessListener; import com.houghtonassociates.bamboo.plugins.dao.GerritService; import com.houghtonassociates.bamboo.plugins.dao.jgit.JGitRepository; import com.opensymphony.xwork2.TextProvider; import com.sonymobile.tools.gerrit.gerritevents.GerritConnectionConfig2; import com.sonymobile.tools.gerrit.gerritevents.dto.GerritEvent; import com.sonymobile.tools.gerrit.gerritevents.dto.events.PatchsetCreated; import com.sonymobile.tools.gerrit.gerritevents.ssh.Authentication; import com.sonymobile.tools.gerrit.gerritevents.watchdog.WatchTimeExceptionData; /** * This class allows bamboo to use Gerrit as if it were a repository. */ public class GerritRepositoryAdapter extends AbstractStandaloneRepository implements AdvancedConfigurationAwareRepository, PushCapableRepository, BranchingAwareRepository, BranchMergingAwareRepository, GerritConnectionConfig2, CustomVariableProviderRepository, CustomSourceDirectoryAwareRepository, RequirementsAwareRepository, GerritProcessListener, BranchInformationProvider { private static final long serialVersionUID = -3518800283574344591L; private static final String REPOSITORY_URL = "repositoryUrl"; private static final String REPOSITORY_USERNAME = "username"; @Deprecated private static final String REPOSITORY_GERRIT_PLAN_KEY = "planKey"; private static final String REPOSITORY_GERRIT_PROJECT_KEY = "projectKey"; private static final String REPOSITORY_GERRIT_PROJECT_NAME = "projectName"; private static final String REPOSITORY_GERRIT_CHAIN_KEY = "chainKey"; private static final String REPOSITORY_GERRIT_CHAIN_NAME = "chainName"; private static final String REPOSITORY_GERRIT_CHAIN_DESC = "chainDescription"; private static final String REPOSITORY_GERRIT_REPOSITORY_HOSTNAME = "repository.gerrit.hostname"; private static final String REPOSITORY_GERRIT_REPOSITORY_PORT = "repository.gerrit.port"; private static final String REPOSITORY_GERRIT_PROJECT = "repository.gerrit.project"; private static final String REPOSITORY_GERRIT_BRANCH = "repository.gerrit.branch"; private static final String REPOSITORY_GERRIT_DEFAULT_BRANCH = "repository.gerrit.default.branch"; private static final String REPOSITORY_GERRIT_CUSTOM_BRANCH = "repository.gerrit.custom.branch"; private static final String REPOSITORY_GERRIT_USERNAME = "repository.gerrit.username"; private static final String REPOSITORY_GERRIT_EMAIL = "repository.gerrit.email"; private static final String SHARED_CREDENTIALS = "SHARED_CREDENTIALS"; private static final String REPOSITORY_GIT_SHAREDCREDENTIALS_ID = "repository.gerrit.sharedCrendentials"; private static final String REPOSITORY_GIT_AUTHENTICATION_TYPE = "repository.gerrit.authenticationType"; private static final String REPOSITORY_GERRIT_SSH_KEY = "repository.gerrit.ssh.key"; private static final String REPOSITORY_GERRIT_SSH_KEY_FILE = "repository.gerrit.ssh.keyfile"; private static final String REPOSITORY_GERRIT_CONFIG_DIR = "repository.gerrit.config.dir"; private static final String REPOSITORY_GERRIT_SSH_PASSPHRASE = "repository.gerrit.ssh.passphrase"; private static final String TEMPORARY_GERRIT_SSH_PASSPHRASE = "temporary.gerrit.ssh.passphrase"; private static final String TEMPORARY_GERRIT_SSH_PASSPHRASE_CHANGE = "temporary.gerrit.ssh.passphrase.change"; private static final String TEMPORARY_GERRIT_SSH_KEY_FROM_FILE = "temporary.gerrit.ssh.keyfile"; private static final String TEMPORARY_GERRIT_SSH_KEY_CHANGE = "temporary.gerrit.ssh.key.change"; private static final String REPOSITORY_GERRIT_USE_SHALLOW_CLONES = "repository.gerrit.useShallowClones"; private static final String REPOSITORY_GERRIT_USE_SUBMODULES = "repository.gerrit.useSubmodules"; private static final String REPOSITORY_GERRIT_COMMAND_TIMEOUT = "repository.gerrit.commandTimeout"; private static final String REPOSITORY_GERRIT_VERBOSE_LOGS = "repository.gerrit.verbose.logs"; private static final int DEFAULT_COMMAND_TIMEOUT_IN_MINUTES = 180; private static final String GIT_COMMIT_ACTION = "/COMMIT_MSG"; protected static boolean USE_SHALLOW_CLONES = new SystemProperty(false, "atlassian.bamboo.git.useShallowClones", "ATLASSIAN_BAMBOO_GIT_USE_SHALLOW_CLONES").getValue(true); private static final String REPOSITORY_GERRIT_CHANGE_ID = "repository.gerrit.change.id"; private static final String REPOSITORY_GERRIT_CHANGE_NUMBER = "repository.gerrit.change.number"; private static final String REPOSITORY_GERRIT_REVISION_NUMBER = "repository.gerrit.revision.number"; private static final Logger log = Logger.getLogger(GerritRepositoryAdapter.class); private static final VcsBranch ALL_BRANCH = new VcsBranchImpl("All branches"); private static final VcsBranch MASTER_BRANCH = new VcsBranchImpl("master"); private static final String CUSTOM_BRANCH_SET = "Custom"; private String hostname = ""; private int port = 29418; private String project = ""; private String username = ""; private String userEmail = ""; private String sshKey = ""; private String relativeConfigPath = ""; private String absConfigPath = ""; private String relativeSSHKeyFilePath = ""; private File sshKeyFile = null; private String sshPassphrase = ""; private boolean useShallowClones = false; private boolean useSubmodules = false; private boolean verboseLogs = false; private int commandTimeout = 0; private VcsBranch vcsBranch = MASTER_BRANCH; private GerritService gerritDAO = null; private static final BandanaManager bandanaManager = new DefaultBandanaManager(new MemoryBandanaPersister()); private PlanManager planManager = null; private PlanExecutionManager planExeManager = null; private ChainBranchManager chainBranchManager = null; private RepositoryDefinitionManager repositoryDefinitionManager = null; private VariableDefinitionManager variableDefinitionManager = null; private EventPublisher eventPublisher = null; private EncryptionService encryptionService; private GerritConfig gc = new GerritConfig(); private GerritChangeVO lastGerritChange = null; @Override public void init(ModuleDescriptor moduleDescriptor) { super.init(moduleDescriptor); log.debug("Initialized repository adapter."); } public void setPlanManager(PlanManager planManager) { this.planManager = planManager; } public void setPlanExeManager(PlanExecutionManager planExeManager) { this.planExeManager = planExeManager; } public ChainBranchManager getChainBranchManager() { return chainBranchManager; } public void setChainBranchManager(ChainBranchManager chainBranchManager) { this.chainBranchManager = chainBranchManager; } public VariableDefinitionManager getVariableDefinitionManager() { return variableDefinitionManager; } public RepositoryDefinitionManager getRepositoryDefinitionManager() { return repositoryDefinitionManager; } public void setRepositoryDefinitionManager(RepositoryDefinitionManager repositoryDefinitionManager) { this.repositoryDefinitionManager = repositoryDefinitionManager; } public void setVariableDefinitionManager(VariableDefinitionManager variableDefinitionManager) { this.variableDefinitionManager = variableDefinitionManager; } public void setEventPublisher(EventPublisher eventPublisher) { this.eventPublisher = eventPublisher; } public BandanaManager getBandanaManager() { return bandanaManager; } @Override public void prepareConfigObject(@NotNull BuildConfiguration buildConfiguration) { super.prepareConfigObject(buildConfiguration); log.debug("Preparing gerrit repository adapter..."); String strHostName = buildConfiguration.getString(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME, "").trim(); buildConfiguration.setProperty(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME, strHostName); String strPort = buildConfiguration.getString(REPOSITORY_GERRIT_REPOSITORY_PORT, "").trim(); buildConfiguration.setProperty(REPOSITORY_GERRIT_REPOSITORY_PORT, strPort); String strProject = buildConfiguration.getString(REPOSITORY_GERRIT_PROJECT, "").trim(); buildConfiguration.setProperty(REPOSITORY_GERRIT_PROJECT, strProject); String strUserName = buildConfiguration.getString(REPOSITORY_GERRIT_USERNAME, "").trim(); buildConfiguration.setProperty(REPOSITORY_GERRIT_USERNAME, strUserName); String strPhrase = buildConfiguration.getString(TEMPORARY_GERRIT_SSH_PASSPHRASE); if (buildConfiguration.getBoolean(TEMPORARY_GERRIT_SSH_PASSPHRASE_CHANGE)) { buildConfiguration.setProperty(REPOSITORY_GERRIT_SSH_PASSPHRASE, encryptionService.encrypt(strPhrase)); } String decryptedKey = ""; if (buildConfiguration.getBoolean(TEMPORARY_GERRIT_SSH_KEY_CHANGE)) { final Object o = buildConfiguration.getProperty(TEMPORARY_GERRIT_SSH_KEY_FROM_FILE); if (o instanceof File) { File f = (File) o; try { decryptedKey = FileUtils.readFileToString(f); } catch (IOException e) { log.error(textProvider.getText("repository.gerrit.messages.error.ssh.key.read"), e); return; } buildConfiguration.setProperty(REPOSITORY_GERRIT_SSH_KEY, encryptionService.encrypt(decryptedKey)); } else { buildConfiguration.clearProperty(REPOSITORY_GERRIT_SSH_KEY); } } else { decryptedKey = encryptionService.decrypt(buildConfiguration.getString(REPOSITORY_GERRIT_SSH_KEY, "")); } relativeConfigPath = this.getRelativeConfigDirectory(buildConfiguration); buildConfiguration.setProperty(REPOSITORY_GERRIT_CONFIG_DIR, relativeConfigPath); relativeSSHKeyFilePath = getRelativeRepoPath(buildConfiguration); File f = prepareSSHKeyFile(relativeSSHKeyFilePath, decryptedKey); if (f != null) { buildConfiguration.setProperty(REPOSITORY_GERRIT_SSH_KEY_FILE, relativeSSHKeyFilePath); } String strDefBranch = buildConfiguration.getString(REPOSITORY_GERRIT_DEFAULT_BRANCH, ""); String strCustBranch = buildConfiguration.getString(REPOSITORY_GERRIT_CUSTOM_BRANCH, ""); if (strDefBranch == null || strDefBranch.isEmpty()) strDefBranch = CUSTOM_BRANCH_SET; if (strCustBranch == null || strCustBranch.isEmpty()) { strDefBranch = MASTER_BRANCH.getName(); strCustBranch = MASTER_BRANCH.getName(); } if (strDefBranch.equals(MASTER_BRANCH.getName()) || strDefBranch.equals(ALL_BRANCH.getName())) { buildConfiguration.setProperty(REPOSITORY_GERRIT_DEFAULT_BRANCH, strDefBranch); buildConfiguration.setProperty(REPOSITORY_GERRIT_CUSTOM_BRANCH, strDefBranch); buildConfiguration.setProperty(REPOSITORY_GERRIT_BRANCH, strDefBranch); } else { buildConfiguration.setProperty(REPOSITORY_GERRIT_DEFAULT_BRANCH, CUSTOM_BRANCH_SET); buildConfiguration.setProperty(REPOSITORY_GERRIT_CUSTOM_BRANCH, strCustBranch); buildConfiguration.setProperty(REPOSITORY_GERRIT_BRANCH, strCustBranch); } } private String getBaseBuildWorkingDirectory() { File parentDirectoryFile = this.buildDirectoryManager.getBaseBuildWorkingDirectory(); String parentDirectory = parentDirectoryFile.getAbsolutePath(); return parentDirectory; } private String getRelativePath(BuildConfiguration buildConfiguration) { String projectChainKey = "linked"; String planKey = buildConfiguration.getString(REPOSITORY_GERRIT_PLAN_KEY); String projectKey = buildConfiguration.getString(REPOSITORY_GERRIT_PROJECT_KEY); String chainKey = buildConfiguration.getString(REPOSITORY_GERRIT_CHAIN_KEY); String strProject = buildConfiguration.getString(REPOSITORY_GERRIT_PROJECT, "").trim(); if ((projectKey != null) && (chainKey != null)) projectChainKey = projectKey + "-" + chainKey; if (strProject != null) projectChainKey = projectChainKey + File.separator + strProject; String workingDirectory = GerritService.SYSTEM_DIRECTORY; if (planKey != null) { workingDirectory = workingDirectory + File.separator + planKey; } else { workingDirectory = workingDirectory + File.separator + projectChainKey; } return workingDirectory; } private String getRelativeRepoPath(BuildConfiguration buildConfiguration) { String workingDirectory = getRelativePath(buildConfiguration); workingDirectory = workingDirectory + File.separator + "GerritSSHKey.txt"; return workingDirectory.replace("\\", "/"); } private String getRelativeConfigDirectory(BuildConfiguration buildConfiguration) { String workingDirectory = getRelativePath(buildConfiguration); workingDirectory = workingDirectory + File.separator + GerritService.CONFIG_DIRECTORY; return workingDirectory.replace("\\", "/"); } public synchronized File prepareConfigDir(String strRelativePath) { String filePath = getBaseBuildWorkingDirectory() + File.separator + strRelativePath; File f = new File(filePath); f.setReadable(true, true); f.setWritable(true, true); f.setExecutable(true, true); f.mkdir(); return f; } public synchronized File prepareSSHKeyFile(String strRelativePath, String sshKey) { String filePath = getBaseBuildWorkingDirectory() + File.separator + strRelativePath; File f = new File(filePath); f.setReadable(true, true); f.setWritable(true, true); f.setExecutable(false, false); try { FileUtils.writeStringToFile(f, sshKey); } catch (IOException e) { log.error(e.getMessage()); return null; } try { if (SystemUtils.IS_OS_UNIX || SystemUtils.IS_OS_LINUX || SystemUtils.IS_OS_MAC_OSX) Runtime.getRuntime().exec("chmod 700 " + filePath); } catch (IOException e) { log.warn(e.getMessage()); } return f; } @Override public ErrorCollection validate(BuildConfiguration buildConfiguration) { boolean error = false; ErrorCollection errorCollection = super.validate(buildConfiguration); String hostame = StringUtils.trim(buildConfiguration.getString(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME)); if (!StringUtils.isNotBlank(hostame)) { errorCollection.addError(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME, "Hostname null!"); error = true; } String strPort = buildConfiguration.getString(REPOSITORY_GERRIT_REPOSITORY_PORT, "").trim(); if (!StringUtils.isNotBlank(strPort)) { errorCollection.addError(REPOSITORY_GERRIT_REPOSITORY_PORT, "Port null!"); error = true; } String strProject = buildConfiguration.getString(REPOSITORY_GERRIT_PROJECT, "").trim(); if (!StringUtils.isNotBlank(strProject)) { errorCollection.addError(REPOSITORY_GERRIT_PROJECT, "Project null!"); error = true; } String username = StringUtils.trim(buildConfiguration.getString(REPOSITORY_GERRIT_USERNAME)); if (!StringUtils.isNotBlank(username)) { errorCollection.addError(REPOSITORY_GERRIT_USERNAME, "Username null!"); error = true; } if (buildConfiguration.getBoolean(TEMPORARY_GERRIT_SSH_KEY_CHANGE)) { final Object o = buildConfiguration.getProperty(TEMPORARY_GERRIT_SSH_KEY_FROM_FILE); if (o == null) { errorCollection.addError(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME, textProvider.getText("repository.gerrit.messages.error.ssh.key.missing")); error = true; } } String key = encryptionService.decrypt(buildConfiguration.getString(REPOSITORY_GERRIT_SSH_KEY, "")); if (!StringUtils.isNotBlank(key)) { errorCollection.addError(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME, textProvider.getText("repository.gerrit.messages.error.ssh.key.missing")); error = true; } String strPhrase; if (buildConfiguration.getBoolean(TEMPORARY_GERRIT_SSH_PASSPHRASE_CHANGE)) { strPhrase = buildConfiguration.getString(TEMPORARY_GERRIT_SSH_PASSPHRASE); } else { strPhrase = buildConfiguration.getString(REPOSITORY_GERRIT_SSH_PASSPHRASE, ""); if (StringUtils.isNotBlank(strPhrase)) strPhrase = encryptionService.decrypt(strPhrase); } String keyFilePath = buildConfiguration.getString(REPOSITORY_GERRIT_SSH_KEY_FILE); if (!StringUtils.isNotBlank(keyFilePath)) { errorCollection.addError(REPOSITORY_GERRIT_SSH_KEY_FILE, "Your SSH private key is required for connection!"); error = true; } if (error) { return errorCollection; } try { ErrorCollection e2 = testGerritConnection(keyFilePath, key, hostame, Integer.valueOf(strPort), username, strProject, strPhrase); errorCollection.addErrorCollection(e2); } catch (RepositoryException e) { errorCollection.addError(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME, e.getMessage()); } return errorCollection; } @Override public void populateFromConfig(HierarchicalConfiguration config) { super.populateFromConfig(config); hostname = StringUtils.trimToEmpty(config.getString(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME)); username = config.getString(REPOSITORY_GERRIT_USERNAME); userEmail = config.getString(REPOSITORY_GERRIT_EMAIL); sshKey = config.getString(REPOSITORY_GERRIT_SSH_KEY, ""); sshPassphrase = encryptionService.decrypt(config.getString(REPOSITORY_GERRIT_SSH_PASSPHRASE)); port = config.getInt(REPOSITORY_GERRIT_REPOSITORY_PORT, 29418); project = config.getString(REPOSITORY_GERRIT_PROJECT); String strDefBranch = config.getString(REPOSITORY_GERRIT_DEFAULT_BRANCH, ""); String strCustBranch = config.getString(REPOSITORY_GERRIT_CUSTOM_BRANCH, ""); if (strDefBranch.equals(MASTER_BRANCH.getName()) || strDefBranch.equals(ALL_BRANCH.getName())) { vcsBranch = new VcsBranchImpl(strDefBranch); } else { vcsBranch = new VcsBranchImpl(strCustBranch); } useShallowClones = config.getBoolean(REPOSITORY_GERRIT_USE_SHALLOW_CLONES); useSubmodules = config.getBoolean(REPOSITORY_GERRIT_USE_SUBMODULES); commandTimeout = config.getInt(REPOSITORY_GERRIT_COMMAND_TIMEOUT, DEFAULT_COMMAND_TIMEOUT_IN_MINUTES); verboseLogs = config.getBoolean(REPOSITORY_GERRIT_VERBOSE_LOGS, false); String gitRepoUrl = "ssh://" + username + "@" + hostname + ":" + port + "/" + project; String tmpCP = config.getString(REPOSITORY_GERRIT_CONFIG_DIR); if (tmpCP == null || tmpCP.isEmpty()) { tmpCP = GerritService.SYSTEM_DIRECTORY + File.separator + GerritService.CONFIG_DIRECTORY; } relativeConfigPath = tmpCP.replace("\\", "/"); absConfigPath = prepareConfigDir(relativeConfigPath).getAbsolutePath(); String tmpSSHKFP = config.getString(REPOSITORY_GERRIT_SSH_KEY_FILE); if (tmpSSHKFP == null || tmpSSHKFP.isEmpty()) { tmpSSHKFP = GerritService.SYSTEM_DIRECTORY + File.separator + GerritService.CONFIG_DIRECTORY; } relativeSSHKeyFilePath = tmpSSHKFP.replace("\\", "/"); String decryptedKey = encryptionService.decrypt(sshKey); sshKeyFile = prepareSSHKeyFile(relativeSSHKeyFilePath, decryptedKey); gc.setHost(hostname); gc.setPort(port); gc.setRepositoryUrl(gitRepoUrl); gc.setWorkingDirectory(absConfigPath); gc.setSshKeyFile(sshKeyFile); gc.setSshKey(decryptedKey); gc.setSshPassphrase(sshPassphrase); gc.setUsername(username); gc.setUserEmail(userEmail); gc.setUseShallowClones(useShallowClones); gc.setUseSubmodules(useSubmodules); gc.setVerboseLogs(verboseLogs); gc.setCommandTimeout(commandTimeout); try { initializeGerritService(); if (this.isOnLocalAgent()) { if (isRemoteTriggeringReop()) { getGerritDAO().addListener(this); } else { getGerritDAO().removeListener(this); } } } catch (RepositoryException e) { log.error(e.getMessage()); } } @Override public HierarchicalConfiguration toConfiguration() { HierarchicalConfiguration configuration = super.toConfiguration(); configuration.setProperty(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME, hostname); configuration.setProperty(REPOSITORY_GERRIT_USERNAME, username); configuration.setProperty(REPOSITORY_GERRIT_EMAIL, userEmail); configuration.setProperty(REPOSITORY_GERRIT_PROJECT, project); if (!vcsBranch.equals(MASTER_BRANCH) && !vcsBranch.equals(ALL_BRANCH)) { configuration.setProperty(REPOSITORY_GERRIT_DEFAULT_BRANCH, CUSTOM_BRANCH_SET); configuration.setProperty(REPOSITORY_GERRIT_CUSTOM_BRANCH, vcsBranch.getName()); } else { String br = vcsBranch.getName(); configuration.setProperty(REPOSITORY_GERRIT_DEFAULT_BRANCH, br); configuration.setProperty(REPOSITORY_GERRIT_CUSTOM_BRANCH, br); } configuration.setProperty(REPOSITORY_GERRIT_BRANCH, vcsBranch.getName()); configuration.setProperty(REPOSITORY_GERRIT_SSH_KEY, sshKey); configuration.setProperty(REPOSITORY_GERRIT_SSH_PASSPHRASE, encryptionService.encrypt(sshPassphrase)); configuration.setProperty(REPOSITORY_GERRIT_CONFIG_DIR, relativeConfigPath); configuration.setProperty(REPOSITORY_GERRIT_SSH_KEY_FILE, relativeSSHKeyFilePath); configuration.setProperty(REPOSITORY_GERRIT_REPOSITORY_PORT, port); configuration.setProperty(REPOSITORY_GERRIT_USE_SHALLOW_CLONES, useShallowClones); configuration.setProperty(REPOSITORY_GERRIT_USE_SUBMODULES, useSubmodules); configuration.setProperty(REPOSITORY_GERRIT_COMMAND_TIMEOUT, commandTimeout); configuration.setProperty(REPOSITORY_GERRIT_VERBOSE_LOGS, verboseLogs); return configuration; } private void initializeGerritService() throws RepositoryException { getGerritDAO().initialize(); String strVersion = getGerritDAO().getGerritVersion(); log.info(String.format("Gerrit Version: %s", strVersion)); userEmail = getGerritDAO().getGerritSystemUserEmail(); } @Override public void setBuildDirectoryManager(BuildDirectoryManager buildDirectoryManager) { super.setBuildDirectoryManager(buildDirectoryManager); } @Override public void setBuildLoggerManager(BuildLoggerManager buildLoggerManager) { super.setBuildLoggerManager(buildLoggerManager); } @Override public void setCustomVariableContext(CustomVariableContext customVariableContext) { super.setCustomVariableContext(customVariableContext); } @Override public void setTemplateRenderer(TemplateRenderer templateRenderer) { super.setTemplateRenderer(templateRenderer); } @Override public synchronized void setTextProvider(TextProvider textProvider) { super.setTextProvider(textProvider); } public ErrorCollection testGerritConnection(String sshRelKeyFile, String key, String strHost, int port, String strUsername, String strProject, String phrase) throws RepositoryException { ErrorCollection errorCollection = new SimpleErrorCollection(); File f = prepareSSHKeyFile(sshRelKeyFile, key); if (!f.isFile()) { throw new RepositoryException(textProvider.getText("repository.gerrit.messages.error.ssh.key.missing")); } String gitRepoUrl = "ssh://" + username + "@" + strHost + ":" + port + "/" + strProject; gc.setHost(strHost); gc.setPort(port); gc.setRepositoryUrl(gitRepoUrl); gc.setSshKeyFile(f); gc.setSshKey(key); gc.setSshPassphrase(phrase); gc.setUsername(strUsername); GerritService gerrit = new GerritService(gc); try { gerrit.testGerritConnection(); if (!gerrit.isGerritProject(strProject)) errorCollection.addError(REPOSITORY_GERRIT_PROJECT, "Project doesn't exist!"); } catch (Exception e) { errorCollection.addError(REPOSITORY_GERRIT_REPOSITORY_HOSTNAME, e.getMessage()); } return errorCollection; } public GerritService getGerritDAO() { if (gerritDAO == null) { log.debug("SSH-KEY-FILE=" + sshKeyFile); gerritDAO = new GerritService(gc); } return gerritDAO; } public GerritConfig getGerritConfig() { return gc; } public BuildLoggerManager getBuildLoggerManager() { return buildLoggerManager; } @Override public BuildRepositoryChanges collectChangesSinceLastBuild(String planKey, String lastVcsRevisionKey) throws RepositoryException { PlanKey actualKey = PlanKeys.getPlanKey(planKey); final BuildLogger buildLogger = buildLoggerManager.getLogger(actualKey); List<Commit> commits = new ArrayList<Commit>(); GerritChangeVO change = null; if (this.getVcsBranch().equals(ALL_BRANCH)) { change = getGerritDAO().getLastUnverifiedChange(project); if (change == null) { change = getGerritDAO().getLastChange(project); } } else { change = getGerritDAO().getLastUnverifiedChange(project, this.getVcsBranch().getName()); if (change == null) { change = getGerritDAO().getLastChange(project, this.getVcsBranch().getName()); } } if (change == null) { buildLogger.addBuildLogEntry(textProvider.getText("processor.gerrit.messages.build.error.nochanges")); BuildRepositoryChanges buildChanges = this.getBuildChangesFromJGit(actualKey, lastVcsRevisionKey); return buildChanges; } else { log.debug(String.format("collectChangesSinceLastBuild: %s, %s, %s", change.getBranch(), change.getId(), change.getCurrentPatchSet().getRef())); buildLogger.addBuildLogEntry(textProvider.getText("repository.gerrit.messages.ccRecover.completed")); if (lastVcsRevisionKey == null) { buildLogger.addBuildLogEntry( textProvider.getText("repository.gerrit.messages.ccRepositoryNeverChecked", Arrays.asList(change.getLastRevision()))); } else if (change.getLastRevision().equals(lastVcsRevisionKey)) { // Time is unreliable as comments change the last update field. // We need to track by last patchset revision Object lastRevForChange = bandanaManager.getValue(PlanAwareBandanaContext.GLOBAL_CONTEXT, change.getId()); if ((lastRevForChange != null) && lastRevForChange.equals(change.getLastRevision())) return new BuildRepositoryChangesImpl(change.getLastRevision()); } } commits.add(convertChangeToCommit(change, true)); BuildRepositoryChanges buildChanges = new BuildRepositoryChangesImpl(change.getLastRevision(), commits); if (!this.getVcsBranch().isEqualToBranchWith(change.getBranch())) buildChanges.setOverriddenVcsBranch(this.getVcsBranch()); bandanaManager.setValue(PlanAwareBandanaContext.GLOBAL_CONTEXT, change.getId(), change.getLastRevision()); return buildChanges; } protected BuildRepositoryChanges getBuildChangesFromJGit(PlanKey actualKey, String lastVcsRevisionKey) throws RepositoryException { String currentRevision = ""; PersonIdent authorIdent = null; BuildRepositoryChanges buildChanges = null; JGitRepository jgitRepo = new JGitRepository(); jgitRepo.setAccessData(gc); jgitRepo.open(this.getSourceCodeDirectory(actualKey)); jgitRepo.openSSHTransport(); currentRevision = jgitRepo.getLatestRevisionForBranch(this.getVcsBranch().getName()); if (!currentRevision.equals(lastVcsRevisionKey)) { String author = Author.UNKNOWN_AUTHOR; try { RevCommit rev = jgitRepo.resolveRev(currentRevision); if (rev != null) { authorIdent = rev.getAuthorIdent(); } } catch (RepositoryException e) { log.debug(String.format("Failed to retrieve author for %s.", currentRevision)); lastVcsRevisionKey = currentRevision; } jgitRepo.close(); if (author.isEmpty() && (authorIdent != null)) author = authorIdent.getName(); Builder cc = CommitContextImpl.builder(); cc.author(author); cc.comment(textProvider.getText("processor.gerrit.messages.build.error.nochanges")); if (authorIdent != null) cc.date(authorIdent.getWhen()); cc.branch(this.getVcsBranch().getName()); cc.changesetId(lastVcsRevisionKey); List<CommitContext> commitlist = Collections.singletonList((CommitContext) cc.build()); buildChanges = new BuildRepositoryChangesImpl(lastVcsRevisionKey, commitlist); } else { buildChanges = new BuildRepositoryChangesImpl(lastVcsRevisionKey); } return buildChanges; } protected CommitImpl convertChangeToCommit(GerritChangeVO change, boolean useLast) { CommitImpl commit = new CommitImpl(); PatchSet patch; if (useLast) patch = change.getCurrentPatchSet(); else patch = change.getPatchSets().iterator().next(); commit.setComment(change.getSubject()); String author = patch.getAuthorName(); if (author == null || author.isEmpty()) author = change.getOwnerName(); commit.setAuthor(new AuthorCachingFacade(author)); commit.setDate(change.getCreatedOn()); commit.setChangeSetId(patch.getRevision()); commit.setCreationDate(change.getCreatedOn()); commit.setLastModificationDate(change.getLastUpdate()); Set<FileSet> fileSets = patch.getFileSets(); for (FileSet fileSet : fileSets) { if (!fileSet.getFile().equals(GIT_COMMIT_ACTION)) { CommitFile file = new CommitFileImpl(patch.getRevision(), fileSet.getFile()); commit.addFile(file); } } return commit; } @Override public boolean isRepositoryDifferent(@NotNull Repository repository) { if (repository instanceof GerritRepositoryAdapter) { GerritRepositoryAdapter gitRepo = (GerritRepositoryAdapter) repository; return !new EqualsBuilder().append(gc.getRepositoryUrl(), gitRepo.gc.getRepositoryUrl()) .append(this.getProject(), gitRepo.getProject()) .append(this.getVcsBranch(), gitRepo.getVcsBranch()) .append(gc.getUsername(), gitRepo.gc.getUsername()) .append(gc.getSshKey(), gitRepo.gc.getSshKey()).isEquals(); } else { return true; } } @Override public boolean equals(Object obj) { if (obj instanceof Repository) return !isRepositoryDifferent((Repository) obj); return false; } @Override public void addDefaultValues(BuildConfiguration buildConfiguration) { buildConfiguration.setProperty(REPOSITORY_GERRIT_COMMAND_TIMEOUT, String.valueOf(DEFAULT_COMMAND_TIMEOUT_IN_MINUTES)); buildConfiguration.clearTree(REPOSITORY_GERRIT_VERBOSE_LOGS); buildConfiguration.setProperty(REPOSITORY_GERRIT_USE_SHALLOW_CLONES, true); buildConfiguration.clearTree(REPOSITORY_GERRIT_USE_SUBMODULES); } @Override public String getHost() { return ""; } public int getPort() { return port; } public String getHostname() { return hostname; } public String getUsername() { return username; } @Override public String getGerritEMail() { return userEmail; } @Override public String getName() { return textProvider.getText("repository.gerrit.name"); } public String getProject() { return project; } @Override @NotNull public VcsBranch getVcsBranch() { return vcsBranch; } @Override public void setVcsBranch(@NotNull final VcsBranch vcsBranch) { this.vcsBranch = vcsBranch; } private boolean isOnLocalAgent() { return !(buildDirectoryManager instanceof RemoteBuildDirectoryManager); } @Override @NotNull public String retrieveSourceCode(@NotNull BuildContext buildContext, String vcsRevisionKey, File sourceDirectory) throws RepositoryException { return retrieveSourceCode(buildContext, vcsRevisionKey, sourceDirectory, 1); } @Override @NotNull public String retrieveSourceCode(@NotNull BuildContext buildContext, String vcsRevisionKey, File sourceDirectory, int depth) throws RepositoryException { String originalVcsRevisionKey = vcsRevisionKey; PlanKey actualKey = PlanKeys.getPlanKey(buildContext.getPlanKey()); final BuildLogger buildLogger = buildLoggerManager.getLogger(actualKey); final boolean doShallowFetch = USE_SHALLOW_CLONES && gc.isUseShallowClones() && depth == 1; lastGerritChange = null; GerritChangeVO change = this.getGerritDAO().getChangeByRevision(vcsRevisionKey); if (change != null) { buildLogger.addBuildLogEntry(String.format("Retrieving Source for Change: %s, %s, %s", change.getBranch(), change.getId(), change.getCurrentPatchSet().getRef())); lastGerritChange = change; vcsRevisionKey = change.getCurrentPatchSet().getRef(); } else { throw new RepositoryException(textProvider.getText("repository.gerrit.messages.error.retrieve")); } log.debug(String.format("getVcsBranch()=%s", getVcsBranch().getName())); JGitRepository jgitRepo = new JGitRepository(); jgitRepo.setAccessData(gc); jgitRepo.open(sourceDirectory); jgitRepo.openSSHTransport(); if (doShallowFetch) jgitRepo.fetch(vcsRevisionKey, depth); else jgitRepo.fetch(vcsRevisionKey); jgitRepo.checkout(vcsRevisionKey); jgitRepo.close(); return originalVcsRevisionKey; } @Override public boolean isMergingSupported() { return true; } @Override public boolean mergeWorkspaceWith(@NotNull final BuildContext buildContext, @NotNull final File file, @NotNull final String s) throws RepositoryException { JGitRepository jgitRepo = new JGitRepository(); jgitRepo.setAccessData(gc); jgitRepo.open(file); jgitRepo.openSSHTransport(); jgitRepo.fetch("refs/heads/" + this.getVcsBranch().getName()); org.eclipse.jgit.api.MergeResult mr = jgitRepo.merge(s); jgitRepo.close(); MergeStatus ms = mr.getMergeStatus(); if (ms.isSuccessful()) { return true; } return false; } @Override public File getGerritAuthKeyFile() { return this.sshKeyFile; } @Override public String getGerritAuthKeyFilePassword() { return sshPassphrase; } @Override public Authentication getGerritAuthentication() { return new Authentication(this.sshKeyFile, username, sshPassphrase); } @Override public String getGerritHostName() { return this.getHostname(); } @Override public int getGerritSshPort() { return port; } @Override public String getGerritUserName() { return username; } @Override public int getWatchdogTimeoutSeconds() { return 0; } @Override public WatchTimeExceptionData getExceptionData() { return null; } @Override public String getGerritProxy() { return null; } @Override public void pushRevision(@NotNull final File file, @Nullable final String s) throws RepositoryException { JGitRepository jgitRepo = new JGitRepository(); jgitRepo.setAccessData(gc); jgitRepo.open(file, true); jgitRepo.openSSHTransport(); jgitRepo.push(file, s); } @NotNull @Override public String commit(@NotNull final File file, @NotNull final String s) throws RepositoryException { JGitRepository jgitRepo = new JGitRepository(); jgitRepo.setAccessData(gc); jgitRepo.open(file, true); jgitRepo.openSSHTransport(); jgitRepo.add("."); RevCommit c = jgitRepo.commit(s); return c.name(); } public void setEncryptionService(EncryptionService encryptionService) { this.encryptionService = encryptionService; } @Override @NotNull public Map<String, String> getCustomVariables() { Map<String, String> ret = new HashMap<String, String>(); if (lastGerritChange != null) { ret.put(REPOSITORY_GERRIT_CHANGE_ID, lastGerritChange.getId()); ret.put(REPOSITORY_GERRIT_CHANGE_NUMBER, String.valueOf(lastGerritChange.getNumber())); ret.put(REPOSITORY_GERRIT_REVISION_NUMBER, lastGerritChange.getLastRevision()); } return ret; } @NotNull public Map<String, String> getPlanRepositoryVariables() { Map<String, String> variables = new HashMap<String, String>(); variables.put(REPOSITORY_URL, gc.getRepositoryUrl()); variables.put(REPOSITORY_USERNAME, gc.getUsername()); return variables; } @Override public Set<Requirement> getRequirements() { return new HashSet<Requirement>(); } @Override public List<VcsBranch> getOpenBranches(String context) throws RepositoryException { List<VcsBranch> vcsBranches = new ArrayList<VcsBranch>(); PlanKey planKey = findFirstPlanKey(false); if (planKey != null) { JGitRepository jgitRepo = new JGitRepository(); jgitRepo.setAccessData(gc); // PlanHelper. jgitRepo.open(this.getSourceCodeDirectory(planKey)); jgitRepo.openSSHTransport(); Collection<Ref> branches = jgitRepo.lsRemoteBranches(); for (Ref b : branches) { String strBranch = b.getName(); if (strBranch.contains("/")) strBranch = strBranch.substring(strBranch.lastIndexOf("/") + 1); if (this.getVcsBranch() != null && !this.getVcsBranch().isEqualToBranchWith(strBranch)) vcsBranches.add(new VcsBranchImpl(strBranch)); } jgitRepo.close(); } return vcsBranches; } @Override public CommitContext getLastCommit() throws RepositoryException { CommitImpl commit = new CommitImpl(); if (lastGerritChange != null) { commit = convertChangeToCommit(lastGerritChange, true); } return commit; } @Override public CommitContext getFirstCommit() throws RepositoryException { CommitImpl commit = new CommitImpl(); if (lastGerritChange != null) { commit = convertChangeToCommit(lastGerritChange, false); } return commit; } @Override public void createBranch(long repositoryId, String branchName, BuildContext buildContext) throws RepositoryException { PlanKey planKey = PlanKeys.getPlanKey(buildContext.getPlanKey()); if (planKey != null) { JGitRepository jgitRepo = new JGitRepository(); jgitRepo.setAccessData(gc); jgitRepo.open(this.getSourceCodeDirectory(planKey)); jgitRepo.openSSHTransport(); jgitRepo.createBranch(branchName); jgitRepo.close(); } } @Override public boolean usePollingForBranchDetection() { return true; } /** * Check to see if this repository belongs to a plan. * * @param isRemoteTriggeredBy * @return */ private PlanKey findFirstPlanKey(boolean isRemoteTriggeredBy) { Collection<TopLevelPlan> plans = null; PlanKey planKey = null; Map<Project, Collection<TopLevelPlan>> projectBuilds = planManager.getProjectPlanMap(TopLevelPlan.class, false); Iterator<Project> it = projectBuilds.keySet().iterator(); while (it.hasNext()) { Project p = it.next(); plans = projectBuilds.get(p); for (TopLevelPlan pl : plans) { if (pl.isSuspendedFromBuilding()) continue; if (isRepoTriggerFor(pl, isRemoteTriggeredBy)) return pl.getPlanKey(); int branchCount = chainBranchManager.getBranchCount(pl); if (branchCount > 0) { List<ChainBranch> chains = chainBranchManager.getBranchesForChain(pl); for (ChainBranch c : chains) { if (isRepoTriggerFor(c, isRemoteTriggeredBy)) return c.getPlanKey(); } } } } return planKey; } /** * Return the frist plan which uses this repository * * @param isRemoteTriggeredBy * @return */ private ImmutableChain findFirstPlan(boolean isRemoteTriggeredBy) { PlanKey planKey = findFirstPlanKey(isRemoteTriggeredBy); if (planKey == null) return null; return planManager.getPlanByKey(planKey, Chain.class); } /** * Determine if plan uses this repository * * @param chain * @param isRemoteTrigger * , dertmined if plan uses repository for remote use * @return */ private boolean isRepoTriggerFor(ImmutableChain chain, boolean isRemoteTrigger) { List<BuildStrategy> strats = chain.getTriggers(); List<RepositoryDefinition> cRepos = new ArrayList<RepositoryDefinition>(); cRepos = chain.getEffectiveRepositoryDefinitions(); for (RepositoryDefinition rd : cRepos) { if (rd.getName().toLowerCase().equals(this.getName().toLowerCase()) && rd.getPluginKey().equals(this.getKey())) { HierarchicalConfiguration hconfig = rd.getConfiguration(); String strDefBranch = hconfig.getString(REPOSITORY_GERRIT_DEFAULT_BRANCH, ""); String strCustBranch = hconfig.getString(REPOSITORY_GERRIT_CUSTOM_BRANCH, ""); if (this.getVcsBranch().isEqualToBranchWith(strDefBranch) || this.getVcsBranch().isEqualToBranchWith(strCustBranch)) { if (isRemoteTrigger) { for (BuildStrategy s : strats) { if (s instanceof TriggeredBuildStrategy) { TriggeredBuildStrategy tbs = Narrow.downTo(s, TriggeredBuildStrategy.class); Set<Long> repos = tbs.getTriggeringRepositories(); if (repos.contains(rd.getId())) { return true; } else { for (Long rID : repos) { RepositoryDataEntity rde = repositoryDefinitionManager .getRepositoryDataEntity(rID); if (rde.getName().equals(this.getName()) && rde.getPluginKey().equals(this.getKey())) { return true; } } } } else { return false; } } } else { return true; } } } } return false; } private boolean isRemoteTriggeringReop() { ImmutableChain c = this.findFirstPlan(true); return (c != null); } private void publishChangeDetectionEvent(ImmutableChain c, GerritEvent e) { if (c != null) { VcsBranch triggerBranch = this.getVcsBranch(); System.out.println(""); if (e instanceof PatchsetCreated) { PatchsetCreated ps = (PatchsetCreated) e; triggerBranch = new VcsBranchImpl(ps.getChange().getBranch()); project = ps.getChange().getProject(); log.debug("GerritRepository processing PatchsetCreated: " + ps.getChange().getUrl()); } BuildDefinition buildDefinition = c.getBuildDefinition(); // 5.8+ if (this.getProject().equals(project)) { for (TriggerDefinition td : c.getTriggerDefinitions()) { if (this.getVcsBranch().equals(triggerBranch)) { eventPublisher.publish(new ChangeDetectionRequiredEvent(this, c.getKey(), td, false)); } } } } } @Override public void processGerritEvent(GerritEvent e) { log.debug("GerritRepository processing event: " + e.getEventType().toString()); Collection<TopLevelPlan> plans = null; Map<Project, Collection<TopLevelPlan>> projectBuilds = planManager.getProjectPlanMap(TopLevelPlan.class, false); Iterator<Project> it = projectBuilds.keySet().iterator(); while (it.hasNext()) { Project p = it.next(); plans = projectBuilds.get(p); for (TopLevelPlan pl : plans) { if (pl.isSuspendedFromBuilding()) continue; if (isRepoTriggerFor(pl, true)) { publishChangeDetectionEvent(pl, e); } int branchCount = chainBranchManager.getBranchCount(pl); if (branchCount > 0) { List<ChainBranch> chains = chainBranchManager.getBranchesForChain(pl); for (ChainBranch c : chains) { if (isRepoTriggerFor(c, true)) { publishChangeDetectionEvent(c, e); } } } } } } @Override public String getGerritFrontEndUrl() { // TODO Auto-generated method stub return null; } @Override public Credentials getHttpCredentials() { // TODO Auto-generated method stub return null; } @Override public int getWatchdogTimeoutMinutes() { // TODO Auto-generated method stub return 0; } }