001: /*
002: * Copyright 2004 Outerthought bvba and Schaubroeck nv
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: package org.outerj.daisy.repository.clientimpl;
017:
018: import org.outerj.daisy.repository.*;
019: import org.outerj.daisy.repository.spi.ExtensionProvider;
020: import org.outerj.daisy.repository.user.Role;
021: import org.outerj.daisy.repository.clientimpl.schema.RemoteSchemaStrategy;
022: import org.outerj.daisy.repository.clientimpl.user.RemoteUserManagementStrategy;
023: import org.outerj.daisy.repository.clientimpl.acl.RemoteAclStrategy;
024: import org.outerj.daisy.repository.clientimpl.variant.RemoteVariantStrategy;
025: import org.outerj.daisy.repository.clientimpl.comment.RemoteCommentStrategy;
026: import org.outerj.daisy.repository.commonimpl.*;
027: import org.outerj.daisy.repository.commonimpl.schema.CommonRepositorySchema;
028: import org.outerj.daisy.jms.JmsClient;
029: import org.apache.avalon.framework.configuration.Configurable;
030: import org.apache.avalon.framework.configuration.Configuration;
031: import org.apache.avalon.framework.configuration.ConfigurationException;
032: import org.apache.avalon.framework.activity.Initializable;
033: import org.apache.avalon.framework.activity.Disposable;
034: import org.apache.avalon.framework.thread.ThreadSafe;
035: import org.apache.avalon.framework.service.Serviceable;
036: import org.apache.avalon.framework.service.ServiceManager;
037: import org.apache.avalon.framework.service.ServiceException;
038: import org.apache.avalon.framework.logger.Logger;
039: import org.apache.commons.httpclient.HttpClient;
040: import org.apache.commons.httpclient.HostConfiguration;
041: import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
042: import org.apache.commons.logging.Log;
043: import org.apache.commons.logging.LogFactory;
044:
045: import java.util.Map;
046: import java.util.Collections;
047: import java.util.HashMap;
048: import java.net.URI;
049:
050: public class RemoteRepositoryManager implements RepositoryManager,
051: Configurable, Initializable, ThreadSafe, ExtensionRegistrar,
052: Serviceable, Disposable {
053: private String baseURL;
054: private String jmsTopic;
055: private RemoteDocumentStrategy documentStrategy;
056: private CommonRepository commonRepository;
057: private Credentials cacheUserCredentials;
058: private AuthenticatedUser cacheUser;
059: private Context context = new Context();
060: private Map<String, ExtensionProvider> registeredExtensions = Collections
061: .synchronizedMap(new HashMap<String, ExtensionProvider>());
062: private JmsClient jmsClient;
063: private MultiThreadedHttpConnectionManager httpConnectionManager;
064: private HttpClient httpClient;
065: private boolean requireJms = false;
066: private HostConfiguration hostConfiguration;
067: private static final int DEFAULT_MAX_HTTP_CONNECTIONS = 60;
068: private int maxHttpConnections = DEFAULT_MAX_HTTP_CONNECTIONS;
069: private final Log logger = LogFactory.getLog(getClass());
070:
071: /**
072: * Default constructor, only to be used when respecting the Avalon lifecycle
073: * interfaces.
074: */
075: public RemoteRepositoryManager() {
076: // default constructor
077: }
078:
079: public RemoteRepositoryManager(String url,
080: Credentials cacheUserCredentials) throws Exception {
081: this (url, cacheUserCredentials, DEFAULT_MAX_HTTP_CONNECTIONS);
082: }
083:
084: public RemoteRepositoryManager(String url,
085: Credentials cacheUserCredentials, int maxHttpConnections)
086: throws Exception {
087: this (url, cacheUserCredentials, null, null, maxHttpConnections);
088: }
089:
090: /**
091: * @deprecated use the constructor without logger argument
092: */
093: public RemoteRepositoryManager(String url,
094: Credentials cacheUserCredentials, JmsClient jmsClient,
095: String jmsTopic, Logger logger) throws Exception {
096: this (url, cacheUserCredentials, jmsClient, jmsTopic,
097: DEFAULT_MAX_HTTP_CONNECTIONS);
098: }
099:
100: public RemoteRepositoryManager(String url,
101: Credentials cacheUserCredentials, JmsClient jmsClient,
102: String jmsTopic) throws Exception {
103: this (url, cacheUserCredentials, jmsClient, jmsTopic,
104: DEFAULT_MAX_HTTP_CONNECTIONS);
105: }
106:
107: /**
108: * @deprecated use the constructor without logger argument
109: */
110: public RemoteRepositoryManager(String url,
111: Credentials cacheUserCredentials, JmsClient jmsClient,
112: String jmsTopic, Logger logger, int maxHttpConnections)
113: throws Exception {
114: this (url, cacheUserCredentials, jmsClient, jmsTopic,
115: maxHttpConnections);
116: }
117:
118: public RemoteRepositoryManager(String url,
119: Credentials cacheUserCredentials, JmsClient jmsClient,
120: String jmsTopic, int maxHttpConnections) throws Exception {
121: if (url == null)
122: throw new NullPointerException("baseURL parameter missing.");
123: if (cacheUserCredentials == null)
124: throw new NullPointerException(
125: "cacheUserCredentials parameter missing.");
126:
127: this .jmsClient = jmsClient;
128: this .jmsTopic = jmsTopic;
129: this .baseURL = url;
130: this .cacheUserCredentials = cacheUserCredentials;
131: this .maxHttpConnections = maxHttpConnections;
132:
133: initialize();
134: }
135:
136: public void service(ServiceManager serviceManager)
137: throws ServiceException {
138: if (serviceManager.hasService("org.outerj.daisy.jms.JmsClient")) {
139: jmsClient = (JmsClient) serviceManager
140: .lookup("org.outerj.daisy.jms.JmsClient");
141: } else {
142: logger
143: .error("RemoteRepositoryManager: failed to lookup jms client component, will do without.");
144: }
145: }
146:
147: public void configure(Configuration configuration)
148: throws ConfigurationException {
149: this .baseURL = configuration.getChild(
150: "repository-server-base-url").getValue();
151:
152: Configuration cacheUserConf = configuration
153: .getChild("cacheUser");
154: final String cacheUserLogin = cacheUserConf
155: .getAttribute("login");
156: final String cacheUserPassword = cacheUserConf
157: .getAttribute("password");
158: cacheUserCredentials = new Credentials(cacheUserLogin,
159: cacheUserPassword);
160:
161: Configuration[] extensionConfs = configuration.getChild(
162: "extensions").getChildren("extension");
163: for (Configuration extensionConf : extensionConfs) {
164: String name = extensionConf.getAttribute("name");
165: String className = extensionConf.getAttribute("class");
166: ExtensionProvider extensionProvider;
167: try {
168: Class clazz = getClass().getClassLoader().loadClass(
169: className);
170: extensionProvider = (ExtensionProvider) clazz
171: .newInstance();
172: } catch (Exception e) {
173: throw new ConfigurationException(
174: "Problem loading repository extension " + name,
175: e);
176: }
177: registerExtension(name, extensionProvider);
178: }
179:
180: jmsTopic = configuration.getChild("jmsTopic").getValue("daisy");
181: maxHttpConnections = configuration.getChild(
182: "maxHttpConnections").getValueAsInteger(
183: DEFAULT_MAX_HTTP_CONNECTIONS);
184: requireJms = configuration.getChild("requireJms")
185: .getValueAsBoolean(false);
186: }
187:
188: public void initialize() throws Exception {
189: if (requireJms && jmsClient == null) {
190: String message = "RemoteRepositoryManager: JmsClient is not available but was configured as required.";
191: if (logger != null)
192: logger.error(message);
193: throw new Exception("");
194: } else if (jmsClient == null) {
195: logger
196: .warn("RemoteRepositoryManager: JmsClient is not available, will do without.");
197: }
198:
199: httpConnectionManager = new MultiThreadedHttpConnectionManager();
200: httpConnectionManager.getParams()
201: .setDefaultMaxConnectionsPerHost(maxHttpConnections);
202: httpConnectionManager.getParams().setMaxTotalConnections(
203: maxHttpConnections);
204: httpClient = new HttpClient(httpConnectionManager);
205: httpClient.getParams().setAuthenticationPreemptive(true);
206: httpClient.getParams().setCredentialCharset("ISO-8859-1");
207: httpClient.getParams().setContentCharset("UTF-8");
208: URI parsedBaseURL = new URI(baseURL);
209: hostConfiguration = new HostConfiguration();
210: hostConfiguration.setHost(parsedBaseURL.getHost(),
211: parsedBaseURL.getPort(), parsedBaseURL.getScheme());
212:
213: documentStrategy = new RemoteDocumentStrategy(context);
214: this .cacheUser = documentStrategy.getUser(cacheUserCredentials);
215: // If the cache user has the administrator role, put it in that role, otherwise not.
216: long[] availableRoles = this .cacheUser.getAvailableRoleIds();
217: for (long availableRole : availableRoles) {
218: if (availableRole == Role.ADMINISTRATOR) {
219: this .cacheUser
220: .setActiveRoleIds(new long[] { Role.ADMINISTRATOR });
221: break;
222: }
223: }
224:
225: RemoteRepositoryStrategy repositoryStrategy = new RemoteRepositoryStrategy(
226: context);
227: RemoteSchemaStrategy schemaStrategy = new RemoteSchemaStrategy(
228: context);
229: RemoteAclStrategy aclStrategy = new RemoteAclStrategy(context);
230: RemoteUserManagementStrategy userManagementStrategy = new RemoteUserManagementStrategy(
231: context);
232: RemoteVariantStrategy variantStrategy = new RemoteVariantStrategy(
233: context);
234: RemoteCollectionStrategy collectionStrategy = new RemoteCollectionStrategy(
235: context);
236: RemoteCommentStrategy commentStrategy = new RemoteCommentStrategy(
237: context);
238: this .commonRepository = new RemoteCommonRepository(
239: repositoryStrategy, documentStrategy, schemaStrategy,
240: aclStrategy, userManagementStrategy, variantStrategy,
241: collectionStrategy, commentStrategy, context,
242: registeredExtensions, cacheUser);
243:
244: if (jmsClient != null) {
245: RemoteEventDispatcher remoteEventDispatcher = new RemoteEventDispatcher(
246: jmsClient, jmsTopic);
247: // The RemoteEventDispatcher does not simply fire the events on all listeners registered on
248: // the Repository and RepositorySchema objects, because those listeners are by contract
249: // only informed about local events
250: remoteEventDispatcher
251: .addRepositoryListener(commonRepository
252: .getUserManager().getCacheListener());
253: remoteEventDispatcher
254: .addRepositoryListener(commonRepository
255: .getCollectionManager().getCacheListener());
256: remoteEventDispatcher
257: .addRepositoryListener(commonRepository
258: .getVariantManager().getCacheListener());
259: remoteEventDispatcher
260: .addRepositoryListener(commonRepository
261: .getNamespaceManager().getCacheListener());
262: remoteEventDispatcher
263: .addRepositorySchemaListener(commonRepository
264: .getRepositorySchema().getCacheListener());
265: }
266: }
267:
268: public void dispose() {
269: httpConnectionManager.shutdown();
270: }
271:
272: public Repository getRepository(final Credentials credentials)
273: throws RepositoryException {
274: AuthenticatedUser user = documentStrategy.getUser(credentials);
275: return new RemoteRepositoryImpl(commonRepository, user, context);
276: }
277:
278: public synchronized void registerExtension(String name,
279: ExtensionProvider extensionProvider) {
280: if (registeredExtensions.containsKey(name)) {
281: throw new RuntimeException(
282: "There is already and extension registered using the name "
283: + name);
284: }
285:
286: if (registeredExtensions.containsValue(extensionProvider)) {
287: throw new RuntimeException(
288: "The given extension provider is already registered.");
289: }
290:
291: registeredExtensions.put(name, extensionProvider);
292: }
293:
294: public synchronized void unregisterExtension(
295: ExtensionProvider extensionProvider) {
296: String key = null;
297: for (Map.Entry<String, ExtensionProvider> entry : registeredExtensions
298: .entrySet()) {
299: if (entry.getValue() == extensionProvider) {
300: key = entry.getKey();
301: break;
302: }
303: }
304: if (key != null)
305: registeredExtensions.remove(key);
306: }
307:
308: public class Context {
309: private Context() {
310: // private constructor to make sure no-one else can create this
311: }
312:
313: public CommonRepositorySchema getCommonRepositorySchema() {
314: return commonRepository.getRepositorySchema();
315: }
316:
317: public CommonRepository getCommonRepository() {
318: return commonRepository;
319: }
320:
321: public String getBaseURL() {
322: return baseURL;
323: }
324:
325: public HttpClient getSharedHttpClient() {
326: return httpClient;
327: }
328:
329: public HostConfiguration getSharedHostConfiguration() {
330: return hostConfiguration;
331: }
332: }
333:
334: public String getRepositoryServerVersion() {
335: return commonRepository.getServerVersion(cacheUser);
336: }
337: }
|