diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index de3cf06e96..732f4952ad 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -1035,7 +1035,7 @@ - + false diff --git a/config/alfresco/subsystems/Search/solr/solr-search-context.xml b/config/alfresco/subsystems/Search/solr/solr-search-context.xml index 258b83f4d3..d0f794b459 100644 --- a/config/alfresco/subsystems/Search/solr/solr-search-context.xml +++ b/config/alfresco/subsystems/Search/solr/solr-search-context.xml @@ -56,6 +56,10 @@ + + + + diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrAdminHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrAdminHTTPClient.java new file mode 100644 index 0000000000..336507973f --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrAdminHTTPClient.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.solr; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import javax.servlet.http.HttpServletResponse; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.httpclient.HttpClientFactory; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.search.impl.lucene.LuceneQueryParserException; +import org.alfresco.repo.search.impl.lucene.SolrJSONResultSet; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchParameters.FieldFacet; +import org.alfresco.service.cmr.search.SearchParameters.FieldFacetMethod; +import org.alfresco.service.cmr.search.SearchParameters.FieldFacetSort; +import org.alfresco.service.cmr.search.SearchParameters.SortDefinition; +import org.alfresco.service.cmr.security.PermissionService; +import org.apache.commons.codec.net.URLCodec; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpException; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.commons.httpclient.URI; +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.params.HttpClientParams; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + */ +public class SolrAdminHTTPClient +{ + static Log s_logger = LogFactory.getLog(SolrAdminHTTPClient.class); + + private String baseUrl; + + private HttpClient httpClient; + private HttpClientFactory httpClientFactory; + + public SolrAdminHTTPClient() + { + } + + public void init() + { + StringBuilder sb = new StringBuilder(); + sb.append("/solr/admin/cores"); + this.baseUrl = sb.toString(); + + httpClient = httpClientFactory.getHttpClient(); + HttpClientParams params = httpClient.getParams(); + params.setBooleanParameter(HttpClientParams.PREEMPTIVE_AUTHENTICATION, true); + httpClient.getState().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), new UsernamePasswordCredentials("admin", "admin")); + } + + public void setHttpClientFactory(HttpClientFactory httpClientFactory) + { + this.httpClientFactory = httpClientFactory; + } + + public JSONObject execute(HashMapargs) + { + try + { + URLCodec encoder = new URLCodec(); + StringBuilder url = new StringBuilder(); + + for(String key : args.keySet()) + { + String value = args.get(key); + if(url.length() == 0) + { + url.append(baseUrl); + url.append("?"); + url.append(encoder.encode(key, "UTF-8")); + url.append("="); + url.append(encoder.encode(value, "UTF-8")); + } + else + { + url.append("&"); + url.append(encoder.encode(key, "UTF-8")); + url.append("="); + url.append(encoder.encode(value, "UTF-8")); + } + + } + + PostMethod post = new PostMethod(url.toString()); + + try + { + httpClient.executeMethod(post); + + if(post.getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY || post.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY) + { + Header locationHeader = post.getResponseHeader("location"); + if (locationHeader != null) + { + String redirectLocation = locationHeader.getValue(); + post.setURI(new URI(redirectLocation, true)); + httpClient.executeMethod(post); + } + } + + if (post.getStatusCode() != HttpServletResponse.SC_OK) + { + throw new LuceneQueryParserException("Request failed " + post.getStatusCode() + " " + url.toString()); + } + + Reader reader = new BufferedReader(new InputStreamReader(post.getResponseBodyAsStream())); + // TODO - replace with streaming-based solution e.g. SimpleJSON ContentHandler + JSONObject json = new JSONObject(new JSONTokener(reader)); + return json; + } + finally + { + post.releaseConnection(); + } + } + catch (UnsupportedEncodingException e) + { + throw new LuceneQueryParserException("", e); + } + catch (HttpException e) + { + throw new LuceneQueryParserException("", e); + } + catch (IOException e) + { + throw new LuceneQueryParserException("", e); + } + catch (JSONException e) + { + throw new LuceneQueryParserException("", e); + } + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrChildApplicationContextFactory.java b/source/java/org/alfresco/repo/search/impl/solr/SolrChildApplicationContextFactory.java new file mode 100644 index 0000000000..bc2f395888 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrChildApplicationContextFactory.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.solr; + +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.search.impl.lucene.LuceneQueryParserException; +import org.alfresco.service.cmr.repository.datatype.Duration; +import org.json.JSONException; +import org.json.JSONObject; +import org.springframework.context.ApplicationContext; + +/** + * @author Andy + */ +public class SolrChildApplicationContextFactory extends ChildApplicationContextFactory +{ + + private static String ALFRESCO_ACTIVE = "tracker.alfresco.active"; + + private static String ALFRESCO_LAG = "tracker.alfresco.lag"; + + private static String ALFRESCO_LAG_DURATION = "tracker.alfresco.lag.duration"; + + private static String ARCHIVE_ACTIVE = "tracker.archive.active"; + + private static String ARCHIVE_LAG = "tracker.archive.lag"; + + private static String ARCHIVE_LAG_DURATION = "tracker.archive.lag.duration"; + + @Override + public boolean isUpdateable(String name) + { + // TODO Auto-generated method stub + return super.isUpdateable(name) + && !name.equals(SolrChildApplicationContextFactory.ALFRESCO_ACTIVE) && !name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG) + && !name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG_DURATION) && !name.equals(SolrChildApplicationContextFactory.ARCHIVE_ACTIVE) + && !name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG) && !name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG_DURATION); + } + + @Override + public String getProperty(String name) + { + if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_ACTIVE) + || name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG) || name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG_DURATION) || name.equals(SolrChildApplicationContextFactory.ARCHIVE_ACTIVE) + || name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG) || name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG_DURATION)) + { + try + { + ApplicationContext ctx = getApplicationContext(); + SolrAdminHTTPClient adminClient = (SolrAdminHTTPClient) ctx.getBean("search.solrAdminHTTPCLient"); + HashMap args = new HashMap(); + args.put("action", "SUMMARY"); + args.put("wt", "json"); + JSONObject json = adminClient.execute(args); + JSONObject summary = json.getJSONObject("Summary"); + + Date now = new Date(); + + JSONObject alfresco = summary.getJSONObject("alfresco"); + String alfrescoLag = alfresco.getString("Lag"); + String alfrescoActive = alfresco.getString("Active"); + String alfrescoDuration = alfresco.getString("Duration"); + + + JSONObject archive = summary.getJSONObject("archive"); + String archiveLag = archive.getString("Lag"); + String archiveActive = archive.getString("Active"); + String archiveDuration = archive.getString("Duration"); + + if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_ACTIVE)) + { + return alfrescoActive; + } + else if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG)) + { + return alfrescoLag; + } + else if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG_DURATION)) + { + return alfrescoDuration; + } + else if (name.equals(SolrChildApplicationContextFactory.ARCHIVE_ACTIVE)) + { + return archiveActive; + } + else if (name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG)) + { + return archiveLag; + } + else if (name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG_DURATION)) + { + return archiveDuration; + } + else + { + return "Unavailable"; + } + } + catch (LuceneQueryParserException lqe) + { + return "Unavailable: " + lqe.getMessage(); + } + catch (JSONException e) + { + return "Unavailable: " + e.getMessage(); + } + } + else + { + return super.getProperty(name); + } + } + + @Override + public Set getPropertyNames() + { + Set result = new TreeSet(); + result.add(SolrChildApplicationContextFactory.ALFRESCO_ACTIVE); + result.add(SolrChildApplicationContextFactory.ALFRESCO_LAG); + result.add(SolrChildApplicationContextFactory.ALFRESCO_LAG_DURATION); + result.add(SolrChildApplicationContextFactory.ARCHIVE_ACTIVE); + result.add(SolrChildApplicationContextFactory.ARCHIVE_LAG); + result.add(SolrChildApplicationContextFactory.ARCHIVE_LAG_DURATION); + result.addAll(super.getPropertyNames()); + return result; + } + + public void setProperty(String name, String value) + { + if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_ACTIVE) + || name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG) || name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG_DURATION) || name.equals(SolrChildApplicationContextFactory.ARCHIVE_ACTIVE) + || name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG) || name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG_DURATION)) + { + throw new IllegalStateException("Illegal write to property \"" + name + "\""); + } + super.setProperty(name, value); + } + +} diff --git a/source/java/org/alfresco/repo/solr/SOLRTrackingComponent.java b/source/java/org/alfresco/repo/solr/SOLRTrackingComponent.java index d88ac2ef91..cc418f383a 100644 --- a/source/java/org/alfresco/repo/solr/SOLRTrackingComponent.java +++ b/source/java/org/alfresco/repo/solr/SOLRTrackingComponent.java @@ -142,4 +142,10 @@ public interface SOLRTrackingComponent * @param enabled */ void setEnabled(boolean enabled); + + /** + * Get the last transaction timestamp from the repo + * @return + */ + public Long getMaxTxnCommitTime(); } diff --git a/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java b/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java index c6070492d2..656f26dd51 100644 --- a/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java +++ b/source/java/org/alfresco/repo/solr/SOLRTrackingComponentImpl.java @@ -796,4 +796,13 @@ public class SOLRTrackingComponentImpl implements SOLRTrackingComponent more = callback.handleNodeMetaData(row); } } + + /* (non-Javadoc) + * @see org.alfresco.repo.solr.SOLRTrackingComponent#getLastTransactionTimestamp() + */ + @Override + public Long getMaxTxnCommitTime() + { + return nodeDAO.getMaxTxnCommitTime(); + } }