001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.jetty6;
018    
019    import java.util.ArrayList;
020    import java.util.Arrays;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    import java.util.List;
024    import java.util.Map;
025    import java.util.Map.Entry;
026    import java.util.Set;
027    
028    import org.apache.commons.logging.Log;
029    import org.apache.commons.logging.LogFactory;
030    import org.apache.geronimo.gbean.AbstractName;
031    import org.apache.geronimo.gbean.AbstractNameQuery;
032    import org.apache.geronimo.gbean.GBeanData;
033    import org.apache.geronimo.gbean.GBeanInfo;
034    import org.apache.geronimo.gbean.GBeanInfoBuilder;
035    import org.apache.geronimo.gbean.ReferencePatterns;
036    import org.apache.geronimo.j2ee.j2eeobjectnames.NameFactory;
037    import org.apache.geronimo.jetty6.connector.AJP13Connector;
038    import org.apache.geronimo.jetty6.connector.HTTPBlockingConnector;
039    import org.apache.geronimo.jetty6.connector.HTTPSSelectChannelConnector;
040    import org.apache.geronimo.jetty6.connector.HTTPSSocketConnector;
041    import org.apache.geronimo.jetty6.connector.HTTPSelectChannelConnector;
042    import org.apache.geronimo.jetty6.connector.HTTPSocketConnector;
043    import org.apache.geronimo.jetty6.connector.JettyConnector;
044    import org.apache.geronimo.jetty6.requestlog.JettyLogManager;
045    import org.apache.geronimo.kernel.GBeanNotFoundException;
046    import org.apache.geronimo.kernel.Kernel;
047    import org.apache.geronimo.kernel.config.ConfigurationUtil;
048    import org.apache.geronimo.kernel.config.EditableConfigurationManager;
049    import org.apache.geronimo.kernel.config.InvalidConfigException;
050    import org.apache.geronimo.kernel.proxy.ProxyManager;
051    import org.apache.geronimo.management.geronimo.KeystoreManager;
052    import org.apache.geronimo.management.geronimo.NetworkConnector;
053    import org.apache.geronimo.management.geronimo.WebAccessLog;
054    import org.apache.geronimo.management.geronimo.WebContainer;
055    import org.apache.geronimo.management.geronimo.WebManager;
056    import org.apache.geronimo.management.geronimo.WebManager.ConnectorAttribute;
057    
058    /**
059     * Jetty implementation of WebManager.  Knows how to manipulate
060     * other Jetty objects for management purposes.
061     *
062     * @version $Rev:386276 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
063     */
064    public class JettyManagerImpl implements WebManager {
065        private final static Log log = LogFactory.getLog(JettyManagerImpl.class);
066    
067        private static final ConnectorType HTTP_NIO = new ConnectorType(Messages.getString("JettyManagerImpl.0")); //$NON-NLS-1$
068        private static final ConnectorType HTTPS_NIO = new ConnectorType(Messages.getString("JettyManagerImpl.1")); //$NON-NLS-1$
069        private static final ConnectorType HTTP_BLOCKING_NIO = new ConnectorType(Messages.getString("JettyManagerImpl.2")); //$NON-NLS-1$
070        private static final ConnectorType HTTP_BIO = new ConnectorType(Messages.getString("JettyManagerImpl.3")); //$NON-NLS-1$
071        private static final ConnectorType HTTPS_BIO = new ConnectorType(Messages.getString("JettyManagerImpl.4")); //$NON-NLS-1$
072        private static final ConnectorType AJP_NIO = new ConnectorType(Messages.getString("JettyManagerImpl.5")); //$NON-NLS-1$
073        private static List<ConnectorType> CONNECTOR_TYPES = Arrays.asList(
074                HTTP_NIO,
075                HTTPS_NIO,
076                HTTP_BLOCKING_NIO,
077                HTTP_BIO,
078                HTTPS_BIO,
079                AJP_NIO
080        );
081    
082        private static Map<ConnectorType, List<ConnectorAttribute>> CONNECTOR_ATTRIBUTES = new HashMap<ConnectorType, List<ConnectorAttribute>>();
083    
084        static {
085            List<ConnectorAttribute> connectorAttributes = new ArrayList<ConnectorAttribute>();
086            addCommonConnectorAttributes(connectorAttributes);
087            CONNECTOR_ATTRIBUTES.put(HTTP_NIO, connectorAttributes);
088    
089            connectorAttributes = new ArrayList<ConnectorAttribute>();
090            addCommonConnectorAttributes(connectorAttributes);
091            addSslConnectorAttributes(connectorAttributes);
092            setAttribute(connectorAttributes, "port", 8443); // SSL port
093            CONNECTOR_ATTRIBUTES.put(HTTPS_NIO, connectorAttributes);
094    
095            connectorAttributes = new ArrayList<ConnectorAttribute>();
096            addCommonConnectorAttributes(connectorAttributes);
097            CONNECTOR_ATTRIBUTES.put(HTTP_BIO, connectorAttributes);
098    
099            connectorAttributes = new ArrayList<ConnectorAttribute>();
100            addCommonConnectorAttributes(connectorAttributes);
101            addSslConnectorAttributes(connectorAttributes);
102            setAttribute(connectorAttributes, "port", 8443); // SSL port
103            CONNECTOR_ATTRIBUTES.put(HTTPS_BIO, connectorAttributes);
104    
105            connectorAttributes = new ArrayList<ConnectorAttribute>();
106            addCommonConnectorAttributes(connectorAttributes);
107            CONNECTOR_ATTRIBUTES.put(HTTP_BLOCKING_NIO, connectorAttributes);
108    
109            connectorAttributes = new ArrayList<ConnectorAttribute>();
110            addCommonConnectorAttributes(connectorAttributes);
111            CONNECTOR_ATTRIBUTES.put(AJP_NIO, connectorAttributes);
112    
113        }
114    
115        private static Map<ConnectorType, GBeanInfo> CONNECTOR_GBEAN_INFOS = new HashMap<ConnectorType, GBeanInfo>();
116    
117        static {
118            CONNECTOR_GBEAN_INFOS.put(HTTP_NIO, HTTPSelectChannelConnector.GBEAN_INFO);
119            CONNECTOR_GBEAN_INFOS.put(HTTPS_NIO, HTTPSSelectChannelConnector.GBEAN_INFO);
120            CONNECTOR_GBEAN_INFOS.put(HTTP_BLOCKING_NIO, HTTPBlockingConnector.GBEAN_INFO);
121            CONNECTOR_GBEAN_INFOS.put(HTTP_BIO, HTTPSocketConnector.GBEAN_INFO);
122            CONNECTOR_GBEAN_INFOS.put(HTTPS_BIO, HTTPSSocketConnector.GBEAN_INFO);
123            CONNECTOR_GBEAN_INFOS.put(AJP_NIO, AJP13Connector.GBEAN_INFO);
124        }
125    
126        private final Kernel kernel;
127    
128        public JettyManagerImpl(Kernel kernel) {
129            this.kernel = kernel;
130        }
131    
132        public String getProductName() {
133            return "Jetty";
134        }
135    
136        /**
137         * Get a list of containers for this web implementation.
138         */
139        public Object[] getContainers() {
140            ProxyManager proxyManager = kernel.getProxyManager();
141            AbstractNameQuery query = new AbstractNameQuery(JettyContainer.class.getName());
142            Set names = kernel.listGBeans(query);
143            JettyContainer[] results = new JettyContainer[names.size()];
144            int i = 0;
145            for (Iterator it = names.iterator(); it.hasNext(); i++) {
146                AbstractName name = (AbstractName) it.next();
147                results[i] = (JettyContainer) proxyManager.createProxy(name, JettyContainer.class.getClassLoader());
148            }
149            return results;
150        }
151    
152        /**
153         * Gets the protocols that this web container supports (that you can create
154         * connectors for).
155         */
156        public String[] getSupportedProtocols() {
157            return new String[]{PROTOCOL_HTTP, PROTOCOL_HTTPS, PROTOCOL_AJP};
158        }
159    
160        /**
161         * Removes a connector.  This shuts it down if necessary, and removes it
162         * from the server environment.  It must be a connector that this container
163         * is responsible for.
164         *
165         * @param connectorName
166         */
167        public void removeConnector(AbstractName connectorName) {
168            try {
169                GBeanInfo info = kernel.getGBeanInfo(connectorName);
170                boolean found = false;
171                Set intfs = info.getInterfaces();
172                for (Iterator it = intfs.iterator(); it.hasNext();) {
173                    String intf = (String) it.next();
174                    if (intf.equals(JettyWebConnector.class.getName())) {
175                        found = true;
176                    }
177                }
178                if (!found) {
179                    throw new GBeanNotFoundException(connectorName);
180                }
181                EditableConfigurationManager mgr = ConfigurationUtil.getEditableConfigurationManager(kernel);
182                if (mgr != null) {
183                    try {
184                        mgr.removeGBeanFromConfiguration(connectorName.getArtifact(), connectorName);
185                    } catch (InvalidConfigException e) {
186                        log.error("Unable to add GBean", e);
187                    } finally {
188                        ConfigurationUtil.releaseConfigurationManager(kernel, mgr);
189                    }
190                } else {
191                    log.warn("The ConfigurationManager in the kernel does not allow editing");
192                }
193            } catch (GBeanNotFoundException e) {
194                log.warn("No such GBean '" + connectorName + "'"); //todo: what if we want to remove a failed GBean?
195            } catch (Exception e) {
196                log.error(e);
197            }
198        }
199    
200        /**
201         * Gets the ObjectNames of any existing connectors for the specified
202         * protocol.
203         *
204         * @param protocol A protocol as returned by getSupportedProtocols
205         */
206        public NetworkConnector[] getConnectors(String protocol) {
207            if (protocol == null) {
208                return getConnectors();
209            }
210            List result = new ArrayList();
211            ProxyManager proxyManager = kernel.getProxyManager();
212            AbstractNameQuery query = new AbstractNameQuery(JettyWebConnector.class.getName());
213            Set names = kernel.listGBeans(query);
214            for (Iterator it = names.iterator(); it.hasNext();) {
215                AbstractName name = (AbstractName) it.next();
216                try {
217                    if (kernel.getAttribute(name, "protocol").equals(protocol)) {
218                        result.add(proxyManager.createProxy(name, JettyWebConnector.class.getClassLoader()));
219                    }
220                } catch (Exception e) {
221                    log.error("Unable to check the protocol for a connector", e);
222                }
223            }
224            return (JettyWebConnector[]) result.toArray(new JettyWebConnector[names.size()]);
225        }
226    
227        public WebAccessLog getAccessLog(WebContainer container) {
228            AbstractNameQuery query = new AbstractNameQuery(JettyLogManager.class.getName());
229            Set names = kernel.listGBeans(query);
230            if (names.size() == 0) {
231                return null;
232            } else if (names.size() > 1) {
233                throw new IllegalStateException("Should not be more than one Jetty access log manager");
234            }
235            return (WebAccessLog) kernel.getProxyManager().createProxy((AbstractName) names.iterator().next(), JettyLogManager.class.getClassLoader());
236        }
237    
238        public List<ConnectorType> getConnectorTypes() {
239            return CONNECTOR_TYPES;
240        }
241    
242        public List<ConnectorAttribute> getConnectorAttributes(ConnectorType connectorType) {
243            return ConnectorAttribute.copy(CONNECTOR_ATTRIBUTES.get(connectorType));
244        }
245    
246        public AbstractName getConnectorConfiguration(ConnectorType connectorType, List<ConnectorAttribute> connectorAttributes, WebContainer container, String uniqueName) {
247            GBeanInfo gbeanInfo = CONNECTOR_GBEAN_INFOS.get(connectorType);
248            AbstractName containerName = kernel.getAbstractNameFor(container);
249            AbstractName name = kernel.getNaming().createSiblingName(containerName, uniqueName, NameFactory.GERONIMO_SERVICE);
250            GBeanData gbeanData = new GBeanData(name, gbeanInfo);
251            gbeanData.setReferencePattern(JettyConnector.CONNECTOR_CONTAINER_REFERENCE, containerName);
252            for (ConnectorAttribute connectorAttribute : connectorAttributes) {
253                Object value = connectorAttribute.getValue();
254                if (value != null) {
255                    gbeanData.setAttribute(connectorAttribute.getAttributeName(), connectorAttribute.getValue());
256                }
257            }
258            
259            // provide a reference to KeystoreManager gbean for HTTPS connectors
260            if (connectorType.equals(HTTPS_NIO) || connectorType.equals(HTTPS_BIO)) {
261                AbstractNameQuery query = new AbstractNameQuery(KeystoreManager.class.getName());
262                gbeanData.setReferencePattern("KeystoreManager", query);
263            }
264            
265            EditableConfigurationManager mgr = ConfigurationUtil.getEditableConfigurationManager(kernel);
266            if (mgr != null) {
267                try {
268                    mgr.addGBeanToConfiguration(containerName.getArtifact(), gbeanData, false);
269                } catch (InvalidConfigException e) {
270                    log.error("Unable to add GBean", e);
271                    return null;
272                } finally {
273                    ConfigurationUtil.releaseConfigurationManager(kernel, mgr);
274                }
275            } else {
276                log.warn("The ConfigurationManager in the kernel does not allow editing");
277                return null;
278            }
279            return name;
280        }
281        
282        public ConnectorType getConnectorType(AbstractName connectorName) {
283            ConnectorType connectorType = null; 
284            try {
285                GBeanInfo info = kernel.getGBeanInfo(connectorName);
286                boolean found = false;
287                Set intfs = info.getInterfaces();
288                for (Iterator it = intfs.iterator(); it.hasNext() && !found;) {
289                    String intf = (String) it.next();
290                    if (intf.equals(JettyWebConnector.class.getName())) {
291                        found = true;
292                    }
293                }
294                if (!found) {
295                    throw new GBeanNotFoundException(connectorName);
296                }
297                String searchingFor = info.getName();
298                for (Entry<ConnectorType, GBeanInfo> entry : CONNECTOR_GBEAN_INFOS.entrySet() ) {
299                    String candidate = entry.getValue().getName();
300                    if (candidate.equals(searchingFor)) {
301                        return entry.getKey();
302                    }
303                }
304            } catch (GBeanNotFoundException e) {
305                log.warn("No such GBean '" + connectorName + "'");
306            } catch (Exception e) {
307                log.error(e);
308            }
309                
310            return connectorType;
311        }
312    
313        /**
314         * Gets the ObjectNames of any existing connectors.
315         */
316        public NetworkConnector[] getConnectors() {
317            ProxyManager proxyManager = kernel.getProxyManager();
318            AbstractNameQuery query = new AbstractNameQuery(JettyWebConnector.class.getName());
319            Set names = kernel.listGBeans(query);
320            JettyWebConnector[] results = new JettyWebConnector[names.size()];
321            int i = 0;
322            for (Iterator it = names.iterator(); it.hasNext(); i++) {
323                AbstractName name = (AbstractName) it.next();
324                results[i] = (JettyWebConnector) proxyManager.createProxy(name, JettyWebConnector.class.getClassLoader());
325            }
326            return results;
327        }
328    
329        public NetworkConnector[] getConnectorsForContainer(Object container, String protocol) {
330            if (protocol == null) {
331                return getConnectorsForContainer(container);
332            }
333            AbstractName containerName = kernel.getAbstractNameFor(container);
334            ProxyManager mgr = kernel.getProxyManager();
335            try {
336                List results = new ArrayList();
337                AbstractNameQuery query = new AbstractNameQuery(JettyWebConnector.class.getName());
338                Set set = kernel.listGBeans(query); // all Jetty connectors
339                for (Iterator it = set.iterator(); it.hasNext();) {
340                    AbstractName name = (AbstractName) it.next(); // a single Jetty connector
341                    GBeanData data = kernel.getGBeanData(name);
342                    ReferencePatterns refs = data.getReferencePatterns(JettyConnector.CONNECTOR_CONTAINER_REFERENCE);
343                    if (containerName.equals(refs.getAbstractName())) {
344                        try {
345                            String testProtocol = (String) kernel.getAttribute(name, "protocol");
346                            if (testProtocol != null && testProtocol.equals(protocol)) {
347                                results.add(mgr.createProxy(name, JettyWebConnector.class.getClassLoader()));
348                            }
349                        } catch (Exception e) {
350                            log.error("Unable to look up protocol for connector '" + name + "'", e);
351                        }
352                        break;
353                    }
354                }
355                return (JettyWebConnector[]) results.toArray(new JettyWebConnector[results.size()]);
356            } catch (Exception e) {
357                throw (IllegalArgumentException) new IllegalArgumentException("Unable to look up connectors for Jetty container '" + containerName + "': ").initCause(e);
358            }
359        }
360    
361        public NetworkConnector[] getConnectorsForContainer(Object container) {
362            AbstractName containerName = kernel.getAbstractNameFor(container);
363            ProxyManager mgr = kernel.getProxyManager();
364            try {
365                List results = new ArrayList();
366                AbstractNameQuery query = new AbstractNameQuery(JettyWebConnector.class.getName());
367                Set set = kernel.listGBeans(query); // all Jetty connectors
368                for (Iterator it = set.iterator(); it.hasNext();) {
369                    AbstractName name = (AbstractName) it.next(); // a single Jetty connector
370                    GBeanData data = kernel.getGBeanData(name);
371                    ReferencePatterns refs = data.getReferencePatterns(JettyConnector.CONNECTOR_CONTAINER_REFERENCE);
372                    if (containerName.equals(refs.getAbstractName())) {
373                        results.add(mgr.createProxy(name, JettyWebConnector.class.getClassLoader()));
374                    }
375                }
376                return (JettyWebConnector[]) results.toArray(new JettyWebConnector[results.size()]);
377            } catch (Exception e) {
378                throw (IllegalArgumentException) new IllegalArgumentException("Unable to look up connectors for Jetty container '" + containerName + "'").initCause(e);
379            }
380        }
381        
382        private static void addCommonConnectorAttributes(List<ConnectorAttribute> connectorAttributes) {
383            connectorAttributes.add(new ConnectorAttribute<String>("host", "0.0.0.0", Messages.getString("JettyManagerImpl.30"), String.class, true)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
384            connectorAttributes.add(new ConnectorAttribute<Integer>("port", 8080, Messages.getString("JettyManagerImpl.32"), Integer.class, true)); //$NON-NLS-1$ //$NON-NLS-2$
385            connectorAttributes.add(new ConnectorAttribute<Integer>("maxThreads", 10, Messages.getString("JettyManagerImpl.34"), Integer.class)); //$NON-NLS-1$ //$NON-NLS-2$
386            connectorAttributes.add(new ConnectorAttribute<Integer>("bufferSizeBytes", 8096, Messages.getString("JettyManagerImpl.36"), Integer.class)); //$NON-NLS-1$ //$NON-NLS-2$
387            connectorAttributes.add(new ConnectorAttribute<Integer>("headerBufferSizeBytes", 8192, Messages.getString("JettyManagerImpl.57"), Integer.class)); //$NON-NLS-1$ //$NON-NLS-2$
388            connectorAttributes.add(new ConnectorAttribute<Integer>("acceptQueueSize", 10, Messages.getString("JettyManagerImpl.38"), Integer.class)); //$NON-NLS-1$ //$NON-NLS-2$
389            connectorAttributes.add(new ConnectorAttribute<Integer>("lingerMillis", 30000, Messages.getString("JettyManagerImpl.40"), Integer.class)); //$NON-NLS-1$ //$NON-NLS-2$
390            //connectorAttributes.add(new ConnectorAttribute<Boolean>("tcpNoDelay", false, "If true then setTcpNoDelay(true) is called on accepted sockets.", Boolean.class));
391            connectorAttributes.add(new ConnectorAttribute<Integer>("redirectPort", 8443, Messages.getString("JettyManagerImpl.42"), Integer.class)); //$NON-NLS-1$ //$NON-NLS-2$
392            //connectorAttributes.add(new ConnectorAttribute<Integer>("maxIdleTimeMs", 30000, " The time in milliseconds that a connection can be idle before being closed.", Integer.class));
393        }
394        
395        private static void addSslConnectorAttributes(List<ConnectorAttribute> connectorAttributes) {
396            //connectorAttributes.add(new ConnectorAttribute<Boolean>("clientAuthRequested", false, "clientAuthRequested", Boolean.class));
397            connectorAttributes.add(new ConnectorAttribute<Boolean>("clientAuthRequired", false, Messages.getString("JettyManagerImpl.44"), Boolean.class)); //$NON-NLS-1$ //$NON-NLS-2$
398            connectorAttributes.add(new ConnectorAttribute<String>("keyStore", "", Messages.getString("JettyManagerImpl.47"), String.class, true)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
399            connectorAttributes.add(new ConnectorAttribute<String>("trustStore", "", Messages.getString("JettyManagerImpl.50"), String.class)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
400            //connectorAttributes.add(new ConnectorAttribute<String>("keyAlias", "", "keyAlias", String.class, true));
401            connectorAttributes.add(new ConnectorAttribute<String>("secureProtocol", "", Messages.getString("JettyManagerImpl.53"), String.class)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
402            connectorAttributes.add(new ConnectorAttribute<String>("algorithm", "Default", Messages.getString("JettyManagerImpl.56"), String.class)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
403        }
404    
405        private static <T> void setAttribute (List<ConnectorAttribute> connectorAttributes, String attributeName, T value) {
406            for (ConnectorAttribute connectorAttribute : connectorAttributes) {
407                if (connectorAttribute.getAttributeName().equals(attributeName)) {
408                    connectorAttribute.setValue(value);
409                    return;
410                }
411            }
412        }
413    
414        public static final GBeanInfo GBEAN_INFO;
415    
416        static {
417            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic("Jetty Web Manager", JettyManagerImpl.class);
418            infoFactory.addAttribute("kernel", Kernel.class, false);
419            infoFactory.addInterface(WebManager.class);
420            infoFactory.setConstructor(new String[]{"kernel"});
421            GBEAN_INFO = infoFactory.getBeanInfo();
422        }
423    
424        public static GBeanInfo getGBeanInfo() {
425            return GBEAN_INFO;
426        }
427    }