/** * RTF text extractor * * @author Wouter Heijke * @version $Id$ */ public class SwingRTFExtractor implements Extractor { private static final Logger log = Logging.getLoggerInstance(SwingRTFExtractor.class); private String mimetype = "application/rtf"; @Override public void setMimeType(String mimetype) { this.mimetype = mimetype; } @Override public String getMimeType() { return this.mimetype; } @Override public String extract(InputStream input) throws Exception { log.debug("extract stream"); String result = null; DefaultStyledDocument styledDoc = new DefaultStyledDocument(); try { new RTFEditorKit().read(input, styledDoc, 0); result = styledDoc.getText(0, styledDoc.getLength()); } catch (IOException e) { throw new Exception("Cannot extract text from a RTF document", e); } catch (BadLocationException e) { throw new Exception("Cannot extract text from a RTF document", e); } return result; } }
/** * 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; }
/** * @author Michiel Meeuwissen * @since MMBase-1.9.2 */ public class BitrateLabeler extends Labeler { private static final Logger log = Logging.getLoggerInstance(BitrateLabeler.class); private static final String CONFIG_TAG = MainFilter.FILTERCONFIG_TAG + ".bitrates"; private final Map<String, BitrateInfo> bitrates = new LinkedHashMap<String, BitrateInfo>(); private String key = "bitrate"; private boolean overwrite = true; public void setKey(String k) { key = k; } public void setOverwrite(boolean o) { overwrite = o; } @Override public void configure(DocumentReader reader, Element element) { bitrates.clear(); try { for (Element bitrate : DocumentReader.getChildElements(reader.getElementByPath(element, CONFIG_TAG))) { BitrateInfo bri = new BitrateInfo(bitrate); log.debug("Adding BitrateInfo " + bri); bitrates.put(bri.getName(), bri); } } catch (Exception ex) { log.error("Error in filter.xml:" + ex, ex); } log.info("Configured bit rate labeler " + bitrates); FilterUtils.propertiesConfigure(this, reader, element); } @Override protected void label(URLComposer uc) { for (Map.Entry<String, BitrateInfo> entry : bitrates.entrySet()) { int bitrate = uc.getSource().getIntValue("bitrate"); if (entry.getValue().matches(bitrate)) { log.debug("" + bitrate + " matched " + entry); if (overwrite || !uc.getInfo().containsKey(key)) { uc.getInfo().put(key, entry.getKey()); } } } } }
/** * 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; } }
/** * A cache implementation backed by a {@link java.util.LinkedHashMap}, in access-order mode, and * restricted maximal size ('Least Recently Used' cache algorithm). * * @author Michiel Meeuwissen * @version $Id$ * @see org.mmbase.cache.Cache * @since MMBase-1.8.6 */ public class LRUCache<K, V> implements CacheImplementationInterface<K, V> { private static final Logger log = Logging.getLoggerInstance(LRUCache.class); private int maxSize; private final Map<K, V> backing; public LRUCache() { this(100); } public LRUCache(int size) { maxSize = size; // caches can typically be accessed/modified by multiple threads, so we need to synchronize backing = Collections.synchronizedMap( new LinkedHashMap<K, V>(size, 0.75f, true) { private static final long serialVersionUID = 0L; @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { int overSized = size() - LRUCache.this.maxSize; if (overSized <= 0) { return false; } else if (overSized == 1) { // Using iterator to manualy remove the eldest rather then return true to make // absolutely sure that one // disappears, because that seems to fail sometimes for QueryResultCache. final Iterator<K> i = keySet().iterator(); K actualEldest = i.next(); i.remove(); overSized = size() - LRUCache.this.maxSize; while (overSized > 0) { // if for some reason a key changed in the cache, even 1 i.remove may not // shrink the cache. log.warn( "cache didn't shrink (a)" + eldest.getKey() + " [" + eldest.getKey().getClass() + "] [" + eldest.getKey().hashCode() + "]"); log.warn( "cache didn't shrink (b)" + actualEldest + " [" + actualEldest.getClass() + "] [" + actualEldest.hashCode() + "]"); actualEldest = i.next(); i.remove(); overSized = size() - LRUCache.this.maxSize; } assert overSized <= 0; return false; } else { log.warn("How is this possible? Oversized: " + overSized); log.debug("because", new Exception()); if (overSized > 10) { log.error( "For some reason this cache grew much too big (" + size() + " >> " + LRUCache.this.maxSize + "). This must be some kind of bug. Resizing now."); clear(); } return false; } } }); } @Override public int getCount(K key) { return -1; } /** * Change the maximum size of the table. This may result in removal of entries in the table. * * @param size the new desired size */ @Override public void setMaxSize(int size) { if (size < 0) { throw new IllegalArgumentException("Cannot set size to negative value " + size); } maxSize = size; synchronized (backing) { while (size() > maxSize) { try { Iterator<K> i = keySet().iterator(); i.next(); i.remove(); } catch (Exception e) { log.warn(e); // ConcurentModification? } } } } @Override public final int maxSize() { return maxSize; } /** Returns size, maxSize. */ @Override public String toString() { return "Size=" + size() + ", Max=" + maxSize; } @Override public void config(Map<String, String> map) { // needs no configuration. } @Override public Object getLock() { return backing; } // wrapping for synchronization @Override public int size() { return backing.size(); } @Override public boolean isEmpty() { return backing.isEmpty(); } @Override @SuppressWarnings("element-type-mismatch") public boolean containsKey(Object key) { return backing.containsKey(key); } @Override @SuppressWarnings("element-type-mismatch") public boolean containsValue(Object value) { return backing.containsValue(value); } @Override @SuppressWarnings("element-type-mismatch") public V get(Object key) { return backing.get(key); } @Override public V put(K key, V value) { return backing.put(key, value); } @Override @SuppressWarnings("element-type-mismatch") public V remove(Object key) { return backing.remove(key); } @Override public void putAll(Map<? extends K, ? extends V> map) { backing.putAll(map); } @Override public void clear() { backing.clear(); } @Override public Set<K> keySet() { return backing.keySet(); } @Override public Set<Map.Entry<K, V>> entrySet() { return backing.entrySet(); } @Override public Collection<V> values() { return backing.values(); } }
/** * 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; } } }
/** * Generic tool to execute some SQL * * @since MMBase-1.9.5 * @author Michiel Meeuwissen * @version $Id$ */ public class SqlExecutor implements Runnable { private static final Logger LOG = Logging.getLoggerInstance(SqlExecutor.class); private String query; private String update; private String onlyIfQuery; private Pattern ignore = Pattern.compile(""); protected DataSource dataSource; protected String prefix; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public void setPrefix(String pref) { this.prefix = pref; } public DataSource getDataSource() { return dataSource; } public String getPrefix() { if (prefix == null) { return "$PREFIX"; } else { return prefix; } } public void setIgnoreException(String e) { ignore = Pattern.compile(e); } public void setQuery(String q) { if (update != null) throw new IllegalStateException(); query = q; } public void setUpdate(String u) { if (query != null) throw new IllegalStateException(); update = u; } /** * A query returning either true of false. E.g. <param name="onlyIf"><![CDATA[select 1 = (select * count(*) from mm_versions where m_type='application' and name='Limburg' and m_version < * 7);]]></param> */ public void setOnlyIf(String q) { onlyIfQuery = q; } protected void executeQuery(Statement stmt, String q) throws SQLException { q = q.replace("$PREFIX", getPrefix()); LOG.info(" Executing " + q); ResultSet rs = stmt.executeQuery(q); StringBuilder header = new StringBuilder(); for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { if (i > 1) { header.append("|"); } header.append(rs.getMetaData().getColumnName(i)); } LOG.info(header); int seq = 0; while (true) { boolean valid = rs.next(); if (!valid) break; seq++; StringBuilder line = new StringBuilder(); for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { if (i > 1) { line.append("|"); } line.append(rs.getString(i)); } LOG.info(seq + ":" + line); } } protected void executeUpdate(Statement stmt, String u) throws SQLException { u = u.replace("$PREFIX", getPrefix()); LOG.info(" Executing update " + u); int result = stmt.executeUpdate(u); LOG.service("Result :" + result); } protected boolean executeOnlyIf(Connection con, String q) throws SQLException { if (q == null) return true; Statement stmt = null; try { stmt = con.createStatement(); q = q.replace("$PREFIX", getPrefix()); LOG.debug(" Executing query " + q); ResultSet rs = stmt.executeQuery(q); rs.next(); boolean res = rs.getBoolean(1); LOG.debug("Result: " + res); return res; } catch (SQLException sqe) { LOG.error(sqe.getMessage() + " from " + q); throw sqe; } finally { try { if (stmt != null) { stmt.close(); } } catch (Exception g) { } } } public void run() { Connection con = null; Statement stmt = null; try { DataSource ds = getDataSource(); con = ds.getConnection(); if (executeOnlyIf(con, onlyIfQuery)) { stmt = con.createStatement(); if (query != null) { executeQuery(stmt, query); } else if (update != null) { executeUpdate(stmt, update); } else { throw new IllegalStateException("Both query and update properties are unset"); } } else { LOG.debug("Skipped because of " + onlyIfQuery); } } catch (RuntimeException e) { throw e; } catch (Throwable t) { if (ignore.matcher(t.getMessage()).matches()) { LOG.info("Ignoring " + t.getMessage()); } else { throw new RuntimeException(t.getMessage(), t); } } finally { try { if (stmt != null) { stmt.close(); } } catch (Exception g) { } try { if (con != null) { con.close(); } } catch (Exception g) { } } } @Override public String toString() { if (update != null) { return update; } else if (query != null) { return query; } else { return "No query yet"; } } }
/** * This return page resolver will return the referrer url wiht a parameter 'nodenr' added when there * is a new node in the result container. if there is an error it will return the value of the * errorPage field. TODO: make sure the error page exists and shows the errors well. only global * errors should trigger the error page. field errors should be shown in the editor. * * @author ebunders */ public class ReferrerResolver implements ModelAndViewResolver { private static Logger log = Logging.getLoggerInstance(ReferrerResolver.class); private String errorPage; public ModelAndView getModelAndView(HttpServletRequest request, ResultContainer result) { Map<String, Object> model = new HashMap<String, Object>(); model.put("idmap", result.getIdMap()); ModelAndView errorMandv = new ModelAndView(errorPage); List<GlobalError> globalErrors = result.getGlobalErrors(); if (result.hasGlobalErrors()) { errorMandv.addObject(GlobalError.MODEL_MAPPING_KEY, globalErrors); log.debug("request has global errors, so the return page is: " + errorPage); return errorMandv; } if (result.hasFieldErrors()) { model.put(FieldError.MODEL_MAPPING_KEY, result.getFieldErrors()); // Field errors are not displayed in the error page but in the referrer page (the form) } // has a new object been created? // String newObject = result.getNewObjects(); // set the new object in the request (why?) // if (newObject != null) { // request.setAttribute("newObject", newObject); // if (log.isDebugEnabled()) { // log.debug("object number " + newObject); // } // } final String callerPage = request.getHeader("referer"); if (callerPage == null) { // this is an error for this view resolver globalErrors.add(new GlobalError("error.no.referrer.header", result.getLocale())); log.error("REFERRER NOT SET! This request's redirection wil fail."); errorMandv.addObject(GlobalError.MODEL_MAPPING_KEY, globalErrors); return errorMandv; } // add the node number of the new object to the referer url. // if (result.getNewObject().size() > 0) { // if (log.isDebugEnabled()) { // log.debug("new object created."); // } // String newNodeNr = result.getNewObject().get(0); // //newPage = newPage.substring(0, newPage.indexOf("?") + 1) + "nodenr=" + // newNodeNr; // String newPage = new URLParamMap(callerPage).addParam("nodenr", newNodeNr, // true).toString(); // } URLParamMap u = new URLParamMap(callerPage); if (result.getExtraParams().size() > 0) { for (String param : result.getExtraParams().keySet()) { u.addParam(param, result.getExtraParams().get(param), true); } } RedirectView redirectView = new RedirectView(u.toString()); return new ModelAndView(redirectView, model); } public void setErrorPage(String errorPage) { this.errorPage = errorPage; } }
/** * Representation of a 'context', which can be read as a valid value of the 'owner' field of any * object in MMBase. Rights are distributed using this thing. This is part of cloud context * security, so the 'context' values need to be present in the cloud. * * @author Eduard Witteveen * @author Pierre van Rooden * @author Michiel Meeuwissen * @version $Id$ * @see org.mmbase.security.implementation.cloudcontext.Verify * @see org.mmbase.security.Authorization */ public class Contexts extends MMObjectBuilder { private static final Logger log = Logging.getLoggerInstance(Contexts.class); /** @javadoc */ public static final String DEFAULT_CONTEXT = "default"; // default used to be 'admin', but does that make sense? static final int DEFAULT_MAX_CONTEXTS_IN_QUERY = 50; public static final Parameter<String> PARAMETER_OPERATION = new Parameter<String>("operation", String.class); public static final Parameter<String> PARAMETER_GROUPORUSER = new Parameter<String>("grouporuser", String.class); /** * Things which must be cleared when some security objects change, can all be collected in this * map */ // protected static Map<String,SortedSet<String>> invalidableObjects = new // HashMap<String,SortedSet<String>>(); private boolean readAll = false; private boolean allContextsPossible = true; // if you want to use security for workflow, then you want this to be false private boolean disableContextChecks = false; private int maxContextsInQuery = DEFAULT_MAX_CONTEXTS_IN_QUERY; private ContextProvider provider; protected ContextProvider createProvider() { return new BasicContextProvider(Contexts.this) { @Override protected boolean isAllContextsPossible() { return Contexts.this.allContextsPossible; } @Override protected boolean canReadAll() { return Contexts.this.readAll; } @Override protected int getMaxContextsInQuery() { return Contexts.this.maxContextsInQuery; } @Override protected boolean disableContextChecks() { return Contexts.this.disableContextChecks; } }; } /** @javadoc */ public boolean init() { String s = getInitParameters().get("readall"); readAll = "true".equals(s); s = getInitParameters().get("allcontextspossible"); allContextsPossible = !"false".equals(s); s = getInitParameters().get("maxcontextsinquery"); if (!"".equals(s) && s != null) { maxContextsInQuery = Integer.parseInt(s); } s = getInitParameters().get("disableContextChecks"); if (!"".equals(s) && s != null) { disableContextChecks = "true".equals(s); } provider = createProvider(); return super.init(); } /** Staticly receives the MMObjectBuilder instance (cast to Contexts). A utility function. */ public static Contexts getBuilder() { return (Contexts) MMBase.getMMBase().getBuilder("mmbasecontexts"); } public ContextProvider getProvider() { return provider; } protected boolean isOwnNode(User user, MMObjectNode node) { return Authenticate.getInstance().getUserProvider().isOwnNode(user, node); } protected boolean mayDo( MMObjectNode user, MMObjectNode contextNode, Operation operation, boolean checkOwnRights) { return provider.mayDoOnContext(user, contextNode, operation, checkOwnRights); } /** * Implements check function with same arguments of Authorisation security implementation * * @see Verify#check(UserContext, Query, Operation) */ public Authorization.QueryCheck check(User userContext, Query query, Operation operation) { return provider.check(userContext, query, operation); } public final MMObjectNode getDefaultContextNode() { return getProvider().getContextNode(DEFAULT_CONTEXT); } // ******************************************************************************** // EDIT FUNCTIONS // ******************************************************************************** /** Makes sure unique values and not-null's are filed */ public void setDefaults(MMObjectNode node) { setUniqueValue(node, "name", "context"); } /** @javadoc */ protected MMObjectNode getUserNode(UserContext user) { return ((User) user).getNode(); } protected MMObjectNode getGroupOrUserNode(Parameters a) { MMObjectNode groupOrUser = getNode(a.getString(PARAMETER_GROUPORUSER)); if (groupOrUser == null) throw new IllegalArgumentException( "There is no node with id '" + a.get(PARAMETER_GROUPORUSER) + "'"); MMObjectBuilder parent = groupOrUser.getBuilder(); MMObjectBuilder userBuilder = Authenticate.getInstance().getUserProvider().getUserBuilder(); if (!(parent instanceof Groups || userBuilder.getClass().isInstance(parent))) { throw new IllegalArgumentException( "Node '" + a.get(PARAMETER_GROUPORUSER) + "' does not represent a group or a user"); } return groupOrUser; } public String toString(MMObjectNode n) { return n.getStringValue("name"); } }
/** * A bean can be accessed through the function framework. * * @author Michiel Meeuwissen * @version $Id$ * @since MMBase-1.8 */ public final class ExampleBean { private static final Logger log = Logging.getLoggerInstance(ExampleBean.class); private String parameter1; private Integer parameter2 = 0; private String parameter3 = "default"; private Node node; private Cloud cloud; public void setParameter1(String hoi) { parameter1 = hoi; } public void setParameter2(Integer j) { parameter2 = j; } public Integer getParameter2() { return parameter2; } public void setAnotherParameter(String a) { parameter3 = a; } public String getAnotherParameter() { return parameter3; } /** Makes this bean usable as a Node function. */ public void setNode(Node node) { this.node = node; } /** * Makes the functions usable in bridge (so with security). See also {@link Parameter#CLOUD}. This * is an example of a parameter which is automatically filled by function tags. */ public void setCloud(Cloud c) { cloud = c; } /** A function defined by this class */ public String stringFunction() { return "[[" + parameter1 + "/" + parameter3 + "]]"; } public Integer integerFunction() { return parameter2 * 3; } /** A function returning a Map */ public Map<String, String> mapFunction() { Map<String, String> map = new HashMap<String, String>(); map.put("bloe", parameter1); return map; } /** A function returning a Node as a core object (deprecated). */ /* public MMObjectNode nodeFunction1() { VirtualBuilder builder = new VirtualBuilder(MMBase.getMMBase()); MMObjectNode virtual = builder.getNewNode("admin"); virtual.storeValue("bloe", parameter1); return virtual; } */ public Node nodeFunction1() { Map<String, String> map = new HashMap<String, String>(); map.put("bloe", parameter1); return new org.mmbase.bridge.util.MapNode(map); } /** A function returning a Node as a bridge object, but based on a Map of values. */ public Node nodeFunction2() { Map<String, String> map = new HashMap<String, String>(); map.put("bloe", parameter1); return new org.mmbase.bridge.util.MapNode(map); } public Collection<Object> nodeListFunction() { List<Object> result = new ArrayList<Object>(); result.add(nodeFunction1()); result.add(nodeFunction2()); return result; } public NodeList nodeListFunction1() { Collection<Object> col = nodeListFunction(); col.add(mapFunction()); // return new org.mmbase.bridge.util.CollectionNodeList(col); // it's safer to specify the cloud too, especially to be able to convert the result of // nodeFunction1() return new org.mmbase.bridge.util.CollectionNodeList(col, cloud); } /** * A real node-function (using the node argument). Returns the next newer node of same type. Also * a nice example on the difference between core and bridge. */ public Object successor() { if (node == null) throw new IllegalArgumentException("successor is a node-function"); if (cloud != null) { log.debug("Using bridge (security restrictions will be honoured)"); NodeManager nm = node.getNodeManager(); NodeQuery q = nm.createQuery(); StepField field = q.getStepField(nm.getField("number")); q.setConstraint( q.createConstraint( field, FieldCompareConstraint.GREATER, Integer.valueOf(node.getNumber()))); q.addSortOrder(field, SortOrder.ORDER_ASCENDING); q.setMaxNumber(1); NodeIterator i = nm.getList(q).nodeIterator(); return i.hasNext() ? i.nextNode() : null; } else { log.debug("Using core."); throw new UnsupportedOperationException("Core implementation was dropped. See source code."); /* This is how it would go with core objects MMObjectBuilder builder = MMBase.getMMBase().getBuilder(node.getNodeManager().getName()); NodeSearchQuery query = new NodeSearchQuery(builder); StepField field = query.getField(builder.getField("number")); BasicFieldValueConstraint cons = new BasicFieldValueConstraint(field, node.getNumber()); cons.setOperator(FieldCompareConstraint.GREATER); query.setConstraint(cons); query.addSortOrder(field); query.setMaxNumber(1); try { java.util.Iterator<MMObjectNode> i = builder.getNodes(query).iterator(); return i.hasNext() ? i.next() : null; } catch (Exception e) { return null; } */ } } }
/** * This maintains a list of reusable {@link CommandExecutor.Method}s. You can obtain on unused one * with {@link #getFreeExecutor}. Supposing that you are going to want to use it in a seperate * thread, it also maintains ThreadPoolExecuters. * * @author Michiel Meeuwissen * @since MMBase-1.9.6 */ public class Executors { private static final Logger LOG = Logging.getLoggerInstance(Processor.class); static { EntityResolver.registerSystemID( Processor.NAMESPACE_CREATECACHES + ".xsd", Processor.XSD_CREATECACHES, Processor.class); } private Executors() { // no instances; } private static int transSeq = 0; private static final Map<Stage, ThreadPoolExecutor> threadPools = new EnumMap<Stage, ThreadPoolExecutor>(Stage.class); private static final ConcurrentHashMap<CommandExecutor.Method, Stage> executorsMap = new ConcurrentHashMap<CommandExecutor.Method, Stage>(); public static Map<CommandExecutor.Method, Stage> getExecutors() { return executorsMap; } protected static final ResourceWatcher watcher = new ResourceWatcher() { @Override public void onChange(String resource) { try { LOG.debug("Reading " + resource); HashMap<CommandExecutor.Method, Stage> newexecutorsMap = new HashMap<CommandExecutor.Method, Stage>(); Document document = getResourceLoader().getDocument(resource); Map<Stage, Integer> totals = new EnumMap<Stage, Integer>(Stage.class); if (document != null) { org.w3c.dom.NodeList ellist = document.getDocumentElement().getChildNodes(); Stage prevStage = Stage.RECOGNIZER; for (int i = 0; i <= ellist.getLength(); i++) { if (ellist.item(i) instanceof Element) { Element el = (Element) ellist.item(i); if (el.getTagName().equals("localhost")) { int max = Integer.parseInt(el.getAttribute("max_simultaneous_transcoders")); Stage s = Stage.valueOf(el.getAttribute("stage").toUpperCase()); Integer t = totals.get(s); if (t == null) t = 0; t += max; totals.put(s, t); for (int j = 1; j <= max; j++) { newexecutorsMap.put(new CommandExecutor.Method(), s); } } else if (el.getTagName().equals("server")) { int max = Integer.parseInt(el.getAttribute("max_simultaneous_transcoders")); Stage s = Stage.valueOf(el.getAttribute("stage").toUpperCase()); Integer t = totals.get(s); if (t == null) t = 0; t += max; totals.put(s, t); String host = el.getAttribute("host"); int port = Integer.parseInt(el.getAttribute("port")); for (int j = 1; j <= max; j++) { newexecutorsMap.put(new CommandExecutor.Method(host, port), s); } } } } for (Map.Entry<Stage, Integer> e : totals.entrySet()) { threadPools.get(e.getKey()).setCorePoolSize(e.getValue()); threadPools.get(e.getKey()).setMaximumPoolSize(e.getValue()); } } else { LOG.warn("No " + resource); } synchronized (executorsMap) { executorsMap.clear(); executorsMap.putAll(newexecutorsMap); } LOG.service( "Reading of configuration file " + resource + " successfull. Executors " + executorsMap + ". Max simultaneous transcoders: " + totals); } catch (Exception e) { LOG.error( e.getClass() + " " + e.getMessage() + " In " + resource + " Executors now " + executorsMap + " (not changed)", e); } } }; static { threadPools.put( Stage.TRANSCODER, new ThreadPoolExecutor( 3, 3, 5 * 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() { public Thread newThread(Runnable r) { return ThreadPools.newThread( r, "TranscoderThread-" + Stage.TRANSCODER + "-" + (transSeq++)); } })); threadPools.put( Stage.RECOGNIZER, new ThreadPoolExecutor( 3, 3, 5 * 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() { public Thread newThread(Runnable r) { return ThreadPools.newThread( r, "TranscoderThread-" + Stage.RECOGNIZER + "-" + (transSeq++)); } })); // register them too ThreadPools.getThreadPools() .put(Executors.class.getName() + "." + Stage.TRANSCODER, threadPools.get(Stage.TRANSCODER)); ThreadPools.getThreadPools() .put(Executors.class.getName() + "." + Stage.RECOGNIZER, threadPools.get(Stage.RECOGNIZER)); // fill the rest of the map too, so we don't have to think about it any more later on. for (Stage s : Stage.values()) { if (!threadPools.containsKey(s)) { threadPools.put(s, ThreadPools.jobsExecutor); } } // default configuration, 5 + 1 executors. for (int i = 0; i < 5; i++) { executorsMap.put(new CommandExecutor.Method(), Stage.TRANSCODER); } executorsMap.put(new CommandExecutor.Method(), Stage.RECOGNIZER); readConfiguration(); } protected static void readConfiguration() { watcher.exit(); watcher.add("streams/createcaches.xml"); watcher.setDelay(10000); watcher.onChange(); watcher.start(); } public static CommandExecutor.Method getFreeExecutor() { // for backwards compatability, be sure to return one return getFreeExecutor(Stage.TRANSCODER); } public static CommandExecutor.Method getFreeExecutor(Stage st) { while (true) { synchronized (executorsMap) { try { for (Map.Entry<CommandExecutor.Method, Stage> entry : executorsMap.entrySet()) { if (!entry.getKey().isInUse() && entry.getValue() == st) { CommandExecutor.Method m = entry.getKey(); m.setInUse(true); return m; } } LOG.info("All executors for stage " + st + " in use (will wait..)"); executorsMap.wait(); } catch (InterruptedException ie) { return null; } } } } public static void notifyExecutors() { synchronized (executorsMap) { executorsMap.notifyAll(); } } public static Future submit(Stage s, Callable c) { return threadPools.get(s).submit(c); } }
/** * A {@link Renderer} implementation based on an MMBase's {@link org.mmbase.util.ResourceLoader}. * Blocks rendered with this, cannot have parameters. * * @author Michiel Meeuwissen * @version $Id$ * @since MMBase-1.9 */ public class ResourceRenderer extends AbstractRenderer { private static final Logger log = Logging.getLoggerInstance(ResourceRenderer.class); protected String resource; protected String resourceType = "web"; protected String xsl = null; protected boolean decorate = false; public ResourceRenderer(Type t, Block parent) { super(t, parent); } public void setResource(String r) { resource = r; } public void setType(String t) { resourceType = t; } public void setXslt(String x) throws MalformedURLException { xsl = x; } public void setDecorate(boolean d) { decorate = d; } private String getResource() { if (resourceType.equals("web")) { return resource.charAt(0) == '/' ? resource : JspRenderer.JSP_ROOT + getBlock().getComponent().getName() + '/' + resource; } else { return resource; } } @Override public void render(Parameters blockParameters, Writer w, RenderHints hints) throws FrameworkException { if (decorate) { try { decorateIntro(hints, w, null); } catch (IOException ioe) { throw new FrameworkException(ioe); } } String name = getResource(); ResourceLoader loader = ResourceLoader.Type.valueOf(resourceType.toUpperCase()).get(); try { InputStream is = loader.getResourceAsStream(name); if (is == null) throw new FrameworkException( "No such resource " + loader.getResource(name) + " in " + loader); if (xsl == null) { Reader r = loader.getReader(is, name); char[] buf = new char[1000]; int c; while ((c = r.read(buf, 0, 1000)) > 0) { w.write(buf, 0, c); } } else { /// convert using the xsl and spit out that. URL x = ResourceLoader.getConfigurationRoot().getResource(xsl); Utils.xslTransform(blockParameters, loader.getResource(name), is, w, x); } } catch (IOException ioe) { throw new FrameworkException(ioe); } catch (javax.xml.transform.TransformerException te) { throw new FrameworkException(te.getMessage(), te); } catch (RuntimeException e) { log.debug(e.getMessage(), e); throw e; } finally { if (decorate) { try { decorateOutro(hints, w); } catch (IOException ioe) { throw new FrameworkException(ioe); } } } } @Override public String toString() { return resource; } @Override public URI getUri() { try { ResourceLoader loader = ResourceLoader.Type.valueOf(resourceType.toUpperCase()).get(); return loader.getResource(getResource()).toURI(); } catch (URISyntaxException use) { log.warn(use); return null; } } }
/** * The Basic Framework is based on a list of {@link UrlConverter}s. It is configured in * 'config/framework.xml'. The order in which the UrlConverters are configured is the order in which * they are processed. * * @author Michiel Meeuwissen * @version $Id$ * @since MMBase-1.9 */ public class BasicFramework extends Framework { private static final Logger log = Logging.getLoggerInstance(BasicFramework.class); public static final String XSD = "basicframework.xsd"; public static final String NAMESPACE = "http://www.mmbase.org/xmlns/basicframework"; static { org.mmbase.util.xml.EntityResolver.registerSystemID(NAMESPACE + ".xsd", XSD, Framework.class); } public static final Parameter<String> ACTION = new Parameter<String>("_action", String.class); private final ChainedUrlConverter urlConverter = new ChainedUrlConverter(); private Parameter<?>[] parDef; protected final UrlConverter fallbackConverter = new BasicUrlConverter(this); protected final LocalizedString description = new LocalizedString("description"); protected final Map<Setting<?>, Object> settingValues = new HashMap<Setting<?>, Object>(); public BasicFramework(Element el) { configure(el); } public BasicFramework() {} @Override public String getUrl( String path, Map<String, ?> parameters, Parameters frameworkParameters, boolean escapeAmps) throws FrameworkException { Url link = urlConverter.getUrl(path, parameters, frameworkParameters, escapeAmps); log.debug("got " + link + " from " + urlConverter); if (link == Url.NOT) { return fallbackConverter.getUrl(path, parameters, frameworkParameters, escapeAmps).getUrl(); } else { return link.getUrl(); } } @Override public String getProcessUrl( String path, Map<String, ?> parameters, Parameters frameworkParameters, boolean escapeAmps) throws FrameworkException { HttpServletRequest request = BasicUrlConverter.getUserRequest(frameworkParameters.get(Parameter.REQUEST)); State state = State.getState(request); frameworkParameters.set(ACTION, state.getId()); Url url = urlConverter.getProcessUrl(path, parameters, frameworkParameters, escapeAmps); if (url == Url.NOT) { log.debug("Fall back url"); return fallbackConverter .getProcessUrl(path, parameters, frameworkParameters, escapeAmps) .getUrl(); } else { log.debug("Url converter url " + url); return url.getUrl(); } } @Override public String getInternalUrl(String page, Map<String, ?> params, Parameters frameworkParameters) throws FrameworkException { if (log.isDebugEnabled()) { log.debug("calling urlConverter " + urlConverter); } return urlConverter.getInternalUrl(page, params, frameworkParameters).getUrl(); } @Override public String getName() { return "BASIC"; } /** * Configures the framework by reading its configuration file 'config/framework.xml' containing a * list with UrlConverters. */ protected final void configure(Element el) { try { description.fillFromXml("description", el); NodeList urlconverters = el.getElementsByTagName("urlconverter"); for (int i = 0; i < urlconverters.getLength(); i++) { Element element = (Element) urlconverters.item(i); UrlConverter uc; try { uc = (UrlConverter) Instantiator.getInstance(element, (Framework) this); } catch (NoSuchMethodException nsme) { uc = (UrlConverter) Instantiator.getInstance(element); } catch (ClassNotFoundException cnfe) { log.warn(org.mmbase.util.xml.XMLWriter.write(element) + " " + cnfe); continue; } catch (Throwable t) { log.error(org.mmbase.util.xml.XMLWriter.write(element) + ": " + t.getMessage(), t); if (t.getCause() != null) { log.error("Caused by: " + t.getCause().getMessage(), t.getCause()); } continue; } urlConverter.add(uc); } parDef = null; } catch (Throwable e) { log.error(e.getMessage(), e); } /* BasicUrlConverter buc = new BasicUrlConverter(this); if (! urlConverter.contains(buc)) { urlConverter.add(buc); } */ log.info( "Configured with " + el.getOwnerDocument().getDocumentURI() + " " + getClass() + " " + this); } @Override public Block getRenderingBlock(Parameters frameworkParameters) { HttpServletRequest request = frameworkParameters.get(Parameter.REQUEST); State state = State.getState(request); if (state.isRendering()) { return state.getBlock(); } else { return null; } } /*public Block getBlock(Parameters frameworkParameters) throws FrameworkException { return urlConverter.getBlock(null, frameworkParameters); } */ /** */ @Override public Parameter<?>[] getParameterDefinition() { if (parDef == null) { parDef = new Parameter<?>[] { ACTION, Parameter.REQUEST, Parameter.CLOUD, new Parameter.Wrapper(urlConverter.getParameterDefinition()) }; } return parDef; } @Override public Parameters createParameters() { return new Parameters(getParameterDefinition()); } public boolean makeRelativeUrl() { return false; } @SuppressWarnings("unchecked") protected void setBlockParametersForRender(State state, Parameters blockParameters) { ServletRequest request = state.getRequest(); String prefix = getPrefix(state); log.debug("prefix " + prefix); blockParameters.setAutoCasting(true); for (Map.Entry<String, String[]> entry : ((Map<String, String[]>) request.getParameterMap()).entrySet()) { String key = entry.getKey(); if (key.startsWith(prefix)) { log.trace("setting" + entry); blockParameters.setIfDefined(key.substring(prefix.length()), entry.getValue()); } } if (log.isDebugEnabled()) { log.debug("Set " + blockParameters); } } /** @todo */ protected void setBlockParametersForProcess(State state, Parameters blockParameters) { ServletRequest request = state.getRequest(); for (Map.Entry<String, ?> entry : blockParameters.toMap().entrySet()) { request.setAttribute(entry.getKey(), entry.getValue()); } } protected String getComponentClass() { return "mm_fw_basic"; } /** * Basic Framework implicitely also processes, i'm not sure if we should require any framework to * do that (perhaps we could say, that the render method must process, if that is necessary, and * not yet done). */ @Override public void render( Renderer renderer, Parameters blockParameters, Parameters frameworkParameters, Writer w, WindowState windowState) throws FrameworkException { ServletRequest request = frameworkParameters.get(Parameter.REQUEST); if (request == null) { throw new IllegalArgumentException("No request object given"); } State state = State.getState(request); if (state .isRendering()) { // mm:component used during rending of a component, that's fine, but use a // new State. state = new State(request); log.debug("Alreadying rendering, taking a new state for sub-block-rendering: " + state); } log.debug("Rendering " + renderer.getBlock() + " " + renderer); Object prevHints = request.getAttribute(RenderHints.KEY); try { request.setAttribute(COMPONENT_CLASS_KEY, getComponentClass()); request.setAttribute(COMPONENT_CURRENTUSER_KEY, getUserNode(frameworkParameters)); Renderer actualRenderer = state.startBlock(frameworkParameters, renderer); if (!actualRenderer.equals(renderer)) { Parameters newBlockParameters = actualRenderer.getBlock().createParameters(); newBlockParameters.setAllIfDefined(blockParameters); blockParameters = newBlockParameters; } state.setAction(request.getParameter(ACTION.getName())); if (state.needsProcess()) { log.service("Performing action on " + actualRenderer.getBlock()); Processor processor = actualRenderer.getBlock().getProcessor(); state.process(processor); log.service("Processing " + actualRenderer.getBlock() + " " + processor); setBlockParametersForProcess(state, blockParameters); processor.process(blockParameters); state.endProcess(); } state.render(actualRenderer); setBlockParametersForRender(state, blockParameters); RenderHints hints = new RenderHints( actualRenderer, windowState, state.getId(), getComponentClass(), RenderHints.Mode.NORMAL); request.setAttribute(RenderHints.KEY, hints); actualRenderer.render(blockParameters, w, hints); request.setAttribute("org.mmbase.framework.hints", hints); } catch (FrameworkException fe) { log.debug(fe); URI uri = renderer.getUri(); Renderer error = new ErrorRenderer( renderer.getType(), renderer.getBlock(), (uri != null) ? uri.toString() : null, 500, fe); RenderHints hints = new RenderHints( error, windowState, state.getId(), getComponentClass(), RenderHints.Mode.NORMAL); error.render(blockParameters, w, hints); } finally { request.setAttribute(RenderHints.KEY, prevHints); state.endBlock(); } } /** * I think in the basic framework this method is never called explicitely, because processing is * done implicitly by the render */ @Override public void process( Processor processor, Parameters blockParameters, Parameters frameworkParameters) throws FrameworkException { HttpServletRequest request = frameworkParameters.get(Parameter.REQUEST); State state = State.getState(request); state.startBlock(frameworkParameters, null); setBlockParametersForProcess(state, blockParameters); processor.process(blockParameters); } @Override public Node getUserNode(Parameters frameworkParameters) { Cloud cloud = frameworkParameters.get(Parameter.CLOUD); if (cloud == null) { return null; } else { try { int userNode = cloud.getCloudContext().getAuthentication().getNode(cloud.getUser()); if (cloud.hasNode(userNode)) { return cloud.getNode(userNode); } else { return null; } } catch (UnsupportedOperationException uoe) { // never mind return null; } } } @Override public String getUserBuilder() { // TODO // return // org.mmbase.module.core.MMBase.getMMBase().getMMBaseCop().getAuthentication().getUserBuilder(); return "mmbaseusers"; } public Map<String, Object> prefix(State state, Map<String, Object> params) { return getMap(state, params); } protected String getPrefix(final State state) { // return "_" + renderer.getBlock().getComponent().getName() + "_" + // renderer.getBlock().getName() + "_" + count + "_"; return state.getId() + ":"; } protected Map<String, Object> getMap(final State state, final Map<String, Object> params) { return new AbstractMap<String, Object>() { @Override public Set<Map.Entry<String, Object>> entrySet() { return new AbstractSet<Map.Entry<String, Object>>() { @Override public int size() { return params.size(); } @Override public Iterator<Map.Entry<String, Object>> iterator() { return new Iterator<Map.Entry<String, Object>>() { private Iterator<Map.Entry<String, Object>> i = params.entrySet().iterator(); @Override public boolean hasNext() { return i.hasNext(); } @Override public Map.Entry<String, Object> next() { Map.Entry<String, Object> e = i.next(); return new org.mmbase.util.Entry<String, Object>( getPrefix(state) + e.getKey(), e.getValue()); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }; } }; } @Override public String toString() { return getName() + ": " + description + ": " + urlConverter.toString(); } private static final Parameter<Boolean> USE_REQ = new Parameter<Boolean>("usesession", Boolean.class, Boolean.TRUE); @Override public Parameters createSettingValueParameters() { return new Parameters(Parameter.REQUEST, Parameter.CLOUD, USE_REQ); } protected String getKey(Setting<?> setting) { return "org.mmbase.framework." + setting.getComponent().getName() + "." + setting.getName(); } @SuppressWarnings("unchecked") @Override public <C> C getSettingValue(Setting<C> setting, Parameters parameters) { boolean useSession = parameters != null && parameters.get(USE_REQ); if (useSession) { HttpServletRequest req = parameters.get(Parameter.REQUEST); if (req != null) { Object v = req.getSession(true).getAttribute(getKey(setting)); if (v != null) { return setting.getDataType().cast(v, null, null); } } } if (settingValues.containsKey(setting)) { return (C) settingValues.get(setting); } else { C settingValue = loadSettingValue(setting); if (settingValue != null) { settingValues.put(setting, settingValue); return settingValue; } return setting.getDataType().getDefaultValue(); } } @SuppressWarnings("unchecked") @Override public <C> C setSettingValue(Setting<C> setting, Parameters parameters, C value) { if (parameters == null) throw new SecurityException("You should provide Cloud and request parameters"); boolean useSession = parameters.get(USE_REQ); if (useSession) { C ret = getSettingValue(setting, parameters); HttpServletRequest req = parameters.get(Parameter.REQUEST); req.getSession(true).setAttribute(getKey(setting), value); return ret; } else { Cloud cloud = parameters.get(Parameter.CLOUD); if (cloud.getUser().getRank() == org.mmbase.security.Rank.ADMIN) { saveSettingValue(setting, value); return (C) settingValues.put(setting, value); } else { throw new SecurityException("Permission denied"); } } } public <C> C loadSettingValue(Setting<C> setting) { String v = null; // TODO SystemProperties.getComponentProperty(setting.getComponent().getName(), // setting.getName()); if (v != null) { return setting.getDataType().cast(v, null, null); } return null; } public <C> void saveSettingValue(Setting<C> setting, C value) { // SystemProperties.setComponentProperty(setting.getComponent().getName(), setting.getName(), // value.toString()); } }
/** * @javadoc * @deprecated is this (cacheversionfile) used? seems obsolete now * @author Daniel Ockeloen * @version $Id$ */ class VersionCacheNode { private static Logger log = Logging.getLoggerInstance(VersionCacheNode.class.getName()); private MMObjectNode versionnode; private List<VersionCacheWhenNode> whens = new Vector<VersionCacheWhenNode>(); private MMBase mmb; public VersionCacheNode(MMBase mmb) { this.mmb = mmb; } /** @javadoc */ public void handleChanged(String buildername, int number) { // method checks if this really something valid // and we should signal a new version boolean dirty = false; for (VersionCacheWhenNode whennode : whens) { List<String> types = whennode.getTypes(); // check if im known in the types part if (types.contains(buildername)) { // is there only 1 builder type ? if (log.isDebugEnabled()) log.debug("types=" + types.toString()); if (types.size() == 1) { dirty = true; } else { // so multiple prepare a multilevel ! List<String> nodes = whennode.getNodes(); List<String> fields = new Vector<String>(); fields.add(buildername + ".number"); List<String> ordervec = new Vector<String>(); List<String> dirvec = new Vector<String>(); List<MMObjectNode> vec = mmb.getClusterBuilder() .searchMultiLevelVector( nodes, fields, "YES", types, buildername + ".number==" + number, ordervec, dirvec); if (log.isDebugEnabled()) log.debug("VEC=" + vec); if (vec != null && vec.size() > 0) { dirty = true; } } } } if (dirty) { // add one to the version of this counter int version = versionnode.getIntValue("version"); versionnode.setValue("version", version + 1); versionnode.commit(); if (log.isDebugEnabled()) log.debug("Changed = " + (version + 1)); } } public void setVersionNode(MMObjectNode versionnode) { this.versionnode = versionnode; } /** @javadoc */ public void addWhen(VersionCacheWhenNode when) { whens.add(when); } }
/** * Test class <code>Transaction</code> from the bridge package. * * @author Michiel Meeuwissen * @version $Id$ * @since MMBase-1.8.6 */ public class TransactionTest extends BridgeTest { private static final Logger log = Logging.getLoggerInstance(TransactionTest.class); public TransactionTest(String name) { super(name); } static int seq = 0; int newNode; int newNode2; public void setUp() { seq++; // Create some test nodes Cloud cloud = getCloud(); { Node node = cloud.getNodeManager("news").createNode(); node.setStringValue("title", "foo"); node.commit(); newNode = node.getNumber(); } { Node node = cloud.getNodeManager("news").createNode(); node.setStringValue("title", "foo"); node.createAlias("test.news." + seq); node.setContext("default"); node.commit(); newNode2 = node.getNumber(); } } public void testCancel() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("cancel1"); Node node = t.getNode(newNode); node.setStringValue("title", "xxxxx"); node.commit(); t.cancel(); node = cloud.getNode(newNode); assertEquals("foo", node.getStringValue("title")); } public void testCancel2() { Cloud cloud = getCloud(); { Transaction t = cloud.getTransaction("cancel2"); Node node = t.getNode(newNode); node.setStringValue("title", "xxxxx"); node.commit(); t.cancel(); } { Transaction t = cloud.getTransaction("cancel2"); Node node = t.getNode(newNode); assertEquals("foo", node.getStringValue("title")); t.cancel(); } } public void testGetTransaction() { Cloud cloud = getCloud(); { Transaction t = cloud.getTransaction("gettransactiontest"); Node node = t.getNode(newNode); node.setStringValue("title", "xxxxx"); } { Transaction t = cloud.getTransaction("gettransactiontest"); Node node = t.getNode(newNode); assertEquals("xxxxx", node.getStringValue("title")); t.cancel(); } } public void testCommit() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("bar3"); Node node = t.getNode(newNode); node.setStringValue("title", "yyyyy"); node.commit(); t.commit(); node = cloud.getNode(newNode); assertEquals("yyyyy", node.getStringValue("title")); } public void testMMB1546() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("test0"); Node nt = t.getNode(newNode); nt.setValue("title", "bla"); // t.cancel(); _DONT_ cancel Node nc = cloud.getNode(newNode); nc.setValue("title", "bloe"); nc.commit(); assertEquals("bloe", nc.getStringValue("title")); assertEquals("bloe", cloud.getNode(newNode).getStringValue("title")); t.cancel(); assertEquals("bloe", cloud.getNode(newNode).getStringValue("title")); } // Test for http://www.mmbase.org/jira/browse/MMB-1621 public void testGetValue() { Cloud cloud = getCloud(); String value = cloud.getNode(newNode).getStringValue("title"); assertFalse("zzzzz".equals(value)); Transaction t = cloud.getTransaction("bar4"); System.out.println("Transaction now " + t); Node node = t.getNode(newNode); assertEquals(1, t.getNodes().size()); node.setStringValue("title", "zzzzz"); assertEquals("zzzzz", node.getStringValue("title")); node.commit(); // committing inside transaction assertEquals("zzzzz", node.getStringValue("title")); assertEquals(value, cloud.getNode(newNode).getStringValue("title")); t.commit(); assertEquals("zzzzz", cloud.getNode(newNode).getStringValue("title")); } public void testReuseTransaction() { Cloud cloud = getCloud(); { Node node = cloud.getNode(newNode); node.setStringValue("title", "zzyyxx"); node.commit(); assertEquals("zzyyxx", cloud.getNode(newNode).getStringValue("title")); } { Transaction t = cloud.getTransaction("bar4"); assertEquals(0, t.getNodes().size()); Node node = t.getNode(newNode); assertEquals("zzyyxx", node.getStringValue("title")); assertEquals(1, t.getNodes().size()); node.setStringValue("title", "wwwwww"); node.commit(); assertEquals("wwwwww", node.getStringValue("title")); t.cancel(); assertEquals("zzyyxx", cloud.getNode(newNode).getStringValue("title")); } assertEquals("zzyyxx", cloud.getNode(newNode).getStringValue("title")); } public void testCancelDelete() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("bar5"); Node node = t.getNode(newNode); node.delete(); t.cancel(); assertTrue(cloud.hasNode(newNode)); } public void testCommitDelete() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("bar6"); Node node = t.getNode(newNode); node.delete(); t.commit(); assertFalse(cloud.hasNode(newNode)); } public void testSetContext() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("bar7"); Node n = t.getNodeManager("news").createNode(); assertEquals(1, t.getNodes().size()); n.setContext("non_default"); assertEquals(1, t.getNodes().size()); assertEquals("non_default", n.getStringValue("owner")); assertEquals("non_default", n.getContext()); assertEquals(1, t.getNodes().size()); t.commit(); Node n2 = cloud.getNode(n.getNumber()); assertEquals("non_default", n2.getStringValue("owner")); assertEquals("non_default", n2.getContext()); } public void testSetContextSubTransaction() { Cloud cloud = getCloud(); Transaction ot = cloud.getTransaction("bar8"); Transaction t = ot.getTransaction("bar9"); Node n = t.getNodeManager("news").createNode(); n.setContext("non_default"); assertEquals("non_default", n.getContext()); t.commit(); Node n2 = ot.getNode(n.getNumber()); assertEquals("non_default", n2.getContext()); ot.commit(); Node n3 = cloud.getNode(n.getNumber()); assertEquals("non_default", n3.getContext()); } public void testEditNodeOutsideTransaction() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("bar10"); Node nodeInTransaction = t.getNode(newNode2); nodeInTransaction.setStringValue("title", "foo2"); Node nodeOutTransaction = cloud.getNode(newNode2); nodeOutTransaction.setStringValue("title", "bar2"); nodeOutTransaction.commit(); t.commit(); // transaction was committed _later_ so its commit of the node must have won assertEquals("foo2", cloud.getNode(newNode2).getStringValue("title")); assertEquals("foo2", nodeInTransaction.getStringValue("title")); // assertEquals("foo2", nodeOutTransaction.getStringValue("title")); // not sure what this // should have done, but anyhow, it now fails } public void testEditNodeOutsideTransaction2() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("bar10"); Node nodeInTransaction = t.getNode(newNode2); nodeInTransaction.setStringValue("title", "foo2"); Node nodeOutTransaction = cloud.getNode(newNode2); nodeOutTransaction.setStringValue("title", "bar2"); t.commit(); nodeOutTransaction.commit(); // transaction was committed _earlier_ so the commit of the node must have won assertEquals("bar2", cloud.getNode(newNode2).getStringValue("title")); // assertEquals("bar2", nodeInTransaction.getStringValue("title"));// not sure what this should // have done, but anyhow, it now fails assertEquals("bar2", nodeOutTransaction.getStringValue("title")); } public void testDeleteNodeOutsideTransaction() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("bar11"); Node nodeInTransaction = t.getNode(newNode2); nodeInTransaction.setStringValue("title", "foo2"); { // now delete the node Node nodeOutTransaction = cloud.getNode(newNode2); nodeOutTransaction.delete(); assertFalse(cloud.hasNode(newNode2)); } try { t.commit(); } catch (Exception e) { // should not give exception. MMB-1680 log.error(e.getMessage(), e); fail(e.getMessage()); } assertTrue(cloud.hasNode(newNode2)); } // same case as above, only no changes are made to the node. public void testDeleteNodeOutsideTransactionNodeInTransactionButNotChanged() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("bar11"); Node nodeInTransaction = t.getNode(newNode2); // nodeInTransaction.setStringValue("title", "foo2"); { // now delete the node Node nodeOutTransaction = cloud.getNode(newNode2); nodeOutTransaction.delete(); assertFalse(cloud.hasNode(newNode2)); } try { // make a relation to the (deleted) node, but in the transaction, where the node still // exists. // This demonstrate that there is an actual problem if the node ends up non-existing now. Node url = t.getNodeManager("urls").createNode(); RelationManager rm = t.getRelationManager("news", "urls", "posrel"); Relation r = nodeInTransaction.createRelation(url, rm); t.commit(); } catch (Exception e) { // should not give exception. MMB-1680 log.error(e.getMessage(), e); fail(e.getMessage()); } assertTrue(cloud.hasNode(newNode2)); assertEquals(1, cloud.getNode(newNode2).countRelations()); } public void testAlias() { Cloud cloud = getCloud(); { Node node = cloud.getNode(newNode2); node.setStringValue("title", "abcdef"); node.commit(); assertEquals("abcdef", node.getStringValue("title")); node = cloud.getNode("test.news." + seq); assertEquals("abcdef", node.getStringValue("title")); } { Transaction t = cloud.getTransaction("bar12"); Node node = t.getNode(newNode2); node.setStringValue("title", "abcdefg"); node.commit(); assertEquals("abcdefg", node.getStringValue("title")); t.commit(); assertEquals("abcdefg", node.getStringValue("title")); node = cloud.getNode("test.news." + seq); assertEquals("abcdefg", node.getStringValue("title")); node = cloud.getNode(newNode2); assertEquals("abcdefg", node.getStringValue("title")); } } public void testGetNodeTwiceWhileChanged() { Cloud cloud1 = getCloud(); String originalTitleValue = cloud1.getNode(newNode2).getStringValue("title"); { Node node = cloud1.getNode(newNode2); String title1 = node.getStringValue("title"); node.setStringValue("title", "bla bla"); // don't commit } // now change something by someone else { Cloud cloud2 = getCloud("foo"); assertTrue(cloud1 != cloud2); Node node = cloud2.getNode(newNode2); node.setStringValue("title", "new title value"); node.commit(); } // now look to the original cloud { Node node = cloud1.getNode(newNode2); assertEquals("new title value", node.getStringValue("title")); } } public void testCreateRelationBetweenNewNodes() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("createrelationtrans"); NodeManager news = t.getNodeManager("news"); Node n = news.createNode(); NodeManager urls = t.getNodeManager("urls"); Node url = urls.createNode(); RelationManager rm = t.getRelationManager("news", "urls", "posrel"); Relation r = n.createRelation(url, rm); t.commit(); } // new node as argument public void testCreateRelationToNewNode() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("createrelationtrans"); Node n = t.getNode(newNode); NodeManager urls = t.getNodeManager("urls"); Node url = urls.createNode(); RelationManager rm = t.getRelationManager("news", "urls", "posrel"); Relation r = n.createRelation(url, rm); t.commit(); } // old node as argument public void testCreateRelationToNewNode2() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("createrelationtrans"); Node n = t.getNode(newNode); NodeManager urls = t.getNodeManager("urls"); Node url = urls.createNode(); RelationManager rm = t.getRelationManager("urls", "news", "posrel"); Relation r = url.createRelation(n, rm); t.commit(); } public void testTransactionsAreEqual() { Cloud cloud = getCloud(); Transaction t1 = cloud.getTransaction("testequals"); Transaction t2 = cloud.getTransaction("testequals"); assertEquals(t1, t2); Node n = t1.getNode(newNode); NodeManager urls = t2.getNodeManager("urls"); Node url = urls.createNode(); RelationManager rm = t2.getRelationManager("urls", "news", "posrel"); Relation r = url.createRelation(n, rm); t2.commit(); assertTrue(t2.isCommitted()); assertTrue(t1.isCommitted()); // assertTrue(t1 == t2); // FAILS IN RMMCI. Perhaps we should simply implement .equals on // transactions } // MMB-1857 public void testGetNodes() { Cloud cloud = getCloud(); Transaction t = cloud.getTransaction("testgetnodes"); Node n = t.getNode(newNode); Node url = t.getNodeManager("urls").createNode(); RelationManager rm = t.getRelationManager("urls", "news", "posrel"); Relation r = url.createRelation(n, rm); { // should not give NPE's or so n.commit(); url.commit(); r.commit(); } assertEquals(3, t.getNodes().size()); // 2 nodes and one relation for (Node rn : t.getNodes()) { // should occur no exceptions rn.commit(); // should have little effect in trans } t.cancel(); } public void testGetNode() { // Create new node. Request the node again. // Should work Cloud cloud = getCloud(); int urlCount = Queries.count(cloud.getNodeManager("urls").createQuery()); Transaction t = cloud.getTransaction("testgetnode"); Node url = t.getNodeManager("urls").createNode(); url.setStringValue("url", "http://bla"); url.commit(); Node reurl = t.getNode(url.getNumber()); assertEquals("http://bla", reurl.getStringValue("url")); } // MMB-1860 public void testCreateAndDelete() { // Create new node. Delete it. Commit the transaction. // The new node must not exist. Cloud cloud = getCloud(); int urlCount = Queries.count(cloud.getNodeManager("urls").createQuery()); Transaction t = cloud.getTransaction("testcreateandelete"); Node url = t.getNodeManager("urls").createNode(); url.commit(); assertEquals(1, t.getNodes().size()); url.delete(); assertEquals( 1, t.getNodes() .size()); // 0 would also be an option, but the node remaisn in the transaction as // 'NOLONGER' t.commit(); int urlCountAfter = Queries.count(cloud.getNodeManager("urls").createQuery()); assertEquals(urlCount, urlCountAfter); } public void testCreateAndDelete2() { // Create new node. Request the node again. Delete that. Commit the transaction. // The new node must not exist. Cloud cloud = getCloud(); int urlCount = Queries.count(cloud.getNodeManager("urls").createQuery()); Transaction t = cloud.getTransaction("testcreateandelete"); Node url = t.getNodeManager("urls").createNode(); url.commit(); assertEquals(1, t.getNodes().size()); Node reurl = t.getNode(url.getNumber()); reurl.delete(); assertEquals( 1, t.getNodes() .size()); // 0 would also be an option, but the node remaisn in the transaction as // 'NOLONGER' t.commit(); int urlCountAfter = Queries.count(cloud.getNodeManager("urls").createQuery()); assertEquals(urlCount, urlCountAfter); } // MMB-1860 (2) public void testCreateRelateAndDelete() { // Create new node. Make a relation to it. Delete the node again. Commit the transaction. // The new node must not exist, but the relation shouldn't have caused errors Cloud cloud = getCloud(); int urlCount = Queries.count(cloud.getNodeManager("urls").createQuery()); Transaction t = cloud.getTransaction("testcreateandelete"); Node news = t.getNode(newNode); Node url = t.getNodeManager("urls").createNode(); RelationManager rm = t.getRelationManager("urls", "news", "posrel"); Relation r = url.createRelation(news, rm); url.delete(true); t.commit(); int urlCountAfter = Queries.count(cloud.getNodeManager("urls").createQuery()); assertEquals(urlCount, urlCountAfter); } // MMB-1860 (3) public void testCreateRelateAndDeleteRelation() { // Create new node. Make a relation to it. Delete the relation again. Commit the transaction. // The new node must exist, but the relation shouldn't. Cloud cloud = getCloud(); int urlCount = Queries.count(cloud.getNodeManager("urls").createQuery()); int relCount = Queries.count(cloud.getNodeManager("insrel").createQuery()); Transaction t = cloud.getTransaction("testcreateandelete"); Node news = t.getNode(newNode); Node url = t.getNodeManager("urls").createNode(); RelationManager rm = t.getRelationManager("urls", "news", "posrel"); Relation r = url.createRelation(news, rm); r.commit(); r.delete(); t.commit(); int urlCountAfter = Queries.count(cloud.getNodeManager("urls").createQuery()); assertEquals(urlCount + 1, urlCountAfter); // Bit the relation should not exist, because it was deleted angain int relCountAfter = Queries.count(cloud.getNodeManager("insrel").createQuery()); assertEquals(relCount, relCountAfter); } // MMB-1860 (4) public void testRelateAndDeleteRelation() { // Make a relation between two nodes. Delete the relation again. Commit the transaction. // Te relation shouldn't exist Cloud cloud = getCloud(); int urlCount = Queries.count(cloud.getNodeManager("urls").createQuery()); int relCount = Queries.count(cloud.getNodeManager("insrel").createQuery()); Node newUrl = cloud.getNodeManager("urls").createNode(); newUrl.commit(); Transaction t = cloud.getTransaction("testrelateandeleterelation"); Node news = t.getNode(newNode); Node url = t.getNode(newUrl.getNumber()); RelationManager rm = t.getRelationManager("urls", "news", "posrel"); Relation r = url.createRelation(news, rm); r.commit(); r.delete(); t.commit(); int urlCountAfter = Queries.count(cloud.getNodeManager("urls").createQuery()); assertEquals(urlCount + 1, urlCountAfter); // But the relation should not exist, because it was deleted angain int relCountAfter = Queries.count(cloud.getNodeManager("insrel").createQuery()); assertEquals(relCount, relCountAfter); } // MMB-1889 public void testCreateRelationAndDeleteNode() { // Make a relation to an existing node. Then delete that node with the 'delete relations' // option'. Commit the transaction. // The new relations should not exist, since the node was deleted. // No errors. Cloud cloud = getCloud(); int urlCount = Queries.count(cloud.getNodeManager("urls").createQuery()); int relCount = Queries.count(cloud.getNodeManager("insrel").createQuery()); Node url = cloud.getNodeManager("urls").createNode(); url.commit(); Transaction t = cloud.getTransaction("relatedandelete"); Node turl = t.getNode(url.getNumber()); Node news = t.getNode(newNode); RelationManager rm = t.getRelationManager("urls", "news", "posrel"); Relation r = turl.createRelation(news, rm); r.commit(); turl.delete(true); t.commit(); int urlCountAfter = Queries.count(cloud.getNodeManager("urls").createQuery()); assertEquals(urlCount, urlCountAfter); int relCountAfter = Queries.count(cloud.getNodeManager("insrel").createQuery()); assertEquals(relCount, relCountAfter); } // MMB-1893 public void testDeleteNodeWitRelationsAndCancel() { Cloud cloud = getCloud(); int urlCount0 = Queries.count(cloud.getNodeManager("urls").createQuery()); int relCount0 = Queries.count(cloud.getNodeManager("insrel").createQuery()); Node url = cloud.getNodeManager("urls").createNode(); url.commit(); Node news = cloud.getNode(newNode); RelationManager rm = cloud.getRelationManager("urls", "news", "posrel"); Relation r = url.createRelation(news, rm); r.commit(); int urlCount = Queries.count(cloud.getNodeManager("urls").createQuery()); int relCount = Queries.count(cloud.getNodeManager("insrel").createQuery()); assertEquals(urlCount0 + 1, urlCount); assertEquals(relCount0 + 1, relCount); Transaction t = cloud.getTransaction("deletewithrelationsandcancel"); Node turl = t.getNode(url.getNumber()); turl.delete(true); t.cancel(); int urlCountAfter = Queries.count(cloud.getNodeManager("urls").createQuery()); assertEquals(urlCount, urlCountAfter); int relCountAfter = Queries.count(cloud.getNodeManager("insrel").createQuery()); assertEquals(relCount, relCountAfter); // MMB-1893 } }
/** * install manager, keeps track of what is being installed, provides background threads and logs * errors & message to feedback to users. * * @author Daniel Ockeloen (MMBased) */ public class UninstallManager { private static Logger log = Logging.getLoggerInstance(UninstallManager.class); // is the uninstall manager active (for dependencies reasons) private static boolean active = false; // signal if we are installing a bundle or a package private static boolean bundle = false; // package we are uninstalling private static PackageInterface pkg; private static BundleInterface bnd; private static uninstallThread runner; private static String state; /** uninstall a package */ public static synchronized boolean uninstallPackage(PackageInterface p) { if (!active) { // turn the uninstallManager to active active = true; // signal we are a package only install bundle = false; // set the package pkg = p; state = "uninstalling"; p.clearInstallSteps(); runner = new uninstallThread(); return true; } else { // returning false _allways_ means we where busy // error feedback is provided by the processsteps return false; } } /** * called by the install thread class, performs the real install in the background and keeps * providing feedback using the steps interfaces */ public static void performUninstall() { try { if (bnd != null) { bnd.uninstall(); state = "waiting"; active = false; bnd = null; } else if (pkg != null) { pkg.uninstall(); state = "waiting"; active = false; pkg = null; } } catch (Exception e) { log.error("performInstall problem"); } } /** uninstall a package */ public static synchronized boolean uninstallBundle(BundleInterface b) { if (!active) { // turn the uninstallManager to active active = true; // signal we are a package only install bundle = true; // set the package bnd = b; state = "uninstalling"; b.clearInstallSteps(); runner = new uninstallThread(); return true; } else { // returning false _allways_ means we where busy // error feedback is provided by the processsteps return false; } } public void setState(String state) { this.state = state; } public String getState() { return state; } public static boolean isActive() { return active; } public static PackageInterface getUnInstallingPackage() { return pkg; } public static BundleInterface getUnInstallingBundle() { return bnd; } public static Iterator<installStep> getUninstallSteps() { return pkg.getInstallSteps(); } }
/** * If rendering of a {@link Block} fails for some reason this renderer should be used to present the * error. * * @author Michiel Meeuwissen * @version $Id$ * @since MMBase-1.9 */ public class ErrorRenderer extends AbstractRenderer { private static final Logger log = Logging.getLoggerInstance(ErrorRenderer.class); protected final Error error; protected final String url; protected static int MAX_CAUSES = 4; public ErrorRenderer(Type t, Block parent, String u, int status, String m) { super(t, parent); error = new Error(status, new Exception(m)); url = u; } public ErrorRenderer(Type t, Block parent, String u, int status, Throwable e) { super(t, parent); error = new Error(status, e); url = u; } @Override public Parameter<?>[] getParameters() { return new Parameter<?>[] {Parameter.RESPONSE}; } @Override public void render(Parameters blockParameters, Writer w, RenderHints hints) throws FrameworkException { log.debug("Error rendering " + blockParameters); switch (getType()) { case BODY: try { decorateIntro(hints, w, "error"); w.write("<h1>" + error.status); w.write(": "); CharTransformer escape = new Xml(Xml.ESCAPE); w.write(escape.transform(error.exception.getMessage())); w.write(" "); w.write(escape.transform(url)); w.write("</h1>"); w.write("<pre>"); HttpServletRequest request = blockParameters.get(Parameter.REQUEST); error.getErrorReport(w, request, escape); w.write("</pre>"); decorateOutro(hints, w); } catch (IOException eio) { throw new FrameworkException(eio.getMessage(), eio); } break; default: } } @Override public String toString() { return "ERROR " + error; } @Override public java.net.URI getUri() { try { return new java.net.URL(url).toURI(); } catch (Exception e) { return null; } } public static class Error { public int status; public final Throwable exception; private Boolean showSession = null; private Pattern requestIgnore = null; private Pattern sessionIgnore = null; private Boolean showMMBaseVersion = null; public Error(int s, Throwable e) { status = s; exception = e; } public void setShowSession(Boolean b) { showSession = b; } public void setRequestIgnore(String i) { requestIgnore = i == null ? null : Pattern.compile(i); } public void setSessionIgnore(String i) { sessionIgnore = i == null ? null : Pattern.compile(i); } public void setShowVersion(Boolean b) { showMMBaseVersion = b; } protected LinkedList<Throwable> getStack() { Throwable e = exception; LinkedList<Throwable> stack = new LinkedList<Throwable>(); while (e != null) { stack.addFirst(e); if (e instanceof NotFoundException) { status = HttpServletResponse.SC_NOT_FOUND; } if (e instanceof ServletException) { Throwable t = ((ServletException) e).getRootCause(); if (t == null) t = e.getCause(); e = t; } else if (e instanceof javax.servlet.jsp.JspException) { Throwable t = ((JspException) e).getRootCause(); if (t == null) t = e.getCause(); e = t; } else { e = e.getCause(); } } return stack; } protected String getTitle(Throwable t) { String message = t.getMessage(); String tit = message; if (tit == null) { StackTraceElement el = t.getStackTrace()[0]; tit = t.getClass().getName().substring(t.getClass().getPackage().getName().length() + 1) + " " + el.getFileName() + ":" + el.getLineNumber(); } return tit; } public String getTitle() { LinkedList<Throwable> stack = getStack(); if (stack.isEmpty()) { return "NO EXCEPTION"; } else { return getTitle(stack.removeFirst()); } } public Writer getErrorReport( Writer to, final HttpServletRequest request, CharTransformer escape) throws IOException { final Writer logMsg = new StringWriter(); final Writer tee = new org.mmbase.util.ChainedWriter(to, logMsg); Writer msg = tee; LinkedList<Throwable> stack = getStack(); String ticket = new Date().toString(); Map<String, String> props; try { props = org.mmbase.util.ApplicationContextReader.getProperties("mmbase_errorpage"); } catch (javax.naming.NamingException ne) { props = Collections.emptyMap(); log.info(ne); } if (request != null) { { msg.append("Headers\n----------\n"); // request properties for (Object name : Collections.list(request.getHeaderNames())) { msg.append( escape.transform( name + ": " + escape.transform(request.getHeader((String) name)) + "\n")); } } { msg.append("\nAttributes\n----------\n"); Pattern p = requestIgnore; if (p == null && props.get("request_ignore") != null) { p = Pattern.compile(props.get("request_ignore")); } for (Object name : Collections.list(request.getAttributeNames())) { if (p == null || !p.matcher((String) name).matches()) { msg.append( escape.transform(name + ": " + request.getAttribute((String) name) + "\n")); } } } if (Boolean.TRUE.equals(showSession) || (showSession == null && !"false".equals(props.get("show_session")))) { HttpSession ses = request.getSession(false); if (ses != null) { msg.append("\nSession\n----------\n"); Pattern p = sessionIgnore; if (p == null && props.get("session_ignore") != null) { p = Pattern.compile(props.get("session_ignore")); } for (Object name : Collections.list(ses.getAttributeNames())) { if (p == null || !p.matcher((String) name).matches()) { msg.append(escape.transform(name + ": " + ses.getAttribute((String) name) + "\n")); } } } } } msg.append("\n"); msg.append("Misc. properties\n----------\n"); if (request != null) { msg.append("method: ").append(escape.transform(request.getMethod())).append("\n"); msg.append("querystring: ").append(escape.transform(request.getQueryString())).append("\n"); msg.append("requesturl: ") .append(escape.transform(request.getRequestURL().toString())) .append("\n"); } if (Boolean.TRUE.equals(showMMBaseVersion) || (showMMBaseVersion == null && !"false".equals(props.get("show_mmbase_version")))) { msg.append("mmbase version: ").append(org.mmbase.Version.get()).append("\n"); } msg.append("status: ").append("").append(String.valueOf(status)).append("\n\n"); if (request != null) { msg.append("Parameters\n----------\n"); // request parameters Enumeration en = request.getParameterNames(); while (en.hasMoreElements()) { String name = (String) en.nextElement(); msg.append(name) .append(": ") .append(escape.transform(request.getParameter(name))) .append("\n"); } } msg.append("\nException ") .append(ticket) .append("\n----------\n\n") .append( exception != null ? (escape.transform(exception.getClass().getName())) : "NO EXCEPTION") .append(": "); int wroteCauses = 0; while (!stack.isEmpty()) { Throwable t = stack.removeFirst(); // add stack stacktraces if (t != null) { if (stack.isEmpty()) { // write last message always msg = tee; } String message = t.getMessage(); if (msg != tee) { to.append("\n=== skipped(see log) : ") .append(escape.transform(t.getClass().getName())) .append(": ") .append(message) .append("\n"); } msg.append("\n\n").append(escape.transform(t.getClass().getName() + ": " + message)); StackTraceElement[] stackTrace = t.getStackTrace(); for (StackTraceElement e : stackTrace) { msg.append("\n at ").append(escape.transform(e.toString())); } if (!stack.isEmpty()) { msg.append("\n-------caused:\n"); } wroteCauses++; if (wroteCauses >= MAX_CAUSES) { msg = logMsg; } } } // write errors to log if (status == 500) { try { if (props.get("to") != null && props.get("to").length() > 0) { javax.naming.Context initCtx = new javax.naming.InitialContext(); javax.naming.Context envCtx = (javax.naming.Context) initCtx.lookup("java:comp/env"); Object mailSession = envCtx.lookup("mail/Session"); Class sessionClass = Class.forName("javax.mail.Session"); Class recipientTypeClass = Class.forName("javax.mail.Message$RecipientType"); Class messageClass = Class.forName("javax.mail.internet.MimeMessage"); Object mail = messageClass.getConstructor(sessionClass).newInstance(mailSession); messageClass .getMethod("addRecipients", recipientTypeClass, String.class) .invoke(mail, recipientTypeClass.getDeclaredField("TO").get(null), props.get("to")); messageClass.getMethod("setSubject", String.class).invoke(mail, ticket); mail.getClass().getMethod("setText", String.class).invoke(mail, logMsg.toString()); Class.forName("javax.mail.Transport") .getMethod("send", Class.forName("javax.mail.Message")) .invoke(null, mail); tee.append("\nmailed to (").append(String.valueOf(props)).append(")"); } } catch (Exception nnfe) { tee.append("\nnot mailed (").append(String.valueOf(nnfe)).append(")"); if (log.isDebugEnabled()) { log.debug(nnfe.getMessage(), nnfe); } } log.error("TICKET " + ticket + ":\n" + logMsg); } return to; } } }
/** * Downloads a media stream from an url for a media item (mediafragments node) puts it in the files * directory and returns it's filename when finished. A timeout parameter let's you set number of * seconds you wish to wait for the download to finish. When you specify an email address as * parameter you get an email when it's really finished. The function starts a thread and calls * {@link Downloader} to do the actual work. The media file itself is saved in a mediasources node * and transcoded by the streams application when the download finishes. Url and information about * success or failure of the download are saved as properties on the mediafragments node. * * @author Michiel Meeuwissen * @author André van Toly * @version $Id$ */ public final class DownloadFunction extends NodeFunction<String> { private static final long serialVersionUID = 0L; private static final Logger log = Logging.getLoggerInstance(DownloadFunction.class); /* url to get */ private static final Parameter<String> URL = new Parameter<String>("url", String.class); /* timeout: how long you are prepaired to wait for a result, default is 5 seconds */ private static final Parameter<Integer> TIMEOUT = new Parameter<Integer>("timeout", Integer.class); /* email address to send ready to */ private static final Parameter<String> EMAIL = new Parameter<String>("email", String.class); public static final Parameter[] PARAMETERS = { URL, EMAIL, TIMEOUT, Parameter.LOCALE, Parameter.REQUEST }; private static final String URL_KEY = DownloadFunction.class.getName() + ".url"; private static final String STATUS_KEY = DownloadFunction.class.getName() + ".status"; private static final Map<Integer, Future<?>> runningJobs = new ConcurrentHashMap<Integer, Future<?>>(); public DownloadFunction() { super("download", PARAMETERS); } protected void setProperty(Node node, String key, String value) { NodeManager properties = node.getCloud().getNodeManager("properties"); Function set = properties.getFunction("set"); Parameters params = set.createParameters(); params.set("node", node); params.set("key", key); if (value.length() > 255) { value = value.substring(0, 255); } params.set("value", value); set.getFunctionValue(params); } protected String getProperty(Node node, String key) { NodeManager properties = node.getCloud().getNodeManager("properties"); Function get = properties.getFunction("get"); Parameters params = get.createParameters(); params.set("node", node); params.set("key", key); return (String) get.getFunctionValue(params); } protected void setDownloadUrl(Node node, String link) { setProperty(node, URL_KEY, link); } protected void setDownloadStatus(Node node, String status) { log.info("Setting status of " + node.getNumber() + " to " + status); setProperty(node, STATUS_KEY, status); } protected String getDownloadStatus(Node node) { return getProperty(node, STATUS_KEY); } private Boolean sendMail(HttpServletRequest req, Node node, String email) { boolean send = false; Cloud cloud = node.getCloud(); String emailbuilder = "email"; try { Module sendmail = cloud.getCloudContext().getModule("sendmail"); emailbuilder = sendmail.getProperty("emailbuilder"); } catch (NotFoundException nfe) { log.warn("No email module " + nfe); } if (cloud.hasNodeManager(emailbuilder)) { NodeManager nm = cloud.getNodeManager(emailbuilder); Node message = nm.createNode(); String host = req.getHeader("host"); if (host == null || "".equals(host)) { try { host = java.net.InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException uhe) { log.warn("No host: " + uhe); } } String from = "downloader@" + host; // do a quick check if we've got something more or less valid Pattern p = Pattern.compile(".+@.+\\.[a-z]+"); Matcher m = p.matcher(from); if (!m.matches()) { from = "*****@*****.**"; } String mediaTitle = node.getStringValue("title"); String mediaUrl = getProperty(node, URL_KEY); StringBuilder body = new StringBuilder(); body.append("*This is an automated message / Dit is een geautomatiseerd bericht*"); body.append("\n\n*English*"); body.append("\n\nDear,"); body.append("\n\nWe have received your file belonging to media item titled '") .append(mediaTitle) .append("'. "); body.append("In about 1 hour, you can find your submission at: "); body.append("http://").append(host).append("/media/").append(node.getNumber()); body.append("\n\nKind regards,"); body.append("\n\n").append(host); body.append("\n\n\n*Nederlands*"); body.append("\n\nBeste,"); body.append("\n\nWe hebben je bestand voor het media item met de titel '") .append(mediaTitle) .append("' ontvangen. "); body.append("Je kunt je bijdrage hier over circa een uur terugvinden: "); body.append("http://").append(host).append("/media/").append(node.getNumber()); body.append("\n\nMet vriendelijke groet,"); body.append("\n\n").append(host); message.setValue("from", from); message.setValue("to", email); message.setValue("subject", "Download complete / Download voltooid"); message.setValue("body", body.toString()); message.commit(); Function mail = message.getFunction("mail"); Parameters mail_params = mail.createParameters(); mail_params.set("type", "oneshot"); mail.getFunctionValue(mail_params); if (log.isDebugEnabled()) { log.debug("Message download ready send to: " + email); } send = true; } else { log.warn("Can not send message - no emailbuilder installed."); } return send; } protected Future<?> submit(final Node node, final Parameters parameters) { return ThreadPools.jobsExecutor.submit( new Callable() { public String call() { String result = ""; Node source = null; try { if (log.isDebugEnabled()) { log.debug("media : " + node); log.debug("params: " + parameters); } URL url = new URL(parameters.get(URL)); DownloadFunction.this.setDownloadUrl(node, parameters.get(URL)); // get or create streamsource node source = CreateSourcesWithoutProcessFunction.getMediaSource(node, false); Downloader downloader = new Downloader(); downloader.setUrl(url); downloader.setNode(source); log.info("Now calling: " + downloader); result = downloader.download(); // download is ready DownloadFunction.this.setDownloadStatus(node, "ok"); source = CreateSourcesWithoutProcessFunction.getMediaSource( node, true); // should force 'reload' of node source.commit(); // send mail? String email = parameters.get(EMAIL); if (email != null && !"".equals(email)) { HttpServletRequest req = parameters.get(Parameter.REQUEST); sendMail(req, node, email); } log.info("Result: " + result + ", calling transcoders for #" + source.getNumber()); source.getFunctionValue( "triggerCaches", new Parameters(org.mmbase.streams.CreateCachesFunction.PARAMETERS) .set("all", true)); } catch (IllegalArgumentException iae) { log.error(iae.getMessage(), iae); DownloadFunction.this.setDownloadStatus(node, "NONHTTP " + iae.getMessage()); } catch (MalformedURLException ue) { log.error(ue.getMessage(), ue); DownloadFunction.this.setDownloadStatus(node, "BADURL " + ue.getMessage()); } catch (IOException ioe) { log.error(ioe.getMessage(), ioe); DownloadFunction.this.setDownloadStatus(node, "IOERROR " + ioe.getMessage()); } catch (Throwable t) { log.error(t.getMessage(), t); DownloadFunction.this.setDownloadStatus(node, "UNEXPECTED " + t.getMessage()); } finally { DownloadFunction.this.runningJobs.remove(node.getNumber()); log.info("Running jobs: " + DownloadFunction.this.runningJobs); } return result; } }); } @Override public String getFunctionValue(final Node node, final Parameters parameters) { if (log.isDebugEnabled()) { log.debug("node #" + node.getNumber()); log.debug("params: " + parameters); } String status = getDownloadStatus(node); int timeout = 5; if (parameters.get(TIMEOUT) != null) { timeout = parameters.get(TIMEOUT); } if (status == null) { Action action = ActionRepository.getInstance().get("streams", "download_media"); if (action == null) { throw new IllegalStateException("Action could not be found"); } if (node.getCloud().may(action, null)) { synchronized (runningJobs) { Future<?> future = runningJobs.get(node.getNumber()); if (future == null) { setDownloadStatus(node, "busy: " + System.currentTimeMillis()); future = submit(node, parameters); ThreadPools.identify( future, DownloadFunction.class.getName() + " downloading... for #" + node.getNumber() + " - status: " + getDownloadStatus(node)); String fname = ThreadPools.getString(future); log.info("Future name: " + fname); try { status = (String) future.get(timeout, TimeUnit.SECONDS); log.info("status: " + status); } catch (TimeoutException te) { status = ThreadPools.getString(future); log.info("TimeoutException: " + status); } catch (Exception e) { log.error(e); } } else { status = ThreadPools.getString(future); } } log.info("status: " + status); return status; } else { throw new org.mmbase.security.SecurityException("Not allowed"); } } return status; } }
/** * The DataType of the field can also itself specify how the input widgets must look like. This * TypeHandler wraps this {@link org.mmbase.datatypes.handlers.Handler} object into a {@link * TypeHandler}. * * <p>Actually, as soon as all TypeHandler implementations are migrated to Handlers, this can become * the only way to do it. * * @author Michiel Meeuwisssen * @since MMBase-1.9.1 * @version $Id$ */ public class DataTypeHandler implements TypeHandler { private static final Logger log = Logging.getLoggerInstance(DataTypeHandler.class); protected final Handler<String> handler; protected final Request request; public DataTypeHandler(Handler<String> h, final FieldInfoTag tag) { handler = h; request = new Request() { { setProperty(HtmlHandler.SESSIONNAME, tag.getSessionName()); try { javax.servlet.jsp.PageContext pc = tag.getContextTag().getPageContext(); setProperty( Parameter.REQUEST, (javax.servlet.http.HttpServletRequest) pc.getRequest()); setProperty( Parameter.RESPONSE, (javax.servlet.http.HttpServletResponse) pc.getResponse()); } catch (JspTagException te) { throw new RuntimeException(te); } } @Override public Cloud getCloud() { try { return tag.getCloudVar(); } catch (JspTagException te) { throw new RuntimeException(te); } } @Override public java.util.Locale getLocale() { try { return tag.getLocale(); } catch (JspTagException te) { throw new RuntimeException(te); } } @Override public void invalidate() { try { FormTag form = tag.getFormTag(false, null); if (form != null) { form.setValid(false); } } catch (JspTagException te) { throw new RuntimeException(te); } } @Override public boolean isValid() { try { FormTag form = tag.getFormTag(false, null); if (form != null) { return form.isValid(); } else { return true; } } catch (JspTagException te) { throw new RuntimeException(te); } } protected String prefix(String s) throws JspTagException { return tag.getPrefix() + "_" + s; } @Override public Object getValue(Field field) { try { Object found = tag.getContextProvider() .getContextContainer() .find(tag.getPageContext(), prefix(field.getName())); log.debug("found fv " + found); return found; } catch (JspTagException te) { throw new RuntimeException(te); } } @Override public Object getValue(Field field, String part) { try { Object found = tag.getContextProvider() .getContextContainer() .find(tag.getPageContext(), prefix(field.getName()) + "_" + part); log.debug("found fv " + found); return found; } catch (JspTagException te) { throw new RuntimeException(te); } } @Override public String getName(Field field) { try { return prefix(field.getName()); } catch (JspTagException te) { throw new RuntimeException(te); } } @Override public <C> C setProperty(Parameter<C> name, C value) { C prev = getProperty(name); tag.getPageContext().setAttribute(name.getName(), value); return prev; } @Override public <C> C getProperty(Parameter<C> name) { return (C) tag.getPageContext().getAttribute(name.getName()); } @Override public boolean isPost() { try { return "POST" .equals( ((javax.servlet.http.HttpServletRequest) tag.getContextTag().getPageContext().getRequest()) .getMethod()); } catch (JspTagException te) { throw new RuntimeException(te); } } }; } @Override public String htmlInput(Node node, Field field, boolean search) throws JspTagException { return handler.input(request, node, field, search); } @Override public String htmlInputId(Node node, Field field) throws JspTagException { return handler.id(request, field); } @Override public String checkHtmlInput(Node node, Field field, boolean errors) throws JspTagException { return handler.check(request, node, field, errors); } @Override public boolean useHtmlInput(Node node, Field field) throws JspTagException { return handler.set(request, node, field); } @Override public String whereHtmlInput(Field field) throws JspTagException { throw new UnsupportedOperationException(); } protected final String findString(Field field) throws JspTagException { String search = org.mmbase.util.Casting.toString(request.getValue(field)); if (search == null || "".equals(search)) { return null; } return search; } @Override public void paramHtmlInput(ParamHandler handler, Field field) throws JspTagException { handler.addParameter(request.getName(field), findString(field)); } @Override public Constraint whereHtmlInput(Field field, Query query) throws JspTagException { return handler.search(request, field, query); } @Override public void init() {} }
/** * TypeRel defines the allowed relations between two object types. Every relations also specifies a * 'role', which is a reference to the RelDef table. * * <p>Relations do principally have a 'source' and a 'destination' object type, but most functions * of this class do ignore this distinction. * * <p>TypeRel is a 'core' MMBase builder. You can get a reference to it via the MMBase instance. * * @author Daniel Ockeloen * @author Pierre van Rooden * @author Michiel Meeuwissen * @version $Id$ * @see RelDef * @see InsRel * @see org.mmbase.module.core.MMBase */ public class TypeRel extends MMObjectBuilder implements SystemEventListener { private static final Logger log = Logging.getLoggerInstance(TypeRel.class); /** Constant for {@link #contains}: return only typerels that exactly match. */ public static final int STRICT = 0; /** * Constant for {@link #contains}: return typerels where source/destination match with a builder * or its descendants */ public static final int INCLUDE_DESCENDANTS = 1; /** * Constant for {@link #contains}: return typerels where source/destination match with a builder * or its parents */ public static final int INCLUDE_PARENTS = 2; /** * Constant for {@link #contains}: return typerels where source/destination match with a builder, * its descendants, or its parents */ public static final int INCLUDE_PARENTS_AND_DESCENDANTS = 3; /** * TypeRel should contain only a limited amount of nodes, so we can simply cache them all, and * avoid all further querying. */ protected TypeRelSet typeRelNodes; // for searching destinations protected TypeRelSet parentTypeRelNodes; // for caching typerels for 'parent' // builders public InverseTypeRelSet inverseTypeRelNodes; // for searching sources private int defaultRelationStepDirection = RelationStep.DIRECTIONS_BOTH; @Override public boolean init() { if (oType != -1) return true; super.init(); createIfNotExists(); // during init not yet all builder are available so inheritance is not // yet possible // This means that calls to getAllowedRelations do not consider // inheritance during initializion of MMBase. // This occurs e.g. in one of the Community-builders. readCache(false); EventManager.getInstance().addEventListener(this); return true; } @Override public void notify(SystemEvent se) { if (se instanceof SystemEvent.ServletContext) { ServletContext sx = ((SystemEvent.ServletContext) se).getServletContext(); String def = sx.getInitParameter("mmbase.defaultRelationStepDirection"); if (def != null && def.length() > 0) { defaultRelationStepDirection = org.mmbase.bridge.util.Queries.getRelationStepDirection(def); log.info("Found default relation step direction " + def); } } } @Override public int getWeight() { return 0; } /** * The TypeRel cache contains all TypeRels MMObjectNodes. Called after init by MMBase, and when * something changes. * * @since MMBase-1.6.2 */ public void readCache() { readCache(true); } /** @since MMBase-1.6.2 */ private void readCache(boolean buildersInitialized) { log.debug("Reading in typerels"); typeRelNodes = new TypeRelSet(); parentTypeRelNodes = new TypeRelSet(); inverseTypeRelNodes = new InverseTypeRelSet(); TypeDef typeDef = mmb.getTypeDef(); typeDef.init(); // Find all typerel nodes List<MMObjectNode> alltypes = getNodes(); for (MMObjectNode typerel : alltypes) { addCacheEntry(typerel, buildersInitialized); } log.debug( "Done reading typerel cache " + (buildersInitialized ? "(considered inheritance)" : "") + ": " + typeRelNodes); } /** * Addes one typerel cache entries, plus inherited relations (if builder are initialized) * * @return A Set with the added entries, which can be used for logging or so, or can be * disregarded * @since MMBase-1.6.2 */ protected TypeRelSet addCacheEntry( final MMObjectNode typeRel, final boolean buildersInitialized) { if (typeRel == null) { throw new IllegalArgumentException("typeRel cannot be null"); } final TypeRelSet added = new TypeRelSet(); // store temporary, which will enable nice logging of what happened // Start to add the actual definition, this is then afterwards again, // except if one of the builders could not be found added.add(typeRel); if (mmb == null) { throw new IllegalStateException("mmb is null"); } final RelDef reldef = mmb.getRelDef(); if (reldef == null) { throw new IllegalStateException("No reldef found"); } final MMObjectNode reldefNode = reldef.getNode(typeRel.getIntValue("rnumber")); if (reldefNode == null) { throw new RuntimeException( "Could not find reldef-node for rnumber= " + typeRel.getIntValue("rnumber")); } final boolean bidirectional = (!InsRel.usesdir) || (reldefNode.getIntValue("dir") > 1); INHERITANCE: if (buildersInitialized) { // handle inheritance, which is // not possible during // initialization of MMBase. final TypeDef typeDef = mmb.getTypeDef(); final String sourceBuilderName = typeDef.getValue(typeRel.getIntValue("snumber")); final MMObjectBuilder sourceBuilder = sourceBuilderName != null ? mmb.getBuilder(sourceBuilderName) : null; final String destinationBuilderName = typeDef.getValue(typeRel.getIntValue("dnumber")); final MMObjectBuilder destinationBuilder = destinationBuilderName != null ? mmb.getBuilder(destinationBuilderName) : null; if (sourceBuilder == null) { if (destinationBuilder == null) { log.info( "Both source and destination of " + typeRel + " are not active builders. Cannot follow descendants."); } else { log.info( "The source of relation type " + typeRel + " is not an active builder. Cannot follow descendants."); } break INHERITANCE; } if (destinationBuilder == null) { log.warn( "The destination of relation type " + typeRel + " is not an active builder. Cannot follow descendants."); break INHERITANCE; } final int rnumber = typeRel.getIntValue("rnumber"); final List<MMObjectBuilder> sources = new ArrayList<MMObjectBuilder>(sourceBuilder.getDescendants()); sources.add(sourceBuilder); final List<MMObjectBuilder> destinations = new ArrayList<MMObjectBuilder>(destinationBuilder.getDescendants()); destinations.add(destinationBuilder); for (MMObjectBuilder s : sources) { for (MMObjectBuilder d : destinations) { MMObjectNode vnode = new VirtualTypeRelNode(s.getNumber(), d.getNumber(), rnumber); added.add(vnode); } } // seek all parents and store typerels for them // this cache is used by contains(INCLUDE_PARENTS / // INCLUDE_PARENTS_AND_DESCENDANTS)); MMObjectBuilder sourceParent = sourceBuilder; while (sourceParent != null) { MMObjectBuilder destinationParent = destinationBuilder; while (destinationParent != null) { MMObjectNode vnode = new VirtualTypeRelNode( sourceParent.getNumber(), destinationParent.getNumber(), rnumber); parentTypeRelNodes.add(vnode); destinationParent = destinationParent.getParentBuilder(); } sourceParent = sourceParent.getParentBuilder(); } added.add(typeRel); // replaces the ones added in the 'inheritance' // loop (so now not any more Virtual) } for (MMObjectNode node : added) { if (!node.isVirtual()) { // make sure 'real' nodes replace virtual nodes. (real and virtual nodes are equal, so will // not be added to set otherwise) // This is especially essential whey you use STRICT in contains typeRelNodes.remove(node); if (bidirectional) inverseTypeRelNodes.remove(node); } typeRelNodes.add(node); if (bidirectional) inverseTypeRelNodes.add(node); } if (log.isDebugEnabled()) { log.debug("Added to typerelcache: " + added); } return added; } /** * Insert a new object (content provided) in the cloud, including an entry for the object alias * (if provided). This method indirectly calls {@link #preCommit}. If the typerel node specified * already exists (i.e. same snumber, dnumber,a nd rnumber fielfds), the typerel creation fails * and returns -1. * * @param owner The administrator creating the node * @param node The object to insert. The object need be of the same type as the current builder. * @return An <code>int</code> value which is the new object's unique number, -1 if the insert * failed. */ @Override public int insert(String owner, MMObjectNode node) { int snumber = node.getIntValue("snumber"); int dnumber = node.getIntValue("dnumber"); int rnumber = node.getIntValue("rnumber"); if (contains(snumber, dnumber, rnumber, STRICT)) { log.error( "The typerel with snumber=" + snumber + ", dnumber=" + dnumber + ", rnumber=" + rnumber + " already exists"); throw new RuntimeException( "The typerel with snumber=" + snumber + ", dnumber=" + dnumber + ", rnumber=" + rnumber + " already exists"); } int res = super.insert(owner, node); return res; } /** * Remove a node from the cloud. * * @param node The node to remove. */ @Override public void removeNode(MMObjectNode node) { super.removeNode(node); } /** * Retrieves all relations which are 'allowed' for a specified node, that is, where the node is * either allowed to be the source, or to be the destination (but where the corresponing relation * definition is bidirectional). The allowed relations are determined by the type of the node * * @param node The node to retrieve the allowed relations of. * @return An <code>Enumeration</code> of nodes containing the typerel relation data */ public Enumeration<MMObjectNode> getAllowedRelations(MMObjectNode node) { return getAllowedRelations(node.getBuilder().getNumber()); } public Enumeration<MMObjectNode> getAllowedRelations(int otype) { Set<MMObjectNode> res = getAllowedRelations(otype, 0, 0, RelationStep.DIRECTIONS_BOTH); return Collections.enumeration(res); } /** * Retrieves all relations which are 'allowed' between two specified nodes. No distinction between * source / destination. * * @param node1 The first objectnode * @param node2 The second objectnode * @return An <code>Enumeration</code> of nodes containing the typerel relation data */ public Enumeration<MMObjectNode> getAllowedRelations(MMObjectNode node1, MMObjectNode node2) { return getAllowedRelations(node1.getOType(), node2.getOType()); } /** * An enumeration of all allowed relations between two builders. No distinction is made between * source and destination. */ public Enumeration<MMObjectNode> getAllowedRelations(int builder1, int builder2) { Set<MMObjectNode> res = getAllowedRelations(builder1, builder2, 0, RelationStep.DIRECTIONS_BOTH); return Collections.enumeration(res); } /** * A Set of all allowed relations of a certain role between two builders. No distinction between * source and destination. * * @since MMBase-1.6.2 */ public Set<MMObjectNode> getAllowedRelations(int builder1, int builder2, int role) { return getAllowedRelations(builder1, builder2, role, RelationStep.DIRECTIONS_BOTH); } /** * A Set of all allowed relations of a certain role between two builders. Distinction is made * between source and destination depending on passed directionality. * * @since MMBase-1.6.2 */ public Set<MMObjectNode> getAllowedRelations( int builder1, int builder2, int role, int directionality) { Set<MMObjectNode> res = new HashSet<MMObjectNode>(); if (directionality != RelationStep.DIRECTIONS_SOURCE) { res.addAll(typeRelNodes.getBySourceDestinationRole(builder1, builder2, role)); } if (directionality != RelationStep.DIRECTIONS_DESTINATION && (directionality != RelationStep.DIRECTIONS_EITHER || res.isEmpty())) { res.addAll(inverseTypeRelNodes.getByDestinationSourceRole(builder2, builder1, role)); } return res; } /** * Retrieves all relations which are 'allowed' between two specified nodes. * * @param snum The first objectnode type (the source) * @param dnum The second objectnode type (the destination) * @return An <code>Enumeration</code> of nodes containing the reldef (not typerel!) sname field */ protected Vector<String> getAllowedRelationsNames(int snum, int dnum) { Vector<String> results = new Vector<String>(); for (Enumeration<MMObjectNode> e = getAllowedRelations(snum, dnum); e.hasMoreElements(); ) { MMObjectNode node = e.nextElement(); int rnumber = node.getIntValue("rnumber"); MMObjectNode snode = mmb.getRelDef().getNode(rnumber); results.addElement(snode.getStringValue("sname")); } return results; } /** * Retrieves the identifying number of the relation definition that is 'allowed' between two * specified node types. The results are dependent on there being only one type of relation * between two node types (not enforced, thus unpredictable). Makes use of a typeRelNodes. * * @param snum The first objectnode type (the source) * @param dnum The second objectnode type (the destination) * @return the number of the found relation, or -1 if either no relation was found, or more than * one was found. */ public int getAllowedRelationType(int snum, int dnum) { Set<MMObjectNode> set = new HashSet<MMObjectNode>(typeRelNodes.getBySourceDestination(snum, dnum)); set.addAll(inverseTypeRelNodes.getByDestinationSource(dnum, snum)); if (set.size() != 1) { return -1; } else { MMObjectNode n = set.iterator().next(); return n.getIntValue("rnumber"); } } /** * Returns the display string for this node It returns a commbination of objecttypes and rolename * : "source->destination (role)". * * @param node Node from which to retrieve the data * @return A <code>String</code> describing the content of the node */ @Override public String getGUIIndicator(MMObjectNode node) { try { String source = mmb.getTypeDef().getValue(node.getIntValue("snumber")); String destination = mmb.getTypeDef().getValue(node.getIntValue("dnumber")); MMObjectNode role = mmb.getRelDef().getNode(node.getIntValue("rnumber")); return source + "->" + destination + " (" + (role != null ? role.getGUIIndicator() : "???") + ")"; } catch (Exception e) { log.warn(e); } return null; } /** * Returns the display string for a specified field. Returns, for snumber and dnumber, the name of * the objecttype they represent, and for rnumber the display (GUI) string for the indicated * relation definition. * * @param field The name of the field to retrieve * @param node Node from which to retrieve the data * @return A <code>String</code> describing the content of the field */ @Override public String getGUIIndicator(String field, MMObjectNode node) { try { if (field.equals("snumber")) { return mmb.getTypeDef().getValue(node.getIntValue("snumber")); } else if (field.equals("dnumber")) { return mmb.getTypeDef().getValue(node.getIntValue("dnumber")); } else if (field.equals("rnumber")) { MMObjectNode reldef = mmb.getRelDef().getNode(node.getIntValue("rnumber")); return (reldef != null ? reldef.getGUIIndicator() : "???"); } } catch (Exception e) { } return null; } /** * Processes the BUILDER-typerel-ALLOWEDRELATIONSNAMES in the LIST command, and (possibly) returns * a Vector containing requested data (based on the content of TYPE and NODE, which can be * retrieved through tagger). * * @javadoc parameters */ @Override public Vector<String> getList(PageInfo sp, StringTagger tagger, StringTokenizer tok) { if (tok.hasMoreTokens()) { String cmd = tok.nextToken(); // Retrieving command. if (cmd.equals("ALLOWEDRELATIONSNAMES")) { try { String tmp = tagger.Value("TYPE"); int number1 = mmb.getTypeDef().getIntValue(tmp); tmp = tagger.Value("NODE"); int number2 = Integer.parseInt(tmp); MMObjectNode node = getNode(number2); return getAllowedRelationsNames(number1, node.getOType()); } catch (Exception e) { log.error(e); } } } return null; } /** * Tests if a specific relation type is defined. * * <p>Note that this routine returns false both when a snumber/dnumber are swapped, and when a * typecombo does not exist - it is not possible to derive whether one or the other has occurred. * * @deprecated use {@link #contains}instead * @param n1 The source type number. * @param n2 The destination type number. * @param r The relation definition (role) number, or -1 if the role does not matter * @return <code>true</code> when the relation exists, false otherwise. */ public boolean reldefCorrect(int n1, int n2, int r) { return contains(n1, n2, r); } /** * Tests if a specific relation type is defined. The method also returns true if the typerel * occurs as a virtual (derived) node. * * <p>Note that this routine returns false both when a snumber/dnumber are swapped, and when a * typecombo does not exist - it is not possible to derive whether one or the other has occurred. * * <p> * * @param n1 The source type number. * @param n2 The destination type number. * @param r The relation definition (role) number, or -1 if the role does not matter * @return <code>true</code> when the relation exists, false otherwise. * @since MMBase-1.6.2 */ public boolean contains(int n1, int n2, int r) { return contains(n1, n2, r, INCLUDE_DESCENDANTS); } /** * Tests if a specific relation type is defined. * * <p>Note that this routine returns false both when a snumber/dnumber are swapped, and when a * typecombo does not exist - it is not possible to derive whether one or the other has occurred. * * <p> * * @param n1 The source type number. * @param n2 The destination type number. * @param r The relation definition (role) number, or -1 if the role does not matter r can only be * -1 if virtual is <code>true</code> * @param restriction if {@link #STRICT}, contains only returns true if the typerel occurs as-is * in the database. if {@link #INCLUDE_DESCENDANTS}, contains returns true if the typerel * occurs as a virtual (derived) node, where source or destination may also be descendants of * the specified type. if {@link #INCLUDE_PARENTS}, contains returns true if the typerel * occurs as a virtual (derived) node, where source or destination may also be parents of the * specified type. if {@link #INCLUDE_PARENTS_AND_DESCENDANTS}, contains returns true if the * typerel occurs as a virtual (derived) node, where source or destination may also be * descendants or parents of the specified type. * @return <code>true</code> when the relation exists, false otherwise. * @since MMBase-1.6.2 */ public boolean contains(int n1, int n2, int r, int restriction) { switch (restriction) { case INCLUDE_DESCENDANTS: return typeRelNodes.contains(new VirtualTypeRelNode(n1, n2, r)); case INCLUDE_PARENTS: return parentTypeRelNodes.contains(new VirtualTypeRelNode(n1, n2, r)); case INCLUDE_PARENTS_AND_DESCENDANTS: return typeRelNodes.contains(new VirtualTypeRelNode(n1, n2, r)) || parentTypeRelNodes.contains(new VirtualTypeRelNode(n1, n2, r)); case STRICT: SortedSet<MMObjectNode> existingNodes = typeRelNodes.getBySourceDestinationRole(n1, n2, r); return (existingNodes.size() > 0 && !existingNodes.first().isVirtual()); default: log.error("Unknown restriction " + restriction); return false; } } /** * Watch for changes on relation types and adjust our memory table accordingly * * @todo Should update artCache en relDefCorrectCache as wel */ /* * (non-Javadoc) * @see org.mmbase.module.core.MMObjectBuilder#notify(org.mmbase.core.event.NodeEvent) */ @Override public void notify(NodeEvent event) { if (log.isDebugEnabled()) { log.debug( "Changed " + event.getMachine() + " " + event.getNodeNumber() + " " + event.getBuilderName() + " " + NodeEvent.newTypeToOldType(event.getType())); } if (tableName.equals(event.getBuilderName())) { if (event.getType() == Event.TYPE_NEW) { MMObjectNode typeRelNode = getNode(event.getNodeNumber()); if (typeRelNode != null) { Set<MMObjectNode> newTypeRels = addCacheEntry(typeRelNode, true); log.service("Added to typerelcache: " + newTypeRels); } else { log.warn("Could not found typerel node with number " + event.getNodeNumber()); } } else { // something else changed in a typerel node? reread the complete typeRelNodes Set log.service( "Received '" + event + "' which is about typrels. Now re-reading the entire cache"); readCache(); } // also, clear all query-caches, because result may change by this. See MMB-348 for (Cache qc : CacheManager.getMap().values()) { if (qc instanceof QueryResultCache) { qc.clear(); } } } super.notify(event); } /** * Optimize as relation step by considering restrictions of TypeRel. TypeRel defines which type of * relations may be created, ergo can exist. * * @since MMBase-1.7 */ public boolean optimizeRelationStep( BasicRelationStep relationStep, int sourceType, int destinationType, int roleInt, int searchDir) { // Determine in what direction(s) this relation can be followed: // Check directionality is requested and supported. if (searchDir != RelationStep.DIRECTIONS_ALL && InsRel.usesdir) { relationStep.setCheckedDirectionality(true); } // this is a bit confusing, can the simple cases like explicit 'source' // or 'destination' not be handled first? boolean sourceToDestination = searchDir != RelationStep.DIRECTIONS_SOURCE && contains(sourceType, destinationType, roleInt, INCLUDE_PARENTS_AND_DESCENDANTS); boolean destinationToSource = searchDir != RelationStep.DIRECTIONS_DESTINATION && contains(destinationType, sourceType, roleInt, INCLUDE_PARENTS_AND_DESCENDANTS); if (destinationToSource && sourceToDestination && (searchDir == RelationStep.DIRECTIONS_EITHER)) { // support old destinationToSource = false; } if (destinationToSource) { // there is a typed relation from destination to src if (sourceToDestination) { // there is ALSO a typed relation from src to destination - make // a more complex query // http://www.mmbase.org/jira/browse/MMB-1653 relationStep.setDirectionality(defaultRelationStepDirection); } else { // there is ONLY a typed relation from destination to src - // optimized query relationStep.setDirectionality(RelationStep.DIRECTIONS_SOURCE); } } else { if (sourceToDestination) { // there is no typed relation from destination to src (assume a // relation between src and destination) - optimized query relationStep.setDirectionality(RelationStep.DIRECTIONS_DESTINATION); } else { // no results possible, do something any way if (searchDir == RelationStep.DIRECTIONS_SOURCE) { // explicitely asked for source, it would be silly to try destination now relationStep.setDirectionality(RelationStep.DIRECTIONS_SOURCE); } else { // the 'normal' way relationStep.setDirectionality(RelationStep.DIRECTIONS_DESTINATION); } return false; } } return true; } /** * Implements equals for a typerel node. Two nodes are equal if the snumber and dnumber fields are * the same, and the rnumber fields are the same, or one of these is '-1' (don't care). * * @since MMBase-1.6.2 */ @Override public boolean equals(MMObjectNode o1, MMObjectNode o2) { if (o2.getBuilder() instanceof TypeRel) { int r1 = o1.getIntValue("rnumber"); int r2 = o2.getIntValue("rnumber"); return o1.getIntValue("snumber") == o2.getIntValue("snumber") && o1.getIntValue("dnumber") == o2.getIntValue("dnumber") && (r1 == -1 || r2 == -1 || r1 == r2); } return false; } /** * Implements for MMObjectNode * * @since MMBase-1.6.2 */ @Override public int hashCode(MMObjectNode o) { int result = 0; result = HashCodeUtil.hashCode(result, o.getIntValue("snumber")); result = HashCodeUtil.hashCode(result, o.getIntValue("dnumber")); result = HashCodeUtil.hashCode(result, o.getIntValue("rnumber")); return result; } @Override public String toString(MMObjectNode n) { try { int snumber = n.getIntValue("snumber"); int dnumber = n.getIntValue("dnumber"); int rnumber = n.getIntValue("rnumber"); String sourceName = mmb.getTypeDef().getValue(snumber); String destName = mmb.getTypeDef().getValue(dnumber); if (sourceName == null) { sourceName = "unknown builder '" + snumber + "'"; } if (destName == null) { destName = "unknown builder '" + dnumber + "'"; } // unfilled should only happen during creation of the node. String source = snumber > -1 ? (sourceName + "(" + snumber + ")") : "[unfilled]"; String destination = dnumber > -1 ? (destName + "(" + dnumber + ")") : "[unfilled]"; MMObjectNode role = rnumber > -1 ? mmb.getRelDef().getNode(rnumber) : null; return source + "->" + destination + " (" + (role != null ? role.getStringValue("sname") : "???") + ") " + (isVirtual() ? "(virtual)" : ""); } catch (Exception e) { log.warn(e); } return "typerel-node"; } /** * Of course, virtual typerel nodes need a virtual typerel builder. Well 'of course', the reason * is not quite obvious to me, it has to do with the bridge/temporarynodemanager which sometimes * needs to know it. * * @since MMBase-1.6.2 */ static class VirtualTypeRel extends TypeRel { static VirtualTypeRel virtualTypeRel = null; VirtualTypeRel(TypeRel t) { mmb = t.getMMBase(); CoreField field = Fields.createField("snumber", Field.TYPE_NODE, Field.STATE_VIRTUAL, null); field.finish(); addField(field); field = Fields.createField("dnumber", Field.TYPE_NODE, Field.STATE_VIRTUAL, null); field.finish(); addField(field); field = Fields.createField("rnumber", Field.TYPE_NODE, Field.STATE_VIRTUAL, null); field.finish(); addField(field); tableName = "virtual_typerel"; virtual = true; } static VirtualTypeRel getVirtualTypeRel(TypeRel t) { if (virtualTypeRel == null) virtualTypeRel = new VirtualTypeRel(t); return virtualTypeRel; } } /** * A TypeRelSet is a Set of typerel nodes. The TypeRel builder maintains such a Set of all typerel * nodes for quick reference. TypeRelSets are also instantiated when doing queries on TypeRel like * getAllowedRelations(MMObjectBuilder) etc. * * @since MMBase-1.6.2 */ protected class TypeRelSet extends TreeSet<MMObjectNode> { protected TypeRelSet() { super( new Comparator<MMObjectNode>() { // sorted by source, destination, role @Override public int compare(MMObjectNode n1, MMObjectNode n2) { { int i1 = n1.getIntValue("snumber"); int i2 = n2.getIntValue("snumber"); if (i1 != i2) return i1 - i2; } { int i1 = n1.getIntValue("dnumber"); int i2 = n2.getIntValue("dnumber"); if (i1 != i2) return i1 - i2; } { int i1 = n1.getIntValue("rnumber"); int i2 = n2.getIntValue("rnumber"); if (i1 > 0 && i2 > 0) return i1 - i2; } return 0; } }); } // make sure only MMObjectNode's are added @Override public boolean add(MMObjectNode node) { return super.add(node); } // find some subsets: SortedSet<MMObjectNode> getBySource(MMObjectBuilder source) { return getBySourceDestinationRole(source.getNumber(), 0, 0); } SortedSet<MMObjectNode> getBySource(int source) { return getBySourceDestinationRole(source, 0, 0); } SortedSet<MMObjectNode> getBySourceDestination( MMObjectBuilder source, MMObjectBuilder destination) { return getBySourceDestinationRole(source.getNumber(), destination.getNumber(), 0); } SortedSet<MMObjectNode> getBySourceDestination(int source, int destination) { return getBySourceDestinationRole(source, destination, 0); } SortedSet<MMObjectNode> getBySourceDestinationRole(int source, int destination, int role) { // determine minimum value - corrects in case '-1' (common MMBase value for N.A.) is passed int roleMin = role <= 0 ? 0 : role; int destinationMin = destination <= 0 ? 0 : destination; int sourceMin = source <= 0 ? 0 : source; // determine maximum value int roleMax = role <= 0 ? 0 : role + 1; // i.e. source, destination, role int destinationMax = role <= 0 ? destinationMin + 1 : destinationMin; // i.e. source, destination, 0 int sourceMax = (destination <= 0 && role <= 0) ? (sourceMin <= 0 ? 0 : sourceMin + 1) : sourceMin; // i.e. source, 0, 0 VirtualTypeRelNode fromTypeRelNode = new VirtualTypeRelNode(sourceMin, destinationMin, roleMin); VirtualTypeRelNode toTypeRelNode = new VirtualTypeRelNode(sourceMax, destinationMax, roleMax); if (log.isDebugEnabled()) { log.debug(" " + fromTypeRelNode + " " + toTypeRelNode); } SortedSet<MMObjectNode> allowed = subSet(fromTypeRelNode, toTypeRelNode); return Collections.unmodifiableSortedSet(allowed); } } /** * An InverseTypeRelSet is a Set of typerel nodes. The TypeRel builder maintains such a Set of all * typerel nodes for quick reference. * * @since MMBase-1.6.2 */ protected class InverseTypeRelSet extends TreeSet<MMObjectNode> { protected InverseTypeRelSet() { super( new Comparator<MMObjectNode>() { // sorted by destination, source, role @Override public int compare(MMObjectNode n1, MMObjectNode n2) { int i1 = n1.getIntValue("dnumber"); int i2 = n2.getIntValue("dnumber"); if (i1 != i2) return i1 - i2; i1 = n1.getIntValue("snumber"); i2 = n2.getIntValue("snumber"); if (i1 != i2) return i1 - i2; i1 = n1.getIntValue("rnumber"); i2 = n2.getIntValue("rnumber"); if (i1 != -1 && i2 != -1 && i1 != i2) return i1 - i2; return 0; } }); } // make sure only MMObjectNode's are added @Override public boolean add(MMObjectNode object) { return super.add(object); } SortedSet<MMObjectNode> getByDestination(MMObjectBuilder destination) { return getByDestinationSourceRole(0, destination.getNumber(), 0); } SortedSet<MMObjectNode> getByDestination(int destination) { return getByDestinationSourceRole(0, destination, 0); } SortedSet<MMObjectNode> getByDestinationSource( MMObjectBuilder source, MMObjectBuilder destination) { return getByDestinationSourceRole(source.getNumber(), destination.getNumber(), 0); } SortedSet<MMObjectNode> getByDestinationSource(int source, int destination) { return getByDestinationSourceRole(source, destination, 0); } SortedSet<MMObjectNode> getByDestinationSourceRole(int source, int destination, int role) { // determine minimum value - corrects in case '-1' (common MMBase value for N.A.) is passed int roleMin = role <= 0 ? 0 : role; int sourceMin = source <= 0 ? 0 : source; int destinationMin = destination <= 0 ? 0 : destination; // determine maximum value int roleMax = role <= 0 ? 0 : role + 1; // i.e. source, destination, role int sourceMax = role <= 0 ? (source <= 0 ? 0 : source + 1) : source; // i.e. source, destination, 0 int destinationMax = (source <= 0 && role <= 0) ? destination + 1 : destination; // i.e. 0, destination, 0 final VirtualTypeRelNode fromTypeRelNode = new VirtualTypeRelNode(sourceMin, destinationMin, roleMin); final VirtualTypeRelNode toTypeRelNode = new VirtualTypeRelNode(sourceMax, destinationMax, roleMax); final SortedSet allowed = subSet(fromTypeRelNode, toTypeRelNode); return Collections.unmodifiableSortedSet(allowed); } } /** * A VirtualTypeRelNode is a MMObjectNode which is added to the typerelset with extensions of the * actual builders specified. So these entries are not in the database. * * @since MMBase-1.6.2 */ protected class VirtualTypeRelNode extends VirtualNode { VirtualTypeRelNode(int snumber, int dnumber) { // only for use in lookups // We don't use this-constructor because some jvm get confused then super(VirtualTypeRel.getVirtualTypeRel(TypeRel.this)); setValue("snumber", snumber); setValue("dnumber", dnumber); setValue("rnumber", -1); } VirtualTypeRelNode(int snumber) { // only for use in lookups // We don't use this-constructor because some jvm get confused then super(VirtualTypeRel.getVirtualTypeRel(TypeRel.this)); setValue("snumber", snumber); setValue("dnumber", -1); setValue("rnumber", -1); } VirtualTypeRelNode(int snumber, int dnumber, int rnumber) { super(VirtualTypeRel.getVirtualTypeRel(TypeRel.this)); setValue("snumber", snumber); setValue("dnumber", dnumber); setValue("rnumber", rnumber); values = Collections.unmodifiableMap(values); // make sure it is not changed any more! } @Override public String toString() { return "V:" + getValue("snumber") + "->" + getValue("dnumber") + "(" + getValue("rnumber") + ")"; } } }
/** @author Daniel Ockeloen */ public class Signature { // logger private static Logger log = Logging.getLoggerInstance(Signature.class); private int id; private Poster parent; private String body; private String mode; private String encoding; public Signature(Poster parent, int id, String body, String mode, String encoding) { this.id = id; this.body = body; this.mode = mode; this.encoding = encoding; this.parent = parent; } public void setBody(String body) { if (!this.body.equals(body)) { this.body = body; save(); } } public void setMode(String mode) { if (!this.mode.equals(mode)) { if (mode.equals("delete")) { delete(); } else { this.mode = mode; save(); } } } public void setEncoding(String encoding) { if (!this.encoding.equals(encoding)) { this.encoding = encoding; save(); } } public String getBody() { return body; } public String getMode() { return mode; } public int getId() { return id; } public String getEncoding() { return encoding; } public boolean delete() { Node node = ForumManager.getCloud().getNode(id); if (node != null) { node.delete(true); } parent.deleteSignature(this); return true; } public boolean save() { Node node = ForumManager.getCloud().getNode(id); node.setValue("mode", mode); node.setValue("body", body); node.setValue("encoding", encoding); node.commit(); return true; } }