blob: 86913cdc32c4394374641948804b827b74eac359 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.maven.mae.app;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.apache.maven.mae.MAEException;
import org.apache.maven.mae.boot.embed.MAEEmbedder;
import org.apache.maven.mae.boot.embed.MAEEmbedderBuilder;
import org.apache.maven.mae.conf.MAEConfiguration;
import org.apache.maven.mae.conf.MAELibrary;
import org.apache.maven.mae.conf.VersionProvider;
import org.apache.maven.mae.conf.ext.ExtensionConfiguration;
import org.apache.maven.mae.conf.ext.ExtensionConfigurationException;
import org.apache.maven.mae.conf.loader.InstanceLibraryLoader;
import org.apache.maven.mae.internal.container.ComponentKey;
import org.apache.maven.mae.internal.container.ComponentSelector;
import org.apache.maven.mae.internal.container.InstanceRegistry;
import org.apache.maven.mae.internal.container.VirtualInstance;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
/**
* {@link MAEApplication} implementation that provides support for loading a full Maven component environment, complete
* with {@link MAELibrary}'s, {@link ComponentSelector} and {@link InstanceRegistry}. This class supervises the assembly
* of the environment, giving the application developer an easy way to inject the behavior he needs.
*
* @author John Casey
*/
public abstract class AbstractMAEApplication
implements MAEApplication
{
private final List<MAELibrary> additionalLibraries = new ArrayList<MAELibrary>();
private final InstanceRegistry instanceRegistry = new InstanceRegistry();
private transient boolean loaded = false;
@SuppressWarnings( { "rawtypes", "unchecked" } )
protected AbstractMAEApplication()
{
withLibrary( this );
withComponentInstance( new ComponentKey( getClass() ), this );
}
/**
* Programmatically add a new {@link MAELibrary} instance, beyond those that are automatically detected via the
* /META-INF/services/org.apache.maven.mae.conf.MAELibrary files on the classpath.
*/
protected final AbstractMAEApplication withLibrary( final MAELibrary library )
{
additionalLibraries.add( library );
return this;
}
/**
* {@inheritDoc}
*
* @see org.apache.maven.mae.app.MAEApplication#load()
*/
@Override
public MAEApplication load()
throws MAEException
{
return doLoad();
}
/**
* Carry out the application loading process. This means: <br/>
* <ul>
* <li>Create a new {@link MAEEmbedderBuilder}</li>
* <li>Add to that an {@link InstanceLibraryLoader} to handle libraries that were programmatically added here</li>
* <li>Call {@link AbstractMAEApplication#beforeLoading()}</li>
* <li>Call {@link AbstractMAEApplication#configureBuilder(MAEEmbedderBuilder)} to allow fine-tuning of the
* {@link MAEEmbedderBuilder} instance</li>
* <li>Call {@link MAEEmbedderBuilder#build} to create an instance of {@link MAEEmbedder}</li>
* <li>For each instance in the {@link InstanceRegistry}, lookup via {@link MAEEmbedder#container()} to ensure
* injectable component dependencies are filled</li>
* <li>Call {@link AbstractMAEApplication#afterLoading()}</li>
* <li>Set the loaded flag, which will prevent this process from repeating for an application that has already been
* loaded</li>
* </ul>
*/
private synchronized final MAEApplication doLoad()
throws MAEException
{
if ( loaded )
{
return this;
}
final MAEEmbedderBuilder builder =
new MAEEmbedderBuilder().withLibraryLoader( new InstanceLibraryLoader( additionalLibraries ) );
configureBuilder( builder );
MAEEmbedder embedder = builder.build();
for ( final ComponentKey<?> key : getInstanceRegistry().getInstances().keySet() )
{
try
{
builder.container().lookup( key.getRoleClass(), key.getHint() );
}
catch ( final ComponentLookupException e )
{
throw new MAEException( "Forced member-injection for registered instance: %s failed. Reason: %s", e,
key, e.getMessage() );
}
}
afterLoading( embedder );
loaded = true;
return this;
}
/**
* Register a new, external component instance for injection into other components, or to have components injected
* into it.
*/
@SuppressWarnings( { "unchecked", "rawtypes" } )
protected final void withComponentInstance( final Object instance )
{
getInstanceRegistry().add( new ComponentKey( instance.getClass() ), instance );
}
/**
* Register a new {@link VirtualInstance}, which allows the component environment to bind its requirements without
* actually having access to the component instance. The instance itself will be injected into the
* {@link VirtualInstance} later.
*/
protected final <C> void withVirtualComponent( final Class<C> virtualClass )
{
getInstanceRegistry().addVirtual( new VirtualInstance<C>( virtualClass ) );
}
/**
* Set the actual instance on a {@link VirtualInstance} that was registered previously.
*/
protected final <C, T extends C> void setVirtualInstance( final Class<C> virtualKey, final T instance )
{
getInstanceRegistry().setVirtualInstance( virtualKey, instance );
}
/**
* Register a new, external component instance to make it available for injection, or to allow other components to
* be injected into it.
*/
protected final <C> void withComponentInstance( final ComponentKey<C> componentKey, final C instance )
{
getInstanceRegistry().add( componentKey, instance );
}
/**
* Register a new {@link VirtualInstance}, which allows the component environment to bind its requirements without
* actually having access to the component instance. The instance itself will be injected into the
* {@link VirtualInstance} later.
*/
protected final <C> void withVirtualComponent( final ComponentKey<C> virtualKey )
{
getInstanceRegistry().addVirtual( virtualKey, new VirtualInstance<C>( virtualKey.getRoleClass() ) );
}
/**
* Set the actual instance on a {@link VirtualInstance} that was registered previously.
*/
protected final <C, T extends C> void setVirtualInstance( final ComponentKey<C> virtualKey, final T instance )
{
getInstanceRegistry().setVirtualInstance( virtualKey, instance );
}
/**
* Fine-tune the {@link MAEEmbedderBuilder} instance before it is used to create the {@link MAEEmbedder} that will
* be used to load the application components.
*/
protected void configureBuilder( final MAEEmbedderBuilder builder )
throws MAEException
{
}
/**
* Hook allowing application developers access to the {@link MAEEmbedder} just after the registered external
* component instances have been injected, but before loading is considered complete.
*/
protected void afterLoading( final MAEEmbedder embedder )
throws MAEException
{
}
@Override
public Logger getLogger()
{
return Logger.getLogger( getLogHandle() );
}
@Override
public ExtensionConfiguration getConfiguration()
{
return null;
}
@Override
public ComponentSelector getComponentSelector()
{
return null;
}
@Override
public Set<ComponentKey<?>> getExportedComponents()
{
return null;
}
@Override
public Set<ComponentKey<?>> getManagementComponents( final Class<?> managementType )
{
return null;
}
@Override
public Map<Class<?>, Set<ComponentKey<?>>> getManagementComponents()
{
return null;
}
@Override
public String getLabel()
{
return getName();
}
@Override
public String getLogHandle()
{
return getId();
}
@Override
public void loadConfiguration( final MAEConfiguration embConfig )
throws ExtensionConfigurationException
{
}
@Override
public final InstanceRegistry getInstanceRegistry()
{
return instanceRegistry;
}
@Override
public String getVersion()
{
final VersionProvider provider = getVersionProvider();
if ( provider == null )
{
throw new IllegalStateException( "Your application booter: " + getClass().getName()
+ " must implement either getVersion() or getVersionProvider()." );
}
return provider.getVersion();
}
protected VersionProvider getVersionProvider()
{
return null;
}
}