View Javadoc
1   package org.codehaus.plexus.classworlds;
2   
3   /*
4    * Copyright 2001-2006 Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * 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, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.io.Closeable;
20  import java.io.IOException;
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.LinkedHashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.function.Predicate;
28  
29  import org.codehaus.plexus.classworlds.realm.ClassRealm;
30  import org.codehaus.plexus.classworlds.realm.DuplicateRealmException;
31  import org.codehaus.plexus.classworlds.realm.FilteredClassRealm;
32  import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
33  
34  /**
35   * A collection of <code>ClassRealm</code>s, indexed by id.
36   *
37   * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
38   */
39  public class ClassWorld implements Closeable {
40      private Map<String, ClassRealm> realms;
41  
42      private final List<ClassWorldListener> listeners = new ArrayList<>();
43  
44      public ClassWorld(String realmId, ClassLoader classLoader) {
45          this();
46  
47          try {
48              newRealm(realmId, classLoader);
49          } catch (DuplicateRealmException e) {
50              // Will never happen as we are just creating the world.
51          }
52      }
53  
54      public ClassWorld() {
55          this.realms = new LinkedHashMap<>();
56      }
57  
58      public ClassRealm newRealm(String id) throws DuplicateRealmException {
59          return newRealm(id, getClass().getClassLoader());
60      }
61  
62      public ClassRealm newRealm(String id, ClassLoader classLoader) throws DuplicateRealmException {
63          return newRealm(id, classLoader, null);
64      }
65  
66      /**
67       * Adds a class realm with filtering.
68       * Only resources/classes whose name matches a given predicate are exposed.
69       * @param id The identifier for this realm, must not be <code>null</code>.
70       * @param classLoader The base class loader for this realm, may be <code>null</code> to use the bootstrap class
71       *            loader.
72       * @param filter a predicate to apply to each resource name to determine if it should be loaded through this class loader
73       * @return the created class realm
74       * @throws DuplicateRealmException in case a realm with the given id does already exist
75       * @since 2.7.0
76       * @see FilteredClassRealm
77       */
78      public synchronized ClassRealm newRealm(String id, ClassLoader classLoader, Predicate<String> filter)
79              throws DuplicateRealmException {
80          if (realms.containsKey(id)) {
81              throw new DuplicateRealmException(this, id);
82          }
83  
84          ClassRealm realm;
85  
86          if (filter == null) {
87              realm = new ClassRealm(this, id, classLoader);
88          } else {
89              realm = new FilteredClassRealm(filter, this, id, classLoader);
90          }
91          realms.put(id, realm);
92  
93          for (ClassWorldListener listener : listeners) {
94              listener.realmCreated(realm);
95          }
96  
97          return realm;
98      }
99  
100     /**
101      * Closes all contained class realms.
102      * @since 2.7.0
103      */
104     @Override
105     public synchronized void close() throws IOException {
106         realms.values().stream().forEach(this::disposeRealm);
107         realms.clear();
108     }
109 
110     public synchronized void disposeRealm(String id) throws NoSuchRealmException {
111         ClassRealm realm = realms.remove(id);
112 
113         if (realm != null) {
114             disposeRealm(realm);
115         } else {
116             throw new NoSuchRealmException(this, id);
117         }
118     }
119 
120     private void disposeRealm(ClassRealm realm) {
121         try {
122             realm.close();
123         } catch (IOException ignore) {
124         }
125         for (ClassWorldListener listener : listeners) {
126             listener.realmDisposed(realm);
127         }
128     }
129 
130     public synchronized ClassRealm getRealm(String id) throws NoSuchRealmException {
131         if (realms.containsKey(id)) {
132             return realms.get(id);
133         }
134 
135         throw new NoSuchRealmException(this, id);
136     }
137 
138     public synchronized Collection<ClassRealm> getRealms() {
139         return Collections.unmodifiableList(new ArrayList<>(realms.values()));
140     }
141 
142     // from exports branch
143     public synchronized ClassRealm getClassRealm(String id) {
144         if (realms.containsKey(id)) {
145             return realms.get(id);
146         }
147 
148         return null;
149     }
150 
151     public synchronized void addListener(ClassWorldListener listener) {
152         // TODO ideally, use object identity, not equals
153         if (!listeners.contains(listener)) {
154             listeners.add(listener);
155         }
156     }
157 
158     public synchronized void removeListener(ClassWorldListener listener) {
159         listeners.remove(listener);
160     }
161 }