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.feature.support;
018    
019    import org.codehaus.gmaven.feature.Component;
020    import org.codehaus.gmaven.feature.Configuration;
021    import org.codehaus.gmaven.feature.Feature;
022    import org.codehaus.gmaven.feature.FeatureException;
023    import org.codehaus.gmaven.feature.Provider;
024    import org.slf4j.Logger;
025    import org.slf4j.LoggerFactory;
026    
027    /**
028     * Provides support for {@link Feature} implementations.
029     *
030     * @version $Id: FeatureSupport.java 76 2009-12-05 12:04:30Z user57 $
031     * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
032     */
033    public abstract class FeatureSupport
034        implements Feature
035    {
036        protected final Logger log = LoggerFactory.getLogger(getClass());
037    
038        protected final String key;
039    
040        protected final Configuration config;
041    
042        protected final boolean supported;
043    
044        protected Provider provider;
045    
046        protected FeatureSupport(final String key, final boolean supported) {
047            assert key != null;
048            
049            this.key = key;
050            this.supported = supported;
051            this.config = new Configuration();
052        }
053    
054        protected FeatureSupport(final String key) {
055            this(key, true);
056        }
057    
058        public String toString() {
059            return asString(this);
060        }
061    
062        public int hashCode() {
063            return key().hashCode();
064        }
065    
066        public String key() {
067            return key;
068        }
069    
070        public String name() {
071            return key();
072        }
073    
074        public boolean supported() {
075            return supported;
076        }
077    
078        public void require() {
079            if (!supported()) {
080                throw new FeatureException("Feature not supported: " + key());
081            }
082        }
083    
084        public Configuration config() {
085            return config;
086        }
087    
088        public Component create(final Configuration context) throws Exception {
089            assert context != null;
090    
091            Component component = create();
092    
093            // Merge in the context
094            Configuration c = component.config();
095            c.merge(context);
096    
097            return component;
098        }
099    
100        public Component create() throws Exception {
101            // First we need to be supported, so require it
102            require();
103    
104            // Then install the provider CL into the TCL to get better behaved CL mucko
105            final ClassLoader tcl = Thread.currentThread().getContextClassLoader();
106            Thread.currentThread().setContextClassLoader(provider().getClass().getClassLoader());
107    
108            try {
109                Component component = doCreate();
110    
111                // Merge our configuration
112                Configuration c = component.config();
113                c.merge(config());
114    
115                return component;
116            }
117            finally {
118                // Reset back to the previous TCL
119                Thread.currentThread().setContextClassLoader(tcl);
120            }
121        }
122    
123        protected abstract Component doCreate() throws Exception;
124    
125        //
126        // Provider registration hooks to work with ProviderSupport
127        //
128    
129        /* package */ synchronized void register(final Provider provider) {
130            if (this.provider != null) {
131                throw new IllegalStateException(
132                        "Duplicate provider registration with feature: " + this +
133                        ", previous provider: " + this.provider +
134                        ", current provider: " + provider);
135            }
136    
137            this.provider = provider;
138        }
139    
140        protected synchronized Provider provider() {
141            if (provider == null) {
142                throw new IllegalStateException("Provider has not been registered with feature: " + this);
143            }
144    
145            return provider;
146        }
147    
148        public static String asString(final Feature feature) {
149            assert feature != null;
150    
151            StringBuffer buff = new StringBuffer();
152    
153            buff.append("[");
154            buff.append(feature.key());
155            buff.append("]");
156    
157            // noinspection StringEquality
158            if (feature.key() != feature.name()) {
159                buff.append(" '");
160                buff.append(feature.name());
161                buff.append("'");
162            }
163    
164            buff.append(" (supported: ");
165            buff.append(feature.supported());
166            buff.append(", type: ");
167            buff.append(feature.getClass().getName());
168            buff.append(")");
169    
170            return buff.toString();
171        }
172    }