/* * Copyright (C) 2012 Jan Pokorsky * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package cz.cas.lib.proarc.common.catalog; import cz.cas.lib.proarc.common.config.CatalogConfiguration; import cz.cas.lib.proarc.common.config.CatalogQueryField; import cz.cas.lib.proarc.common.mods.ModsUtils; import cz.cas.lib.proarc.common.xml.Transformers; import cz.cas.lib.proarc.z3950.Z3950Client; import cz.cas.lib.proarc.z3950.Z3950ClientException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Document; /** * Z39.50 metadata provider. * * @author Jan Pokorsky */ public final class Z3950Catalog implements BibliographicCatalog { public static final String TYPE = "Z3950Catalog"; /** Configuration property name to define database. */ static final String PROPERTY_BASE = "base"; /** Configuration property name to override char set of returned records. */ static final String PROPERTY_RECORD_CHARSET = "recordCharset"; /** Configuration property name to define field's query. */ static final String PROPERTY_FIELD_QUERY = "query"; private static final Logger LOG = Logger.getLogger(Z3950Catalog.class.getName()); private Transformers transformers = new Transformers(); private final Z3950Client client; private final String host; private final String base; private final int port; private final Charset recordCharset; /** fieldId -> field */ private final Map<String, Z3950Field> fields; public static Z3950Catalog get(CatalogConfiguration c) { if (c == null || !TYPE.equals(c.getType())) { return null; } String host = null; int port = -1; String url = c.getUrl(); if (url != null) { try { URI uri = new URI(url); host = uri.getHost(); if (host == null || host.isEmpty()) { LOG.log(Level.SEVERE, "Missing host in URL.\n{0}", c); return null; } port = uri.getPort(); if (port == -1) { LOG.log(Level.SEVERE, "Missing port in URL.\n{0}", c); return null; } } catch (URISyntaxException ex) { LOG.log(Level.SEVERE, c.toString(), ex); return null; } } String base = c.getProperty(PROPERTY_BASE); String recordCharset = c.getProperty(PROPERTY_RECORD_CHARSET); Charset charset = null; if (recordCharset != null) { try { charset = Charset.forName(recordCharset); } catch (Exception ex) { LOG.log(Level.SEVERE, c.toString(), ex); } } Map<String, Z3950Field> fields = readFields(c); return new Z3950Catalog(host, port, base, charset, fields); } static Map<String, Z3950Field> readFields(CatalogConfiguration c) { Map<String, Z3950Field> fields = new HashMap<String, Z3950Field>(); List<CatalogQueryField> queryFields = c.getQueryFields(); for (CatalogQueryField queryField : queryFields) { fields.put(queryField.getName(), new Z3950Field(queryField)); } return fields; } public Z3950Catalog(String host, int port, String base, Charset recordCharset, Map<String, Z3950Field> fields) { this.host = host; this.port = port; this.base = base; this.recordCharset = recordCharset; client = new Z3950Client(host, port, base); this.fields = fields; } @Override public List<MetadataItem> find(String fieldId, String value, Locale locale) throws TransformerException, IOException { String query = buildQuery(fieldId, value); LOG.fine(query); if (query == null) { return Collections.emptyList(); } ArrayList<MetadataItem> result = new ArrayList<MetadataItem>(); int index = 1; try { for (byte[] content : client.search(query)) { String charset = recordCharset == null ? null : recordCharset.name(); if (LOG.isLoggable(Level.FINE)) { String marc21 = new String(content, charset == null ? "UTF-8" : charset); LOG.fine(marc21); } Document marcXml = Z3950Client.toMarcXml(content, charset); if (LOG.isLoggable(Level.FINE)) { StringBuilder sb = new StringBuilder(); transformers.dump(new DOMSource(marcXml), sb); LOG.fine(sb.toString()); } MetadataItem item = createResponse(index++, new DOMSource(marcXml), locale); result.add(item); } return result; } catch (Z3950ClientException ex) { throw new IOException(ex); } finally { client.close(); } } Charset getRecordCharset() { return recordCharset; } String getHost() { return host; } String getBase() { return base; } int getPort() { return port; } private String buildQuery(String fieldId, String value) { String query = null; Z3950Field field = fields.get(fieldId); if (field != null && field.getQuery() != null) { return field.getQuery().replace("%s", value); } // default queries if ("issn".equals(fieldId)) { query = String.format("@attrset bib-1 @attr 1=8 \"%s\"", value); } else if ("isbn".equals(fieldId)) { query = String.format("@attrset bib-1 @attr 1=7 \"%s\"", value); } return query; } private MetadataItem createResponse(int entryIdx, Source marcxmlSrc, Locale locale) throws TransformerException, UnsupportedEncodingException { byte[] modsBytes = transformers.transformAsBytes( marcxmlSrc, Transformers.Format.MarcxmlAsMods3); byte[] modsHtmlBytes = modsAsHtmlBytes(new StreamSource(new ByteArrayInputStream(modsBytes)), locale); byte[] modsTitleBytes = transformers.transformAsBytes( new StreamSource(new ByteArrayInputStream(modsBytes)), Transformers.Format.ModsAsTitle); return new MetadataItem(entryIdx, new String(modsBytes, "UTF-8"), new String(modsHtmlBytes, "UTF-8"), new String(modsTitleBytes, "UTF-8")); } private byte[] modsAsHtmlBytes(Source source, Locale locale) throws TransformerException { byte[] modsHtmlBytes = transformers.transformAsBytes( source, Transformers.Format.ModsAsHtml, ModsUtils.modsAsHtmlParameters(locale)); return modsHtmlBytes; } public static final class Z3950Field extends CatalogQueryField { public Z3950Field(CatalogQueryField cqField) { super(cqField.getName(), cqField.getProperties()); } public String getQuery() { return getProperties().getString(PROPERTY_FIELD_QUERY); } } }