blob: 0d41a4f18e062851ccd3186916b73670ecfa787b [file] [log] [blame]
/*
* 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;
}
}
}
}