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 */ 022package org.granite.config.flex; 023 024import java.io.IOException; 025import java.io.InputStream; 026import java.lang.reflect.Method; 027import java.lang.reflect.Modifier; 028import java.util.ArrayList; 029import java.util.Arrays; 030import java.util.Collections; 031import java.util.HashMap; 032import java.util.List; 033import java.util.Map; 034 035import org.granite.config.api.Configuration; 036import org.granite.logging.Logger; 037import org.granite.messaging.service.annotations.RemoteDestination; 038import org.granite.messaging.service.security.RemotingDestinationSecurizer; 039import org.granite.scan.ScannedItem; 040import org.granite.scan.ScannedItemHandler; 041import org.granite.scan.Scanner; 042import org.granite.scan.ScannerFactory; 043import org.granite.util.TypeUtil; 044import org.granite.util.XMap; 045import org.xml.sax.SAXException; 046 047import flex.messaging.messages.RemotingMessage; 048 049 050/** 051 * @author Franck WOLFF 052 */ 053public 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}