content: /*******************************************************************************
  * Copyright (c) 2013-2015 IBM Corp.
  *
  * 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.acmeair.service;
  
  import java.lang.annotation.Annotation;
  import java.util.Map;
  import java.util.Set;
  import java.util.TreeMap;
  import java.util.concurrent.atomic.AtomicReference;
  
  import javax.annotation.PostConstruct;
  import javax.enterprise.context.spi.CreationalContext;
  import javax.enterprise.inject.Any;
  import javax.enterprise.inject.Default;
  import javax.enterprise.inject.spi.Bean;
  import javax.enterprise.inject.spi.BeanManager;
  import javax.enterprise.util.AnnotationLiteral;
  import javax.inject.Inject;
  import javax.naming.InitialContext;
  import javax.naming.NamingException;
  
  import java.util.logging.Logger;
  
  import org.json.simple.JSONObject;
  import org.json.simple.JSONValue;
  
  
  public class ServiceLocator {
  
  	public static String REPOSITORY_LOOKUP_KEY = "com.acmeair.repository.type";
  	private static String serviceType;
  	private static Logger logger = Logger.getLogger(ServiceLocator.class.getName());
  
  	private static AtomicReference<ServiceLocator> singletonServiceLocator = new AtomicReference<ServiceLocator>();
  
  	@Inject
  	BeanManager beanManager;
  	
  	public static ServiceLocator instance() {
  		if (singletonServiceLocator.get() == null) {
  			synchronized (singletonServiceLocator) {
  				if (singletonServiceLocator.get() == null) {
  					singletonServiceLocator.set(new ServiceLocator());
  				}
  			}
  		}
  		return singletonServiceLocator.get();
  	}
  	
  	@PostConstruct
  	private void initialization()  {		
  		if(beanManager == null){
  			logger.info("Attempting to look up BeanManager through JNDI at java:comp/BeanManager");
  			try {
  				beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
  			} catch (NamingException e) {
  				logger.severe("BeanManager not found at java:comp/BeanManager");
  			}
  		}
  		
  		if(beanManager == null){
  			logger.info("Attempting to look up BeanManager through JNDI at java:comp/env/BeanManager");
  			try {
  				beanManager = (BeanManager) new InitialContext().lookup("java:comp/env/BeanManager");
  			} catch (NamingException e) {
  				logger.severe("BeanManager not found at java:comp/env/BeanManager ");
  			}
  		}
  	}
  	
  	public static void updateService(String serviceName){
  		logger.info("Service Locator updating service to : " + serviceName);
  		serviceType = serviceName;
  	}
  
  	private ServiceLocator() {
  		String type = null;
  		String lookup = REPOSITORY_LOOKUP_KEY.replace('.', '/');
  		javax.naming.Context context = null;
  		javax.naming.Context envContext = null;
  		try {
  			context = new javax.naming.InitialContext();
  			envContext = (javax.naming.Context) context.lookup("java:comp/env");
  			if (envContext != null)
  				type = (String) envContext.lookup(lookup);
  		} catch (NamingException e) {
  			// e.printStackTrace();
  		}
  		
  		if (type != null) {
  			logger.info("Found repository in web.xml:" + type);
  		}
  		else if (context != null) {
  			try {
  				type = (String) context.lookup(lookup);
  				if (type != null)
  					logger.info("Found repository in server.xml:" + type);
  			} catch (NamingException e) {
  				// e.printStackTrace();
  			}
  		}
  
  		if (type == null) {
  			type = System.getProperty(REPOSITORY_LOOKUP_KEY);
  			if (type != null)
  				logger.info("Found repository in jvm property:" + type);
  			else {
  				type = System.getenv(REPOSITORY_LOOKUP_KEY);
  				if (type != null)
  					logger.info("Found repository in environment property:" + type);
  			}
  		}
  
  		if(beanManager == null) {
  			logger.info("Attempting to look up BeanManager through JNDI at java:comp/BeanManager");
  			try {
  				beanManager = (BeanManager) new InitialContext().lookup("java:comp/BeanManager");
  			} catch (NamingException e) {
  				logger.severe("BeanManager not found at java:comp/BeanManager");
  			}
  		}	
  		
  		if(beanManager == null){
  			logger.info("Attempting to look up BeanManager through JNDI at java:comp/env/BeanManager");
  			try {
  				beanManager = (BeanManager) new InitialContext().lookup("java:comp/env/BeanManager");
  			} catch (NamingException e) {
  				logger.severe("BeanManager not found at java:comp/env/BeanManager ");
  			}
  		}
  		
  		if (type==null)
  		{
  			String vcapJSONString = System.getenv("VCAP_SERVICES");
  			if (vcapJSONString != null) {
  				logger.info("Reading VCAP_SERVICES");
  				Object jsonObject = JSONValue.parse(vcapJSONString);
  				logger.fine("jsonObject = " + ((JSONObject)jsonObject).toJSONString());
  				JSONObject json = (JSONObject)jsonObject;
  				String key;
  				for (Object k: json.keySet())
  				{
  					key = (String ) k;
  					if (key.startsWith("ElasticCaching")||key.startsWith("DataCache"))
  					{
  						logger.info("VCAP_SERVICES existed with service:"+key);
  						type ="wxs";
  						break;
  					}
  					if (key.startsWith("mongo"))
  					{
  						logger.info("VCAP_SERVICES existed with service:"+key);
  						type ="morphia";
  						break;
  					}
  					if (key.startsWith("redis"))
  					{
  						logger.info("VCAP_SERVICES existed with service:"+key);
  						type ="redis";
  						break;
  					}
  					if (key.startsWith("mysql")|| key.startsWith("cleardb"))
  					{
  						logger.info("VCAP_SERVICES existed with service:"+key);
  						type ="mysql";
  						break;
  					}
  					if (key.startsWith("postgresql"))
  					{
  						logger.info("VCAP_SERVICES existed with service:"+key);
  						type ="postgresql";
  						break;
  					}
  					if (key.startsWith("db2"))
  					{
  						logger.info("VCAP_SERVICES existed with service:"+key);
  						type ="db2";
  						break;
  					}
  				}
  			}
  		}
  				
  		serviceType = type;
  		logger.info("ServiceType is now : " + serviceType);
  		if (type ==null) {
  			logger.warning("Can not determine type. Use default service implementation.");			
  		}
  	}
  
  	@SuppressWarnings("unchecked")
  	public <T> T getService (Class<T> clazz) {		
  		logger.fine("Looking up service:  "+clazz.getName() + " with service type: " + serviceType);
  		if(beanManager == null) {
  			logger.severe("BeanManager is null!!!");
  		}		
      	Set<Bean<?>> beans = beanManager.getBeans(clazz,new AnnotationLiteral<Any>() {
  			private static final long serialVersionUID = 1L;});
      	for (Bean<?> bean : beans) {
      		logger.fine(" Bean = "+bean.getBeanClass().getName());
      		for (Annotation qualifer: bean.getQualifiers()) {
      			if(null == serviceType) {
      				logger.warning("Service type is not set, searching for the default implementation.");
      				if(Default.class.getName().equalsIgnoreCase(qualifer.annotationType().getName())){
      					CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
      					return  (T) beanManager.getReference(bean, clazz, ctx);
      				}
      			} else {    				   
      				if(DataService.class.getName().equalsIgnoreCase(qualifer.annotationType().getName())){
      					DataService service = (DataService) qualifer;
      					logger.fine("   name="+service.name()+" description="+service.description());
      					if(serviceType.equalsIgnoreCase(service.name())) {
      						CreationalContext<?> ctx = beanManager.createCreationalContext(bean);
      						return  (T) beanManager.getReference(bean, clazz, ctx);
  
      					}
      				}
      			}
      		}
      	}
      	logger.warning("No Service of type: " + serviceType + " found for "+clazz.getName()+" ");
      	return null;
  	}
  	
  	/**
  	 * Retrieves the services that are available for use with the description for each service. 
  	 * The Services are determined by looking up all of the implementations of the 
  	 * Customer Service interface that are using the  DataService qualifier annotation. 
  	 * The DataService annotation contains the service name and description information. 
  	 * @return Map containing a list of services available and a description of each one.
  	 */
  	public Map<String,String> getServices (){
  		TreeMap<String,String> services = new TreeMap<String,String>();
  		logger.fine("Getting CustomerService Impls");
      	Set<Bean<?>> beans = beanManager.getBeans(CustomerService.class,new AnnotationLiteral<Any>() {
  			private static final long serialVersionUID = 1L;});
      	for (Bean<?> bean : beans) {    		
      		for (Annotation qualifer: bean.getQualifiers()){
      			if(DataService.class.getName().equalsIgnoreCase(qualifer.annotationType().getName())){
      				DataService service = (DataService) qualifer;
      				logger.fine("   name="+service.name()+" description="+service.description());
      				services.put(service.name(), service.description());
      			}
      		}
      	}    	
      	return services;
  	}
  	
  	/**
  	 * The type of service implementation that the application is 
  	 * currently configured to use.  
  	 * 
  	 * @return The type of service in use, or "default" if no service has been set. 
  	 */
  	public String getServiceType (){
  		if(serviceType == null){
  			return "default";
  		}
  		return serviceType;
  	}
  }
  