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.serverimpl;
017:
018: import org.outerj.daisy.repository.commonimpl.RepositoryStrategy;
019: import org.outerj.daisy.repository.commonimpl.AuthenticatedUser;
020: import org.outerj.daisy.repository.commonimpl.namespace.NamespaceImpl;
021: import org.outerj.daisy.repository.RepositoryRuntimeException;
022: import org.outerj.daisy.repository.RepositoryException;
023: import org.outerj.daisy.repository.RepositoryEventType;
024: import org.outerj.daisy.repository.namespace.Namespace;
025: import org.outerj.daisy.repository.namespace.NamespaceNotFoundException;
026: import org.outerj.daisy.util.VersionHelper;
027: import org.outerj.daisy.jdbcutil.JdbcHelper;
028: import org.outerx.daisy.x10.NamespaceRegisteredDocument;
029: import org.outerx.daisy.x10.NamespaceUnregisteredDocument;
030: import org.apache.xmlbeans.XmlObject;
031:
032: import java.util.*;
033: import java.util.Date;
034: import java.util.regex.Pattern;
035: import java.util.regex.Matcher;
036: import java.io.IOException;
037: import java.security.SecureRandom;
038: import java.security.NoSuchAlgorithmException;
039: import java.sql.*;
040:
041: public class LocalRepositoryStrategy implements RepositoryStrategy {
042: private SecureRandom random = null;
043: private static final String NAMESPACE_REGEXP = "[a-zA-Z0-9_]+";
044: private Pattern namespacePattern = Pattern
045: .compile(NAMESPACE_REGEXP);
046: private LocalRepositoryManager.Context context;
047: private JdbcHelper jdbcHelper;
048: private EventHelper eventHelper;
049:
050: public LocalRepositoryStrategy(
051: LocalRepositoryManager.Context context,
052: JdbcHelper jdbcHelper) {
053: this .context = context;
054: this .jdbcHelper = jdbcHelper;
055: this .eventHelper = new EventHelper(context, jdbcHelper);
056:
057: // init secure random generator
058: try {
059: random = SecureRandom.getInstance("SHA1PRNG");
060: } catch (java.security.NoSuchAlgorithmException nsae) {
061: // maybe we are on IBM's SDK
062: try {
063: random = SecureRandom.getInstance("IBMSecureRandom");
064: } catch (NoSuchAlgorithmException e) {
065: throw new RuntimeException(
066: "Error setting up LocalRepositoryStrategy secure random.",
067: e);
068: }
069: }
070: }
071:
072: public String getClientVersion(AuthenticatedUser user) {
073: // in the local implementation, client and server version are the same
074: return getServerVersion(user);
075: }
076:
077: public String getServerVersion(AuthenticatedUser user) {
078: Properties versionProps;
079: try {
080: versionProps = VersionHelper
081: .getVersionProperties(getClass().getClassLoader(),
082: "org/outerj/daisy/repository/serverimpl/versioninfo.properties");
083: } catch (IOException e) {
084: throw new RepositoryRuntimeException(
085: "Error getting version information.", e);
086: }
087: String version = VersionHelper.getVersion(versionProps);
088: if (version != null)
089: return version;
090: else
091: throw new RepositoryRuntimeException("Version unknown.");
092: }
093:
094: public NamespaceImpl registerNamespace(String namespaceName,
095: String fingerprint, AuthenticatedUser user)
096: throws RepositoryException {
097: if (!user.isInAdministratorRole())
098: throw new RepositoryException(
099: "Only users in Administrator role can register new namespaces.");
100:
101: if (namespaceName == null || namespaceName.length() == 0)
102: throw new IllegalArgumentException(
103: "Namespace cannot be null or empty string.");
104:
105: if (namespaceName.length() > 200)
106: throw new RepositoryException(
107: "A namespace name should not be longer than 200 characters.");
108:
109: if (fingerprint == null || fingerprint.length() == 0)
110: throw new IllegalArgumentException(
111: "Namespace fingerprint cannot be null or empty string.");
112:
113: if (fingerprint.length() > 255)
114: throw new RepositoryException(
115: "A namespace fingerprint should not be longer than 255 characters.");
116:
117: Matcher matcher = namespacePattern.matcher(namespaceName);
118: if (!matcher.matches())
119: throw new IllegalArgumentException(
120: "Namespace contains illegal characters: \""
121: + namespaceName
122: + "\". It should confirm to this regexp: "
123: + NAMESPACE_REGEXP);
124:
125: long id;
126: Date registeredOn = new Date();
127: NamespaceImpl namespace;
128: Connection conn = null;
129: PreparedStatement stmt = null;
130: try {
131: conn = context.getDataSource().getConnection();
132: jdbcHelper.startTransaction(conn);
133:
134: stmt = conn
135: .prepareStatement("insert into daisy_namespaces(id,name_,fingerprint,registered_by,registered_on) values(?,?,?,?,?)");
136: id = context.getNextNamespaceId();
137: stmt.setLong(1, id);
138: stmt.setString(2, namespaceName);
139: stmt.setString(3, fingerprint);
140: stmt.setLong(4, user.getId());
141: stmt.setTimestamp(5, new Timestamp(registeredOn.getTime()));
142: stmt.execute();
143:
144: namespace = new NamespaceImpl(id, namespaceName,
145: fingerprint, user.getId(), registeredOn);
146: XmlObject eventDescription = createRegisterNamespaceEvent(namespace);
147: eventHelper.createEvent(eventDescription,
148: "NamespaceRegistered", conn);
149:
150: conn.commit();
151: } catch (Throwable e) {
152: jdbcHelper.rollback(conn);
153: throw new RepositoryException(
154: "Error registering namespace.", e);
155: } finally {
156: jdbcHelper.closeStatement(stmt);
157: jdbcHelper.closeConnection(conn);
158: }
159:
160: // notify creation of namespace through synchronous event
161: context.getCommonRepository().fireRepositoryEvent(
162: RepositoryEventType.NAMESPACE_REGISTERED, new Long(id),
163: 0);
164:
165: return namespace;
166: }
167:
168: private NamespaceRegisteredDocument createRegisterNamespaceEvent(
169: Namespace namespace) {
170: NamespaceRegisteredDocument registeredDoc = NamespaceRegisteredDocument.Factory
171: .newInstance();
172: NamespaceRegisteredDocument.NamespaceRegistered registeredXml = registeredDoc
173: .addNewNamespaceRegistered();
174: registeredXml.addNewRegisteredNamespace().setNamespace(
175: namespace.getXml().getNamespace());
176: return registeredDoc;
177: }
178:
179: public NamespaceImpl registerNamespace(String namespace,
180: AuthenticatedUser user) throws RepositoryException {
181: // generate a fingerprint
182: int KEYLENGTH = 30;
183: byte[] bytes = new byte[KEYLENGTH];
184: char[] result = new char[KEYLENGTH * 2];
185: random.nextBytes(bytes);
186:
187: for (int i = 0; i < KEYLENGTH; i++) {
188: byte ch = bytes[i];
189: result[2 * i] = Character.forDigit(Math.abs(ch >> 4), 16);
190: result[2 * i + 1] = Character.forDigit(Math.abs(ch & 0x0f),
191: 16);
192: }
193:
194: String fingerprint = new String(result);
195: return registerNamespace(namespace, fingerprint, user);
196: }
197:
198: public NamespaceImpl unregisterNamespace(long id,
199: AuthenticatedUser user) throws RepositoryException {
200: return unregisterNamespace(null, id, user);
201: }
202:
203: public NamespaceImpl unregisterNamespace(String namespaceName,
204: AuthenticatedUser user) throws RepositoryException {
205: if (namespaceName == null)
206: throw new IllegalArgumentException(
207: "namespaceName argument cannot be null.");
208:
209: return unregisterNamespace(namespaceName, -1, user);
210: }
211:
212: public NamespaceImpl unregisterNamespace(String namespaceName,
213: long namespaceId, AuthenticatedUser user)
214: throws RepositoryException {
215: if (!user.isInAdministratorRole())
216: throw new RepositoryException(
217: "Only users in Administrator role can unregister a namespaces.");
218:
219: // Note: foreign key constraints on the database assure a namespace cannot be removed
220: // if it is still referenced.
221: Connection conn = null;
222: PreparedStatement stmt = null;
223: NamespaceImpl namespace;
224: try {
225: conn = context.getDataSource().getConnection();
226: jdbcHelper.startTransaction(conn);
227:
228: // load namespace to use in JMS event
229: if (namespaceName != null)
230: namespace = loadNamespaceByName(conn, namespaceName,
231: true);
232: else
233: namespace = loadNamespaceById(conn, namespaceId, true);
234:
235: if (namespace.getName().equals(
236: context.getRepositoryNamespace()))
237: throw new RepositoryException(
238: "The repository's own namespace cannot be unregistered.");
239:
240: if (namespaceName != null) {
241: stmt = conn
242: .prepareStatement("delete from daisy_namespaces where name_ = ?");
243: stmt.setString(1, namespaceName);
244: } else {
245: stmt = conn
246: .prepareStatement("delete from daisy_namespaces where id = ?");
247: stmt.setLong(1, namespaceId);
248: }
249: int updateCount = stmt.executeUpdate();
250:
251: if (updateCount != 1)
252: throw new RepositoryException(
253: "Unexpected update count: " + updateCount);
254:
255: XmlObject eventDescription = createUnregisterNamespaceEvent(
256: namespace, user.getId(), new Date());
257: eventHelper.createEvent(eventDescription,
258: "NamespaceUnregistered", conn);
259:
260: conn.commit();
261: } catch (Throwable e) {
262: jdbcHelper.rollback(conn);
263: String identifier = namespaceName != null ? namespaceName
264: : String.valueOf(namespaceId);
265: throw new RepositoryRuntimeException(
266: "Error unregistering namespace \"" + identifier
267: + "\".", e);
268: } finally {
269: jdbcHelper.closeStatement(stmt);
270: jdbcHelper.closeConnection(conn);
271: }
272:
273: // notify creation of namespace through synchronous event
274: context.getCommonRepository().fireRepositoryEvent(
275: RepositoryEventType.NAMESPACE_UNREGISTERED,
276: new Long(namespace.getId()), 0);
277:
278: return namespace;
279: }
280:
281: private NamespaceUnregisteredDocument createUnregisterNamespaceEvent(
282: Namespace namespace, long unregisteredBy,
283: Date unregisteredOn) {
284: NamespaceUnregisteredDocument unregisteredDoc = NamespaceUnregisteredDocument.Factory
285: .newInstance();
286: NamespaceUnregisteredDocument.NamespaceUnregistered unregisteredXml = unregisteredDoc
287: .addNewNamespaceUnregistered();
288:
289: unregisteredXml.addNewUnregisteredNamespace().setNamespace(
290: namespace.getXml().getNamespace());
291: unregisteredXml.setUnregistrarId(unregisteredBy);
292: GregorianCalendar calendar = new GregorianCalendar();
293: calendar.setTime(unregisteredOn);
294: unregisteredXml.setUnregisterTime(calendar);
295:
296: return unregisteredDoc;
297: }
298:
299: private NamespaceImpl loadNamespaceByName(Connection conn,
300: String name, boolean lock) throws RepositoryException {
301: PreparedStatement stmt = null;
302: try {
303: conn = context.getDataSource().getConnection();
304: stmt = conn
305: .prepareStatement("select id, name_, fingerprint, registered_by, registered_on from daisy_namespaces where name_ = ? "
306: + (lock ? jdbcHelper.getSharedLockClause()
307: : ""));
308: stmt.setString(1, name);
309: ResultSet rs = stmt.executeQuery();
310: if (rs.next()) {
311: // namespace names are case sensitive, but SQL isn't, so do an additional check
312: if (!name.equals(rs.getString("name_")))
313: throw new NamespaceNotFoundException(name);
314: return getNamespaceFromResultSet(rs);
315: } else {
316: throw new NamespaceNotFoundException(name);
317: }
318: } catch (NamespaceNotFoundException e) {
319: throw e;
320: } catch (Throwable e) {
321: throw new RepositoryException("Error loading namespace "
322: + name, e);
323: } finally {
324: jdbcHelper.closeStatement(stmt);
325: }
326: }
327:
328: private NamespaceImpl loadNamespaceById(Connection conn, long id,
329: boolean lock) throws RepositoryException {
330: PreparedStatement stmt = null;
331: try {
332: conn = context.getDataSource().getConnection();
333: stmt = conn
334: .prepareStatement("select id, name_, fingerprint, registered_by, registered_on from daisy_namespaces where id = ? "
335: + (lock ? jdbcHelper.getSharedLockClause()
336: : ""));
337: stmt.setLong(1, id);
338: ResultSet rs = stmt.executeQuery();
339: if (rs.next()) {
340: return getNamespaceFromResultSet(rs);
341: } else {
342: throw new NamespaceNotFoundException(id);
343: }
344: } catch (NamespaceNotFoundException e) {
345: throw e;
346: } catch (Throwable e) {
347: throw new RepositoryException("Error loading namespace "
348: + id, e);
349: } finally {
350: jdbcHelper.closeStatement(stmt);
351: }
352: }
353:
354: private NamespaceImpl getNamespaceFromResultSet(ResultSet rs)
355: throws SQLException {
356: long id = rs.getLong(1);
357: String name = rs.getString(2);
358: String fingerprint = rs.getString(3);
359: long registeredBy = rs.getLong(4);
360: Date registeredOn = rs.getTimestamp(5);
361: return new NamespaceImpl(id, name, fingerprint, registeredBy,
362: registeredOn);
363: }
364:
365: public Namespace[] getAllNamespaces(AuthenticatedUser user) {
366: List<Namespace> namespaces = new ArrayList<Namespace>();
367: Connection conn = null;
368: PreparedStatement stmt = null;
369: try {
370: conn = context.getDataSource().getConnection();
371: stmt = conn
372: .prepareStatement("select id, name_, fingerprint, registered_by, registered_on from daisy_namespaces");
373: ResultSet rs = stmt.executeQuery();
374: while (rs.next()) {
375: namespaces.add(getNamespaceFromResultSet(rs));
376: }
377: } catch (Throwable e) {
378: throw new RepositoryRuntimeException(
379: "Error loading namespaces.", e);
380: } finally {
381: jdbcHelper.closeStatement(stmt);
382: jdbcHelper.closeConnection(conn);
383: }
384: return namespaces.toArray(new Namespace[namespaces.size()]);
385: }
386:
387: public Namespace getNamespaceByName(String namespaceName,
388: AuthenticatedUser user) throws RepositoryException {
389: Connection conn = null;
390: try {
391: conn = context.getDataSource().getConnection();
392: return loadNamespaceByName(conn, namespaceName, false);
393: } catch (NamespaceNotFoundException e) {
394: throw e;
395: } catch (Throwable e) {
396: throw new RepositoryException("Error loading namespace \""
397: + namespaceName + "\".", e);
398: } finally {
399: jdbcHelper.closeConnection(conn);
400: }
401: }
402:
403: public String getRepositoryNamespace(AuthenticatedUser user) {
404: return context.getRepositoryNamespace();
405: }
406: }
|