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 }