| /* |
| * Copyright (c) 2010, Rickard Öberg. All Rights Reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.qi4j.library.jmx; |
| |
| import java.net.InetAddress; |
| import java.rmi.registry.LocateRegistry; |
| import java.rmi.registry.Registry; |
| import java.rmi.server.UnicastRemoteObject; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import javax.management.MBeanServer; |
| import javax.management.remote.*; |
| import javax.security.auth.Subject; |
| import org.qi4j.api.activation.ActivatorAdapter; |
| import org.qi4j.api.activation.Activators; |
| import org.qi4j.api.configuration.Configuration; |
| import org.qi4j.api.injection.scope.Service; |
| import org.qi4j.api.injection.scope.This; |
| import org.qi4j.api.mixin.Mixins; |
| import org.qi4j.api.service.ServiceComposite; |
| import org.qi4j.api.service.ServiceReference; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * This service starts a JMX RMI connector. |
| * <p> |
| * It also creates an RMI-registry |
| * to register the connector. The service is configured by changing the |
| * settings in the JMXConnectorConfiguration. |
| * </p> |
| * <p> |
| * Authentication is done with an optional username+password in the configuration. |
| * </p> |
| */ |
| @Mixins(JMXConnectorService.JmxConnectorMixin.class) |
| @Activators( JMXConnectorService.Activator.class ) |
| public interface JMXConnectorService |
| extends ServiceComposite |
| { |
| |
| void startJMXConnector() |
| throws Exception; |
| |
| void stopJMXConnector() |
| throws Exception; |
| |
| static class Activator |
| extends ActivatorAdapter<ServiceReference<JMXConnectorService>> |
| { |
| |
| @Override |
| public void afterActivation( ServiceReference<JMXConnectorService> activated ) |
| throws Exception |
| { |
| activated.get().startJMXConnector(); |
| } |
| |
| @Override |
| public void beforePassivation( ServiceReference<JMXConnectorService> passivating ) |
| throws Exception |
| { |
| passivating.get().stopJMXConnector(); |
| } |
| |
| } |
| |
| abstract class JmxConnectorMixin |
| implements JMXConnectorService |
| { |
| final Logger logger = LoggerFactory.getLogger( JMXConnectorService.class.getName() ); |
| |
| @This |
| Configuration<JMXConnectorConfiguration> config; |
| |
| @Service |
| MBeanServer server; |
| |
| Registry registry; |
| JMXConnectorServer connector; |
| |
| @Override |
| public void startJMXConnector() throws Exception |
| { |
| if (config.get().enabled().get()) |
| { |
| // see java.rmi.server.ObjID |
| System.setProperty( "java.rmi.server.randomIDs", "true" ); |
| |
| int jmxAgentPort = config.get().port().get(); |
| |
| registry = LocateRegistry.createRegistry( jmxAgentPort ); |
| |
| String hostName = InetAddress.getLocalHost().getHostName(); |
| JMXServiceURL url = new JMXServiceURL( |
| "service:jmx:rmi://" + hostName + ":" + jmxAgentPort |
| + "/jndi/rmi://" + hostName + ":" + jmxAgentPort + "/jmxrmi" ); |
| Map env = new HashMap(); |
| |
| if(config.get().username().get() != null) |
| env.put( JMXConnectorServer.AUTHENTICATOR, new ConfigurationJmxAuthenticator() ); |
| |
| try |
| { |
| connector = JMXConnectorServerFactory.newJMXConnectorServer( url, env, server ); |
| connector.start(); |
| } catch (Exception e) |
| { |
| logger.error( "Could not start JMX connector", e ); |
| } |
| } |
| } |
| |
| @Override |
| public void stopJMXConnector() throws Exception |
| { |
| // Stop connector |
| if (connector != null) |
| { |
| connector.stop(); |
| connector = null; |
| } |
| |
| // Remove registry |
| if (registry != null) |
| { |
| UnicastRemoteObject.unexportObject( registry, true ); |
| registry = null; |
| } |
| } |
| |
| class ConfigurationJmxAuthenticator implements JMXAuthenticator |
| { |
| |
| @Override |
| public Subject authenticate( Object credentials ) |
| { |
| |
| Subject subject = null; |
| |
| if (!(credentials instanceof String[])) |
| { |
| // Special case for null so we get a more informative message |
| if (credentials == null) |
| { |
| throw new SecurityException( "Credentials required" ); |
| } |
| throw new SecurityException( "Credentials should be String[]" ); |
| } |
| |
| final String[] aCredentials = (String[]) credentials; |
| if (aCredentials.length != 2) |
| { |
| throw new SecurityException( "Credentials should have 2 elements" ); |
| } |
| |
| String username = aCredentials[0]; |
| String password = aCredentials[1]; |
| |
| String configUsername = config.get().username().get(); |
| |
| if (!(configUsername == null || (configUsername.equals( username ) && !password.equals( config.get().password().get() )))) |
| { |
| throw new SecurityException( "User/password combination not valid." ); |
| } |
| |
| |
| subject = new Subject( true, |
| Collections.singleton( new JMXPrincipal( username ) ), |
| Collections.EMPTY_SET, |
| Collections.EMPTY_SET ); |
| |
| return subject; |
| } |
| } |
| } |
| } |