001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.isis.core.metamodel.specloader; 018 019import java.lang.reflect.Method; 020import java.util.List; 021import java.util.Map; 022 023import javax.annotation.PostConstruct; 024import javax.annotation.PreDestroy; 025 026import com.google.common.collect.Maps; 027 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031import org.apache.isis.applib.DomainObjectContainer; 032import org.apache.isis.core.commons.config.IsisConfiguration; 033import org.apache.isis.core.commons.lang.MethodExtensions; 034 035public class ServiceInitializer { 036 037 private final static Logger LOG = LoggerFactory.getLogger(ServiceInitializer.class); 038 039 private Map<String, String> props; 040 041 static class ServiceInitMethods { 042 Object service; 043 Method postConstruct; 044 Method preDestrory; 045 } 046 047 private Map<Object, Method> postConstructMethodsByService = Maps.newLinkedHashMap(); 048 private Map<Object, Method> preDestroyMethodsByService = Maps.newLinkedHashMap(); 049 050 // ////////////////////////////////////// 051 052 public void validate(final IsisConfiguration configuration, DomainObjectContainer container, final List<Object> services) { 053 054 this.props = configuration.asMap(); 055 056 for (final Object service : services) { 057 LOG.debug("checking for @PostConstruct and @PostDestroy methods on " + service.getClass().getName()); 058 final Method[] methods = service.getClass().getMethods(); 059 060 // @PostConstruct 061 for (final Method method : methods) { 062 063 final PostConstruct postConstructAnnotation = method.getAnnotation(PostConstruct.class); 064 if(postConstructAnnotation == null) { 065 continue; 066 } 067 final Method existing = postConstructMethodsByService.get(service); 068 if(existing != null) { 069 throw new RuntimeException("Found more than one @PostConstruct method; service is: " + service.getClass().getName() + ", found " + existing.getName() + " and " + method.getName()); 070 } 071 072 final Class<?>[] parameterTypes = method.getParameterTypes(); 073 switch(parameterTypes.length) { 074 case 0: 075 break; 076 case 1: 077 if(Map.class != parameterTypes[0]) { 078 throw new RuntimeException("@PostConstruct method must be no-arg or 1-arg accepting java.util.Map; method is: " + service.getClass().getName() + "#" + method.getName()); 079 } 080 break; 081 default: 082 throw new RuntimeException("@PostConstruct method must be no-arg or 1-arg accepting java.util.Map; method is: " + service.getClass().getName() + "#" + method.getName()); 083 } 084 postConstructMethodsByService.put(service, method); 085 } 086 087 // @PreDestroy 088 for (final Method method : methods) { 089 final PreDestroy preDestroyAnnotation = method.getAnnotation(PreDestroy.class); 090 if(preDestroyAnnotation == null) { 091 continue; 092 } 093 final Method existing = preDestroyMethodsByService.get(service); 094 if(existing != null) { 095 throw new RuntimeException("Found more than one @PreDestroy method; service is: " + service.getClass().getName() + ", found " + existing.getName() + " and " + method.getName()); 096 } 097 098 final Class<?>[] parameterTypes = method.getParameterTypes(); 099 switch(parameterTypes.length) { 100 case 0: 101 break; 102 default: 103 throw new RuntimeException("@PreDestroy method must be no-arg; method is: " + service.getClass().getName() + "#" + method.getName()); 104 } 105 preDestroyMethodsByService.put(service, method); 106 } 107 } 108 109 } 110 111 // ////////////////////////////////////// 112 113 public void postConstruct() { 114 LOG.info("calling @PostConstruct on all domain services"); 115 116 for (final Map.Entry<Object, Method> entry : postConstructMethodsByService.entrySet()) { 117 final Object service = entry.getKey(); 118 final Method method = entry.getValue(); 119 120 LOG.info("... calling @PostConstruct method: " + service.getClass().getName() + ": " + method.getName()); 121 122 final int numParams = method.getParameterTypes().length; 123 124 // unlike shutdown, we don't swallow exceptions; would rather fail early 125 if(numParams == 0) { 126 MethodExtensions.invoke(method, service); 127 } else { 128 MethodExtensions.invoke(method, service, new Object[]{props}); 129 } 130 } 131 } 132 133 134 public void preDestroy() { 135 LOG.info("calling @PreDestroy on all domain services"); 136 for (final Map.Entry<Object, Method> entry : preDestroyMethodsByService.entrySet()) { 137 final Object service = entry.getKey(); 138 final Method method = entry.getValue(); 139 140 LOG.info("... calling @PreDestroy method: " + service.getClass().getName() + ": " + method.getName()); 141 142 try { 143 MethodExtensions.invoke(method, service); 144 } catch(Exception ex) { 145 // do nothing 146 LOG.warn("... @PreDestroy method threw exception - continuing anyway", ex); 147 } 148 } 149 } 150 151 152}