/* * Copyright 2012-2015, the original author or authors. * * 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 com.flipkart.phantom.runtime.impl.spring.admin; import com.flipkart.phantom.runtime.impl.server.AbstractNetworkServer; import com.flipkart.phantom.runtime.impl.spring.ServiceProxyComponentContainer; import com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService; import com.flipkart.phantom.task.impl.TaskContextFactory; import com.flipkart.phantom.task.spi.AbstractHandler; import com.flipkart.phantom.task.spi.registry.AbstractHandlerRegistry; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.trpr.platform.core.PlatformException; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.URI; import java.util.*; /** * <code>SPConfigServiceImpl</code> is an implementation of {@link SPConfigService}. * * @author devashishshankar * @version 1.0, 2nd May, 2013 */ public class SPConfigServiceImpl<T extends AbstractHandler> implements SPConfigService<T> { /** Logger instance for this class*/ private static final Logger LOGGER = LoggerFactory.getLogger(SPConfigServiceImpl.class); /** The previous handler file (save file) */ public static final String PREV_HANDLER_FILE = "/spring-proxy-handler-prev.xml"; /** The componentCOntainer instance for reloading the config file */ private ServiceProxyComponentContainer<T> componentContainer; /** The map holding the mapping of a config file to it's handler name */ private Map<URI, List<T> > configURItoHandlerName = new HashMap<URI,List<T>>(); /** The list of deployed TCP Servers */ private List<AbstractNetworkServer> deployedNetworkServers = new LinkedList<AbstractNetworkServer>(); /** List of repositories */ private List<AbstractHandlerRegistry<T>> registries = new ArrayList<AbstractHandlerRegistry<T>>(); /** * Interface method implementation * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#getDeployedNetworkServers() */ public List<AbstractNetworkServer> getDeployedNetworkServers() { return deployedNetworkServers; } /** * Interface method implementation * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#getDeployedRegistries() */ public List<AbstractHandlerRegistry<T>> getDeployedRegistries() { return this.registries; } /** * Interface method implementation * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#addDeployedNetworkServer(com.flipkart.phantom.runtime.impl.server.AbstractNetworkServer) */ public void addDeployedNetworkServer(AbstractNetworkServer server) { deployedNetworkServers.add(server); } /** * Interface method implementation * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#addHandlerRegistry(com.flipkart.phantom.task.spi.registry.AbstractHandlerRegistry) */ public void addHandlerRegistry(AbstractHandlerRegistry<T> registry) { registries.add(registry); } /** * Interface method implementation. * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#getHandlerConfig(java.lang.String) */ public Resource getHandlerConfig(String handlerName) { for (URI configFile: this.configURItoHandlerName.keySet()) { for (AbstractHandler handler : this.configURItoHandlerName.get(configFile)) { if (handler.getName().equals(handlerName)) { return new FileSystemResource(new File(configFile)); } } } return null; } /** * Interface method implementation. * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#modifyHandlerConfig(java.lang.String, org.springframework.core.io.ByteArrayResource) */ public void modifyHandlerConfig(String handlerName, ByteArrayResource modifiedHandlerConfigFile) { // Check if exists File oldHandlerFile = null; try { oldHandlerFile = this.getHandlerConfig(handlerName).getFile(); } catch (IOException e1) { LOGGER.error("Handler Config File for handler: "+handlerName+" not found. Returning"); throw new PlatformException("File not found for handler: "+handlerName,e1); } if (!oldHandlerFile.exists()) { LOGGER.error("Handler Config File: "+oldHandlerFile.getAbsolutePath()+" doesn't exist. Returning"); throw new PlatformException("File not found: "+oldHandlerFile.getAbsolutePath()); } // Check for read permissions if (!oldHandlerFile.canRead()) { LOGGER.error("No read permission for: "+oldHandlerFile.getAbsolutePath()+". Returning"); throw new PlatformException("Read permissions not found for file: "+oldHandlerFile.getAbsolutePath()); } // Check for write permissions if (!oldHandlerFile.canWrite()) { LOGGER.error("No write permission for: "+oldHandlerFile.getAbsolutePath()+". Write permissions are required to modify handler confib"); throw new PlatformException("Required permissions not found for modifying file: "+oldHandlerFile.getAbsolutePath()); } // create backup this.createPrevConfigFile(handlerName); // file_put_contents Java :-/ try { this.upload(modifiedHandlerConfigFile.getByteArray(), oldHandlerFile.getAbsolutePath()); } catch (IOException e) { LOGGER.error("IOException while uploading file to path: "+oldHandlerFile.getAbsolutePath()); this.restorePrevConfigFile(handlerName); throw new PlatformException(e); } // get the registered AbstractHandler for the file name T handler = this.configURItoHandlerName.get(oldHandlerFile.toURI()).get(0); // re-load the handler // TODO // loading component destroys all beans in the config file given by the handler config info // this mean this only works if only one handler per handler xml file try { this.componentContainer.reloadHandler(handler, this.getHandlerConfig(handlerName)); } catch(Exception e) { this.restorePrevConfigFile(handlerName); this.componentContainer.loadComponent(this.getHandlerConfig(handlerName)); throw new PlatformException(e); } this.removePrevConfigFile(handlerName); } /** * Creates a temporary file, which is a duplicate of the current config file, * with the name {@link SPConfigServiceImpl#PREV_HANDLER_FILE} * @param handlerName Name of the Handler */ private void createPrevConfigFile(String handlerName) { // get current file File configFile = null; try { configFile = this.getHandlerConfig(handlerName).getFile(); } catch (IOException e1) { LOGGER.error("Exception while getting handlerConfigFile",e1); throw new PlatformException("Exception while getting handlerConfigFile",e1); } // create the backup file File prevFile = new File(configFile.getParent()+"/"+SPConfigServiceImpl.PREV_HANDLER_FILE); // move current -> backup, create new current file if (configFile.exists()) { if (prevFile.exists()) { prevFile.delete(); } configFile.renameTo(prevFile); try { configFile.createNewFile(); } catch (IOException e) { LOGGER.error("IOException while clearing config File",e); throw new PlatformException("IOException while clearing config File",e); } prevFile.deleteOnExit(); } } /** * This method removes the temporary previous config File * @param handlerName Name of the job */ private void removePrevConfigFile(String handlerName) { File configFile = null; try { configFile = this.getHandlerConfig(handlerName).getFile(); } catch (IOException e) { LOGGER.error("IOException while getting HandlerConfigFile",e); } String prevFilePath = configFile.getParent()+SPConfigServiceImpl.PREV_HANDLER_FILE; File prevFile = new File(prevFilePath); if (prevFile.exists()) { prevFile.delete(); // DELETE previous XML File } } /** * Restores the previous config file, if found * @param handlerName Name of the job */ private void restorePrevConfigFile(String handlerName) { File configFile = null; try { configFile = this.getHandlerConfig(handlerName).getFile(); } catch (IOException e) { LOGGER.error("IOException while getting HandlerConfigFile",e); } if(configFile.exists()) { configFile.delete(); } File prevFile = new File(configFile.getParent()+"/"+SPConfigServiceImpl.PREV_HANDLER_FILE); if(prevFile.exists()) { prevFile.renameTo(configFile); } } /** * Uploads the file to the given path. Creates the file and directory structure, if the file * or parent directory doesn't exist */ private void upload(byte[] fileContents, String destPath) throws IOException { File destFile = new File(destPath); // If exists, overwrite if (destFile.exists()) { destFile.delete(); destFile.createNewFile(); } // Creating directory structure try { destFile.getParentFile().mkdirs(); } catch(Exception e) { LOGGER.error("Unable to create directory structure for uploading file"); throw new PlatformException("Unable to create directory structure for uploading file"); } FileOutputStream fos = new FileOutputStream(destFile); fos.write(fileContents); } /** * Interface method implementation. * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#addHandlerConfigPath(java.io.File, com.flipkart.phantom.task.spi.AbstractHandler) */ public void addHandlerConfigPath(File taskHandlerFile, T handler) { if (this.configURItoHandlerName.get(taskHandlerFile.toURI()) == null) { this.configURItoHandlerName.put(taskHandlerFile.toURI(), new LinkedList<T>()); } this.configURItoHandlerName.get(taskHandlerFile.toURI()).add(handler); } /** * Interface method implementation * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#reinitHandler(String) * @param handlerName The name of the TaskHandler to be re-inited * @throws Exception in case of errors */ public void reinitHandler(String handlerName) throws Exception { for (AbstractHandlerRegistry<T> registry : registries) { if (registry.getHandler(handlerName) != null) { registry.reinitHandler(handlerName,TaskContextFactory.getTaskContext()); } } } /** * Interface method implementation * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#reloadHandler(String) * @param handlerName The name of the TaskHandler to be reloaded * @throws Exception in case of errors */ public void reloadHandler(String handlerName) throws Exception { // get the handler config file File handlerFile = null; try { handlerFile = this.getHandlerConfig(handlerName).getFile(); } catch (NullPointerException e) { LOGGER.error("Resource for handler: " + handlerName + " not found. Returning"); throw new PlatformException("File not found for handler: " + handlerName, e); } catch (IOException e) { LOGGER.error("Handler Config File for handler: " + handlerName + " not found. Returning"); throw new PlatformException("File not found for handler: " + handlerName, e); } // Check if file actually exists if (!handlerFile.exists()) { LOGGER.error("Handler Config File: " + handlerFile.getAbsolutePath() + " doesn't exist. Returning"); throw new PlatformException("File not found: " + handlerFile.getAbsolutePath()); } // Check for read permissions if (!handlerFile.canRead()) { LOGGER.error("No read permission for: " + handlerFile.getAbsolutePath() + ". Returning"); throw new PlatformException("Read permissions not found for file: " + handlerFile.getAbsolutePath()); } // get the registered AbstractHandler for the file name T handler = this.configURItoHandlerName.get(handlerFile.toURI()).get(0); // re-load the handler // loading component destroys all beans in the config file given by the handler config info // this mean this only works if there is only one handler per handler xml file // else all handlers in the xml file are reloaded this.componentContainer.reloadHandler(handler, this.getHandlerConfig(handlerName)); } /** * Interface method implementation * @see com.flipkart.phantom.runtime.spi.spring.admin.SPConfigService#getAllHandlers() */ public List<AbstractHandler> getAllHandlers() { List<AbstractHandler> list = new ArrayList<AbstractHandler>(); for (AbstractHandlerRegistry<T> registry : registries) { list.addAll(registry.getHandlers()); } return list; } /** Getter/Setter methods */ public ServiceProxyComponentContainer<T> getComponentContainer() { return componentContainer; } public void setComponentContainer(ServiceProxyComponentContainer<T> componentContainer) { this.componentContainer = componentContainer; } /** End Getter/Setter methods */ }