001: /*
002: * MetadataSchema.java
003: *
004: * Version: $Revision: 1523 $
005: *
006: * Date: $Date: 2006-05-26 09:23:17 -0500 (Fri, 26 May 2006) $
007: *
008: * Copyright (c) 2002-2005, Hewlett-Packard Company and Massachusetts
009: * Institute of Technology. All rights reserved.
010: *
011: * Redistribution and use in source and binary forms, with or without
012: * modification, are permitted provided that the following conditions are
013: * met:
014: *
015: * - Redistributions of source code must retain the above copyright
016: * notice, this list of conditions and the following disclaimer.
017: *
018: * - Redistributions in binary form must reproduce the above copyright
019: * notice, this list of conditions and the following disclaimer in the
020: * documentation and/or other materials provided with the distribution.
021: *
022: * - Neither the name of the Hewlett-Packard Company nor the name of the
023: * Massachusetts Institute of Technology nor the names of their
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
030: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
032: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
033: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
034: * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
035: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
036: * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
037: * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
038: * DAMAGE.
039: */
040: package org.dspace.content;
041:
042: import java.io.IOException;
043: import java.sql.Connection;
044: import java.sql.PreparedStatement;
045: import java.sql.ResultSet;
046: import java.sql.SQLException;
047: import java.util.HashMap;
048: import java.util.ArrayList;
049: import java.util.List;
050:
051: import org.apache.log4j.Logger;
052: import org.dspace.authorize.AuthorizeException;
053: import org.dspace.authorize.AuthorizeManager;
054: import org.dspace.core.Context;
055: import org.dspace.core.LogManager;
056: import org.dspace.storage.rdbms.DatabaseManager;
057: import org.dspace.storage.rdbms.TableRow;
058: import org.dspace.storage.rdbms.TableRowIterator;
059:
060: /**
061: * Class representing a schema in DSpace.
062: * <p>
063: * The schema object exposes a name which can later be used to generate
064: * namespace prefixes in RDF or XML, e.g. the core DSpace Dublin Core schema
065: * would have a name of <code>'dc'</code>.
066: * </p>
067: *
068: * @author Martin Hald
069: * @version $Revision: 1523 $
070: * @see org.dspace.content.MetadataValue, org.dspace.content.MetadataField
071: */
072: public class MetadataSchema {
073: /** log4j logger */
074: private static Logger log = Logger.getLogger(MetadataSchema.class);
075:
076: /** Numeric Identifier of built-in Dublin Core schema. */
077: public static final int DC_SCHEMA_ID = 1;
078:
079: /** Short Name of built-in Dublin Core schema. */
080: public static final String DC_SCHEMA = "dc";
081:
082: /** The row in the table representing this type */
083: private TableRow row;
084:
085: private int schemaID;
086: private String namespace;
087: private String name;
088:
089: // cache of schema by ID (Integer)
090: private static HashMap id2schema = null;
091:
092: // cache of schema by short name
093: private static HashMap name2schema = null;
094:
095: /**
096: * Default constructor.
097: */
098: public MetadataSchema() {
099: }
100:
101: /**
102: * Object constructor.
103: *
104: * @param schemaID database key ID number
105: * @param namespace XML namespace URI
106: * @param name short name of schema
107: */
108: public MetadataSchema(int schemaID, String namespace, String name) {
109: this .schemaID = schemaID;
110: this .namespace = namespace;
111: this .name = name;
112: }
113:
114: /**
115: * Immutable object constructor for creating a new schema.
116: *
117: * @param namespace XML namespace URI
118: * @param name short name of schema
119: */
120: public MetadataSchema(String namespace, String name) {
121: this .namespace = namespace;
122: this .name = name;
123: }
124:
125: /**
126: * Constructor for loading the metadata schema from the database.
127: *
128: * @param row table row object from which to populate this schema.
129: */
130: public MetadataSchema(TableRow row) {
131: if (row != null) {
132: this .schemaID = row.getIntColumn("metadata_schema_id");
133: this .namespace = row.getStringColumn("namespace");
134: this .name = row.getStringColumn("short_id");
135: this .row = row;
136: }
137: }
138:
139: /**
140: * Get the schema namespace.
141: *
142: * @return namespace String
143: */
144: public String getNamespace() {
145: return namespace;
146: }
147:
148: /**
149: * Set the schema namespace.
150: *
151: * @param namespace XML namespace URI
152: */
153: public void setNamespace(String namespace) {
154: this .namespace = namespace;
155: }
156:
157: /**
158: * Get the schema name.
159: *
160: * @return name String
161: */
162: public String getName() {
163: return name;
164: }
165:
166: /**
167: * Set the schema name.
168: *
169: * @param name short name of schema
170: */
171: public void setName(String name) {
172: this .name = name;
173: }
174:
175: /**
176: * Get the schema record key number.
177: *
178: * @return schema record key
179: */
180: public int getSchemaID() {
181: return schemaID;
182: }
183:
184: /**
185: * Creates a new metadata schema in the database, out of this object.
186: *
187: * @param context
188: * DSpace context object
189: * @throws SQLException
190: * @throws AuthorizeException
191: * @throws NonUniqueMetadataException
192: */
193: public void create(Context context) throws SQLException,
194: AuthorizeException, NonUniqueMetadataException {
195: // Check authorisation: Only admins may create metadata schemas
196: if (!AuthorizeManager.isAdmin(context)) {
197: throw new AuthorizeException(
198: "Only administrators may modify the metadata registry");
199: }
200:
201: // Ensure the schema name is unique
202: if (!uniqueShortName(context, name)) {
203: throw new NonUniqueMetadataException(
204: "Please make the name " + name + " unique");
205: }
206:
207: // Ensure the schema namespace is unique
208: if (!uniqueNamespace(context, namespace)) {
209: throw new NonUniqueMetadataException(
210: "Please make the namespace " + namespace
211: + " unique");
212: }
213:
214: // Create a table row and update it with the values
215: row = DatabaseManager.create(context, "MetadataSchemaRegistry");
216: row.setColumn("namespace", namespace);
217: row.setColumn("short_id", name);
218: DatabaseManager.update(context, row);
219:
220: // invalidate our fast-find cache.
221: decache();
222:
223: // Remember the new row number
224: this .schemaID = row.getIntColumn("metadata_schema_id");
225:
226: log.info(LogManager.getHeader(context,
227: "create_metadata_schema", "metadata_schema_id="
228: + row.getIntColumn("metadata_schema_id")));
229: }
230:
231: /**
232: * Get the schema object corresponding to this namespace URI.
233: *
234: * @param context DSpace context
235: * @param namespace namespace URI to match
236: * @return metadata schema object or null if none found.
237: * @throws SQLException
238: */
239: public static MetadataSchema findByNamespace(Context context,
240: String namespace) throws SQLException {
241: // Grab rows from DB
242: TableRowIterator tri = DatabaseManager
243: .queryTable(
244: context,
245: "MetadataSchemaRegistry",
246: "SELECT * FROM MetadataSchemaRegistry WHERE namespace= ? ",
247: namespace);
248:
249: TableRow row = null;
250: if (tri.hasNext()) {
251: row = tri.next();
252: }
253:
254: // close the TableRowIterator to free up resources
255: tri.close();
256:
257: if (row == null) {
258: return null;
259: } else {
260: return new MetadataSchema(row);
261: }
262: }
263:
264: /**
265: * Update the metadata schema in the database.
266: *
267: * @param context DSpace context
268: * @throws SQLException
269: * @throws AuthorizeException
270: * @throws NonUniqueMetadataException
271: */
272: public void update(Context context) throws SQLException,
273: AuthorizeException, NonUniqueMetadataException {
274: // Check authorisation: Only admins may update the metadata registry
275: if (!AuthorizeManager.isAdmin(context)) {
276: throw new AuthorizeException(
277: "Only administrators may modify the metadata registry");
278: }
279:
280: // Ensure the schema name is unique
281: if (!uniqueShortName(context, name)) {
282: throw new NonUniqueMetadataException(
283: "Please make the name " + name + " unique");
284: }
285:
286: // Ensure the schema namespace is unique
287: if (!uniqueNamespace(context, namespace)) {
288: throw new NonUniqueMetadataException(
289: "Please make the namespace " + namespace
290: + " unique");
291: }
292:
293: row.setColumn("namespace", getNamespace());
294: row.setColumn("short_id", getName());
295: DatabaseManager.update(context, row);
296:
297: decache();
298:
299: log.info(LogManager.getHeader(context,
300: "update_metadata_schema", "metadata_schema_id="
301: + getSchemaID() + "namespace=" + getNamespace()
302: + "name=" + getName()));
303: }
304:
305: /**
306: * Delete the metadata schema.
307: *
308: * @param context DSpace context
309: * @throws SQLException
310: * @throws AuthorizeException
311: */
312: public void delete(Context context) throws SQLException,
313: AuthorizeException {
314: // Check authorisation: Only admins may create DC types
315: if (!AuthorizeManager.isAdmin(context)) {
316: throw new AuthorizeException(
317: "Only administrators may modify the metadata registry");
318: }
319:
320: log.info(LogManager.getHeader(context,
321: "delete_metadata_schema", "metadata_schema_id="
322: + getSchemaID()));
323:
324: DatabaseManager.delete(context, row);
325: }
326:
327: /**
328: * Return all metadata schemas.
329: *
330: * @param context DSpace context
331: * @return array of metadata schemas
332: * @throws SQLException
333: */
334: public static MetadataSchema[] findAll(Context context)
335: throws SQLException {
336: List schemas = new ArrayList();
337:
338: // Get all the metadataschema rows
339: TableRowIterator tri = DatabaseManager
340: .queryTable(context, "MetadataSchemaRegistry",
341: "SELECT * FROM MetadataSchemaRegistry ORDER BY metadata_schema_id");
342:
343: // Make into DC Type objects
344: while (tri.hasNext()) {
345: schemas.add(new MetadataSchema(tri.next()));
346: }
347: // close the TableRowIterator to free up resources
348: tri.close();
349:
350: // Convert list into an array
351: MetadataSchema[] typeArray = new MetadataSchema[schemas.size()];
352: return (MetadataSchema[]) schemas.toArray(typeArray);
353: }
354:
355: /**
356: * Return true if and only if the passed name appears within the allowed
357: * number of times in the current schema.
358: *
359: * @param context DSpace context
360: * @param namespace namespace URI to match
361: * @return true of false
362: * @throws SQLException
363: */
364: private boolean uniqueNamespace(Context context, String namespace)
365: throws SQLException {
366: Connection con = context.getDBConnection();
367: TableRow reg = DatabaseManager.row("MetadataSchemaRegistry");
368:
369: String query = "SELECT COUNT(*) FROM " + reg.getTable() + " "
370: + "WHERE metadata_schema_id != ? "
371: + "AND namespace= ? ";
372: PreparedStatement statement = con.prepareStatement(query);
373: statement.setInt(1, schemaID);
374: statement.setString(2, namespace);
375:
376: ResultSet rs = statement.executeQuery();
377:
378: int count = 0;
379: if (rs.next()) {
380: count = rs.getInt(1);
381: }
382:
383: return (count == 0);
384: }
385:
386: /**
387: * Return true if and only if the passed name is unique.
388: *
389: * @param context DSpace context
390: * @param name short name of schema
391: * @return true of false
392: * @throws SQLException
393: */
394: private boolean uniqueShortName(Context context, String name)
395: throws SQLException {
396: Connection con = context.getDBConnection();
397: TableRow reg = DatabaseManager.row("MetadataSchemaRegistry");
398:
399: String query = "SELECT COUNT(*) FROM " + reg.getTable() + " "
400: + "WHERE metadata_schema_id != ? "
401: + "AND short_id = ? ";
402:
403: PreparedStatement statement = con.prepareStatement(query);
404: statement.setInt(1, schemaID);
405: statement.setString(2, name);
406:
407: ResultSet rs = statement.executeQuery();
408:
409: int count = 0;
410: if (rs.next()) {
411: count = rs.getInt(1);
412: }
413:
414: return (count == 0);
415: }
416:
417: /**
418: * Get the schema corresponding with this numeric ID.
419: * The ID is a database key internal to DSpace.
420: *
421: * @param context
422: * context, in case we need to read it in from DB
423: * @param id
424: * the schema ID
425: * @return the metadata schema object
426: * @throws SQLException
427: */
428: public static MetadataSchema find(Context context, int id)
429: throws SQLException {
430: initCache(context);
431: Integer iid = new Integer(id);
432:
433: // sanity check
434: if (!id2schema.containsKey(iid))
435: return null;
436:
437: return (MetadataSchema) id2schema.get(iid);
438: }
439:
440: /**
441: * Get the schema corresponding with this short name.
442: *
443: * @param context
444: * context, in case we need to read it in from DB
445: * @param shortName
446: * the short name for the schema
447: * @return the metadata schema object
448: * @throws SQLException
449: */
450: public static MetadataSchema find(Context context, String shortName)
451: throws SQLException {
452: // If we are not passed a valid schema name then return
453: if (shortName == null)
454: return null;
455:
456: initCache(context);
457:
458: if (!name2schema.containsKey(shortName))
459: return null;
460:
461: return (MetadataSchema) name2schema.get(shortName);
462: }
463:
464: // invalidate the cache e.g. after something modifies DB state.
465: private static void decache() {
466: id2schema = null;
467: name2schema = null;
468: }
469:
470: // load caches if necessary
471: private static void initCache(Context context) throws SQLException {
472: if (id2schema != null && name2schema != null)
473: return;
474:
475: log.info("Loading schema cache for fast finds");
476: id2schema = new HashMap();
477: name2schema = new HashMap();
478:
479: TableRowIterator tri = DatabaseManager.queryTable(context,
480: "MetadataSchemaRegistry",
481: "SELECT * from MetadataSchemaRegistry");
482: while (tri.hasNext()) {
483: TableRow row = tri.next();
484:
485: MetadataSchema s = new MetadataSchema(row);
486: id2schema.put(new Integer(s.schemaID), s);
487: name2schema.put(s.name, s);
488: }
489: // close the TableRowIterator to free up resources
490: tri.close();
491: }
492: }
|