@@ -16,7 +16,6 @@ import io.netty.buffer.UnpooledByteBufAllocator
1616import io.netty.channel.ChannelOption
1717import io.netty.util.concurrent.FastThreadLocal
1818import org.gradle.api.Action
19- import org.gradle.api.NamedDomainObjectList
2019import org.gradle.api.Project
2120import org.gradle.api.artifacts.ConfigurationContainer
2221import org.gradle.api.artifacts.ResolvableDependencies
@@ -144,8 +143,8 @@ private const val PORT_HTTP_HEADER_NAME = "port"
144143internal fun OciRegistriesService (
145144 buildServiceRegistry : BuildServiceRegistry ,
146145 name : String ,
147- registries : NamedDomainObjectList <OciRegistry >,
148- repositoryPort : Property <Int >,
146+ registries : List <OciRegistry >,
147+ repositoryPort : Provider <Int >,
149148 imageMapping : OciImageMappingImpl ,
150149): OciRegistriesService {
151150 val registriesService = buildServiceRegistry.registerIfAbsent(name, OciRegistriesService ::class ) {}.get()
@@ -154,45 +153,65 @@ internal fun OciRegistriesService(
154153}
155154
156155internal abstract class OciRegistriesService : BuildService <BuildServiceParameters .None >, AutoCloseable {
157- lateinit var registries: NamedDomainObjectList <OciRegistry >
158- lateinit var repositoryPort: Property <Int >
159- lateinit var imageMapping: OciImageMappingImpl
160- private var isStarted = false
161- private val httpServers = mutableListOf<DisposableServer >()
156+ private var state: State ? = null
157+
158+ sealed class State {
159+ class Initialized (
160+ val registries : List <OciRegistry >,
161+ val repositoryPort : Provider <Int >,
162+ val imageMapping : OciImageMappingImpl ,
163+ ) : State()
164+
165+ class Started (
166+ val httpServers : List <DisposableServer >,
167+ ) : State()
162168
163- fun init (
164- registries : NamedDomainObjectList <OciRegistry >,
165- repositoryPort : Property <Int >,
166- imageMapping : OciImageMappingImpl ,
167- ) {
168- this .registries = registries
169- this .repositoryPort = repositoryPort
170- this .imageMapping = imageMapping
169+ object NoRegistries : State()
170+
171+ object Closed : State()
172+ }
173+
174+ fun init (registries : List <OciRegistry >, repositoryPort : Provider <Int >, imageMapping : OciImageMappingImpl ) {
175+ if (state != null ) {
176+ throw IllegalStateException ()
177+ }
178+ state = State .Initialized (registries, repositoryPort, imageMapping)
171179 }
172180
173181 fun start () {
174- if (isStarted) {
182+ val currentState = state
183+ if (currentState !is State .Initialized ) {
184+ if ((currentState == null ) || (currentState == State .Closed )) {
185+ throw IllegalStateException ()
186+ }
175187 return
176188 }
177- isStarted = true
178- if (registries.isNotEmpty()) {
189+ state = if (currentState.registries.isEmpty()) {
190+ State .NoRegistries
191+ } else {
192+ val httpServers = mutableListOf<DisposableServer >()
179193 val loopResources = OciLoopResources .acquire()
180- startRedirectServer(repositoryPort.get(), loopResources)
194+ val redirectServer = startRedirectServer(currentState.repositoryPort.get(), loopResources)
195+ if (redirectServer != null ) {
196+ httpServers + = redirectServer
197+ }
181198 val imageMetadataRegistry = OciImageMetadataRegistry (OciRegistryApi (OciRegistryHttpClient .acquire()))
182- val imageMappingData = imageMapping.getData()
183- for (registry in registries) {
184- startRegistryServer(registry, loopResources, imageMetadataRegistry, imageMappingData)
199+ val imageMappingData = currentState. imageMapping.getData()
200+ for (registry in currentState. registries) {
201+ httpServers + = startRegistryServer(registry, loopResources, imageMetadataRegistry, imageMappingData)
185202 }
203+ State .Started (httpServers)
186204 }
187205 }
188206
189- private fun startRedirectServer (port : Int , loopResources : LoopResources ) {
190- try {
207+ private fun startRedirectServer (port : Int , loopResources : LoopResources ): DisposableServer ? {
208+ return try {
191209 startHttpServer(port, loopResources) { request, response ->
192210 val redirectPort = request.requestHeaders()[PORT_HTTP_HEADER_NAME ]
193211 response.sendRedirect(" http://localhost:" + redirectPort + request.uri())
194212 }
195213 } catch (_: ChannelBindException ) {
214+ null
196215 }
197216 }
198217
@@ -201,20 +220,21 @@ internal abstract class OciRegistriesService : BuildService<BuildServiceParamete
201220 loopResources : LoopResources ,
202221 imageMetadataRegistry : OciImageMetadataRegistry ,
203222 imageMappingData : OciImageMappingData ,
204- ) {
223+ ): DisposableServer {
205224 val credentials = registry.credentials.orNull?.let { Credentials (it.username!! , it.password!! ) }
206- val port = startHttpServer(
225+ val httpServer = startHttpServer(
207226 0 ,
208227 loopResources,
209228 OciRepositoryHandler (imageMetadataRegistry, imageMappingData, credentials),
210- ).port()
229+ )
211230 registry.repository.credentials(HttpHeaderCredentials ::class ) {
212231 name = PORT_HTTP_HEADER_NAME
213- value = port.toString()
232+ value = httpServer. port() .toString()
214233 }
215234 registry.repository.authentication {
216235 create<HttpHeaderAuthentication >(" header" )
217236 }
237+ return httpServer
218238 }
219239
220240 private fun startHttpServer (
@@ -223,14 +243,12 @@ internal abstract class OciRegistriesService : BuildService<BuildServiceParamete
223243 handler : BiFunction <in HttpServerRequest , in HttpServerResponse , out Publisher <Void >>,
224244 ): DisposableServer {
225245 return try {
226- val httpServer = HttpServer .create()
246+ HttpServer .create()
227247 .bindAddress { InetSocketAddress (" localhost" , port) }
228248 .runOn(loopResources)
229249 .childOption(ChannelOption .ALLOCATOR , UnpooledByteBufAllocator .DEFAULT )
230250 .handle(handler)
231251 .bindNow()
232- httpServers + = httpServer
233- httpServer
234252 } finally {
235253 // Netty adds a thread local to the current thread that then retains a reference to the current classloader.
236254 // The current classloader can then not be collected, although it has a narrower scope then the current thread.
@@ -239,14 +257,15 @@ internal abstract class OciRegistriesService : BuildService<BuildServiceParamete
239257 }
240258
241259 final override fun close () {
242- if (httpServers.isNotEmpty()) {
243- for (httpServer in httpServers) {
260+ val currentState = state
261+ if (currentState is State .Started ) {
262+ for (httpServer in currentState.httpServers) {
244263 httpServer.disposeNow()
245264 }
246- httpServers.clear()
247265 OciRegistryHttpClient .release()
248266 OciLoopResources .release()
249267 }
268+ state = State .Closed
250269 }
251270}
252271
@@ -271,9 +290,11 @@ internal fun setupProjectOciRegistries(
271290 registries : OciRegistries ,
272291 imageMapping : OciImageMappingImpl ,
273292) {
293+ var isOciRegistriesStarted = false
274294 configurationContainer.configureEach {
275295 incoming.beforeResolve {
276- if (resolvesOciImages()) {
296+ if (! isOciRegistriesStarted && resolvesOciImages()) {
297+ isOciRegistriesStarted = true
277298 val settingsRegistration = buildServiceRegistry.registrations.findByName(SERVICE_BASE_NAME )
278299 if (settingsRegistration != null ) {
279300 (settingsRegistration.service.get() as OciRegistriesService ).start()
0 commit comments