1   package com.trendmicro.grid.acl.ds;
2   
3   import static com.trendmicro.grid.acl.ds.config.DataSourcePropertySection.KEY_AUTOSELECT_IMPLEMENTATION_BY_NAMES;
4   import static com.trendmicro.grid.acl.ds.config.DataSourcePropertySection.getPropertySection;
5   import org.slf4j.Logger;
6   import org.slf4j.LoggerFactory;
7   import org.springframework.beans.factory.annotation.Autowired;
8   import org.springframework.stereotype.Component;
9   
10  import javax.annotation.PostConstruct;
11  import javax.annotation.Resource;
12  import java.util.*;
13  
14  /**
15   * Is a central context that allows accessing all registered data repositories.
16   * <p/>
17   * <b>Note:</b> Avoid using the context directly, instead just implement {@link SelectorAware},
18   * inside your spring beans and an implementation of {@link RepositorySelectorsRepository} is
19   * injected whenever selector instances have changed.
20   *
21   * @author juergen_kellerer, 2010-05-05
22   * @version 1.0
23   */
24  @Component
25  public class RepositoriesContext implements RepositorySelectorsRepository {
26  
27  	private static final Logger log = LoggerFactory.getLogger(RepositoriesContext.class);
28  
29  	@SuppressWarnings("unchecked")
30  	private static void extractRepositoryInterfaces(Class cls, List<Class<? extends Repository>> interfaces) {
31  		if (cls != null) {
32  			for (Class<?> ifCls : cls.getInterfaces()) {
33  				if (Repository.class.isAssignableFrom(ifCls))
34  					interfaces.add((Class<? extends Repository>) ifCls);
35  			}
36  			extractRepositoryInterfaces(cls.getSuperclass(), interfaces);
37  		}
38  	}
39  
40  	private RepositorySelectorsFile selectorsFile = new RepositorySelectorsFile();
41  
42  	@Resource
43  	private Map<String, ? extends Repository> repositoryImplementations;
44  
45  	@Autowired(required = false)
46  	private Collection<SelectorAware> selectorAwareBeans = Collections.emptyList();
47  
48  	@PostConstruct
49  	@SuppressWarnings("unchecked")
50  	public void initializeAllSelectors() {
51  		selectorsFile.loadRepositorySelection();
52  		RepositorySelectors selectors = selectorsFile.getSelectors();
53  
54  		// Register / create selectors
55  		String[] autoSelectNames = getPropertySection().
56  				getPropertyValue(KEY_AUTOSELECT_IMPLEMENTATION_BY_NAMES, "").split("\\s+");
57  		Map<Class<? extends Repository>, Map<String, ? extends Repository>> selectorsInputMap = getRepositories();
58  		for (Map.Entry<Class<? extends Repository>,
59  				Map<String, ? extends Repository>> entry : selectorsInputMap.entrySet()) {
60  			RepositorySelector<?> selector = new RepositorySelector(entry.getValue(), entry.getKey());
61  			autoSelect(selector, autoSelectNames);
62  			selectors.addSelector(selector);
63  		}
64  
65  		// Persist final config
66  		selectorsFile.storeRepositorySelection();
67  
68  		// Log final selection
69  		if (log.isDebugEnabled()) {
70  			for (RepositorySelector<?> selector : selectors.getAllSelectors()) {
71  				log.debug("Initially selected repository {} ({})",
72  						selector.getSelectedKey(), selector.getRepositoryClass());
73  			}
74  		}
75  
76  		// Handle injection into dependent beans.
77  		for (SelectorAware bean : selectorAwareBeans)
78  			bean.refreshSelectors(this);
79  	}
80  
81  	private void autoSelect(RepositorySelector<?> selector, String[] names) {
82  		if (names != null && names.length > 0 && selector.isAutoSelectedKey()) {
83  			for (String key : names) {
84  				for (String availableKey : selector.getAvailableKeys())
85  					if (availableKey.contains(key)) {
86  						selector.setSelectedKey(availableKey, true);
87  						break;
88  					}
89  			}
90  		}
91  	}
92  
93  	@SuppressWarnings("unchecked")
94  	Map<Class<? extends Repository>, Map<String, ? extends Repository>> getRepositories() {
95  		// Sort repository implementations by interface class.
96  		Map<Class<? extends Repository>, Map<String, ? extends Repository>> selectorsInputMap =
97  				new HashMap<Class<? extends Repository>, Map<String, ? extends Repository>>();
98  		for (Map.Entry<String, ? extends Repository> entry : repositoryImplementations.entrySet()) {
99  			Repository r = entry.getValue();
100 
101 			List<Class<? extends Repository>> interfaces = new ArrayList<Class<? extends Repository>>();
102 			extractRepositoryInterfaces(r.getClass(), interfaces);
103 
104 			if (interfaces.isEmpty()) {
105 				String msg = "Couldn't find valid repository interface on " + r.getClass() +
106 						". This is a bug, not continuing with the startup process.";
107 				log.error("TMACL-00480:" + msg);
108 				throw new RuntimeException(msg);
109 			}
110 
111 			for (Class<? extends Repository> ifClass : interfaces) {
112 				Map m = selectorsInputMap.get(ifClass);
113 				if (m == null)
114 					selectorsInputMap.put(ifClass, m = new HashMap());
115 				m.put(entry.getKey(), r);
116 			}
117 		}
118 		return selectorsInputMap;
119 	}
120 
121 	public List<RepositorySelector<?>> getAllSelectors() {
122 		return selectorsFile.getSelectors().getAllSelectors();
123 	}
124 
125 	/**
126 	 * {@inheritDoc}
127 	 */
128 	public <R extends Repository> RepositorySelector<R> getSelector(Class<R> repositoryClass) {
129 		return selectorsFile.getSelectors().getSelector(repositoryClass);
130 	}
131 
132 	/**
133 	 * Returns the selected repository implementation for the given repository interface.
134 	 *
135 	 * @param repositoryClass the repository interface of the repository to return.
136 	 * @param <R>             the type of repository interface to return.
137 	 * @return the selected repository implementation for the given repository
138 	 *         interface or 'null' if no implementation is registered.
139 	 */
140 	public <R extends Repository> R getRepository(Class<R> repositoryClass) {
141 		return getSelector(repositoryClass).getRepository();
142 	}
143 }