1 /*
2 * The SmartWeb Framework
3 * Copyright (C) 2004-2006
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * For further informations on the SmartWeb Framework please visit
20 *
21 * http://smartweb.sourceforge.net
22 */
23 package net.smartlab.web;
24
25 import java.net.URL;
26
27 import net.smartlab.config.Configuration;
28 import net.smartlab.config.ConfigurationException;
29 import net.smartlab.web.config.DomainConfigurationStrategy;
30 import net.smartlab.web.config.FileDomainConfigurationStrategy;
31
32 import org.apache.commons.logging.Log;
33 import org.apache.commons.logging.LogFactory;
34 import org.hibernate.HibernateException;
35 import org.hibernate.StaleObjectStateException;
36 import org.hibernate.Transaction;
37
38 /**
39 * @author rlogiacco
40 * @uml.dependency supplier="net.smartlab.web.config.DomainConfigurationStrategy"
41 * @uml.dependency supplier="net.smartlab.web.BusinessException"
42 * @uml.dependency supplier="net.smartlab.web.BusinessObjectFactory"
43 */
44 public abstract class Domain implements ManageableDomain {
45
46 /**
47 * Provides the execution context through which additional informations can
48 * be transparently transmitted in a multiple tier architecture.
49 */
50 final static ThreadLocal context = new ThreadLocal();
51
52 /**
53 * Provides logging capabilities to the domain.
54 */
55 protected final Log logger = LogFactory.getLog(this.getClass());
56
57 /**
58 * The internal configuration.
59 */
60 private Configuration config;
61
62 /**
63 * Strategy to retrieve the configuration file.
64 */
65 private static DomainConfigurationStrategy strategy = new FileDomainConfigurationStrategy();
66 static {
67 try {
68 String strategy = System.getProperty("smartweb.domain.strategy");
69 if (strategy != null) {
70 Domain.strategy = (DomainConfigurationStrategy)Class.forName(strategy).newInstance();
71 } else {
72 LogFactory.getLog(Domain.class).warn("No configuration found: falling back to default configuration");
73 }
74 } catch (Exception e) {
75 LogFactory.getLog(Domain.class).fatal("Error configuring SmartWeb", e);
76 }
77 }
78
79
80 /**
81 * Starts a new transaction, allowing all the operations performed in the
82 * same context to be atomically applied (commit) or reverted (rollback).
83 * The context is identifyied by a <code>BusinessObjectFactory</code>
84 * instance but the context spans over all the instances sharing the same
85 * configuration.
86 *
87 * @param factory the identifier for the context
88 * @return an object representation of the transaction.
89 * @throws BusinessException if something wrong occurs during the operation.
90 */
91 protected Transaction begin(BusinessObjectFactory factory) throws BusinessException {
92 try {
93 return factory.current().beginTransaction();
94 } catch (HibernateException he) {
95 logger.error("[ smartweb ] failed to begin transaction");
96 throw new BusinessException("persistence.error.begin", he);
97 } catch (DAOException boe) {
98 throw new BusinessException(boe);
99 }
100 }
101
102 /**
103 * Commits the specified transaction applying all the changes happened since
104 * its beginning.
105 *
106 * @param transaction the transaction to commit.
107 * @throws BusinessException if something wrong occurs during the operation.
108 */
109 protected void commit(Transaction transaction) throws BusinessException {
110 try {
111 transaction.commit();
112 } catch (StaleObjectStateException sose) {
113 logger.info("[ smartweb ] optimistical locking collision");
114 throw new BusinessException("persistence.locking.collision", sose);
115 } catch (HibernateException he) {
116 logger.error("[ smartweb ] failed to commit transaction");
117 throw new BusinessException("persistence.error.commit", he);
118 }
119 }
120
121 /**
122 * Rollbacks the specified transaction reverting all the changes happened
123 * since its beginning.
124 *
125 * @param transaction the transaction to rollback.
126 * @throws BusinessException if something wrong occurs during the operation.
127 */
128 protected void rollback(Transaction transaction) throws BusinessException {
129 try {
130 transaction.rollback();
131 } catch (HibernateException he) {
132 logger.warn("[ smartweb ] failed to rollback transaction");
133 }
134 }
135
136 /**
137 * Changes the strategy used to configure the domain.
138 *
139 * @param strategy an implementation of the
140 * <code>DomainConfigurationStrategy</code> interface.
141 */
142 public static void setConfigurationStrategy(DomainConfigurationStrategy strategy) {
143 Domain.strategy = strategy;
144 }
145
146 /**
147 * Subclasses should make their own constructors private and behave like
148 * singletons.
149 */
150 protected Domain() {
151 if (logger.isDebugEnabled()) {
152 logger.debug(this.getClass().getName() + " instantiated.");
153 }
154 }
155
156 /**
157 * Returns the <code>Configuration</code> instance used by this instance.
158 *
159 * @return the <code>Configuration</code> instance used by this instance.
160 * @throws ConfigurationException if something wrong occurs while reading
161 * the configuration file, usually meaning the configuration file is
162 * missing.
163 */
164 public Configuration getConfiguration() throws ConfigurationException {
165 if (config == null) {
166 this.config = strategy.getConfiguration(this);
167 }
168 return config;
169 }
170
171 /**
172 * Returns the <code>Configuration</code> instance used by this instance.
173 *
174 * @param filename the configuration file name.
175 * @return the <code>Configuration</code> instance used by this instance.
176 * @throws ConfigurationException if something wrong occurs while reading
177 * the configuration file, usually meaning the configuration file is
178 * missing.
179 */
180 public Configuration getConfiguration(String filename) throws ConfigurationException {
181 if (config == null) {
182 config = strategy.getConfiguration(this, filename);
183 }
184 return config;
185 }
186
187 /**
188 * Returns the first resource in the list available in the specified
189 * context.
190 *
191 * @param context the class identifying the search context.
192 * @param names the list, ordered from first to last, of resources to search
193 * for.
194 * @return the first resource in the list available in the context.
195 */
196 public static URL getResource(Class context, String[] names) {
197 Log logger = LogFactory.getLog(context);
198 URL resource = null;
199 for (int i = 0; i < names.length; i++) {
200 resource = context.getResource(names[i]);
201 logger.trace(" trying `" + names[i] + "`");
202 if (resource != null) {
203 break;
204 }
205 }
206 logger.debug(" resource is `" + resource + "`");
207 if (resource == null) {
208 logger.warn("No resource found in " + names);
209 }
210 return resource;
211 }
212
213 /**
214 * Returns the name of the last archive containing the class or an
215 * <code>empty</code> string if the class is in no archive.
216 *
217 * @param type the class to search for.
218 * @return the name of the last archive containing the class or an
219 * <code>empty</code> string if the class is in no archive.
220 */
221 public static String getLastArchiveName(Class type) {
222 Log logger = LogFactory.getLog(type);
223 String path = type.getProtectionDomain().getCodeSource().getLocation().getFile();
224 if (path.indexOf("/WEB-INF/classes") > -1) {
225 // non jar packaged deploy, we are packaged as war classes
226 path.substring(0, path.indexOf("/WEB-INF/classes"));
227 }
228 logger.debug(" archive path is `" + path + "`");
229 return path.substring(path.lastIndexOf('/') + 1);
230 }
231 }