001    /**
002     *
003     * Copyright 2004 The Apache Software Foundation
004     *
005     *  Licensed under the Apache License, Version 2.0 (the "License");
006     *  you may not use this file except in compliance with the License.
007     *  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    
018    package org.apache.geronimo.security.remoting.jmx;
019    
020    import java.io.IOException;
021    import java.net.InetSocketAddress;
022    import java.net.URI;
023    import java.net.URISyntaxException;
024    import javax.management.ObjectName;
025    import org.activeio.AcceptListener;
026    import org.activeio.AsyncChannelServer;
027    import org.activeio.Channel;
028    import org.activeio.Packet;
029    import org.activeio.RequestChannel;
030    import org.activeio.SyncChannel;
031    import org.activeio.SyncChannelServer;
032    import org.activeio.adapter.AsyncChannelToServerRequestChannel;
033    import org.activeio.adapter.AsyncToSyncChannel;
034    import org.activeio.adapter.SyncToAsyncChannel;
035    import org.activeio.adapter.SyncToAsyncChannelServer;
036    import org.activeio.filter.PacketAggregatingAsyncChannel;
037    import org.activeio.net.SocketMetadata;
038    import org.activeio.net.SocketSyncChannelFactory;
039    import org.apache.commons.logging.Log;
040    import org.apache.commons.logging.LogFactory;
041    import org.apache.geronimo.gbean.GBeanInfo;
042    import org.apache.geronimo.gbean.GBeanInfoBuilder;
043    import org.apache.geronimo.gbean.GBeanLifecycle;
044    import org.apache.geronimo.kernel.ObjectNameUtil;
045    import org.apache.geronimo.management.geronimo.NetworkConnector;
046    import org.apache.geronimo.security.jaas.server.JaasLoginServiceMBean;
047    
048    
049    /**
050     * A server-side utility that exposes a JaasLoginService to remote clients.
051     * It prevents clients from connecting to arbitrary server-side MBeans through
052     * this listener -- only the JaasLoginService is exposed.
053     *
054     * @version $Rev: 417891 $ $Date: 2006-06-28 15:45:07 -0700 (Wed, 28 Jun 2006) $
055     */
056    public class JaasLoginServiceRemotingServer implements GBeanLifecycle, NetworkConnector {
057    
058        public static final ObjectName REQUIRED_OBJECT_NAME = ObjectNameUtil.getObjectName("geronimo.remoting:target=JaasLoginServiceRemotingServer");
059    
060        private static final Log log = LogFactory.getLog(JaasLoginServiceRemotingServer.class);
061        private AsyncChannelServer server;
062        private JaasLoginServiceMBean loginService;
063        private String protocol;
064        private String host;
065        private int port;
066    
067        public JaasLoginServiceRemotingServer(String protocol, String host, int port, JaasLoginServiceMBean loginService) {
068            this.protocol = protocol;
069            this.host = host;
070            this.port = port;
071            this.loginService = loginService;
072        }
073    
074        public String getProtocol() {
075            return protocol;
076        }
077    
078        public void setProtocol(String protocol) {
079            this.protocol = protocol;
080        }
081    
082        public String getHost() {
083            return host;
084        }
085    
086        public void setHost(String host) {
087            this.host = host;
088        }
089    
090        public int getPort() {
091            return port;
092        }
093    
094        public void setPort(int port) {
095            this.port = port;
096        }
097    
098        public URI getClientConnectURI() {
099            return server.getConnectURI();
100        }
101    
102        public InetSocketAddress getListenAddress() {
103            if (server != null) {
104                URI uri = server.getBindURI();
105                return new InetSocketAddress(uri.getHost(), uri.getPort());
106            } else {
107                return new InetSocketAddress(host, port);
108            }
109        }
110    
111        public void doStart() throws Exception {
112            final ReflexiveInterceptor loginServiceInterceptor = new ReflexiveInterceptor(loginService);
113    
114            server = createAsyncChannelServer();
115            server.setAcceptListener(new AcceptListener() {
116                public void onAccept(Channel channel) {
117                    RequestChannel requestChannel = null;
118                    try {
119                        SyncChannel syncChannel = AsyncToSyncChannel.adapt(channel);
120                        SocketMetadata socket = (SocketMetadata) syncChannel.narrow(SocketMetadata.class);
121                        socket.setTcpNoDelay(true);
122    
123                        requestChannel = createRequestChannel(syncChannel);
124    
125                        RequestChannelInterceptorInvoker invoker = new RequestChannelInterceptorInvoker(loginServiceInterceptor, loginService.getClass().getClassLoader());
126                        requestChannel.setRequestListener(invoker);
127                        requestChannel.start();
128                    } catch (IOException e) {
129                        log.warn("Failed to accept connection.", e);
130                        if (requestChannel != null)
131                            requestChannel.dispose();
132                        else
133                            channel.dispose();
134                    }
135                }
136    
137                public void onAcceptError(IOException error) {
138                    log.warn("Accept Failed: " + error);
139                }
140            });
141    
142            server.start();
143            log.debug("Remote login service started on: " + server.getConnectURI() + " clients can connect to: " + server.getConnectURI());
144        }
145    
146        private AsyncChannelServer createAsyncChannelServer() throws IOException, URISyntaxException {
147            SocketSyncChannelFactory factory = new SocketSyncChannelFactory();
148            SyncChannelServer server = factory.bindSyncChannel(new URI(protocol, null, host, port, null, null, null));
149            return new SyncToAsyncChannelServer(server);
150        }
151    
152        private RequestChannel createRequestChannel(SyncChannel channel) throws IOException {
153    
154            return new AsyncChannelToServerRequestChannel(new PacketAggregatingAsyncChannel(SyncToAsyncChannel.adapt(channel))) {
155                /**
156                 * close out the channel once one request has been serviced.
157                 */
158                public void onPacket(Packet packet) {
159                    super.onPacket(packet);
160                    dispose();
161                }
162            };
163        }
164    
165        public void doStop() {
166            server.dispose();
167            server = null;
168            log.debug("Stopped remote login service.");
169        }
170    
171        public void doFail() {
172            if (server != null) {
173                server.dispose();
174                server = null;
175            }
176            log.warn("Failed remote login service.");
177        }
178    
179        public static final GBeanInfo GBEAN_INFO;
180    
181        static {
182            GBeanInfoBuilder infoFactory = GBeanInfoBuilder.createStatic("Remote Login Listener", JaasLoginServiceRemotingServer.class); //has fixed name, j2eeType is irrelevant
183            infoFactory.addAttribute("clientConnectURI", URI.class, false);
184            infoFactory.addReference("LoginService", JaasLoginServiceMBean.class, "JaasLoginService");
185            infoFactory.addInterface(NetworkConnector.class, new String[]{"host", "port", "protocol"}, new String[]{"host", "port"});
186            infoFactory.setConstructor(new String[]{"protocol", "host", "port", "LoginService"});
187            GBEAN_INFO = infoFactory.getBeanInfo();
188        }
189    
190        public static GBeanInfo getGBeanInfo() {
191            return GBEAN_INFO;
192        }
193    }