001    /*
002     * Copyright (C) 2006-2007 the original author or authors.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.codehaus.gmaven.runtime.loader.realm;
018    
019    import org.codehaus.gmaven.feature.Provider;
020    import org.codehaus.plexus.classworlds.ClassWorld;
021    import org.codehaus.plexus.classworlds.ClassWorldException;
022    import org.codehaus.plexus.classworlds.realm.ClassRealm;
023    import org.codehaus.plexus.classworlds.realm.NoSuchRealmException;
024    import org.codehaus.plexus.classworlds.strategy.ParentFirstStrategy;
025    import org.codehaus.plexus.classworlds.strategy.Strategy;
026    import org.slf4j.Logger;
027    import org.slf4j.LoggerFactory;
028    
029    import java.io.ByteArrayOutputStream;
030    import java.io.PrintStream;
031    import java.lang.reflect.Field;
032    import java.net.URL;
033    import java.util.HashMap;
034    import java.util.Map;
035    
036    /**
037     * The default {@link RealmManager} component.
038     *
039     * @plexus.component role="org.codehaus.gmaven.runtime.loader.realm.RealmManager"
040     *
041     * @version $Id: DefaultRealmManager.java 83 2009-12-11 11:01:28Z user57 $
042     * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
043     */
044    public class DefaultRealmManager
045        implements RealmManager
046    {
047        private final Logger log = LoggerFactory.getLogger(getClass());
048        
049        private ClassWorld classWorld = new ClassWorld();
050    
051        private Map providerRealms = new HashMap();
052    
053        public ClassRealm createProviderRealm(final String key, final URL[] classPath, final ClassLoader parent) throws ClassWorldException {
054            assert key != null;
055            assert classPath != null;
056            assert parent != null;
057    
058            String id = Provider.class.getName() + "[" + key + "]";
059            
060            log.debug("Creating provider realm: {}", id);
061    
062            ClassRealm realm = classWorld.newRealm(id, parent);
063            setupRealm(realm, classPath);
064            
065            providerRealms.put(key, realm);
066    
067            return realm;
068        }
069    
070        private int uniqueCounter = 0;
071    
072        private synchronized String uniqueId() {
073            return System.currentTimeMillis() + ":" + (uniqueCounter++);
074        }
075    
076        public ClassRealm createComponentRealm(final Provider provider, final URL[] classPath) throws ClassWorldException {
077            assert provider != null;
078            assert classPath != null;
079    
080            String id = provider.getClass().getName() + "#component[" + uniqueId() + "]";
081    
082            log.debug("Creating component realm: {}", id);
083    
084            ClassRealm providerRealm = (ClassRealm)providerRealms.get(provider.key());
085    
086            if (providerRealm == null) {
087                throw new Error("No realm for provider: " + provider);
088            }
089    
090            log.debug("    Provider realm: {}", providerRealm.getId());
091    
092            ClassRealm realm = providerRealm.createChildRealm(id);
093            setupRealm(realm, classPath);
094    
095            return realm;
096        }
097    
098        public void releaseComponentRealm(final ClassRealm realm) throws NoSuchRealmException {
099            assert realm != null;
100    
101            log.debug("Releasing component realm: {}", realm.getId());
102    
103            classWorld.disposeRealm(realm.getId());
104        }
105    
106        private void setupRealm(final ClassRealm realm, final URL[] classPath) {
107            assert realm != null;
108            assert classPath != null;
109    
110            // HACK: Force the realm to use parent-first, instead of the default self-first
111            setStrategy(realm, new ParentFirstStrategy(realm));
112    
113            for (int i=0; i<classPath.length; i++) {
114                realm.addURL(classPath[i]);
115    
116                log.debug("    {}", classPath[i]);
117            }
118    
119            if (log.isTraceEnabled()) {
120                ByteArrayOutputStream buff = new ByteArrayOutputStream();
121                realm.display(new PrintStream(buff, true));
122                log.trace("Realm configuration:\n" + new String(buff.toByteArray()));
123            }
124        }
125    
126        /**
127         * There is no public API to set/change a realms strategy, so we use some reflection muck to set the private field.
128         */
129        private void setStrategy(final ClassRealm realm, final Strategy strategy) {
130            assert realm != null;
131            assert strategy != null;
132    
133            try {
134                Field field = realm.getClass().getDeclaredField("strategy");
135    
136                try {
137                    field.set(realm, strategy);
138                }
139                catch (IllegalAccessException ignore) {
140                    // try again
141                    field.setAccessible(true);
142    
143                    try {
144                        field.set(realm, strategy);
145                    }
146                    catch (IllegalAccessException e) {
147                        throw new IllegalAccessError(e.getMessage());
148                    }
149                }
150            }
151            catch (NoSuchFieldException e) {
152                throw new Error(e);
153            }
154        }
155    }