/* * Copyright 2013 Robert von Burg <eitch@eitchnet.ch> * * 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 li.strolch.agent.api; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import java.util.Properties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.handler.SystemUserAction; import li.strolch.runtime.StrolchConstants; import li.strolch.runtime.configuration.ComponentConfiguration; import li.strolch.runtime.privilege.RunRunnable; /** * <p> * A {@link StrolchComponent} is a configurable extension to Strolch. Every major feature should be implemented as a * {@link StrolchComponent} so that they can be easily added or removed from a Strolch runtime. * </p> * * <p> * A {@link StrolchComponent} has access to the container and can perform different operations. They can be passive or * active and their life cycle is bound to the container's life cycle * </p> * * <p> * A {@link StrolchComponent} is registered in the Strolch configuration file and can have different configuration * depending on the container's runtime environment * </p> * * @author Robert von Burg <eitch@eitchnet.ch> */ public class StrolchComponent { public static final String COMPONENT_VERSION_PROPERTIES = "/componentVersion.properties"; //$NON-NLS-1$ protected static final Logger logger = LoggerFactory.getLogger(StrolchComponent.class); private final ComponentContainer container; private final String componentName; private ComponentState state; private ComponentVersion version; private ComponentConfiguration configuration; /** * Constructor which takes a reference to the container and the component's name under which it can be retrieved at * runtime (although one mostly retrieves the component by interface class for automatic casting) * * @param container * the container * @param componentName * the component name */ public StrolchComponent(ComponentContainer container, String componentName) { this.container = container; this.componentName = componentName; this.state = ComponentState.UNDEFINED; } /** * @return the componentName */ public String getName() { return this.componentName; } /** * Returns the current component's state * * @return the component's current state */ public ComponentState getState() { return this.state; } /** * Returns the reference to the container for sub classes * * @return the reference to the container */ protected ComponentContainer getContainer() { return this.container; } /** * The components current configuration dependent on the environment which is loaded * * @return the component's configuration */ protected ComponentConfiguration getConfiguration() { return this.configuration; } /** * Can be used by sub classes to assert that the component is started and thus ready to use, before any component * methods are used */ protected void assertStarted() { if (getState() != ComponentState.STARTED) { String msg = "Component {0} is not yet started!"; //$NON-NLS-1$ throw new IllegalStateException(MessageFormat.format(msg, this.componentName)); } } /** * Can be used by sub classes to assert that the entire container is started and thus ready to use, before any * component methods are used */ protected void assertContainerStarted() { if (this.container.getState() != ComponentState.STARTED) { String msg = "Container is not yet started!"; //$NON-NLS-1$ throw new IllegalStateException(msg); } } /** * Life cycle step setup. This is a very early step in the container's startup phase. * * @param configuration */ public void setup(ComponentConfiguration configuration) { this.state = this.state.validateStateChange(ComponentState.SETUP); } /** * Life cycle step initialize. Here you would typically read configuration values * * @param configuration * @throws Exception */ public void initialize(ComponentConfiguration configuration) throws Exception { this.configuration = configuration; this.state = this.state.validateStateChange(ComponentState.INITIALIZED); } /** * Life cycle step start. This is the last step of startup and is where threads and connections etc. would be * prepared. Can also be called after stop, to restart the component. * * @throws Exception */ public void start() throws Exception { this.state = this.state.validateStateChange(ComponentState.STARTED); } /** * Life cycle step stop. This is the first step in the tearing down of the container. Stop all active threads and * connections here. After stop is called, another start might also be called to restart the component. * * @throws Exception */ public void stop() throws Exception { this.state = this.state.validateStateChange(ComponentState.STOPPED); } /** * Life cycle step destroy. This is the last step in the tearing down of the container. Here you would release * remaining resources and the component can not be started anymore afterwards * * @throws Exception */ public void destroy() throws Exception { this.state = this.state.validateStateChange(ComponentState.DESTROYED); } /** * Performs the given {@link SystemUserAction} as a system user with the given username. Returns the action for * chaining calls * * @param username * the name of the system user to perform the action as * @param action * the action to perform * * @return the action performed for chaining calls * * @throws PrivilegeException */ protected <V extends SystemUserAction> V runAs(String username, V action) throws PrivilegeException { return this.container.getPrivilegeHandler().runAsSystem(username, action); } /** * Performs the given {@link SystemUserAction} as the privileged system user * {@link StrolchConstants#PRIVILEGED_SYSTEM_USER}. Returns the action for chaining calls * * @param action * the action to perform * * @return the action performed for chaining calls * * @throws PrivilegeException */ protected <V extends SystemUserAction> V runPrivileged(V action) throws PrivilegeException { return this.container.getPrivilegeHandler().runAsSystem(StrolchConstants.PRIVILEGED_SYSTEM_USER, action); } /** * Performs the given {@link SystemUserAction} as the privileged system user * {@link StrolchConstants#PRIVILEGED_SYSTEM_USER}. Returns the action for chaining calls * * @param action * the action to perform * * @throws PrivilegeException */ protected <T> T runPrivilegedRunnable(RunRunnable.Runnable<T> action) throws PrivilegeException { return this.container.getPrivilegeHandler() .runAsSystem(StrolchConstants.PRIVILEGED_SYSTEM_USER, new RunRunnable<>(action)).getResult(); } /** * Returns the version of this component. The version should be stored in the file * {@link #COMPONENT_VERSION_PROPERTIES}. See {@link ComponentVersion} for more information * * @return the component's version. * @throws IOException */ public ComponentVersion getVersion() throws IOException { if (this.version == null) { try (InputStream stream = getClass().getResourceAsStream(COMPONENT_VERSION_PROPERTIES)) { if (stream == null) { throw new RuntimeException("/componentVersion.properties does not exist"); //$NON-NLS-1$ } Properties properties = new Properties(); properties.load(stream); ComponentVersion componentVersion = new ComponentVersion(getName(), properties); this.version = componentVersion; } } return this.version; } }