View Javadoc
1   /*
2    * Copyright (C) 2015, 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.net.URISyntaxException;
14  import java.text.MessageFormat;
15  import java.util.Collections;
16  import java.util.EnumSet;
17  import java.util.HashMap;
18  import java.util.Set;
19  
20  import org.eclipse.jgit.errors.NotSupportedException;
21  import org.eclipse.jgit.errors.TransportException;
22  import org.eclipse.jgit.internal.JGitText;
23  import org.eclipse.jgit.lib.Repository;
24  import org.eclipse.jgit.transport.BasePackFetchConnection.FetchConfig;
25  import org.eclipse.jgit.transport.resolver.ReceivePackFactory;
26  import org.eclipse.jgit.transport.resolver.UploadPackFactory;
27  
28  /**
29   * Protocol for transport between manually-specified repositories in tests.
30   * <p>
31   * Remote repositories are registered using
32   * {@link #register(Object, Repository)}, after which they can be accessed using
33   * the returned URI. As this class provides both the client side (the protocol)
34   * and the server side, the caller is responsible for setting up and passing the
35   * connection context, whatever form that may take.
36   * <p>
37   * Unlike the other built-in protocols, which are automatically-registered
38   * singletons, callers are expected to register/unregister specific protocol
39   * instances on demand with
40   * {@link org.eclipse.jgit.transport.Transport#register(TransportProtocol)}.
41   *
42   * @param <C>
43   *            the connection type
44   * @since 4.0
45   */
46  public class TestProtocol<C> extends TransportProtocol {
47  	private static final String SCHEME = "test"; //$NON-NLS-1$
48  
49  	private static FetchConfig fetchConfig;
50  
51  	private class Handle {
52  		final C req;
53  		final Repository remote;
54  
55  		Handle(C req, Repository remote) {
56  			this.req = req;
57  			this.remote = remote;
58  		}
59  	}
60  
61  	final UploadPackFactory<C> uploadPackFactory;
62  	final ReceivePackFactory<C> receivePackFactory;
63  	private final HashMap<URIish, Handle> handles;
64  
65  	/**
66  	 * Constructor for TestProtocol.
67  	 *
68  	 * @param uploadPackFactory
69  	 *            factory for creating
70  	 *            {@link org.eclipse.jgit.transport.UploadPack} used by all
71  	 *            connections from this protocol instance.
72  	 * @param receivePackFactory
73  	 *            factory for creating
74  	 *            {@link org.eclipse.jgit.transport.ReceivePack} used by all
75  	 *            connections from this protocol instance.
76  	 */
77  	public TestProtocol(UploadPackFactory<C> uploadPackFactory,
78  			ReceivePackFactory<C> receivePackFactory) {
79  		this.uploadPackFactory = uploadPackFactory;
80  		this.receivePackFactory = receivePackFactory;
81  		this.handles = new HashMap<>();
82  	}
83  
84  	/** {@inheritDoc} */
85  	@Override
86  	public String getName() {
87  		return JGitText.get().transportProtoTest;
88  	}
89  
90  	/** {@inheritDoc} */
91  	@Override
92  	public Set<String> getSchemes() {
93  		return Collections.singleton(SCHEME);
94  	}
95  
96  	/** {@inheritDoc} */
97  	@Override
98  	public Transport open(URIish uri, Repository local, String remoteName)
99  			throws NotSupportedException, TransportException {
100 		Handle h = handles.get(uri);
101 		if (h == null) {
102 			throw new NotSupportedException(MessageFormat.format(
103 					JGitText.get().URINotSupported, uri));
104 		}
105 		return new TransportInternal(local, uri, h);
106 	}
107 
108 	/** {@inheritDoc} */
109 	@Override
110 	public Set<URIishField> getRequiredFields() {
111 		return EnumSet.of(URIishField.HOST, URIishField.PATH);
112 	}
113 
114 	/** {@inheritDoc} */
115 	@Override
116 	public Set<URIishField> getOptionalFields() {
117 		return Collections.emptySet();
118 	}
119 
120 	static void setFetchConfig(FetchConfig c) {
121 		fetchConfig = c;
122 	}
123 
124 	/**
125 	 * Register a repository connection over the internal test protocol.
126 	 *
127 	 * @param req
128 	 *            connection context. This instance is reused for all connections
129 	 *            made using this protocol; if it is stateful and usable only for
130 	 *            one connection, the same repository should be registered
131 	 *            multiple times.
132 	 * @param remote
133 	 *            remote repository to connect to.
134 	 * @return a URI that can be used to connect to this repository for both fetch
135 	 *         and push.
136 	 */
137 	public synchronized URIish register(C req, Repository remote) {
138 		URIish uri;
139 		try {
140 			int n = handles.size();
141 			uri = new URIish(SCHEME + "://test/conn" + n); //$NON-NLS-1$
142 		} catch (URISyntaxException e) {
143 			throw new IllegalStateException(e);
144 		}
145 		handles.put(uri, new Handle(req, remote));
146 		return uri;
147 	}
148 
149 	private class TransportInternal extends Transport implements PackTransport {
150 		private final Handle handle;
151 
152 		TransportInternal(Repository local, URIish uri, Handle handle) {
153 			super(local, uri);
154 			this.handle = handle;
155 		}
156 
157 		@Override
158 		public FetchConnection openFetch() throws NotSupportedException,
159 				TransportException {
160 			handle.remote.incrementOpen();
161 			return new InternalFetchConnection<C>(this, uploadPackFactory,
162 					handle.req, handle.remote) {
163 				@Override
164 				FetchConfig getFetchConfig() {
165 					return fetchConfig != null ? fetchConfig
166 							: super.getFetchConfig();
167 				}
168 			};
169 		}
170 
171 		@Override
172 		public PushConnection openPush() throws NotSupportedException,
173 				TransportException {
174 			handle.remote.incrementOpen();
175 			return new InternalPushConnection<>(
176 					this, receivePackFactory, handle.req, handle.remote);
177 		}
178 
179 		@Override
180 		public void close() {
181 			// Resources must be established per-connection.
182 		}
183 	}
184 }