001 /**
002 * GRANITE DATA SERVICES
003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 * This file is part of the Granite Data Services Platform.
006 *
007 * Granite Data Services is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * Granite Data Services is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
015 * General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
020 * USA, or see <http://www.gnu.org/licenses/>.
021 */
022 package org.granite.config.flex;
023
024 import java.io.IOException;
025 import java.io.InputStream;
026 import java.lang.reflect.Method;
027 import java.lang.reflect.Modifier;
028 import java.util.ArrayList;
029 import java.util.Arrays;
030 import java.util.Collections;
031 import java.util.HashMap;
032 import java.util.List;
033 import java.util.Map;
034
035 import org.granite.config.api.Configuration;
036 import org.granite.logging.Logger;
037 import org.granite.messaging.service.annotations.RemoteDestination;
038 import org.granite.messaging.service.security.RemotingDestinationSecurizer;
039 import org.granite.scan.ScannedItem;
040 import org.granite.scan.ScannedItemHandler;
041 import org.granite.scan.Scanner;
042 import org.granite.scan.ScannerFactory;
043 import org.granite.util.TypeUtil;
044 import org.granite.util.XMap;
045 import org.xml.sax.SAXException;
046
047 import flex.messaging.messages.RemotingMessage;
048
049
050 /**
051 * @author Franck WOLFF
052 */
053 public class ServicesConfig implements ScannedItemHandler {
054
055 ///////////////////////////////////////////////////////////////////////////
056 // Fields.
057
058 private static final Logger log = Logger.getLogger(ServicesConfig.class);
059 private static final String SERVICES_CONFIG_PROPERTIES = "META-INF/services-config.properties";
060
061 private final Map<String, Service> services = new HashMap<String, Service>();
062 private final Map<String, Channel> channels = new HashMap<String, Channel>();
063 private final Map<String, Factory> factories = new HashMap<String, Factory>();
064
065
066 ///////////////////////////////////////////////////////////////////////////
067 // Classpath scan initialization.
068
069 private void scanConfig(String serviceConfigProperties, List<ScannedItemHandler> handlers) {
070 Scanner scanner = ScannerFactory.createScanner(this, serviceConfigProperties != null ? serviceConfigProperties : SERVICES_CONFIG_PROPERTIES);
071 scanner.addHandlers(handlers);
072 try {
073 scanner.scan();
074 } catch (Exception e) {
075 log.error(e, "Could not scan classpath for configuration");
076 }
077 }
078
079 public boolean handleMarkerItem(ScannedItem item) {
080 return false;
081 }
082
083 public void handleScannedItem(ScannedItem item) {
084 if ("class".equals(item.getExtension()) && item.getName().indexOf('$') == -1) {
085 try {
086 handleClass(item.loadAsClass());
087 } catch (Throwable t) {
088 log.error(t, "Could not load class: %s", item);
089 }
090 }
091 }
092
093 public void handleClass(Class<?> clazz) {
094 RemoteDestination anno = clazz.getAnnotation(RemoteDestination.class);
095 if (anno != null && !("".equals(anno.id()))) {
096 XMap props = new XMap("properties");
097
098 // Owning service.
099 Service service = null;
100 if (anno.service().length() > 0) {
101 log.info("Configuring service from RemoteDestination annotation: service=%s (class=%s, anno=%s)", anno.service(), clazz, anno);
102 service = this.services.get(anno.service());
103 }
104 else if (this.services.size() > 0) {
105 // Lookup remoting service
106 log.info("Looking for service(s) with RemotingMessage type (class=%s, anno=%s)", clazz, anno);
107 int count = 0;
108 for (Service s : this.services.values()) {
109 if (RemotingMessage.class.getName().equals(s.getMessageTypes())) {
110 log.info("Found service with RemotingMessage type: service=%s (class=%s, anno=%s)", s, clazz, anno);
111 service = s;
112 count++;
113 }
114 }
115 if (count == 1 && service != null)
116 log.info("Service " + service.getId() + " selected for destination in class: " + clazz.getName());
117 else {
118 log.error("Found %d matching services (should be exactly 1, class=%s, anno=%s)", count, clazz, anno);
119 service = null;
120 }
121 }
122 if (service == null)
123 throw new RuntimeException("No service found for destination in class: " + clazz.getName());
124
125 // Channel reference.
126 List<String> channelIds = null;
127 if (anno.channels().length > 0)
128 channelIds = Arrays.asList(anno.channels());
129 else if (anno.channel().length() > 0)
130 channelIds = Collections.singletonList(anno.channel());
131 else if (this.channels.size() == 1) {
132 channelIds = new ArrayList<String>(this.channels.keySet());
133 log.info("Channel " + channelIds.get(0) + " selected for destination in class: " + clazz.getName());
134 }
135 else {
136 log.warn("No (or ambiguous) channel definition found for destination in class: " + clazz.getName());
137 channelIds = Collections.emptyList();
138 }
139
140 // Factory reference.
141 String factoryId = null;
142 if (anno.factory().length() > 0)
143 factoryId = anno.factory();
144 else if (this.factories.isEmpty()) {
145 props.put("scope", anno.scope());
146 props.put("source", clazz.getName());
147 log.info("Default POJO factory selected for destination in class: " + clazz.getName() + " with scope: " + anno.scope());
148 }
149 else if (this.factories.size() == 1) {
150 factoryId = this.factories.keySet().iterator().next();
151 log.info("Factory " + factoryId + " selected for destination in class: " + clazz.getName());
152 }
153 else
154 throw new RuntimeException("No (or ambiguous) factory definition found for destination in class: " + clazz.getName());
155
156 if (factoryId != null)
157 props.put("factory", factoryId);
158 if (!(anno.source().equals("")))
159 props.put("source", anno.source());
160
161 // Security roles.
162 List<String> roles = null;
163 if (anno.securityRoles().length > 0) {
164 roles = new ArrayList<String>(anno.securityRoles().length);
165 for (String role : anno.securityRoles())
166 roles.add(role);
167 }
168
169 // Securizer
170 if (anno.securizer() != RemotingDestinationSecurizer.class)
171 props.put("securizer", anno.securizer().getName());
172
173 Destination destination = new Destination(anno.id(), channelIds, props, roles, null, clazz);
174
175 service.getDestinations().put(destination.getId(), destination);
176 }
177 }
178
179 ///////////////////////////////////////////////////////////////////////////
180 // Static ServicesConfig loaders.
181
182 public ServicesConfig(InputStream customConfigIs, Configuration configuration, boolean scan) throws IOException, SAXException {
183 if (customConfigIs != null)
184 loadConfig(customConfigIs);
185
186 if (scan)
187 scan(configuration);
188 }
189
190 public void scan(Configuration configuration) {
191 List<ScannedItemHandler> handlers = new ArrayList<ScannedItemHandler>();
192 for (Factory factory : factories.values()) {
193 try {
194 Class<?> clazz = TypeUtil.forName(factory.getClassName());
195 Method method = clazz.getMethod("getScannedItemHandler");
196 if ((Modifier.STATIC & method.getModifiers()) != 0 && method.getParameterTypes().length == 0) {
197 ScannedItemHandler handler = (ScannedItemHandler)method.invoke(null);
198 handlers.add(handler);
199 }
200 else
201 log.warn("Factory class %s contains an unexpected signature for method: %s", factory.getClassName(), method);
202 }
203 catch (NoSuchMethodException e) {
204 // ignore
205 }
206 catch (ClassNotFoundException e) {
207 log.error(e, "Could not load factory class: %s", factory.getClassName());
208 }
209 catch (Exception e) {
210 log.error(e, "Error while calling %s.getScannedItemHandler() method", factory.getClassName());
211 }
212 }
213 scanConfig(configuration != null ? configuration.getFlexServicesConfigProperties() : null, handlers);
214 }
215
216 private void loadConfig(InputStream configIs) throws IOException, SAXException {
217 XMap doc = new XMap(configIs);
218 forElement(doc);
219 }
220
221 ///////////////////////////////////////////////////////////////////////////
222 // Services.
223
224 public Service findServiceById(String id) {
225 return services.get(id);
226 }
227
228 public List<Service> findServicesByMessageType(String messageType) {
229 List<Service> services = new ArrayList<Service>();
230 for (Service service : this.services.values()) {
231 if (messageType.equals(service.getMessageTypes()))
232 services.add(service);
233 }
234 return services;
235 }
236
237 public void addService(Service service) {
238 services.put(service.getId(), service);
239 }
240
241 ///////////////////////////////////////////////////////////////////////////
242 // Channels.
243
244 public Channel findChannelById(String id) {
245 return channels.get(id);
246 }
247
248 public void addChannel(Channel channel) {
249 channels.put(channel.getId(), channel);
250 }
251
252 ///////////////////////////////////////////////////////////////////////////
253 // Factories.
254
255 public Factory findFactoryById(String id) {
256 return factories.get(id);
257 }
258
259 public void addFactory(Factory factory) {
260 factories.put(factory.getId(), factory);
261 }
262
263 ///////////////////////////////////////////////////////////////////////////
264 // Destinations.
265
266 public Destination findDestinationById(String messageType, String id) {
267 for (Service service : services.values()) {
268 if (messageType == null || messageType.equals(service.getMessageTypes())) {
269 Destination destination = service.findDestinationById(id);
270 if (destination != null)
271 return destination;
272 }
273 }
274 return null;
275 }
276
277 public List<Destination> findDestinationsByMessageType(String messageType) {
278 List<Destination> destinations = new ArrayList<Destination>();
279 for (Service service : services.values()) {
280 if (messageType.equals(service.getMessageTypes()))
281 destinations.addAll(service.getDestinations().values());
282 }
283 return destinations;
284 }
285
286 ///////////////////////////////////////////////////////////////////////////
287 // Static helper.
288
289 private void forElement(XMap element) {
290 XMap services = element.getOne("services");
291 if (services != null) {
292 for (XMap service : services.getAll("service")) {
293 Service serv = Service.forElement(service);
294 this.services.put(serv.getId(), serv);
295 }
296
297 /* TODO: service-include...
298 for (Element service : (List<Element>)services.getChildren("service-include")) {
299 config.services.add(Service.forElement(service));
300 }
301 */
302 }
303
304 XMap channels = element.getOne("channels");
305 if (channels != null) {
306 for (XMap channel : channels.getAll("channel-definition")) {
307 Channel chan = Channel.forElement(channel);
308 this.channels.put(chan.getId(), chan);
309 }
310 }
311 else {
312 log.info("No channel definition found, using defaults");
313 EndPoint defaultEndpoint = new EndPoint("http://{server.name}:{server.port}/{context.root}/graniteamf/amf", "flex.messaging.endpoints.AMFEndpoint");
314 Channel defaultChannel = new Channel("my-graniteamf", "mx.messaging.channels.AMFChannel", defaultEndpoint, XMap.EMPTY_XMAP);
315 this.channels.put(defaultChannel.getId(), defaultChannel);
316 }
317
318 XMap factories = element.getOne("factories");
319 if (factories != null) {
320 for (XMap factory : factories.getAll("factory")) {
321 Factory fact = Factory.forElement(factory);
322 this.factories.put(fact.getId(), fact);
323 }
324 }
325 }
326
327
328 /**
329 * Remove service (new addings for osgi).
330 * @param clazz service class.
331 */
332 public void handleRemoveService(Class<?> clazz){
333 RemoteDestination anno = clazz.getAnnotation(RemoteDestination.class);
334 if(anno!=null){
335 Service service=null;
336 if (anno.service().length() > 0){
337 service=services.get(anno.service());
338 }
339 else if (services.size() > 0) {
340 // Lookup remoting service
341 for (Service s : services.values()) {
342 if (RemotingMessage.class.getName().equals(s.getMessageTypes())) {
343 service = s;
344 log.info("Service " + service.getId() + " selected for destination in class: " + clazz.getName());
345 break;
346 }
347 }
348 }
349 if(service!=null){
350 Destination dest=service.getDestinations().remove(anno.id());
351 if (dest != null) {
352 dest.remove();
353 log.info("RemoteDestination:"+dest.getId()+" has been removed");
354 }
355 }else{
356 log.info("Service NOT Found!!");
357 }
358 }
359 }
360
361 }