Java tutorial
// Copyright (C) 2009 The Android Open Source Project // // 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.google.gerrit.sshd.commands; import com.google.gerrit.common.CollectionsUtil; import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.Permission; import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.Project.SubmitType; import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.config.ProjectCreatorGroups; import com.google.gerrit.server.config.ProjectOwnerGroups; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.git.ReplicationQueue; import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.sshd.BaseCommand; import com.google.inject.Inject; import org.apache.sshd.server.Environment; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.CommitBuilder; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RefUpdate.Result; import org.kohsuke.args4j.Option; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** Create a new project. **/ final class CreateProject extends BaseCommand { private static final Logger log = LoggerFactory.getLogger(CreateProject.class); @Option(name = "--name", required = true, aliases = { "-n" }, metaVar = "NAME", usage = "name of project to be created") private String projectName; @Option(name = "--owner", aliases = { "-o" }, usage = "owner(s) of project") private List<AccountGroup.UUID> ownerIds; @Option(name = "--parent", aliases = { "-p" }, metaVar = "NAME", usage = "parent project") private ProjectControl newParent; @Option(name = "--description", aliases = { "-d" }, metaVar = "DESC", usage = "description of project") private String projectDescription = ""; @Option(name = "--submit-type", aliases = { "-t" }, usage = "project submit type\n" + "(default: MERGE_IF_NECESSARY)") private SubmitType submitType = SubmitType.MERGE_IF_NECESSARY; @Option(name = "--use-contributor-agreements", aliases = { "--ca" }, usage = "if contributor agreement is required") private boolean contributorAgreements; @Option(name = "--use-signed-off-by", aliases = { "--so" }, usage = "if signed-off-by is required") private boolean signedOffBy; @Option(name = "--use-content-merge", usage = "allow automatic conflict resolving within files") private boolean contentMerge; @Option(name = "--require-short-message", usage = "if short commit subject is required") private boolean requireShortMessage; @Option(name = "--require-change-id", aliases = { "--id" }, usage = "if change-id is required") private boolean requireChangeID; @Option(name = "--branch", aliases = { "-b" }, metaVar = "BRANCH", usage = "initial branch name\n" + "(default: master)") private String branch = Constants.MASTER; @Option(name = "--empty-commit", usage = "to create initial empty commit") private boolean createEmptyCommit; @Inject private GitRepositoryManager repoManager; @Inject private ProjectCache projectCache; @Inject private GroupCache groupCache; @Inject @ProjectCreatorGroups private Set<AccountGroup.UUID> projectCreatorGroups; @Inject @ProjectOwnerGroups private Set<AccountGroup.UUID> projectOwnerGroups; @Inject private IdentifiedUser currentUser; @Inject private ReplicationQueue rq; @Inject @GerritPersonIdent private PersonIdent serverIdent; @Inject MetaDataUpdate.User metaDataUpdateFactory; private Project.NameKey nameKey; @Override public void start(final Environment env) { startThread(new CommandRunnable() { @Override public void run() throws Exception { PrintWriter p = toPrintWriter(out); parseCommandLine(); try { validateParameters(); nameKey = new Project.NameKey(projectName); final Repository repo = repoManager.createRepository(nameKey); try { RefUpdate u = repo.updateRef(Constants.HEAD); u.disableRefLog(); u.link(branch); createProject(); repoManager.setProjectDescription(nameKey, projectDescription); if (createEmptyCommit) { createEmptyCommit(repo, nameKey, branch); } rq.replicateNewProject(nameKey, branch); } finally { repo.close(); } } catch (Exception e) { p.print("Error when trying to create project: " + e.getMessage() + "\n"); p.flush(); } } }); } private void createEmptyCommit(final Repository repo, final Project.NameKey project, final String ref) throws IOException { ObjectInserter oi = repo.newObjectInserter(); try { CommitBuilder cb = new CommitBuilder(); cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {})); cb.setAuthor(metaDataUpdateFactory.getUserPersonIdent()); cb.setCommitter(serverIdent); cb.setMessage("Initial empty repository\n"); ObjectId id = oi.insert(cb); oi.flush(); RefUpdate ru = repo.updateRef(Constants.HEAD); ru.setNewObjectId(id); final Result result = ru.update(); switch (result) { case NEW: rq.scheduleUpdate(project, ref); break; default: { throw new IOException(result.name()); } } } catch (IOException e) { log.error("Cannot create empty commit for " + projectName, e); throw e; } finally { oi.release(); } } private void createProject() throws IOException, ConfigInvalidException { MetaDataUpdate md = metaDataUpdateFactory.create(nameKey); try { ProjectConfig config = ProjectConfig.read(md); config.load(md); Project newProject = config.getProject(); newProject.setDescription(projectDescription); newProject.setSubmitType(submitType); newProject.setUseContributorAgreements(contributorAgreements); newProject.setUseSignedOffBy(signedOffBy); newProject.setUseContentMerge(contentMerge); newProject.setRequireShortMessage(requireShortMessage); newProject.setRequireChangeID(requireChangeID); if (newParent != null) { newProject.setParentName(newParent.getProject().getName()); } if (!ownerIds.isEmpty()) { AccessSection all = config.getAccessSection(AccessSection.ALL, true); for (AccountGroup.UUID ownerId : ownerIds) { AccountGroup accountGroup = groupCache.get(ownerId); GroupReference group = config.resolve(accountGroup); all.getPermission(Permission.OWNER, true).add(new PermissionRule(group)); } } md.setMessage("Created project\n"); if (!config.commit(md)) { throw new IOException("Cannot create " + projectName); } } finally { md.close(); } projectCache.onCreateProject(nameKey); } private void validateParameters() throws Failure { if (projectName.endsWith(Constants.DOT_GIT_EXT)) { projectName = projectName.substring(0, // projectName.length() - Constants.DOT_GIT_EXT.length()); } if (!CollectionsUtil.isAnyIncludedIn(currentUser.getEffectiveGroups(), projectCreatorGroups)) { throw new Failure(1, "fatal: Not permitted to create " + projectName); } if (ownerIds != null && !ownerIds.isEmpty()) { ownerIds = new ArrayList<AccountGroup.UUID>(new HashSet<AccountGroup.UUID>(ownerIds)); } else { ownerIds = new ArrayList<AccountGroup.UUID>(projectOwnerGroups); } while (branch.startsWith("/")) { branch = branch.substring(1); } if (!branch.startsWith(Constants.R_HEADS)) { branch = Constants.R_HEADS + branch; } if (!Repository.isValidRefName(branch)) { throw new Failure(1, "--branch \"" + branch + "\" is not a valid name"); } } }