1 /*
2 * Copyright 2004-2006 Stefan Reuter
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17 package org.asteriskjava.fastagi;
18
19 import java.util.*;
20
21 /**
22 * A MappingStrategy that is configured via a resource bundle.<p>
23 * The resource bundle contains the script part of the url as key and the fully
24 * qualified class name of the corresponding AgiScript as value.<p>
25 * Example:
26 *
27 * <pre>
28 * leastcostdial.agi = com.example.fastagi.LeastCostDialAgiScript
29 * hello.agi = com.example.fastagi.HelloAgiScript
30 * </pre>
31 *
32 * LeastCostDialAgiScript and HelloAgiScript must both implement the AgiScript
33 * interface and have a default constructor with no parameters.<p>
34 * The resource bundle (properties) file is called
35 * <code>fastagi-mapping.properties</code> by default and must be available on
36 * the classpath.
37 *
38 * @author srt
39 * @version $Id$
40 */
41 public class ResourceBundleMappingStrategy extends AbstractMappingStrategy
42 {
43 private static final String DEFAULT_RESOURCE_BUNDLE_NAME = "fastagi-mapping";
44 private String resourceBundleName;
45 private Map<String, String> mappings;
46 private Map<String, AgiScript> instances;
47 private boolean shareInstances;
48
49 /**
50 * Creates a new ResourceBundleMappingStrategy using shared instances..
51 */
52 public ResourceBundleMappingStrategy()
53 {
54 this(DEFAULT_RESOURCE_BUNDLE_NAME);
55 }
56
57 /**
58 * Creates a new ResourceBundleMappingStrategy with the given basename
59 * of the resource bundle to use.
60 *
61 * @param resourceBundleName basename of the resource bundle to use
62 */
63 public ResourceBundleMappingStrategy(String resourceBundleName)
64 {
65 this(resourceBundleName, true);
66 }
67
68 /**
69 * Creates a new ResourceBundleMappingStrategy indicating whether to use shared
70 * instances or not.
71 *
72 * @param shareInstances <code>true</code> to use shared instances,
73 * <code>false</code> to create a new instance for
74 * each request.
75 * @since 0.3
76 */
77 public ResourceBundleMappingStrategy(boolean shareInstances)
78 {
79 this(DEFAULT_RESOURCE_BUNDLE_NAME, shareInstances);
80 }
81
82 /**
83 * Creates a new ResourceBundleMappingStrategy with the given basename
84 * of the resource bundle to use and indicating whether to use shared
85 * instances or not.
86 *
87 * @param resourceBundleName basename of the resource bundle to use
88 * @param shareInstances <code>true</code> to use shared instances,
89 * <code>false</code> to create a new instance for
90 * each request.
91 * @since 0.3
92 */
93 public ResourceBundleMappingStrategy(String resourceBundleName, boolean shareInstances)
94 {
95 super();
96 this.resourceBundleName = resourceBundleName;
97 this.shareInstances = shareInstances;
98 }
99
100 /**
101 * Sets the basename of the resource bundle to use.<p>
102 * Default is "fastagi-mapping".
103 *
104 * @param resourceBundleName basename of the resource bundle to use
105 */
106 public void setResourceBundleName(String resourceBundleName)
107 {
108 this.resourceBundleName = resourceBundleName;
109 synchronized (this)
110 {
111 this.mappings = null;
112 this.instances = null;
113 }
114 }
115
116 /**
117 * Sets whether to use shared instances or not. If set to <code>true</code>
118 * all AgiRequests are served by the same instance of an
119 * AgiScript, if set to <code>false</code> a new instance is created for
120 * each request.<p>
121 * Default is <code>true</code>.
122 *
123 * @param shareInstances <code>true</code> to use shared instances,
124 * <code>false</code> to create a new instance for
125 * each request.
126 * @since 0.3
127 */
128 public synchronized void setShareInstances(boolean shareInstances)
129 {
130 this.shareInstances = shareInstances;
131 }
132
133 private synchronized void loadResourceBundle()
134 {
135 ResourceBundle resourceBundle;
136 Enumeration<?> keys;
137
138 mappings = new HashMap<String, String>();
139 if (shareInstances)
140 {
141 instances = new HashMap<String, AgiScript>();
142 }
143
144 try
145 {
146 resourceBundle = ResourceBundle.getBundle(resourceBundleName, Locale.getDefault(), getClassLoader());
147 }
148 catch (MissingResourceException e)
149 {
150 logger.info("Resource bundle '" + resourceBundleName + "' not found.");
151 return;
152 }
153
154 keys = resourceBundle.getKeys();
155
156 while (keys.hasMoreElements())
157 {
158 String scriptName;
159 String className;
160 AgiScript agiScript;
161
162 scriptName = (String) keys.nextElement();
163 className = resourceBundle.getString(scriptName);
164
165 mappings.put(scriptName, className);
166
167 if (shareInstances)
168 {
169 agiScript = createAgiScriptInstance(className);
170 if (agiScript == null)
171 {
172 continue;
173 }
174 instances.put(scriptName, agiScript);
175 }
176
177 logger.info("Added mapping for '" + scriptName + "' to class " + className);
178 }
179 }
180
181 public synchronized AgiScript determineScript(AgiRequest request)
182 {
183 if (mappings == null || (shareInstances && instances == null))
184 {
185 loadResourceBundle();
186 }
187
188 if (shareInstances)
189 {
190 return instances.get(request.getScript());
191 }
192 else
193 {
194 return createAgiScriptInstance(mappings.get(request.getScript()));
195 }
196 }
197 }