001 /* 002 * Copyright 2003-2004 The Apache Software Foundation. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.apache.commons.mapper; 018 019 import java.util.ArrayList; 020 import java.util.HashMap; 021 import java.util.Iterator; 022 import java.util.List; 023 import java.util.Map; 024 025 import org.apache.commons.mapper.util.ObjectFactory; 026 027 /** 028 * <code>MapperFactory</code> is responsible for creating mappers based on 029 * domain class names or any other String name. This allows a domain class to 030 * be a value bean and delegate data storage responsibility to a specific 031 * <code>Mapper</code> subclass. 032 * <p> 033 * <code>MapperFactory.getMapper()</code> method uses caching to prevent 034 * creating mappers multiple times. This means that a Mapper returned from 035 * <code>getMapper()</code> will not be a new instance which could cause 036 * problems in a multi-threaded environment (the servlet container environment 037 * is multi-threaded). Mappers returned by the <code>MapperFactory</code> 038 * should not use instance variables to maintain state, rather, they should 039 * use local method variables so they are thread-safe. 040 * <p> 041 * Note that you do not need to subclass <code>MapperFactory</code> to specify 042 * the specific <code>Mapper</code> implementation classes to return from 043 * <code>getMapper()</code>. Because <code>Mapper</code> implementation 044 * classes are defined in a <code>Map</code>, you can have any combination of 045 * <code>Mapper</code> types in use at the same time. For example, you could 046 * have EjbPersonMapper using an EJB to persist Person objects and a 047 * JdbcOrderMapper using JDBC calls to persist Order objects. You can register 048 * <code>MapperFactoryListener</code> implementations with the factory to 049 * initialize the various types of Mappers when they are created. 050 * 051 * @see Mapper 052 * @see MapperFactoryListener 053 */ 054 public class MapperFactory { 055 056 /** 057 * This factory actually creates the mappers. 058 */ 059 protected ObjectFactory factory = null; 060 061 /** 062 * Holds mapper instances that have already been created. The key is a 063 * String name which is often a domain class' name and the value is a 064 * <code>Mapper</code> instance. 065 */ 066 private Map mapperCache = new HashMap(); 067 068 /** 069 * The List of listeners to notify when a Mapper is created. 070 */ 071 private List listeners = new ArrayList(); 072 073 /** 074 * Create a new MapperFactory with the mappings contained in the given Map. 075 * @param map Any map containing logical names (often domain class names) 076 * as the keys and mapper class names as the values. 077 * @throws IllegalArgumentException if there is a problem finding the 078 * mapped classes. 079 */ 080 public MapperFactory(Map map) { 081 super(); 082 this.factory = new ObjectFactory(map); 083 } 084 085 /** 086 * Factory method for getting the mapper associated with the given class. 087 * @param mappedClass The class whose mapper should be returned 088 * @return Mapper the mapper associated with the given class 089 * @throws IllegalArgumentException if the given Class was not found in 090 * the map or there was an error creating an instance of the Mapper. This 091 * is usually caused by a missing public no-arg constructor. 092 */ 093 public Mapper getMapper(Class mappedClass) { 094 return this.getMapper(mappedClass.getName()); 095 } 096 097 /** 098 * Factory method for getting the mapper associated with the given class. 099 * @param name The name of the mapper to be returned. 100 * @return Mapper the mapper associated with the given name. 101 * @throws IllegalArgumentException if the given name was not found in 102 * the map or there was an error creating an instance of the Mapper. This 103 * is usually caused by a missing public no-arg constructor. 104 */ 105 public Mapper getMapper(String name) { 106 107 synchronized (this.mapperCache) { 108 Mapper m = (Mapper) this.mapperCache.get(name); 109 if (m == null) { 110 m = (Mapper) this.factory.create(name); 111 this.fireMapperCreated(m); 112 this.mapperCache.put(name, m); 113 } 114 115 return m; 116 } 117 } 118 119 /** 120 * Register a listener with this MapperFactory to receive notifications 121 * of events. 122 * @param listener 123 */ 124 public void addMapperFactoryListener(MapperFactoryListener listener) { 125 synchronized (this.listeners) { 126 this.listeners.add(listener); 127 } 128 } 129 130 /** 131 * Unregister a listener from this MapperFactory. 132 * @param listener 133 */ 134 public void removeMapperFactoryListener(MapperFactoryListener listener) { 135 synchronized (this.listeners) { 136 this.listeners.remove(listener); 137 } 138 } 139 140 /** 141 * Call the mapperCreated() method of all registered listeners. 142 * @param created The Mapper that was just created and should be passed to 143 * the listeners for initialization. 144 */ 145 private void fireMapperCreated(Mapper created) { 146 MapperFactoryEvent event = new MapperFactoryEvent(this, created); 147 148 synchronized (this.listeners) { 149 Iterator iter = this.listeners.iterator(); 150 while (iter.hasNext()) { 151 MapperFactoryListener listener = (MapperFactoryListener) iter.next(); 152 listener.mapperCreated(event); 153 } 154 } 155 156 } 157 158 }