View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.cling.invoker.mvn.resident;
20  
21  import java.util.ArrayList;
22  import java.util.concurrent.ConcurrentHashMap;
23  
24  import org.apache.maven.api.cli.InvokerException;
25  import org.apache.maven.api.cli.InvokerRequest;
26  import org.apache.maven.api.services.Lookup;
27  import org.apache.maven.cling.invoker.mvn.MavenContext;
28  import org.apache.maven.cling.invoker.mvn.MavenInvoker;
29  
30  /**
31   * Resident invoker implementation, specialization of Maven Invoker, but keeps Maven instance resident. This implies, that
32   * things like environment, system properties, extensions etc. are loaded only once. It is caller duty to ensure
33   * that subsequent call is right for the resident instance (ie no env change or different extension needed).
34   * This implementation "pre-populates" MavenContext with pre-existing stuff (except for very first call)
35   * and does not let DI container to be closed.
36   */
37  public class ResidentMavenInvoker extends MavenInvoker {
38  
39      private final ConcurrentHashMap<String, MavenContext> residentContext;
40  
41      public ResidentMavenInvoker(Lookup protoLookup) {
42          super(protoLookup, null);
43          this.residentContext = new ConcurrentHashMap<>();
44      }
45  
46      @Override
47      public void close() throws InvokerException {
48          ArrayList<Exception> exceptions = new ArrayList<>();
49          for (MavenContext context : residentContext.values()) {
50              try {
51                  context.doCloseContainer();
52              } catch (Exception e) {
53                  exceptions.add(e);
54              }
55          }
56          if (!exceptions.isEmpty()) {
57              InvokerException exception = new InvokerException("Could not cleanly shut down context pool");
58              exceptions.forEach(exception::addSuppressed);
59              throw exception;
60          }
61      }
62  
63      @Override
64      protected MavenContext createContext(InvokerRequest invokerRequest) {
65          // TODO: in a moment Maven stop pushing user properties to system properties (and maybe something more)
66          // and allow multiple instances per JVM, this may become a pool? derive key based in invokerRequest?
67          MavenContext result = residentContext.computeIfAbsent("resident", k -> new MavenContext(invokerRequest, false));
68          return copyIfDifferent(result, invokerRequest);
69      }
70  
71      protected MavenContext copyIfDifferent(MavenContext mavenContext, InvokerRequest invokerRequest) {
72          if (invokerRequest == mavenContext.invokerRequest) {
73              return mavenContext;
74          }
75          MavenContext shadow = new MavenContext(invokerRequest, false);
76  
77          // we carry over only "resident" things
78          shadow.containerCapsule = mavenContext.containerCapsule;
79          shadow.lookup = mavenContext.lookup;
80          shadow.eventSpyDispatcher = mavenContext.eventSpyDispatcher;
81          shadow.maven = mavenContext.maven;
82  
83          return shadow;
84      }
85  }