/** * Performs general periodic maintenance. This routine handles alle open pages/main and * pages/mirror file service requests. These requests are obtained from the netfiles builder. For * each file that should be serviced, the filechange method is called. This routine handles a * maximum of 10 page/main, and 50 page/mirror service calls each time it is called. The first * time this method is call, nothing happens (?) * * @return <code>true</code> if maintenance was performed, <code>false</code> otherwise */ public boolean probeCall() { if (first) { // skip first time this method is called first = false; } else { // handle up to 10 pages/main fileservice requests try { Netfiles bul = (Netfiles) Vwms.getMMBase().getMMObject("netfiles"); // Enumeration e=bul.search("WHERE service='pages' AND subservice='main' AND // status="+Netfiles.STATUS_REQUEST+" ORDER BY number DESC"); Enumeration e = bul.search("service=='pages'+subservice=='main'+status=" + Netfiles.STATUS_REQUEST); int i = 0; while (e.hasMoreElements() && i < 10) { MMObjectNode node = (MMObjectNode) e.nextElement(); fileChange("" + node.getIntValue("number"), "c"); i++; } } catch (Exception e) { log.error(Logging.stackTrace(e)); } // handle up to 50 pages/mirror fileservice requests try { Netfiles bul = (Netfiles) Vwms.getMMBase().getMMObject("netfiles"); Enumeration e = bul.search("service=='pages'+subservice=='mirror'+status=" + Netfiles.STATUS_REQUEST); // Enumeration e=bul.search("WHERE service='pages' AND subservice='mirror' AND // status="+Netfiles.STATUS_REQUEST+" ORDER BY number DESC"); int i = 0; while (e.hasMoreElements() && i < 50) { MMObjectNode node = (MMObjectNode) e.nextElement(); fileChange("" + node.getIntValue("number"), "c"); i++; } } catch (Exception e) { log.error(Logging.stackTrace(e)); } } return true; }
/** * The resources builder can be used by {@link org.mmbase.util.ResourceLoader} to load resources * from (configuration files, classes, resourcebundles). * * @author Michiel Meeuwissen * @version $Id$ * @since MMBase-1.8 */ public class Resources extends Attachments { private static final Logger log = Logging.getLoggerInstance(Resources.class); /** Implements virtual filename field. {@inheritDoc} */ @Override public Object getValue(MMObjectNode node, String field) { if (field.equals(NodeURLStreamHandlerFactory.FILENAME_FIELD)) { String s = node.getStringValue(NodeURLStreamHandlerFactory.RESOURCENAME_FIELD); int i = s.lastIndexOf("/"); if (i > 0) { return s.substring(i + 1); } else { return s; } } else { return super.getValue(node, field); } } }
/** * A VWM that manages the files by scheduling them to be send to one or more mirror sites. Requests * for scheduling is done in the netfile builder. This VWM handles those netfile requests whose * service is 'pages'. Available subservices are 'main' and 'mirror'. Requests for file copy are * checked periodically. This results in one or more requests for a 'mirror' service, which then * result in a file copy request, which is handled in a separate thread. This VWM also has methods * for recalculating pages and handling page changes (which in turn result in a request for file * copy.) Entry point for these requests are the FileChange methods from the {@link * VwmServiceInterface}. * * @author Daniel Ockeloen * @author Pierre van Rooden (javadocs) * @version $Id$ */ public class PageMaster extends Vwm implements MMBaseObserver, VwmServiceInterface { private static final Logger log = Logging.getLoggerInstance(PageMaster.class); // field used to skip first probeCall (why???) boolean first = true; Object syncobj = new Object(); // used in commented code /** Queue containing the file-copy tasks that need to be performed by {@link #filecopier} */ Queue files2copy = new Queue(128); /** Thread that handles the actual file transfers. */ FileCopier filecopier = new FileCopier(files2copy); /** Cache for mirror servers */ Vector mirrornodes; // Hashtable properties; (unused) /** Constructor for the PageMaster VWM. */ public PageMaster() { log.debug("ready for action"); } /** * Performs general periodic maintenance. This routine handles alle open pages/main and * pages/mirror file service requests. These requests are obtained from the netfiles builder. For * each file that should be serviced, the filechange method is called. This routine handles a * maximum of 10 page/main, and 50 page/mirror service calls each time it is called. The first * time this method is call, nothing happens (?) * * @return <code>true</code> if maintenance was performed, <code>false</code> otherwise */ public boolean probeCall() { if (first) { // skip first time this method is called first = false; } else { // handle up to 10 pages/main fileservice requests try { Netfiles bul = (Netfiles) Vwms.getMMBase().getMMObject("netfiles"); // Enumeration e=bul.search("WHERE service='pages' AND subservice='main' AND // status="+Netfiles.STATUS_REQUEST+" ORDER BY number DESC"); Enumeration e = bul.search("service=='pages'+subservice=='main'+status=" + Netfiles.STATUS_REQUEST); int i = 0; while (e.hasMoreElements() && i < 10) { MMObjectNode node = (MMObjectNode) e.nextElement(); fileChange("" + node.getIntValue("number"), "c"); i++; } } catch (Exception e) { log.error(Logging.stackTrace(e)); } // handle up to 50 pages/mirror fileservice requests try { Netfiles bul = (Netfiles) Vwms.getMMBase().getMMObject("netfiles"); Enumeration e = bul.search("service=='pages'+subservice=='mirror'+status=" + Netfiles.STATUS_REQUEST); // Enumeration e=bul.search("WHERE service='pages' AND subservice='mirror' AND // status="+Netfiles.STATUS_REQUEST+" ORDER BY number DESC"); int i = 0; while (e.hasMoreElements() && i < 50) { MMObjectNode node = (MMObjectNode) e.nextElement(); fileChange("" + node.getIntValue("number"), "c"); i++; } } catch (Exception e) { log.error(Logging.stackTrace(e)); } } return true; } /** * Called when a remote node is changed. * * @param machine Name of the machine that changed the node. * @param number Number of the changed node as a <code>String</code> * @param builder type of the changed node * @param ctype command type, 'c'=changed, 'd'=deleted', 'r'=relations changed, 'n'=new * @return <code>true</code> */ public boolean nodeRemoteChanged(String machine, String number, String builder, String ctype) { return nodeChanged(machine, number, builder, ctype); } /** * Called when a local node is changed. * * @param machine Name of the machine that changed the node. * @param number Number of the changed node as a <code>String</code> * @param builder type of the changed node * @param ctype command type, 'c'=changed, 'd'=deleted', 'r'=relations changed, 'n'=new * @return <code>true</code> */ public boolean nodeLocalChanged(String machine, String number, String builder, String ctype) { return nodeChanged(machine, number, builder, ctype); } /** * Called when a local or remote node is changed. Does not take any action. * * @param machine Name of the machine that changed the node. * @param number Number of the changed node as a <code>String</code> * @param builder type of the changed node * @param ctype command type, 'c'=changed, 'd'=deleted', 'r'=relations changed, 'n'=new * @return <code>true</code> */ public boolean nodeChanged(String machine, String number, String builder, String ctype) { // log.debug("sees that : "+number+" has changed type="+ctype); return true; } /** * Schedules a service-request on a file. Only "pages/main" services are handled. The * service-request is later handled through the {@link #probeCall} method. * * @param service the service to be performed * @param subservice the subservice to be performed * @param filename the filename to service * @return <code>true</code> if maintenance was performed, <code>false</code> otherwise */ public boolean fileChange(String service, String subservice, String filename) { log.debug("frontend change -> " + filename); log.service("s=" + service + " sub=" + subservice + "file=" + filename); // jump to correct subhandles based on the subservice if (subservice.equals("main")) { handleMainCheck(service, subservice, filename); } return true; } /** * Handles a service-request on a file, registered in the netfiles builder. Depending on the * subservice requested, this routine calls {@link #handleMirror} or {@link #handleMain}. * * @param number Number of the node in the netfiles buidler than contain service request * information. * @param ctype the type of change on that node ("c" : node was changed) * @return <code>true</code> */ public boolean fileChange(String number, String ctype) { // log.debug("fileChange="+number+" "+ctype); // first get the change node so we can see what is the matter with it. Netfiles bul = (Netfiles) Vwms.getMMBase().getMMObject("netfiles"); MMObjectNode filenode = bul.getNode(number); if (filenode != null) { // obtain all the basic info on the file. String service = filenode.getStringValue("service"); String subservice = filenode.getStringValue("subservice"); int status = filenode.getIntValue("status"); // jump to correct subhandles based on the subservice if (subservice.equals("main")) { return handleMain(filenode, status, ctype); } else if (subservice.equals("mirror")) { return handleMirror(filenode, status, ctype); } } return true; } /** * Handles a pages/mirror service request. Places a page in the file2copy queue, so it will be * sent to a mirror site by the FileCopier. * * @param filenode the filenet node that contains the service request * @param status the current status of the node * @param ctype the type of change on that node ("c" : node was changed) * @return <code>true</code> */ public boolean handleMirror(MMObjectNode filenode, int status, String ctype) { switch (status) { case Netfiles.STATUS_REQUEST: // Request // register the node as being On Its Way filenode.setValue("status", Netfiles.STATUS_ON_ITS_WAY); filenode.commit(); String filename = filenode.getStringValue("filename"); String dstserver = filenode.getStringValue("mmserver"); // recover the correct source/dest properties for this mirror // // why does it say "demoserver" ?? // String sshpath = getProperty("demoserver", "sshpath"); log.debug("sshpath=" + sshpath); String srcpath = getProperty("demoserver", "path"); log.debug("srcpath=" + srcpath); String dstuser = getProperty(dstserver, "user"); log.debug("dstuser="******"host"); log.debug("dsthost=" + dsthost); String dstpath = getProperty(dstserver, "path"); log.debug("dstpath=" + dstpath); /* this code can be dropped as it is handled in FileCopier SCPcopy scpcopy=new SCPcopy(sshpath,dstuser,dsthost,dstpath); synchronized(syncobj) { scpcopy.copy(srcpath,filename); } */ // create a new file2copy object and add it to the queue, // so the FileCopier thread will handle it. files2copy.append(new aFile2Copy(dstuser, dsthost, dstpath, srcpath, filename, sshpath)); // register the node as being Done filenode.setValue("status", Netfiles.STATUS_DONE); filenode.commit(); break; case Netfiles.STATUS_ON_ITS_WAY: // On its way break; case Netfiles.STATUS_DONE: // Done break; } return true; } /** * Handles a pages/main service request. The events handled are:<br> * - requests for handling: schedules requests to mirror this page using {@link #doMainRequest} * <br> * - changed: page is scheduled to be recalculated<br> * - recaculate" page is recaclcutated and scheduled to be handled<br> * * @param filenode the netfiles node that contains the service request * @param status the current status of the node * @param ctype the type of change on that node ("c" : node was changed) * @return <code>true</code> */ public boolean handleMain(MMObjectNode filenode, int status, String ctype) { switch (status) { case Netfiles.STATUS_REQUEST: // Request // register the node as being On Its Way filenode.setValue("status", Netfiles.STATUS_ON_ITS_WAY); filenode.commit(); // do stuff doMainRequest(filenode); // register the node as being Done filenode.setValue("status", Netfiles.STATUS_DONE); filenode.commit(); break; case Netfiles.STATUS_ON_ITS_WAY: // On Its Way break; case Netfiles.STATUS_DONE: // Done break; case Netfiles.STATUS_CHANGED: // Dirty (?) filenode.setValue("status", Netfiles.STATUS_CALC_PAGE); filenode.commit(); break; case Netfiles.STATUS_CALC_PAGE: // Recalculate Page String filename = filenode.getStringValue("filename"); calcPage(filename); filenode.setValue("status", Netfiles.STATUS_REQUEST); filenode.commit(); break; } return true; } /** * Handles a main subservice on a page. The page is scheduled to be sent to all appropriate * mirrorsites for this service, by setting the request status in the associated mirror nodes. If * no mirror nodes are associated with this page, nothing happens. * * @param filenode the netfiles node with the original (main) request */ public boolean doMainRequest(MMObjectNode filenode) { // so this file has changed probably, check if the file is ready on // disk and set the mirrors to request. String filename = filenode.getStringValue("filename"); // find and change all the mirror nodes so they get resend Netfiles bul = (Netfiles) Vwms.getMMBase().getMMObject("netfiles"); Enumeration e = bul.search("WHERE filename='" + filename + "' AND service='pages' AND subservice='mirror'"); while (e.hasMoreElements()) { MMObjectNode mirrornode = (MMObjectNode) e.nextElement(); mirrornode.setValue("status", Netfiles.STATUS_REQUEST); mirrornode.commit(); } return true; } /** * Schedules a netfile object to be send to its mirror sites. The routine searches the appropriate * netfile node, and sets its status to 'request'. If a node does not exits, a new node is * created. In the latter case, the system also creates mirrornodes for each mirrorsite associated * with this service. * * @param service the service to be performed * @param subservice the subservice to be performed * @param filename the filename to service */ public void handleMainCheck(String service, String subservice, String filename) { log.debug("Reached handleMainCheck"); Netfiles bul = (Netfiles) Vwms.getMMBase().getMMObject("netfiles"); Enumeration e = bul.search( "WHERE filename='" + filename + "' AND service='" + service + "' AND subservice='" + subservice + "'"); if (e.hasMoreElements()) { MMObjectNode mainnode = (MMObjectNode) e.nextElement(); mainnode.setValue("status", Netfiles.STATUS_REQUEST); mainnode.commit(); } else { MMObjectNode mainnode = bul.getNewNode("system"); mainnode.setValue("filename", filename); mainnode.setValue("mmserver", Vwms.getMMBase().getMachineName()); mainnode.setValue("service", service); mainnode.setValue("subservice", subservice); mainnode.setValue("status", Netfiles.STATUS_REQUEST); mainnode.setValue("filesize", -1); bul.insert("system", mainnode); Enumeration f = getMirrorNodes(service).elements(); while (f.hasMoreElements()) { MMObjectNode n2 = (MMObjectNode) f.nextElement(); // hack hack also have to create mirror nodes ! mainnode = bul.getNewNode("system"); mainnode.setValue("filename", filename); mainnode.setValue("mmserver", n2.getStringValue("name")); mainnode.setValue("service", service); mainnode.setValue("subservice", "mirror"); mainnode.setValue("status", Netfiles.STATUS_DONE); mainnode.setValue("filesize", -1); bul.insert("system", mainnode); } } } /** * Retrieves a named property of a server. * * @param machine name of the server * @param key name of the property to retrieve * @return the property value */ public String getProperty(String machine, String key) { MMServers mmservers = (MMServers) Vwms.getMMBase().getMMObject("mmservers"); return mmservers.getMMServerProperty(machine, key); } /** * Recalculate a page. Invokes the SCAN parser (which will re-cache the page through the scancache * module) Only works for SCAN. * * @param url of the page to cache */ public void calcPage(String url) { scanparser m = (scanparser) Vwms.getMMBase().getModule("SCANPARSER"); url = url.substring(0, url.length() - 5); url = url.replace(':', '?'); log.debug("getPage=" + url); if (m != null) { scanpage sp = new scanpage(); m.calcPage(url, sp, 0); } } /** * Retrieves a list of Mirror Servers. This is done by obtaining a fileserver node and retrieving * associated mmserver nodes. This method should be renamed and moved to the netfilesrv builder. * * @param service preseumably the service to query for. Unused. * @return a <code>Vector</code> containing mmserver nodes that act as mirror server for this * service */ public Vector getMirrorNodes(String service) { if (mirrornodes != null) return mirrornodes; NetFileSrv bul = (NetFileSrv) Vwms.getMMBase().getMMObject("netfilesrv"); if (bul != null) { Enumeration e = bul.search("service=='pages'+subservice=='mirror'"); if (e.hasMoreElements()) { MMObjectNode n1 = (MMObjectNode) e.nextElement(); mirrornodes = n1.getRelatedNodes("mmservers"); if (mirrornodes != null) return mirrornodes; } } mirrornodes = new Vector(); return mirrornodes; } }
/** * If for some reason you also need to do Queries next to MMBase. * * @author Michiel Meeuwissen * @version $Id$ */ public class JdbcIndexDefinition implements IndexDefinition { private static final Logger log = Logging.getLoggerInstance(JdbcIndexDefinition.class); private static int directConnections = 0; private static final int CACHE_SIZE = 10 * 1024; protected static Cache<String, LazyMap> nodeCache = new Cache<String, LazyMap>(CACHE_SIZE) { { putCache(); } @Override public final String getName() { return "LuceneJdbcNodes"; } @Override public final String getDescription() { return "Node identifier -> Map"; } }; private final DataSource dataSource; private final String key; private final String identifier; private final String indexSql; private final String findSql; private final Analyzer analyzer; private final Set<String> keyWords = new HashSet<String>(); private final Map<String, Indexer.Multiple> nonDefaultMultiples = new HashMap<String, Indexer.Multiple>(); private final Map<String, Float> boosts = new HashMap<String, Float>(); private final Collection<IndexDefinition> subQueries = new ArrayList<IndexDefinition>(); private final boolean isSub; private String id; JdbcIndexDefinition( DataSource ds, Element element, Set allIndexedFields, boolean storeText, boolean mergeText, Analyzer a, boolean isSub) { this.dataSource = ds; indexSql = element.getAttribute("sql"); key = element.getAttribute("key"); String elementId = element.getAttribute("identifier"); identifier = "".equals(elementId) ? key : elementId; findSql = element.getAttribute("find"); NodeList childNodes = element.getChildNodes(); for (int k = 0; k < childNodes.getLength(); k++) { if (childNodes.item(k) instanceof Element) { Element childElement = (Element) childNodes.item(k); if ("field".equals(childElement.getLocalName())) { if (childElement.getAttribute("keyword").equals("true")) { keyWords.add(childElement.getAttribute("name")); } String m = childElement.getAttribute("multiple"); if ("".equals(m)) m = "add"; if (!m.equals("add")) { nonDefaultMultiples.put( childElement.getAttribute("name"), Indexer.Multiple.valueOf(m.toUpperCase())); } String b = childElement.getAttribute("boost"); if (!b.equals("")) { boosts.put(childElement.getAttribute("name"), Float.valueOf(b)); } } else if ("related".equals(childElement.getLocalName())) { subQueries.add( new JdbcIndexDefinition( ds, childElement, allIndexedFields, storeText, mergeText, a, true)); } } } this.analyzer = a; this.isSub = isSub; assert !isSub || "".equals(findSql); } @Override public void setId(String i) { id = i; } @Override public String getId() { return id; } /** * Jdbc connection pooling of MMBase would kill the statement if too duratious. This produces a * 'direct connection' in that case, to circumvent that problem (Indexing queries _may_ take a * while). */ protected Connection getDirectConnection() throws SQLException { directConnections++; try { if (dataSource instanceof GenericDataSource) { return ((GenericDataSource) dataSource).getDirectConnection(); } else { return dataSource.getConnection(); } } catch (SQLException sqe) { log.error("With direct connection #" + directConnections + ": " + sqe.getMessage()); throw sqe; } catch (Throwable t) { throw new RuntimeException("direct connection #" + directConnections, t); } } @Override public Analyzer getAnalyzer() { return analyzer; } protected String getFindSql(String identifier) { assert !isSub; if (findSql == null || "".equals(findSql)) throw new RuntimeException("No find query defined"); if (identifier == null) throw new RuntimeException("No find query defined"); String s = findSql.replaceAll("\\[IDENTIFIER\\]", identifier); s = s.replaceAll("\\[KEY\\]", identifier); // deprecated return s; } @Override public boolean inIndex(String identifier) { CloseableIterator<JdbcEntry> i = getSqlCursor(getFindSql(identifier)); boolean result = i.hasNext(); try { i.close(); } catch (IOException ex) { log.warn(ex); } return result; } protected String getSql(String identifier) { if (indexSql == null || "".equals(indexSql)) throw new RuntimeException("No sql defined"); if (identifier == null) throw new RuntimeException("No query defined"); String s = indexSql.replaceAll("\\[PARENTKEY\\]", identifier); s = s.replaceAll("\\[KEY\\]", identifier); // deprecated return s; } CloseableIterator<JdbcEntry> getSqlCursor(final String sql) { try { long start = System.currentTimeMillis(); final Connection con = getDirectConnection(); log.debug("About to execute " + sql + " (" + directConnections + ")"); final Statement statement = con.createStatement(); final ResultSet results = statement.executeQuery(sql); if (log.isDebugEnabled()) { log.debug("Executed " + sql + " in " + (System.currentTimeMillis() - start) + " ms"); } final ResultSetMetaData meta = results.getMetaData(); return new CloseableIterator<JdbcEntry>() { boolean hasNext = results.isBeforeFirst(); int i = 0; @Override public boolean hasNext() { return hasNext; } @Override public JdbcEntry next() { if (!hasNext) { throw new NoSuchElementException(); } try { results.next(); hasNext = !results.isLast(); } catch (java.sql.SQLException sqe) { log.error(sqe); hasNext = false; } JdbcEntry entry = new JdbcEntry(meta, results, sql); i++; if (log.isServiceEnabled()) { if (i % 100 == 0) { log.service("jdbc cursor " + i + " (now at id=" + entry.getIdentifier() + ")"); } else if (log.isDebugEnabled()) { log.trace("jdbc cursor " + i + " (now at id=" + entry.getIdentifier() + ")"); } } return entry; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void close() { log.debug("Closing " + con); try { if (results != null) results.close(); if (statement != null) statement.close(); if (con != null) con.close(); } catch (Exception e) { log.error(e); } } }; } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } /** * A map representing a row in a database. But only filled when actually used. So, only on first * use, a query is done. And not before that. * * @since MMBase-1.9 */ protected class LazyMap extends AbstractMap<String, String> { private Map<String, String> map = null; private final Map<String, String> keys; private final String identifier; LazyMap(String identifier, Map<String, String> keys) { this.identifier = identifier; this.keys = keys; } protected void check() { if (map == null) { Connection connection = null; Statement statement = null; ResultSet results = null; try { connection = dataSource.getConnection(); statement = connection.createStatement(); long start = System.currentTimeMillis(); String s = getFindSql(identifier); if (log.isTraceEnabled()) { log.trace("About to execute " + s + " because ", new Exception()); } results = statement.executeQuery(s); ResultSetMetaData meta = results.getMetaData(); map = new HashMap<String, String>(); if (results.next()) { for (int i = 1; i <= meta.getColumnCount(); i++) { String value = org.mmbase.util.Casting.toString(results.getString(i)); map.put(meta.getColumnName(i).toLowerCase(), value); } } long duration = (System.currentTimeMillis() - start); if (duration > 500) { log.warn("Executed " + s + " in " + duration + " ms"); } else if (duration > 100) { log.debug("Executed " + s + " in " + duration + " ms"); } else { log.trace("Executed " + s + " in " + duration + " ms"); } } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } finally { if (results != null) try { results.close(); } catch (Exception e) { } if (statement != null) try { statement.close(); } catch (Exception e) { } if (connection != null) try { connection.close(); } catch (Exception e) { } } } } @Override public Set<Map.Entry<String, String>> entrySet() { check(); return map.entrySet(); } @Override public int size() { check(); return map.size(); } @Override public String get(Object key) { if (JdbcIndexDefinition.this.identifier.equals(key)) return identifier; if (keys.containsKey(key)) return keys.get(key); check(); return map.get(key); } @Override public boolean containsKey(Object key) { if (JdbcIndexDefinition.this.identifier.equals(key)) return true; if (keys.containsKey(key)) return true; check(); return map.containsKey(key); } @Override public String toString() { if (map != null) { return map.toString(); } else { return "[LAZY node " + identifier + "]"; } } } @Override public org.mmbase.bridge.Node getNode(final Cloud userCloud, final Document doc) { String docId = doc.get("number"); if (docId == null) { throw new IllegalArgumentException("No number found in " + doc); } LazyMap m = nodeCache.get(docId); // if (m == null) { Map<String, String> keys = new HashMap<String, String>(); for (String keyWord : keyWords) { keys.put(keyWord, doc.get(keyWord)); } m = new LazyMap(docId, keys); nodeCache.put(docId, m); } org.mmbase.bridge.Node node = new MapNode<String>( m, new MapNodeManager(userCloud, m) { @Override public boolean hasField(String name) { if (JdbcIndexDefinition.this.key.equals(name)) return true; return super.hasField(name); } @Override public org.mmbase.bridge.Field getField(String name) { if (map == null && JdbcIndexDefinition.this.key.equals(name)) { org.mmbase.core.CoreField fd = org.mmbase.core.util.Fields.createField( name, org.mmbase.core.util.Fields.classToType(Object.class), org.mmbase.bridge.Field.TYPE_UNKNOWN, org.mmbase.bridge.Field.STATE_VIRTUAL, null); return new org.mmbase.bridge.implementation.BasicField(fd, this); } else { return super.getField(name); } } }); if (log.isDebugEnabled()) { log.debug("Returning node for " + node); } return node; } @Override public CloseableIterator<JdbcEntry> getCursor() { assert !isSub; return getSqlCursor(indexSql); } @Override public CloseableIterator<JdbcEntry> getSubCursor(String identifier) { if (isSub) { log.debug("Using getSubCursor for " + identifier); return getSqlCursor(getSql(identifier)); } else { return getSqlCursor(getFindSql(identifier)); } } @Override public String toString() { return indexSql; } public class JdbcEntry implements IndexEntry { final ResultSetMetaData meta; final ResultSet results; final String sql; JdbcEntry(ResultSetMetaData m, ResultSet r, String s) { log.trace("new JDBC Entry"); meta = m; results = r; sql = s; } @Override public void index(Document document) { if (log.isTraceEnabled()) { log.trace( "Indexing " + sql + " id=" + JdbcIndexDefinition.this.identifier + ", key = " + JdbcIndexDefinition.this.key); } String id = getIdentifier(); if (id != null) { document.add( new Field( "builder", "VIRTUAL BUILDER", Field.Store.YES, Field.Index.NOT_ANALYZED)); // keyword document.add( new Field( "number", getIdentifier(), Field.Store.YES, Field.Index.NOT_ANALYZED)); // keyword } try { for (int i = 1; i <= meta.getColumnCount(); i++) { String value = org.mmbase.util.Casting.toString(results.getString(i)); if (log.isTraceEnabled()) { log.trace( "Indexing " + value + " for " + meta.getColumnName(i) + " on " + getIdentifier()); } String fieldName = meta.getColumnName(i); if (keyWords.contains(fieldName)) { Indexer.addField( document, new Field(fieldName, value, Field.Store.YES, Field.Index.NOT_ANALYZED), nonDefaultMultiples.get(fieldName)); // keyword } else { Field field = new Field(fieldName, value, Field.Store.YES, Field.Index.ANALYZED); Float boost = boosts.get(fieldName); if (boost != null) { field.setBoost(boost); } Indexer.addField(document, field, nonDefaultMultiples.get(fieldName)); Field fullText = new Field("fulltext", value, Field.Store.YES, Field.Index.ANALYZED); if (boost != null) { fullText.setBoost(boost); } document.add(fullText); } } } catch (SQLException sqe) { log.error(sqe.getMessage(), sqe); } } @Override public Collection<IndexDefinition> getSubDefinitions() { return JdbcIndexDefinition.this.subQueries; } @Override public String getIdentifier() { if (JdbcIndexDefinition.this.identifier != null && !JdbcIndexDefinition.this.identifier.equals("")) { try { return results.getString(JdbcIndexDefinition.this.identifier); } catch (SQLException sqe) { log.error(meta + " " + sqe.getMessage(), sqe); return ""; } } else { return null; } } @Override public String getKey() { if (JdbcIndexDefinition.this.key != null && !JdbcIndexDefinition.this.key.equals("")) { try { return results.getString(JdbcIndexDefinition.this.key); } catch (SQLException sqe) { log.error(sqe.getMessage(), sqe); return ""; } } else { return null; } } public Set<String> getIdentifiers() { Set<String> ids = new HashSet<String>(); ids.add(getIdentifier()); return ids; } } }