1 /* 2 * Copyright (C) 2011, Google Inc. and others 3 * 4 * This program and the accompanying materials are made available under the 5 * terms of the Eclipse Distribution License v. 1.0 which is available at 6 * https://www.eclipse.org/org/documents/edl-v10.php. 7 * 8 * SPDX-License-Identifier: BSD-3-Clause 9 */ 10 11 package org.eclipse.jgit.transport; 12 13 import java.util.Collections; 14 import java.util.EnumSet; 15 import java.util.Set; 16 17 import org.eclipse.jgit.errors.NotSupportedException; 18 import org.eclipse.jgit.errors.TransportException; 19 import org.eclipse.jgit.internal.JGitText; 20 import org.eclipse.jgit.lib.Repository; 21 22 /** 23 * Describes a way to connect to another Git repository. 24 * <p> 25 * Implementations of this class are typically immutable singletons held by 26 * static class members, for example: 27 * 28 * <pre> 29 * package com.example.my_transport; 30 * 31 * class MyTransport extends Transport { 32 * public static final TransportProtocol PROTO = new TransportProtocol() { 33 * public String getName() { 34 * return "My Protocol"; 35 * } 36 * }; 37 * } 38 * </pre> 39 * 40 * <p> 41 * Applications may register additional protocols for use by JGit by calling 42 * {@link org.eclipse.jgit.transport.Transport#register(TransportProtocol)}. 43 * Because that API holds onto the protocol object by a WeakReference, 44 * applications must ensure their own ClassLoader retains the TransportProtocol 45 * for the life of the application. Using a static singleton pattern as above 46 * will ensure the protocol is valid so long as the ClassLoader that defines it 47 * remains valid. 48 * <p> 49 * Applications may automatically register additional protocols by filling in 50 * the names of their TransportProtocol defining classes using the services file 51 * {@code META-INF/services/org.eclipse.jgit.transport.Transport}. For each 52 * class name listed in the services file, any static fields of type 53 * {@code TransportProtocol} will be automatically registered. For the above 54 * example the string {@code com.example.my_transport.MyTransport} should be 55 * listed in the file, as that is the name of the class that defines the static 56 * PROTO singleton. 57 */ 58 public abstract class TransportProtocol { 59 /** Fields within a {@link URIish} that a transport uses. */ 60 public enum URIishField { 61 /** the user field */ 62 USER, 63 /** the pass (aka password) field */ 64 PASS, 65 /** the host field */ 66 HOST, 67 /** the port field */ 68 PORT, 69 /** the path field */ 70 PATH, 71 } 72 73 /** 74 * Get text name of the protocol suitable for display to a user. 75 * 76 * @return text name of the protocol suitable for display to a user. 77 */ 78 public abstract String getName(); 79 80 /** 81 * Get immutable set of schemes supported by this protocol. 82 * 83 * @return immutable set of schemes supported by this protocol. 84 */ 85 public Set<String> getSchemes() { 86 return Collections.emptySet(); 87 } 88 89 /** 90 * Get immutable set of URIishFields that must be filled in. 91 * 92 * @return immutable set of URIishFields that must be filled in. 93 */ 94 public Set<URIishField> getRequiredFields() { 95 return Collections.unmodifiableSet(EnumSet.of(URIishField.PATH)); 96 } 97 98 /** 99 * Get immutable set of URIishFields that may be filled in. 100 * 101 * @return immutable set of URIishFields that may be filled in. 102 */ 103 public Set<URIishField> getOptionalFields() { 104 return Collections.emptySet(); 105 } 106 107 /** 108 * Get the default port if the protocol supports a port, else -1. 109 * 110 * @return the default port if the protocol supports a port, else -1. 111 */ 112 public int getDefaultPort() { 113 return -1; 114 } 115 116 /** 117 * Determine if this protocol can handle a particular URI. 118 * <p> 119 * Implementations should try to avoid looking at the local filesystem, but 120 * may look at implementation specific configuration options in the remote 121 * block of {@code local.getConfig()} using {@code remoteName} if the name 122 * is non-null. 123 * <p> 124 * The default implementation of this method matches the scheme against 125 * {@link #getSchemes()}, required fields against 126 * {@link #getRequiredFields()}, and optional fields against 127 * {@link #getOptionalFields()}, returning true only if all of the fields 128 * match the specification. 129 * 130 * @param uri 131 * address of the Git repository; never null. 132 * @return true if this protocol can handle this URI; false otherwise. 133 */ 134 public boolean canHandle(URIish uri) { 135 return canHandle(uri, null, null); 136 } 137 138 /** 139 * Determine if this protocol can handle a particular URI. 140 * <p> 141 * Implementations should try to avoid looking at the local filesystem, but 142 * may look at implementation specific configuration options in the remote 143 * block of {@code local.getConfig()} using {@code remoteName} if the name 144 * is non-null. 145 * <p> 146 * The default implementation of this method matches the scheme against 147 * {@link #getSchemes()}, required fields against 148 * {@link #getRequiredFields()}, and optional fields against 149 * {@link #getOptionalFields()}, returning true only if all of the fields 150 * match the specification. 151 * 152 * @param uri 153 * address of the Git repository; never null. 154 * @param local 155 * the local repository that will communicate with the other Git 156 * repository. May be null if the caller is only asking about a 157 * specific URI and does not have a local Repository. 158 * @param remoteName 159 * name of the remote, if the remote as configured in 160 * {@code local}; otherwise null. 161 * @return true if this protocol can handle this URI; false otherwise. 162 */ 163 public boolean canHandle(URIish uri, Repository local, String remoteName) { 164 if (!getSchemes().isEmpty() && !getSchemes().contains(uri.getScheme())) 165 return false; 166 167 for (URIishField field : getRequiredFields()) { 168 switch (field) { 169 case USER: 170 if (uri.getUser() == null || uri.getUser().length() == 0) 171 return false; 172 break; 173 174 case PASS: 175 if (uri.getPass() == null || uri.getPass().length() == 0) 176 return false; 177 break; 178 179 case HOST: 180 if (uri.getHost() == null || uri.getHost().length() == 0) 181 return false; 182 break; 183 184 case PORT: 185 if (uri.getPort() <= 0) 186 return false; 187 break; 188 189 case PATH: 190 if (uri.getPath() == null || uri.getPath().length() == 0) 191 return false; 192 break; 193 194 default: 195 return false; 196 } 197 } 198 199 Set<URIishField> canHave = EnumSet.copyOf(getRequiredFields()); 200 canHave.addAll(getOptionalFields()); 201 202 if (uri.getUser() != null && !canHave.contains(URIishField.USER)) 203 return false; 204 if (uri.getPass() != null && !canHave.contains(URIishField.PASS)) 205 return false; 206 if (uri.getHost() != null && !canHave.contains(URIishField.HOST)) 207 return false; 208 if (uri.getPort() > 0 && !canHave.contains(URIishField.PORT)) 209 return false; 210 if (uri.getPath() != null && !canHave.contains(URIishField.PATH)) 211 return false; 212 213 return true; 214 } 215 216 /** 217 * Open a Transport instance to the other repository. 218 * <p> 219 * Implementations should avoid making remote connections until an operation 220 * on the returned Transport is invoked, however they may fail fast here if 221 * they know a connection is impossible, such as when using the local 222 * filesystem and the target path does not exist. 223 * <p> 224 * Implementations may access implementation-specific configuration options 225 * within {@code local.getConfig()} using the remote block named by the 226 * {@code remoteName}, if the name is non-null. 227 * 228 * @param uri 229 * address of the Git repository. 230 * @param local 231 * the local repository that will communicate with the other Git 232 * repository. 233 * @param remoteName 234 * name of the remote, if the remote as configured in 235 * {@code local}; otherwise null. 236 * @return the transport. 237 * @throws org.eclipse.jgit.errors.NotSupportedException 238 * this protocol does not support the URI. 239 * @throws org.eclipse.jgit.errors.TransportException 240 * the transport cannot open this URI. 241 */ 242 public abstract Transport open(URIish uri, Repository local, 243 String remoteName) 244 throws NotSupportedException, TransportException; 245 246 /** 247 * Open a new transport instance to the remote repository. Use default 248 * configuration instead of reading from configuration files. 249 * 250 * @param uri 251 * a {@link org.eclipse.jgit.transport.URIish} object. 252 * @return new Transport 253 * @throws org.eclipse.jgit.errors.NotSupportedException 254 * @throws org.eclipse.jgit.errors.TransportException 255 */ 256 public Transport open(URIish uri) 257 throws NotSupportedException, TransportException { 258 throw new NotSupportedException(JGitText 259 .get().transportNeedsRepository); 260 } 261 }