DefaultAsAgentJar.java

/*-
 * #%L
 * io.earcam.instrumental.archive
 * %%
 * Copyright (C) 2018 earcam
 * %%
 * SPDX-License-Identifier: (BSD-3-Clause OR EPL-1.0 OR Apache-2.0 OR MIT)
 * 
 * You <b>must</b> choose to accept, in full - any individual or combination of 
 * the following licenses:
 * <ul>
 * 	<li><a href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</a></li>
 * 	<li><a href="https://www.eclipse.org/legal/epl-v10.html">EPL-1.0</a></li>
 * 	<li><a href="https://www.apache.org/licenses/LICENSE-2.0">Apache-2.0</a></li>
 * 	<li><a href="https://opensource.org/licenses/MIT">MIT</a></li>
 * </ul>
 * #L%
 */
package io.earcam.instrumental.archive;

import java.lang.instrument.Instrumentation;
import java.util.jar.Manifest;

import io.earcam.instrumental.reflect.Methods;

class DefaultAsAgentJar extends AbstractAsJarBuilder<AsAgentJar> implements AsAgentJar {

	static final String PREMAIN_CLASS = "Premain-Class";
	static final String AGENT_CLASS = "Agent-Class";

	static final String CAN_REDEFINE_CLASSES = "Can-Redefine-Classes";
	static final String CAN_RETRANSFORM_CLASSES = "Can-Retransform-Classes";

	private boolean canRedefine = false;
	private boolean canRetransform = false;


	DefaultAsAgentJar()
	{}


	@Override
	protected AsAgentJar self()
	{
		return this;
	}


	@Override
	public void process(Manifest manifest)
	{
		withManifestHeader(CAN_REDEFINE_CLASSES, Boolean.toString(canRedefine));
		withManifestHeader(CAN_RETRANSFORM_CLASSES, Boolean.toString(canRetransform));

		super.process(manifest);
	}


	/**
	 * <p>
	 * withAgentClass.
	 * </p>
	 *
	 * @param type a {@link java.lang.Class} object.
	 * @return a {@link io.earcam.instrumental.archive.DefaultAsAgentJar} object.
	 */
	public DefaultAsAgentJar withAgentClass(Class<?> type)
	{
		if(validate()) {
			requireAgentMainMethod(type);
		}
		source.with(type);
		String name = type.getCanonicalName();
		if(hasAgentPreMainMethod(type)) {
			withManifestHeader(PREMAIN_CLASS, name);
		}
		withManifestHeader(AGENT_CLASS, name);
		return this;
	}


	private boolean hasAgentPreMainMethod(Class<?> type)
	{
		return findAgentMethod(type, "premain");
	}


	private boolean findAgentMethod(Class<?> type, String name)
	{
		return Methods.getMethod(type, name, String.class, Instrumentation.class)
				.filter(DefaultAsAgentJar::isPublicStaticVoid)
				.isPresent();
	}


	private void requireAgentMainMethod(Class<?> type)
	{
		if(!findAgentMethod(type, "agentmain")) {
			throw new IllegalArgumentException("'public static void agentmain(String arg0, Instrumentation arg1)' method not found on " + type);
		}
	}


	/**
	 * <p>
	 * canRedefineClasses.
	 * </p>
	 *
	 * @return a {@link io.earcam.instrumental.archive.DefaultAsAgentJar} object.
	 */
	public DefaultAsAgentJar canRedefineClasses()
	{
		canRedefine = true;
		return this;
	}


	/**
	 * <p>
	 * canNotRedefineClasses.
	 * </p>
	 *
	 * @return a {@link io.earcam.instrumental.archive.DefaultAsAgentJar} object.
	 */
	public DefaultAsAgentJar canNotRedefineClasses()
	{
		canRedefine = false;
		return this;
	}


	/**
	 * <p>
	 * canRetransformClasses.
	 * </p>
	 *
	 * @return a {@link io.earcam.instrumental.archive.DefaultAsAgentJar} object.
	 */
	public DefaultAsAgentJar canRetransformClasses()
	{
		canRetransform = true;
		return this;
	}


	/**
	 * <p>
	 * canNotRetransformClasses.
	 * </p>
	 *
	 * @return a {@link io.earcam.instrumental.archive.DefaultAsAgentJar} object.
	 */
	public DefaultAsAgentJar canNotRetransformClasses()
	{
		canRetransform = false;
		return this;
	}
}