├── resources ├── .gitignore ├── solr │ ├── multicore │ │ ├── posts │ │ │ ├── .gitignore │ │ │ └── conf │ │ │ │ ├── spellings.txt │ │ │ │ ├── protwords.txt │ │ │ │ ├── synonyms.txt │ │ │ │ ├── stopwords.txt │ │ │ │ └── solrconfig.xml │ │ ├── channels │ │ │ ├── .gitignore │ │ │ └── conf │ │ │ │ ├── spellings.txt │ │ │ │ ├── protwords.txt │ │ │ │ ├── synonyms.txt │ │ │ │ ├── stopwords.txt │ │ │ │ ├── solrconfig.xml │ │ │ │ └── schema.xml │ │ └── solr.xml │ └── start.jar ├── schema │ ├── update-schema-0.sql │ ├── drop-schema.sql │ └── create-schema.sql └── init_d │ ├── buddycloud-solr │ ├── buddycloud-crawler │ └── buddycloud-search ├── .gitignore ├── design docs ├── Architecture.png ├── FindChannels.png └── DiscoverChannels.png ├── .m2 └── org │ └── igniterealtime │ └── whack │ ├── 1.0.0 │ ├── whack-1.0.0.jar │ └── whack-1.0.0.pom │ └── maven-metadata-local.xml ├── src ├── main │ └── java │ │ ├── org │ │ └── jivesoftware │ │ │ └── smackx │ │ │ └── pubsub │ │ │ ├── BuddycloudFirehoseNode.java │ │ │ ├── BuddycloudAffiliationsProvider.java │ │ │ ├── BuddycloudAffiliationProvider.java │ │ │ ├── BuddycloudAffiliations.java │ │ │ ├── BuddycloudAffiliation.java │ │ │ ├── BuddycloudPubsubManager.java │ │ │ └── BuddycloudNode.java │ │ └── com │ │ └── buddycloud │ │ └── channeldirectory │ │ ├── cli │ │ ├── Query.java │ │ ├── QueryToDBMS.java │ │ ├── QueryToSolr.java │ │ └── Main.java │ │ ├── search │ │ ├── handler │ │ │ ├── QueryHandler.java │ │ │ ├── response │ │ │ │ ├── ContentData.java │ │ │ │ ├── Geolocation.java │ │ │ │ ├── FakeDataGenerator.java │ │ │ │ ├── ChannelData.java │ │ │ │ └── PostData.java │ │ │ ├── AbstractQueryHandler.java │ │ │ ├── common │ │ │ │ ├── mahout │ │ │ │ │ ├── RecommendationResponse.java │ │ │ │ │ ├── ChannelRecommenderDataModel.java │ │ │ │ │ ├── MemoryRecommenderDataModel.java │ │ │ │ │ ├── PostgreSQLRecommenderDataModel.java │ │ │ │ │ └── ChannelRecommender.java │ │ │ │ └── PostQueryHandler.java │ │ │ ├── recommendation │ │ │ │ └── RecommendationQueryHandler.java │ │ │ ├── similarity │ │ │ │ └── SimilarityQueryHandler.java │ │ │ ├── metadata │ │ │ │ └── MetadataQueryHandler.java │ │ │ ├── active │ │ │ │ └── MostActiveQueryHandler.java │ │ │ ├── nearby │ │ │ │ └── NearbyQueryHandler.java │ │ │ └── content │ │ │ │ └── ContentQueryHandler.java │ │ ├── utils │ │ │ ├── GeolocationUtils.java │ │ │ ├── FeatureUtils.java │ │ │ └── XMPPUtils.java │ │ ├── rsm │ │ │ ├── RSM.java │ │ │ ├── SolrRSMUtils.java │ │ │ ├── MahoutRSMUtils.java │ │ │ └── RSMUtils.java │ │ ├── Main.java │ │ └── ChannelDirectoryComponent.java │ │ ├── commons │ │ ├── solr │ │ │ ├── SolrUtils.java │ │ │ └── SolrServerFactory.java │ │ ├── ConfigurationUtils.java │ │ └── db │ │ │ ├── CreateSchema.java │ │ │ └── ChannelDirectoryDataSource.java │ │ └── crawler │ │ ├── node │ │ ├── NodeCrawler.java │ │ ├── DiscoveryUtils.java │ │ └── FirehoseCrawler.java │ │ ├── PubSubManagers.java │ │ ├── Main.java │ │ └── PubSubSubscriptionListener.java ├── log4j.properties └── test │ └── java │ └── com │ └── buddycloud │ ├── HSQLDBTest.java │ └── channeldirectory │ └── rsm │ └── RSMTest.java ├── queries.json ├── configuration.properties.example ├── bin ├── exec-query ├── stop-solr.sh ├── setup-schema.sh ├── stop-crawler.sh ├── start-solr.sh ├── stop-search.sh ├── start-search.sh ├── start-crawler.sh ├── start-solr.bat ├── start-crawler.bat ├── start-search.bat └── setup-schema.bat └── pom.xml /resources/.gitignore: -------------------------------------------------------------------------------- 1 | /channel-taste 2 | -------------------------------------------------------------------------------- /resources/solr/multicore/posts/.gitignore: -------------------------------------------------------------------------------- 1 | /data 2 | -------------------------------------------------------------------------------- /resources/solr/multicore/channels/.gitignore: -------------------------------------------------------------------------------- 1 | /data 2 | -------------------------------------------------------------------------------- /resources/solr/multicore/posts/conf/spellings.txt: -------------------------------------------------------------------------------- 1 | pizza 2 | history 3 | -------------------------------------------------------------------------------- /resources/solr/multicore/channels/conf/spellings.txt: -------------------------------------------------------------------------------- 1 | pizza 2 | history 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .settings 3 | .project 4 | .classpath 5 | configuration.properties 6 | -------------------------------------------------------------------------------- /resources/schema/update-schema-0.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE subscribed_node ADD last_item_crawled VARCHAR(300); -------------------------------------------------------------------------------- /resources/solr/start.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/channel-directory/HEAD/resources/solr/start.jar -------------------------------------------------------------------------------- /design docs/Architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/channel-directory/HEAD/design docs/Architecture.png -------------------------------------------------------------------------------- /design docs/FindChannels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/channel-directory/HEAD/design docs/FindChannels.png -------------------------------------------------------------------------------- /design docs/DiscoverChannels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/channel-directory/HEAD/design docs/DiscoverChannels.png -------------------------------------------------------------------------------- /.m2/org/igniterealtime/whack/1.0.0/whack-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buddycloud/channel-directory/HEAD/.m2/org/igniterealtime/whack/1.0.0/whack-1.0.0.jar -------------------------------------------------------------------------------- /src/main/java/org/jivesoftware/smackx/pubsub/BuddycloudFirehoseNode.java: -------------------------------------------------------------------------------- 1 | package org.jivesoftware.smackx.pubsub; 2 | 3 | import org.jivesoftware.smack.XMPPConnection; 4 | 5 | public class BuddycloudFirehoseNode extends Node { 6 | 7 | public BuddycloudFirehoseNode(XMPPConnection connection) { 8 | super(connection, "/firehose"); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /queries.json: -------------------------------------------------------------------------------- 1 | [ 2 | {name="personal-channels-count", type="solr", agg="count", core="channels", q="channel-type:personal"}, 3 | {name="topic-channels-count", type="solr", agg="count", core="channels", q="channel-type:topic"}, 4 | {name="post-count", type="solr", agg="count", core="posts", q="content:[* TO *]"}, 5 | {name="domain-count", type="dbms", q="SELECT COUNT(*) FROM subscribed_server"} 6 | ] -------------------------------------------------------------------------------- /.m2/org/igniterealtime/whack/maven-metadata-local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.igniterealtime 4 | whack 5 | 6 | 1.0.0 7 | 8 | 1.0.0 9 | 10 | 20140613143036 11 | 12 | 13 | -------------------------------------------------------------------------------- /resources/schema/drop-schema.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS subscribed_server; 2 | DROP TABLE IF EXISTS subscribed_node; 3 | DROP TABLE IF EXISTS taste_preferences; 4 | DROP TABLE IF EXISTS item; 5 | DROP TABLE IF EXISTS t_user; 6 | DROP TABLE IF EXISTS taste_item_similarity; 7 | DROP TABLE IF EXISTS channel_activity; 8 | 9 | DROP SEQUENCE IF EXISTS item_id_seq; 10 | DROP SEQUENCE IF EXISTS user_id_seq; 11 | -------------------------------------------------------------------------------- /.m2/org/igniterealtime/whack/1.0.0/whack-1.0.0.pom: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | org.igniterealtime 6 | whack 7 | 1.0.0 8 | POM was created from install:install-file 9 | 10 | -------------------------------------------------------------------------------- /src/main/java/org/jivesoftware/smackx/pubsub/BuddycloudAffiliationsProvider.java: -------------------------------------------------------------------------------- 1 | package org.jivesoftware.smackx.pubsub; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import org.jivesoftware.smack.packet.PacketExtension; 7 | import org.jivesoftware.smack.provider.EmbeddedExtensionProvider; 8 | 9 | public class BuddycloudAffiliationsProvider extends EmbeddedExtensionProvider { 10 | 11 | @SuppressWarnings("unchecked") 12 | @Override 13 | protected PacketExtension createReturnExtension(String currentElement, 14 | String currentNamespace, Map attributeMap, 15 | List content) { 16 | return new BuddycloudAffiliations((List)content); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/org/jivesoftware/smackx/pubsub/BuddycloudAffiliationProvider.java: -------------------------------------------------------------------------------- 1 | package org.jivesoftware.smackx.pubsub; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import org.jivesoftware.smack.packet.PacketExtension; 7 | import org.jivesoftware.smack.provider.EmbeddedExtensionProvider; 8 | 9 | public class BuddycloudAffiliationProvider extends EmbeddedExtensionProvider { 10 | 11 | @Override 12 | protected PacketExtension createReturnExtension(String currentElement, 13 | String currentNamespace, Map attributeMap, 14 | List content) { 15 | return new BuddycloudAffiliation(attributeMap.get("jid"), 16 | BuddycloudAffiliation.Type.valueOf(attributeMap.get("affiliation"))); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /configuration.properties.example: -------------------------------------------------------------------------------- 1 | # Search engine properties 2 | 3 | xmpp.host=localhost 4 | xmpp.port=5275 5 | xmpp.subdomain=channeldirectory 6 | xmpp.secretkey=secret 7 | 8 | # Crawler properties 9 | 10 | crawler.xmpp.username=crawler-user 11 | crawler.xmpp.password=crawler-pass 12 | crawler.xmpp.host=hostname.buddycloud.com 13 | crawler.xmpp.servicename=buddycloud.com 14 | crawler.xmpp.port=5222 15 | crawler.servertocrawl=broadcaster.buddycloud.com 16 | 17 | # In milliseconds 18 | crawler.crawlinterval=300000 19 | 20 | # Solr cores 21 | 22 | solr.channelcore=http://localhost:8983/solr/channels/ 23 | solr.postcore=http://localhost:8983/solr/posts/ 24 | 25 | # Recommender: [memory, postgresql] 26 | 27 | mahout.recommender=postgresql 28 | mahout.jdbc.url=jdbc:postgresql://localhost:5432/channeldir?user=postgres&password=postgres 29 | mahout.dumpfile=resources/channel-taste/dump.csv -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/cli/Query.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.cli; 17 | 18 | import java.util.Properties; 19 | 20 | /** 21 | * @author Abmar 22 | * 23 | */ 24 | public interface Query { 25 | 26 | public String exec(String args, Properties configuration) throws Exception; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /resources/solr/multicore/channels/conf/protwords.txt: -------------------------------------------------------------------------------- 1 | # The ASF licenses this file to You under the Apache License, Version 2.0 2 | # (the "License"); you may not use this file except in compliance with 3 | # the License. You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | #----------------------------------------------------------------------- 14 | # Use a protected word file to protect against the stemmer reducing two 15 | # unrelated words to the same base word. 16 | 17 | # Some non-words that normally won't be encountered, 18 | # just to test that they won't be stemmed. 19 | dontstems 20 | zwhacky 21 | 22 | -------------------------------------------------------------------------------- /resources/solr/multicore/posts/conf/protwords.txt: -------------------------------------------------------------------------------- 1 | # The ASF licenses this file to You under the Apache License, Version 2.0 2 | # (the "License"); you may not use this file except in compliance with 3 | # the License. You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | #----------------------------------------------------------------------- 14 | # Use a protected word file to protect against the stemmer reducing two 15 | # unrelated words to the same base word. 16 | 17 | # Some non-words that normally won't be encountered, 18 | # just to test that they won't be stemmed. 19 | dontstems 20 | zwhacky 21 | 22 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/QueryHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler; 17 | 18 | import org.xmpp.packet.IQ; 19 | 20 | /** 21 | * Handle content queries (iq gets) to this component. 22 | * 23 | */ 24 | public interface QueryHandler { 25 | 26 | IQ handle(IQ query); 27 | 28 | String getNamespace(); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /bin/exec-query: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Execute a query directly into buddycloud channel directory 20 | # ----------------------------------------------------------------------------- 21 | 22 | # Cd to Search home and performs the query 23 | 24 | BIN_DIR=`dirname $0` 25 | cd "$BIN_DIR"/.. 26 | 27 | java -cp .:./*:./lib/* com.buddycloud.channeldirectory.cli.Main "$@" -------------------------------------------------------------------------------- /bin/stop-solr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Stop script for the solr server 20 | # ----------------------------------------------------------------------------- 21 | 22 | BIN_DIR=`dirname $0` 23 | SOLR_PID=solr.pid 24 | cd "$BIN_DIR" 25 | 26 | if [ ! -f $SOLR_PID ]; then 27 | echo PID file does not exist 28 | exit 1 29 | fi 30 | 31 | kill `cat $SOLR_PID` 32 | rm $SOLR_PID 33 | exit 0 34 | -------------------------------------------------------------------------------- /resources/solr/multicore/posts/conf/synonyms.txt: -------------------------------------------------------------------------------- 1 | # The ASF licenses this file to You under the Apache License, Version 2.0 2 | # (the "License"); you may not use this file except in compliance with 3 | # the License. You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | #----------------------------------------------------------------------- 14 | #some test synonym mappings unlikely to appear in real input text 15 | aaafoo => aaabar 16 | bbbfoo => bbbfoo bbbbar 17 | cccfoo => cccbar cccbaz 18 | fooaaa,baraaa,bazaaa 19 | 20 | # Some synonym groups specific to this example 21 | GB,gib,gigabyte,gigabytes 22 | MB,mib,megabyte,megabytes 23 | Television, Televisions, TV, TVs 24 | #notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming 25 | #after us won't split it into two words. 26 | 27 | # Synonym mappings can be used for spelling correction too 28 | pixima => pixma 29 | 30 | -------------------------------------------------------------------------------- /bin/setup-schema.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Start script for the Channel Directory Solr 20 | # ----------------------------------------------------------------------------- 21 | 22 | # Go to ChannelDir home and invoke the CreateSchema class 23 | 24 | BIN_DIR=`dirname $0` 25 | cd "$BIN_DIR"/.. 26 | java -cp .:./*:./lib/* com.buddycloud.channeldirectory.commons.db.CreateSchema 27 | 28 | exit 0 29 | -------------------------------------------------------------------------------- /resources/solr/multicore/channels/conf/synonyms.txt: -------------------------------------------------------------------------------- 1 | # The ASF licenses this file to You under the Apache License, Version 2.0 2 | # (the "License"); you may not use this file except in compliance with 3 | # the License. You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | # See the License for the specific language governing permissions and 11 | # limitations under the License. 12 | 13 | #----------------------------------------------------------------------- 14 | #some test synonym mappings unlikely to appear in real input text 15 | aaafoo => aaabar 16 | bbbfoo => bbbfoo bbbbar 17 | cccfoo => cccbar cccbaz 18 | fooaaa,baraaa,bazaaa 19 | 20 | # Some synonym groups specific to this example 21 | GB,gib,gigabyte,gigabytes 22 | MB,mib,megabyte,megabytes 23 | Television, Televisions, TV, TVs 24 | #notice we use "gib" instead of "GiB" so any WordDelimiterFilter coming 25 | #after us won't split it into two words. 26 | 27 | # Synonym mappings can be used for spelling correction too 28 | pixima => pixma 29 | 30 | -------------------------------------------------------------------------------- /bin/stop-crawler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Stop script for the crawler 20 | # ----------------------------------------------------------------------------- 21 | 22 | BIN_DIR=`dirname $0` 23 | CRAWLER_PID=crawler.pid 24 | cd "$BIN_DIR" 25 | 26 | if [ ! -f $CRAWLER_PID ]; then 27 | echo PID file does not exist 28 | exit 1 29 | fi 30 | 31 | kill `cat $CRAWLER_PID` 32 | rm $CRAWLER_PID 33 | exit 0 34 | -------------------------------------------------------------------------------- /bin/start-solr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Start script for the Channel Directory Solr 20 | # ----------------------------------------------------------------------------- 21 | 22 | # Go to Solr home and start it 23 | 24 | BIN_DIR=`dirname $0` 25 | BIN_DIR=`cd $BIN_DIR && pwd` 26 | cd "$BIN_DIR"/../resources/solr 27 | java -Dsolr.solr.home=multicore -jar start.jar & 28 | echo $! > "$BIN_DIR"/solr.pid 29 | exit 0 30 | -------------------------------------------------------------------------------- /bin/stop-search.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Stop script for the Channel Directory search engine 20 | # ----------------------------------------------------------------------------- 21 | 22 | BIN_DIR=`dirname $0` 23 | SEARCH_PID=search.pid 24 | cd "$BIN_DIR" 25 | 26 | if [ ! -f $SEARCH_PID ]; then 27 | echo PID file does not exist 28 | exit 1 29 | fi 30 | 31 | kill `cat $SEARCH_PID` 32 | rm $SEARCH_PID 33 | exit 0 34 | -------------------------------------------------------------------------------- /bin/start-search.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Start script for the Channel Directory search engine 20 | # ----------------------------------------------------------------------------- 21 | 22 | # Go to Search home and start it 23 | 24 | BIN_DIR=`dirname $0` 25 | BIN_DIR=`cd $BIN_DIR && pwd` 26 | cd "$BIN_DIR"/.. 27 | java -cp .:./*:./lib/* com.buddycloud.channeldirectory.search.Main & 28 | echo $! > "$BIN_DIR"/search.pid 29 | exit 0 30 | -------------------------------------------------------------------------------- /src/main/java/org/jivesoftware/smackx/pubsub/BuddycloudAffiliations.java: -------------------------------------------------------------------------------- 1 | package org.jivesoftware.smackx.pubsub; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | import org.jivesoftware.smack.packet.PacketExtension; 7 | import org.jivesoftware.smack.util.XmlStringBuilder; 8 | import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; 9 | 10 | public class BuddycloudAffiliations implements PacketExtension { 11 | 12 | public static final String ELEMENT_NAME = "affiliations"; 13 | 14 | protected List items = Collections.emptyList(); 15 | 16 | public BuddycloudAffiliations(List subList) { 17 | items = subList; 18 | } 19 | 20 | public List getAffiliations() { 21 | return items; 22 | } 23 | 24 | public String getElementName() { 25 | return ELEMENT_NAME; 26 | } 27 | 28 | public String getNamespace() { 29 | return PubSubNamespace.OWNER.getXmlns(); 30 | } 31 | 32 | @Override 33 | public CharSequence toXML() { 34 | XmlStringBuilder xml = new XmlStringBuilder(); 35 | xml.openElement(getElementName()); 36 | if (items != null) { 37 | for (BuddycloudAffiliation affiliation : items) { 38 | xml.append(affiliation.toXML()); 39 | } 40 | } 41 | xml.closeElement(getElementName()); 42 | return xml; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /bin/start-crawler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | # ----------------------------------------------------------------------------- 19 | # Start script for the Channel Directory Crawler 20 | # ----------------------------------------------------------------------------- 21 | 22 | # Go to Search home and start the Crawler 23 | 24 | BIN_DIR=`dirname $0` 25 | BIN_DIR=`cd $BIN_DIR && pwd` 26 | cd "$BIN_DIR"/.. 27 | java -cp .:./*:./lib/* com.buddycloud.channeldirectory.crawler.Main & 28 | echo $! > "$BIN_DIR"/crawler.pid 29 | exit 0 30 | -------------------------------------------------------------------------------- /src/main/java/org/jivesoftware/smackx/pubsub/BuddycloudAffiliation.java: -------------------------------------------------------------------------------- 1 | package org.jivesoftware.smackx.pubsub; 2 | 3 | import org.jivesoftware.smack.packet.PacketExtension; 4 | import org.jivesoftware.smack.util.XmlStringBuilder; 5 | import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; 6 | 7 | public class BuddycloudAffiliation implements PacketExtension { 8 | 9 | public static final String ELEMENT_NAME = "affiliation"; 10 | 11 | protected String node; 12 | protected Type type; 13 | 14 | public enum Type { 15 | member, none, outcast, owner, publisher, moderator, followerPlus 16 | } 17 | 18 | public BuddycloudAffiliation(String nodeId, Type affiliation) { 19 | node = nodeId; 20 | type = affiliation; 21 | } 22 | 23 | public String getNodeId() { 24 | return node; 25 | } 26 | 27 | public Type getType() { 28 | return type; 29 | } 30 | 31 | public String getElementName() { 32 | return ELEMENT_NAME; 33 | } 34 | 35 | public String getNamespace() { 36 | return PubSubNamespace.OWNER.getXmlns(); 37 | } 38 | 39 | @Override 40 | public CharSequence toXML() { 41 | XmlStringBuilder xml = new XmlStringBuilder(); 42 | xml.halfOpenElement(getElementName()); 43 | xml.optAttribute("node", node); 44 | xml.optAttribute(ELEMENT_NAME, type.toString()); 45 | xml.closeEmptyElement(); 46 | return xml; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/response/ContentData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.response; 17 | 18 | import com.buddycloud.channeldirectory.search.rsm.RSM; 19 | 20 | /** 21 | * Content query response in RSMable format. 22 | * Must have an identifier for indexing and paging purposes. 23 | * 24 | * @see RSM 25 | */ 26 | public class ContentData { 27 | 28 | private String id; 29 | private String type; 30 | 31 | public void setType(String type) { 32 | this.type = type; 33 | } 34 | 35 | public String getType() { 36 | return type; 37 | } 38 | 39 | public String getId() { 40 | return id; 41 | } 42 | 43 | public void setId(String id) { 44 | this.id = id; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/commons/solr/SolrUtils.java: -------------------------------------------------------------------------------- 1 | package com.buddycloud.channeldirectory.commons.solr; 2 | 3 | import java.util.Date; 4 | 5 | import org.apache.solr.common.SolrDocument; 6 | 7 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 8 | import com.buddycloud.channeldirectory.search.handler.response.Geolocation; 9 | 10 | public class SolrUtils { 11 | 12 | public static ChannelData convertToChannelData(SolrDocument solrDocument) { 13 | ChannelData channelData = new ChannelData(); 14 | String latLonStr = (String) solrDocument.getFieldValue("geoloc"); 15 | if (latLonStr != null) { 16 | String[] latLonSplit = latLonStr.split(","); 17 | channelData.setGeolocation(new Geolocation( 18 | Double.parseDouble(latLonSplit[0]), 19 | Double.parseDouble(latLonSplit[1]))); 20 | } 21 | 22 | channelData.setCreationDate((Date) solrDocument.getFieldValue("creation-date")); 23 | channelData.setChannelType((String) solrDocument.getFieldValue("channel-type")); 24 | channelData.setId((String) solrDocument.getFieldValue("jid")); 25 | channelData.setTitle((String) solrDocument.getFieldValue("title")); 26 | channelData.setDescription((String) solrDocument.getFieldValue("description")); 27 | channelData.setDefaultAffiliation((String) solrDocument.getFieldValue("default-affiliation")); 28 | 29 | return channelData; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG, FILE 2 | 3 | log4j.logger.com.buddycloud.channeldirectory.crawler=DEBUG, CRAWLERFILE 4 | log4j.logger.com.buddycloud.channeldirectory.search=DEBUG, SEARCHFILE 5 | 6 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 7 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 8 | log4j.appender.CONSOLE.layout.ConversionPattern=%d [%t] %-5p %c - %m%n 9 | 10 | log4j.appender.FILE=org.apache.log4j.RollingFileAppender 11 | log4j.appender.FILE.layout=org.apache.log4j.PatternLayout 12 | log4j.appender.FILE.layout.ConversionPattern=%d [%t] %-5p %c - %m%n 13 | log4j.appender.FILE.File=logs/log 14 | 15 | log4j.appender.SEARCHFILE=org.apache.log4j.RollingFileAppender 16 | log4j.appender.SEARCHFILE.layout=org.apache.log4j.PatternLayout 17 | log4j.appender.SEARCHFILE.layout.ConversionPattern=%d [%t] %-5p %c - %m%n 18 | log4j.appender.SEARCHFILE.File=logs/search.log 19 | 20 | log4j.appender.CRAWLERFILE=org.apache.log4j.RollingFileAppender 21 | log4j.appender.CRAWLERFILE.layout=org.apache.log4j.PatternLayout 22 | log4j.appender.CRAWLERFILE.layout.ConversionPattern=%d [%t] %-5p %c - %m%n 23 | log4j.appender.CRAWLERFILE.File=logs/crawler.log 24 | 25 | log4j.appender.SYSLOG=org.apache.log4j.net.SyslogAppender 26 | log4j.appender.SYSLOG.syslogHost=127.0.0.1 27 | log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout 28 | log4j.appender.SYSLOG.layout.ConversionPattern=%-5p %t: %c{1} - %m 29 | log4j.appender.SYSLOG.Facility=USER -------------------------------------------------------------------------------- /resources/solr/multicore/channels/conf/stopwords.txt: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | #----------------------------------------------------------------------- 17 | # a couple of test stopwords to test that the words are really being 18 | # configured from this file: 19 | stopworda 20 | stopwordb 21 | 22 | #Standard english stop words taken from Lucene's StopAnalyzer 23 | a 24 | an 25 | and 26 | are 27 | as 28 | at 29 | be 30 | but 31 | by 32 | for 33 | if 34 | in 35 | into 36 | is 37 | it 38 | no 39 | not 40 | of 41 | on 42 | or 43 | s 44 | such 45 | t 46 | that 47 | the 48 | their 49 | then 50 | there 51 | these 52 | they 53 | this 54 | to 55 | was 56 | will 57 | with 58 | 59 | -------------------------------------------------------------------------------- /resources/solr/multicore/posts/conf/stopwords.txt: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one or more 2 | # contributor license agreements. See the NOTICE file distributed with 3 | # this work for additional information regarding copyright ownership. 4 | # The ASF licenses this file to You under the Apache License, Version 2.0 5 | # (the "License"); you may not use this file except in compliance with 6 | # the License. You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | #----------------------------------------------------------------------- 17 | # a couple of test stopwords to test that the words are really being 18 | # configured from this file: 19 | stopworda 20 | stopwordb 21 | 22 | #Standard english stop words taken from Lucene's StopAnalyzer 23 | a 24 | an 25 | and 26 | are 27 | as 28 | at 29 | be 30 | but 31 | by 32 | for 33 | if 34 | in 35 | into 36 | is 37 | it 38 | no 39 | not 40 | of 41 | on 42 | or 43 | s 44 | such 45 | t 46 | that 47 | the 48 | their 49 | then 50 | there 51 | these 52 | they 53 | this 54 | to 55 | was 56 | will 57 | with 58 | 59 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/utils/GeolocationUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.utils; 17 | 18 | import org.dom4j.Element; 19 | 20 | import com.buddycloud.channeldirectory.search.handler.response.Geolocation; 21 | 22 | public class GeolocationUtils { 23 | 24 | public static void appendGeoLocation(Element geoElement, Geolocation geoLocation) { 25 | 26 | if (geoElement == null || geoLocation == null) { 27 | return; 28 | } 29 | 30 | if (geoLocation.getLat() != null) { 31 | geoElement.addElement("lat").setText( 32 | geoLocation.getLat().toString()); 33 | } 34 | 35 | if (geoLocation.getLng() != null) { 36 | geoElement.addElement("lon").setText( 37 | geoLocation.getLng().toString()); 38 | } 39 | 40 | if (geoLocation.getText() != null) { 41 | geoElement.addElement("text").setText( 42 | geoLocation.getText()); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/crawler/node/NodeCrawler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.crawler.node; 17 | 18 | import org.jivesoftware.smackx.pubsub.BuddycloudNode; 19 | import org.jivesoftware.smackx.pubsub.Node; 20 | 21 | /** 22 | * Implementations of this interface are responsible for 23 | * crawling a {@link Node} in a specific interest context. 24 | * 25 | */ 26 | public interface NodeCrawler { 27 | 28 | /** 29 | * Retrieves information from a {@link Node} and 30 | * commits it into the channel directory database. 31 | * 32 | * @param node 33 | * @param server 34 | * @throws Exception 35 | */ 36 | void crawl(BuddycloudNode node, String server) throws Exception; 37 | 38 | 39 | /** 40 | * Returns whether a {@link Node} should be processed 41 | * by this {@link NodeCrawler} 42 | * 43 | * @param node 44 | * @return 45 | */ 46 | boolean accept(BuddycloudNode node); 47 | } 48 | -------------------------------------------------------------------------------- /resources/solr/multicore/solr.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 25 | 26 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/response/Geolocation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.response; 17 | 18 | /** 19 | * Represents a Latitude/Longitude location pair, 20 | * and a text representation of a location 21 | * 22 | */ 23 | public class Geolocation { 24 | 25 | public static final String NAMESPACE = "http://jabber.org/protocol/geoloc"; 26 | 27 | private Double lat; 28 | private Double lng; 29 | private String text; 30 | 31 | public Geolocation() {} 32 | 33 | public Geolocation(Double lat, Double lng) { 34 | this.lat = lat; 35 | this.lng = lng; 36 | } 37 | 38 | public void setLat(Double lat) { 39 | this.lat = lat; 40 | } 41 | 42 | public Double getLat() { 43 | return lat; 44 | } 45 | 46 | public void setLng(Double lng) { 47 | this.lng = lng; 48 | } 49 | 50 | public Double getLng() { 51 | return lng; 52 | } 53 | 54 | public void setText(String text) { 55 | this.text = text; 56 | } 57 | 58 | public String getText() { 59 | return text; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/response/FakeDataGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.response; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | 22 | public class FakeDataGenerator { 23 | 24 | public static List createFakeChannels() { 25 | 26 | List fakeList = new ArrayList(); 27 | 28 | ChannelData object1 = new ChannelData(); 29 | object1.setGeolocation(new Geolocation(45.44, 12.33)); 30 | object1.setId("topicchanne01@example.org"); 31 | object1.setTitle("A channel about topic 01"); 32 | fakeList.add(object1); 33 | 34 | ChannelData object2 = new ChannelData(); 35 | object2.setGeolocation(new Geolocation(45.44, 12.33)); 36 | object2.setId("topicchanne02@example.org"); 37 | object2.setTitle("A channel about topic 02"); 38 | fakeList.add(object2); 39 | 40 | return fakeList; 41 | } 42 | 43 | public static List createFakePosts() { 44 | 45 | List fakeList = new ArrayList(); 46 | return fakeList; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /resources/schema/create-schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE subscribed_server ( 2 | name VARCHAR(300), 3 | PRIMARY KEY (name) 4 | ); 5 | 6 | CREATE TABLE subscribed_node ( 7 | name VARCHAR(300), 8 | server VARCHAR(300), 9 | metadata_updated TIMESTAMP, 10 | subscribers_updated TIMESTAMP, 11 | items_crawled TIMESTAMP, 12 | PRIMARY KEY (name, server) 13 | ); 14 | 15 | CREATE SEQUENCE user_id_seq; 16 | CREATE TABLE t_user ( 17 | id BIGINT PRIMARY KEY DEFAULT nextval('user_id_seq'), 18 | jid VARCHAR(300) 19 | ); 20 | CREATE INDEX user_jid_index ON t_user(jid); 21 | 22 | CREATE SEQUENCE item_id_seq; 23 | CREATE TABLE item ( 24 | id BIGINT PRIMARY KEY DEFAULT nextval('item_id_seq'), 25 | jid VARCHAR(300), 26 | title VARCHAR(300), 27 | description VARCHAR(500) 28 | ); 29 | CREATE INDEX item_jid_index ON item(jid); 30 | 31 | CREATE TABLE taste_preferences ( 32 | user_id BIGINT NOT NULL, 33 | item_id BIGINT NOT NULL, 34 | PRIMARY KEY (user_id, item_id) 35 | ); 36 | 37 | CREATE INDEX taste_preferences_user_id_index ON taste_preferences (user_id); 38 | CREATE INDEX taste_preferences_item_id_index ON taste_preferences (item_id); 39 | 40 | CREATE TABLE taste_item_similarity ( 41 | item_id_a BIGINT NOT NULL, 42 | item_id_b BIGINT NOT NULL, 43 | similarity FLOAT NOT NULL, 44 | PRIMARY KEY (item_id_a, item_id_b) 45 | ); 46 | 47 | CREATE TABLE channel_activity ( 48 | channel_jid VARCHAR(300), 49 | detailed_activity VARCHAR(2048), 50 | summarized_activity BIGINT, 51 | updated TIMESTAMP, 52 | earliest TIMESTAMP, 53 | PRIMARY KEY (channel_jid) 54 | ); 55 | 56 | CREATE INDEX channel_activity_summary_index ON channel_activity (summarized_activity DESC); 57 | CREATE INDEX channel_activity_updated_index ON channel_activity (updated DESC); -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/AbstractQueryHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler; 17 | 18 | import java.util.Properties; 19 | 20 | import org.apache.log4j.Logger; 21 | 22 | /** 23 | * Generic QueryHandler. 24 | * QueryHandler implementations should extend this class. 25 | * 26 | * @see QueryHandler 27 | * 28 | */ 29 | public abstract class AbstractQueryHandler implements QueryHandler { 30 | 31 | private final String namespace; 32 | private final Logger logger; 33 | private final Properties properties; 34 | 35 | /** 36 | * Creates a QueryHandler for a given namespace 37 | * @param namespace 38 | */ 39 | public AbstractQueryHandler(String namespace, Properties properties) { 40 | this.namespace = namespace; 41 | this.properties = properties; 42 | this.logger = Logger.getLogger(getClass()); 43 | } 44 | 45 | @Override 46 | public String getNamespace() { 47 | return namespace; 48 | } 49 | 50 | protected Logger getLogger() { 51 | return logger; 52 | } 53 | 54 | protected Properties getProperties() { 55 | return properties; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/com/buddycloud/HSQLDBTest.java: -------------------------------------------------------------------------------- 1 | package com.buddycloud; 2 | 3 | import java.beans.PropertyVetoException; 4 | import java.io.FileInputStream; 5 | import java.io.FileNotFoundException; 6 | import java.io.IOException; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | import java.util.Properties; 10 | 11 | import org.apache.commons.io.IOUtils; 12 | import org.junit.After; 13 | import org.junit.Before; 14 | 15 | import com.buddycloud.channeldirectory.commons.db.ChannelDirectoryDataSource; 16 | 17 | public class HSQLDBTest { 18 | 19 | private ChannelDirectoryDataSource dataSource; 20 | 21 | @Before 22 | public void setUpDB() throws Exception { 23 | Properties p = new Properties(); 24 | p.setProperty("mahout.jdbc.url", "jdbc:hsqldb:mem:channels;create=true"); 25 | p.setProperty("mahout.jdbc.driver", "org.hsqldb.jdbcDriver"); 26 | this.dataSource = new ChannelDirectoryDataSource(p); 27 | 28 | runBatch("resources/schema/create-schema.sql"); 29 | runBatch("resources/schema/update-schema-0.sql"); 30 | } 31 | 32 | @After 33 | public void tearDownDB() throws Exception { 34 | runBatch("resources/schema/drop-schema.sql"); 35 | } 36 | 37 | private void runBatch(String batchFile) throws PropertyVetoException, 38 | SQLException, IOException, FileNotFoundException { 39 | Statement st = dataSource.createStatement(); 40 | String schemaStr = IOUtils.toString(new FileInputStream(batchFile)); 41 | st.addBatch("SET DATABASE SQL SYNTAX PGS TRUE"); 42 | for (String sqlStr : schemaStr.split(";")) { 43 | if (sqlStr.trim().length() > 0) { 44 | st.addBatch(sqlStr); 45 | } 46 | } 47 | st.executeBatch(); 48 | ChannelDirectoryDataSource.close(st); 49 | } 50 | 51 | public ChannelDirectoryDataSource getDataSource() { 52 | return dataSource; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/commons/solr/SolrServerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.commons.solr; 17 | 18 | import java.net.MalformedURLException; 19 | import java.util.Properties; 20 | 21 | import org.apache.solr.client.solrj.SolrServer; 22 | import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer; 23 | 24 | public class SolrServerFactory { 25 | 26 | private static final String SOLR_CHANNELCORE_PROP = "solr.channelcore"; 27 | private static final String SOLR_POSTCORE_PROP = "solr.postcore"; 28 | 29 | private static SolrServer createSolrCore(Properties properties, 30 | String coreProperty) throws MalformedURLException { 31 | String solrChannelUrl = (String) properties.get(coreProperty); 32 | return new CommonsHttpSolrServer(solrChannelUrl); 33 | } 34 | 35 | public SolrServer createChannelCore(Properties properties) 36 | throws MalformedURLException { 37 | return createSolrCore(properties, SOLR_CHANNELCORE_PROP); 38 | } 39 | 40 | public SolrServer createPostCore(Properties properties) 41 | throws MalformedURLException { 42 | return createSolrCore(properties, SOLR_POSTCORE_PROP); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/commons/ConfigurationUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.commons; 17 | 18 | import java.io.FileInputStream; 19 | import java.io.IOException; 20 | import java.util.Properties; 21 | 22 | import org.apache.log4j.Logger; 23 | 24 | /** 25 | * General utils for accessing the engine configuration 26 | * 27 | */ 28 | public class ConfigurationUtils { 29 | 30 | private static final String CONFIGURATION_FILE = getChannelDirHome() 31 | + "/configuration.properties"; 32 | 33 | private static Logger LOGGER = Logger.getLogger(ConfigurationUtils.class); 34 | 35 | public static Properties loadConfiguration() throws IOException { 36 | Properties configuration = new Properties(); 37 | try { 38 | configuration.load(new FileInputStream(CONFIGURATION_FILE)); 39 | } catch (IOException e) { 40 | LOGGER.fatal("Configuration could not be loaded.", e); 41 | throw e; 42 | } 43 | return configuration; 44 | } 45 | 46 | 47 | public static String getChannelDirHome() { 48 | String channelDirHome = System.getenv("CHANNEL_DIRECTORY_HOME"); 49 | return channelDirHome == null ? "." : channelDirHome; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bin/start-solr.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem Licensed to the Apache Software Foundation (ASF) under one or more 3 | rem contributor license agreements. See the NOTICE file distributed with 4 | rem this work for additional information regarding copyright ownership. 5 | rem The ASF licenses this file to You under the Apache License, Version 2.0 6 | rem (the "License"); you may not use this file except in compliance with 7 | rem the License. You may obtain a copy of the License at 8 | rem 9 | rem http://www.apache.org/licenses/LICENSE-2.0 10 | rem 11 | rem Unless required by applicable law or agreed to in writing, software 12 | rem distributed under the License is distributed on an "AS IS" BASIS, 13 | rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | rem See the License for the specific language governing permissions and 15 | rem limitations under the License. 16 | 17 | rem --------------------------------------------------------------------------- 18 | rem Start script for the Channel Directory Solr 19 | rem --------------------------------------------------------------------------- 20 | 21 | rem Guess CHANNEL_DIRECTORY_HOME if not defined 22 | set CURRENT_DIR=%cd% 23 | if not "%CHANNEL_DIRECTORY_HOME%" == "" goto gotHome 24 | set CHANNEL_DIRECTORY_HOME=%CURRENT_DIR% 25 | if exist "%CHANNEL_DIRECTORY_HOME%\resources\solr\start.jar" goto okHome 26 | cd .. 27 | set CHANNEL_DIRECTORY_HOME=%cd% 28 | cd %CURRENT_DIR% 29 | :gotHome 30 | if exist "%CHANNEL_DIRECTORY_HOME%\resources\solr\start.jar" goto okHome 31 | echo The CHANNEL_DIRECTORY_HOME environment variable is not defined correctly 32 | echo This environment variable is needed to run this program 33 | goto end 34 | :okHome 35 | 36 | rem Go to Solr home and start it 37 | cd %CHANNEL_DIRECTORY_HOME%\resources\solr 38 | java -Dsolr.solr.home=multicore -jar start.jar 39 | 40 | :end -------------------------------------------------------------------------------- /bin/start-crawler.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem Licensed to the Apache Software Foundation (ASF) under one or more 3 | rem contributor license agreements. See the NOTICE file distributed with 4 | rem this work for additional information regarding copyright ownership. 5 | rem The ASF licenses this file to You under the Apache License, Version 2.0 6 | rem (the "License"); you may not use this file except in compliance with 7 | rem the License. You may obtain a copy of the License at 8 | rem 9 | rem http://www.apache.org/licenses/LICENSE-2.0 10 | rem 11 | rem Unless required by applicable law or agreed to in writing, software 12 | rem distributed under the License is distributed on an "AS IS" BASIS, 13 | rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | rem See the License for the specific language governing permissions and 15 | rem limitations under the License. 16 | 17 | rem --------------------------------------------------------------------------- 18 | rem Start script for the Channel Directory Crawler 19 | rem --------------------------------------------------------------------------- 20 | 21 | rem Guess CHANNEL_DIRECTORY_HOME if not defined 22 | set CURRENT_DIR=%cd% 23 | if not "%CHANNEL_DIRECTORY_HOME%" == "" goto gotHome 24 | set CHANNEL_DIRECTORY_HOME=%CURRENT_DIR% 25 | if exist "%CHANNEL_DIRECTORY_HOME%\lib\channel-directory.jar" goto okHome 26 | cd .. 27 | set CHANNEL_DIRECTORY_HOME=%cd% 28 | cd %CURRENT_DIR% 29 | :gotHome 30 | if exist "%CHANNEL_DIRECTORY_HOME%\lib\channel-directory.jar" goto okHome 31 | echo The CHANNEL_DIRECTORY_HOME environment variable is not defined correctly 32 | echo This environment variable is needed to run this program 33 | goto end 34 | :okHome 35 | 36 | rem Start the search engine 37 | cd %CHANNEL_DIRECTORY_HOME% 38 | java -cp .;.\*;.\lib\* com.buddycloud.channeldirectory.crawler.Main 39 | 40 | :end -------------------------------------------------------------------------------- /bin/start-search.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem Licensed to the Apache Software Foundation (ASF) under one or more 3 | rem contributor license agreements. See the NOTICE file distributed with 4 | rem this work for additional information regarding copyright ownership. 5 | rem The ASF licenses this file to You under the Apache License, Version 2.0 6 | rem (the "License"); you may not use this file except in compliance with 7 | rem the License. You may obtain a copy of the License at 8 | rem 9 | rem http://www.apache.org/licenses/LICENSE-2.0 10 | rem 11 | rem Unless required by applicable law or agreed to in writing, software 12 | rem distributed under the License is distributed on an "AS IS" BASIS, 13 | rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | rem See the License for the specific language governing permissions and 15 | rem limitations under the License. 16 | 17 | rem --------------------------------------------------------------------------- 18 | rem Start script for the Channel Directory Search Engine 19 | rem --------------------------------------------------------------------------- 20 | 21 | rem Guess CHANNEL_DIRECTORY_HOME if not defined 22 | set CURRENT_DIR=%cd% 23 | if not "%CHANNEL_DIRECTORY_HOME%" == "" goto gotHome 24 | set CHANNEL_DIRECTORY_HOME=%CURRENT_DIR% 25 | if exist "%CHANNEL_DIRECTORY_HOME%\lib\channel-directory.jar" goto okHome 26 | cd .. 27 | set CHANNEL_DIRECTORY_HOME=%cd% 28 | cd %CURRENT_DIR% 29 | :gotHome 30 | if exist "%CHANNEL_DIRECTORY_HOME%\lib\channel-directory.jar" goto okHome 31 | echo The CHANNEL_DIRECTORY_HOME environment variable is not defined correctly 32 | echo This environment variable is needed to run this program 33 | goto end 34 | :okHome 35 | 36 | rem Start the search engine 37 | cd %CHANNEL_DIRECTORY_HOME% 38 | java -cp .;.\*;.\lib\* com.buddycloud.channeldirectory.search.Main 39 | 40 | :end -------------------------------------------------------------------------------- /bin/setup-schema.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | rem Licensed to the Apache Software Foundation (ASF) under one or more 3 | rem contributor license agreements. See the NOTICE file distributed with 4 | rem this work for additional information regarding copyright ownership. 5 | rem The ASF licenses this file to You under the Apache License, Version 2.0 6 | rem (the "License"); you may not use this file except in compliance with 7 | rem the License. You may obtain a copy of the License at 8 | rem 9 | rem http://www.apache.org/licenses/LICENSE-2.0 10 | rem 11 | rem Unless required by applicable law or agreed to in writing, software 12 | rem distributed under the License is distributed on an "AS IS" BASIS, 13 | rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | rem See the License for the specific language governing permissions and 15 | rem limitations under the License. 16 | 17 | rem --------------------------------------------------------------------------- 18 | rem Start script for the Channel Directory Search Engine 19 | rem --------------------------------------------------------------------------- 20 | 21 | rem Guess CHANNEL_DIRECTORY_HOME if not defined 22 | set CURRENT_DIR=%cd% 23 | if not "%CHANNEL_DIRECTORY_HOME%" == "" goto gotHome 24 | set CHANNEL_DIRECTORY_HOME=%CURRENT_DIR% 25 | if exist "%CHANNEL_DIRECTORY_HOME%\lib\channel-directory.jar" goto okHome 26 | cd .. 27 | set CHANNEL_DIRECTORY_HOME=%cd% 28 | cd %CURRENT_DIR% 29 | :gotHome 30 | if exist "%CHANNEL_DIRECTORY_HOME%\lib\channel-directory.jar" goto okHome 31 | echo The CHANNEL_DIRECTORY_HOME environment variable is not defined correctly 32 | echo This environment variable is needed to run this program 33 | goto end 34 | :okHome 35 | 36 | rem Start the search engine 37 | cd %CHANNEL_DIRECTORY_HOME% 38 | java -cp .;.\*;.\lib\* com.buddycloud.channeldirectory.commons.db.CreateSchema 39 | 40 | :end -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/common/mahout/RecommendationResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.common.mahout; 17 | 18 | import java.util.List; 19 | 20 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 21 | 22 | /** 23 | * 24 | */ 25 | public class RecommendationResponse { 26 | 27 | private List response; 28 | private int numFound; 29 | 30 | /** 31 | * @param response 32 | * @param numFound 33 | */ 34 | public RecommendationResponse(List response, int numFound) { 35 | this.response = response; 36 | this.numFound = numFound; 37 | } 38 | 39 | /** 40 | * @return the response 41 | */ 42 | public List getResponse() { 43 | return response; 44 | } 45 | 46 | /** 47 | * @param response the response to set 48 | */ 49 | public void setResponse(List response) { 50 | this.response = response; 51 | } 52 | 53 | /** 54 | * @return the numFound 55 | */ 56 | public int getNumFound() { 57 | return numFound; 58 | } 59 | 60 | /** 61 | * @param numFound the numFound to set 62 | */ 63 | public void setNumFound(int numFound) { 64 | this.numFound = numFound; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/crawler/PubSubManagers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.crawler; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import org.jivesoftware.smack.XMPPConnection; 22 | import org.jivesoftware.smackx.pubsub.BuddycloudPubsubManager; 23 | import org.jivesoftware.smackx.pubsub.PubSubManager; 24 | 25 | /** 26 | * Simply maintain a collection of {@link PubSubManager}, 27 | * so different crawling strategies can use the same 28 | * node cache. 29 | * 30 | */ 31 | public class PubSubManagers { 32 | 33 | private final Map pubSubManagers = new HashMap(); 34 | private final XMPPConnection connection; 35 | 36 | public PubSubManagers(XMPPConnection connection) { 37 | this.connection = connection; 38 | } 39 | 40 | public BuddycloudPubsubManager getPubSubManager(String pubSubServer) { 41 | BuddycloudPubsubManager pubSubManager = pubSubManagers.get(pubSubServer); 42 | if (pubSubManager == null) { 43 | pubSubManager = new BuddycloudPubsubManager(connection, pubSubServer); 44 | pubSubManagers.put(pubSubServer, pubSubManager); 45 | } 46 | return pubSubManager; 47 | } 48 | 49 | /** 50 | * @return 51 | */ 52 | public XMPPConnection getConnection() { 53 | return connection; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/common/mahout/ChannelRecommenderDataModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.common.mahout; 17 | 18 | import org.apache.mahout.cf.taste.model.DataModel; 19 | 20 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 21 | 22 | /** 23 | * Implementations should embed a Mahout data model, 24 | * and also should be responsible for translating 25 | * Mahout-specific ids to jids. 26 | * 27 | */ 28 | public interface ChannelRecommenderDataModel { 29 | 30 | /** 31 | * Returns a Mahout {@link DataModel}, 32 | * which is required by the recommender. 33 | * 34 | * @return 35 | */ 36 | public DataModel getDataModel(); 37 | 38 | /** 39 | * Converts a user jid into a long id, 40 | * which is required by Mahout. 41 | * 42 | * @param userJid 43 | * @return 44 | */ 45 | public Long toUserId(String userJid); 46 | 47 | /** 48 | * Given the Mahout long id for an item, 49 | * returns the respective channel data. 50 | * 51 | * @param itemID 52 | * @return 53 | */ 54 | public ChannelData toChannelData(long itemID); 55 | 56 | /** 57 | * Converts a channel jid into a long id, 58 | * which is required by Mahout. 59 | * 60 | * @param channelJid 61 | * @return 62 | */ 63 | public Long toChannelId(String channelJid); 64 | 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/cli/QueryToDBMS.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.cli; 17 | 18 | import java.sql.PreparedStatement; 19 | import java.sql.ResultSet; 20 | import java.sql.SQLException; 21 | import java.util.Properties; 22 | 23 | import com.buddycloud.channeldirectory.commons.db.ChannelDirectoryDataSource; 24 | import com.mchange.v2.c3p0.DataSources; 25 | 26 | /** 27 | * @author Abmar 28 | * 29 | */ 30 | public class QueryToDBMS implements Query { 31 | 32 | private String sql; 33 | 34 | public QueryToDBMS(String sql) { 35 | this.sql = sql; 36 | } 37 | 38 | /* (non-Javadoc) 39 | * @see com.buddycloud.channeldirectory.cli.Query#exec(java.lang.String, java.util.Properties) 40 | */ 41 | @Override 42 | public String exec(String args, Properties configuration) throws Exception { 43 | 44 | ChannelDirectoryDataSource dataSource = new ChannelDirectoryDataSource(configuration); 45 | PreparedStatement statement = null; 46 | try { 47 | statement = dataSource.prepareStatement(sql); 48 | ResultSet resultSet = statement.executeQuery(); 49 | if (!resultSet.next()) { 50 | return ""; 51 | } 52 | return resultSet.getString(1); 53 | 54 | } catch (SQLException e1) { 55 | throw e1; 56 | } finally { 57 | ChannelDirectoryDataSource.close(statement); 58 | DataSources.destroy(dataSource.getDataSource()); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/cli/QueryToSolr.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.cli; 17 | 18 | import java.util.Properties; 19 | 20 | import org.apache.solr.client.solrj.SolrQuery; 21 | import org.apache.solr.client.solrj.SolrServer; 22 | import org.apache.solr.client.solrj.response.QueryResponse; 23 | 24 | import com.buddycloud.channeldirectory.commons.solr.SolrServerFactory; 25 | 26 | /** 27 | * @author Abmar 28 | * 29 | */ 30 | public class QueryToSolr implements Query { 31 | 32 | private final String agg; 33 | private final String core; 34 | private final String q; 35 | 36 | public QueryToSolr(String agg, String core, String q) { 37 | this.agg = agg; 38 | this.core = core; 39 | this.q = q; 40 | } 41 | 42 | public String exec(String args, Properties configuration) throws Exception { 43 | 44 | SolrServer solrServer = null; 45 | 46 | if (core.equals("posts")) { 47 | solrServer = new SolrServerFactory().createPostCore(configuration); 48 | } else if (core.equals("channels")) { 49 | solrServer = new SolrServerFactory().createChannelCore(configuration); 50 | } 51 | 52 | SolrQuery solrQuery = new SolrQuery(q); 53 | QueryResponse queryResponse = solrServer.query(solrQuery); 54 | 55 | if (agg.equals("count")) { 56 | Long numFound = queryResponse.getResults().getNumFound(); 57 | return numFound.toString(); 58 | } 59 | 60 | return null; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /resources/solr/multicore/posts/conf/solrconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 23 | 24 | LUCENE_31 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | solr 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /resources/solr/multicore/channels/conf/solrconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 23 | 24 | LUCENE_31 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | solr 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/rsm/RSM.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.rsm; 17 | 18 | /** 19 | * Represents a container for attributes related 20 | * to the RSM format (http://xmpp.org/extensions/xep-0059.html). 21 | * 22 | */ 23 | public class RSM { 24 | 25 | public static String NAMESPACE = "http://jabber.org/protocol/rsm"; 26 | 27 | private Integer max; 28 | private Integer index = 0; 29 | private String before; 30 | private String after; 31 | 32 | private String first; 33 | private String last; 34 | private Integer count; 35 | 36 | public String getFirst() { 37 | return first; 38 | } 39 | 40 | public void setFirst(String first) { 41 | this.first = first; 42 | } 43 | 44 | public String getLast() { 45 | return last; 46 | } 47 | 48 | public void setLast(String last) { 49 | this.last = last; 50 | } 51 | 52 | public Integer getCount() { 53 | return count; 54 | } 55 | 56 | public void setCount(Integer count) { 57 | this.count = count; 58 | } 59 | 60 | public void setMax(Integer max) { 61 | this.max = max; 62 | } 63 | 64 | public void setIndex(Integer index) { 65 | this.index = index; 66 | } 67 | 68 | public void setBefore(String before) { 69 | this.before = before; 70 | } 71 | 72 | public void setAfter(String after) { 73 | this.after = after; 74 | } 75 | 76 | public Integer getMax() { 77 | return max; 78 | } 79 | 80 | public Integer getIndex() { 81 | return index; 82 | } 83 | 84 | public String getBefore() { 85 | return before; 86 | } 87 | 88 | public String getAfter() { 89 | return after; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/rsm/SolrRSMUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.rsm; 17 | 18 | import org.apache.solr.client.solrj.SolrQuery; 19 | import org.apache.solr.client.solrj.response.QueryResponse; 20 | 21 | 22 | /** 23 | * It is responsible for providing utility methods related to RSM format 24 | * (http://xmpp.org/extensions/xep-0059.html), 25 | * which are used on the Solr query processing and response. 26 | * 27 | * @see RSM 28 | * 29 | */ 30 | public class SolrRSMUtils { 31 | 32 | public static void preprocess( 33 | SolrQuery query, RSM rsm) 34 | throws IllegalArgumentException { 35 | 36 | String after = rsm.getAfter(); 37 | String before = rsm.getBefore(); 38 | 39 | int initialIndex = rsm.getIndex(); 40 | int lastIndex = -1; 41 | 42 | if (after != null) { 43 | initialIndex = Integer.valueOf(after); 44 | } 45 | if (before != null && !before.isEmpty()) { 46 | lastIndex = Integer.valueOf(before) - 2; 47 | } 48 | 49 | if (rsm.getMax() != null) { 50 | if (before != null) { 51 | initialIndex = lastIndex - rsm.getMax() + 1; 52 | } else { 53 | lastIndex = initialIndex + rsm.getMax() - 1; 54 | } 55 | } 56 | 57 | if (lastIndex >= 0) { 58 | query.setStart(initialIndex); 59 | query.setRows(lastIndex - initialIndex + 1); 60 | } 61 | 62 | rsm.setIndex(initialIndex); 63 | rsm.setFirst(String.valueOf(initialIndex + 1)); 64 | rsm.setLast(String.valueOf(lastIndex + 1)); 65 | } 66 | 67 | public static void postprocess( 68 | QueryResponse queryResponse, RSM rsm) 69 | throws IllegalArgumentException { 70 | 71 | rsm.setCount((int)queryResponse.getResults().getNumFound()); 72 | 73 | if (queryResponse.getResponse().size() == 0) { 74 | rsm.setFirst(null); 75 | rsm.setLast(null); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/jivesoftware/smackx/pubsub/BuddycloudPubsubManager.java: -------------------------------------------------------------------------------- 1 | package org.jivesoftware.smackx.pubsub; 2 | 3 | import org.jivesoftware.smack.SmackException.NoResponseException; 4 | import org.jivesoftware.smack.SmackException.NotConnectedException; 5 | import org.jivesoftware.smack.XMPPConnection; 6 | import org.jivesoftware.smack.XMPPException.XMPPErrorException; 7 | import org.jivesoftware.smack.provider.ProviderManager; 8 | import org.jivesoftware.smackx.disco.packet.DiscoverItems; 9 | import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; 10 | 11 | public class BuddycloudPubsubManager { 12 | 13 | static { 14 | // Use the BuddycloudAffiliationsProvider 15 | BuddycloudAffiliationsProvider affiliationsProvider = new BuddycloudAffiliationsProvider(); 16 | ProviderManager.addExtensionProvider(BuddycloudAffiliations.ELEMENT_NAME, 17 | PubSubNamespace.OWNER.getXmlns(), affiliationsProvider); 18 | ProviderManager.addExtensionProvider(BuddycloudAffiliations.ELEMENT_NAME, 19 | PubSubNamespace.BASIC.getXmlns(), affiliationsProvider); 20 | 21 | // Use the BuddycloudAffiliationProvider for buddycloud-specific affiliations 22 | BuddycloudAffiliationProvider affiliationProvider = new BuddycloudAffiliationProvider(); 23 | ProviderManager.addExtensionProvider(BuddycloudAffiliation.ELEMENT_NAME, 24 | PubSubNamespace.OWNER.getXmlns(), affiliationProvider); 25 | ProviderManager.addExtensionProvider(BuddycloudAffiliation.ELEMENT_NAME, 26 | PubSubNamespace.BASIC.getXmlns(), affiliationProvider); 27 | } 28 | 29 | private final PubSubManager manager; 30 | private XMPPConnection connection; 31 | private final String toAddress; 32 | 33 | public BuddycloudPubsubManager(XMPPConnection connection, String toAddress) { 34 | this.manager = new PubSubManager(connection, toAddress); 35 | this.connection = connection; 36 | this.toAddress = toAddress; 37 | } 38 | 39 | public BuddycloudNode getNode(String id) throws NoResponseException, 40 | XMPPErrorException, NotConnectedException { 41 | return new BuddycloudNode(manager.getNode(id)); 42 | } 43 | 44 | public DiscoverItems discoverNodes(String nodeId) 45 | throws NoResponseException, XMPPErrorException, 46 | NotConnectedException { 47 | return manager.discoverNodes(nodeId); 48 | } 49 | 50 | public BuddycloudNode getFirehoseNode() { 51 | BuddycloudFirehoseNode firehose = new BuddycloudFirehoseNode(connection); 52 | firehose.setTo(toAddress); 53 | return new BuddycloudNode(firehose); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /resources/init_d/buddycloud-solr: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Original /etc/init.d/skeleton modified for http://mydebian.blogdns.org 3 | 4 | # PATH should only include /usr/* if it runs after the mountnfs.sh script 5 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 6 | DESC="Buddycloud channel directory - Apache Solr" 7 | NAME="channeldirectory-solr" 8 | RUNFROM=/opt/buddycloud-channeldirectory/resources/solr 9 | DAEMON=/usr/bin/java 10 | DAEMON_ARGS="-Dsolr.solr.home=multicore -jar start.jar" 11 | PIDFILE=/var/run/$NAME.pid 12 | SCRIPTNAME=/etc/init.d/buddycloud-solr 13 | 14 | # the user that will run the script 15 | USER=buddycloud-channeldir 16 | 17 | # Make sure only root can run our script 18 | if [ "$(id -u)" != "0" ]; then 19 | echo "This script must be run as root. Try using sudo." 1>&2 20 | exit 1 21 | fi 22 | 23 | 24 | # NO NEED TO MODIFY THE LINES BELOW 25 | 26 | # Load the VERBOSE setting and other rcS variables 27 | . /lib/init/vars.sh 28 | 29 | # Define LSB log_* functions. 30 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. 31 | . /lib/lsb/init-functions 32 | 33 | # 34 | # Function that starts the daemon/service 35 | # 36 | do_start() 37 | { 38 | 39 | start-stop-daemon -b --start --quiet --chuid $USER --chdir $RUNFROM -m -p $PIDFILE --exec $DAEMON -- $DAEMON_ARGS \ 40 | || return 2 41 | } 42 | 43 | # 44 | # Function that stops the daemon/service 45 | # 46 | do_stop() 47 | { 48 | start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE 49 | RETVAL="$?" 50 | rm -f $PIDFILE 51 | return "$RETVAL" 52 | } 53 | 54 | case "$1" in 55 | start) 56 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" 57 | do_start 58 | case "$?" in 59 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 60 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 61 | esac 62 | ;; 63 | stop) 64 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" 65 | do_stop 66 | case "$?" in 67 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 68 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 69 | esac 70 | ;; 71 | restart) 72 | # 73 | # If the "reload" option is implemented then remove the 74 | # 'force-reload' alias 75 | # 76 | log_daemon_msg "Restarting $DESC" "$NAME" 77 | do_stop 78 | case "$?" in 79 | 0|1) 80 | do_start 81 | case "$?" in 82 | 0) log_end_msg 0 ;; 83 | 1) log_end_msg 1 ;; # Old process is still running 84 | *) log_end_msg 1 ;; # Failed to start 85 | esac 86 | ;; 87 | *) 88 | # Failed to stop 89 | log_end_msg 1 90 | ;; 91 | esac 92 | ;; 93 | *) 94 | echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2 95 | exit 3 96 | ;; 97 | esac 98 | 99 | : -------------------------------------------------------------------------------- /resources/init_d/buddycloud-crawler: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Original /etc/init.d/skeleton modified for http://mydebian.blogdns.org 3 | 4 | # PATH should only include /usr/* if it runs after the mountnfs.sh script 5 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 6 | DESC="Buddycloud channel directory - Crawler" 7 | NAME="channeldirectory-crawler" 8 | RUNFROM=/opt/buddycloud-channeldirectory 9 | DAEMON=/usr/bin/java 10 | DAEMON_ARGS="-cp .:./*:./lib/* com.buddycloud.channeldirectory.crawler.Main" 11 | PIDFILE=/var/run/$NAME.pid 12 | SCRIPTNAME=/etc/init.d/buddycloud-crawler 13 | 14 | # the user that will run the script 15 | USER=buddycloud-channeldir 16 | 17 | # Make sure only root can run our script 18 | if [ "$(id -u)" != "0" ]; then 19 | echo "This script must be run as root. Try using sudo." 1>&2 20 | exit 1 21 | fi 22 | 23 | 24 | # NO NEED TO MODIFY THE LINES BELOW 25 | 26 | # Load the VERBOSE setting and other rcS variables 27 | . /lib/init/vars.sh 28 | 29 | # Define LSB log_* functions. 30 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. 31 | . /lib/lsb/init-functions 32 | 33 | # 34 | # Function that starts the daemon/service 35 | # 36 | do_start() 37 | { 38 | 39 | start-stop-daemon -b --start --quiet --chuid $USER --chdir $RUNFROM -m -p $PIDFILE --exec $DAEMON -- $DAEMON_ARGS \ 40 | || return 2 41 | } 42 | 43 | # 44 | # Function that stops the daemon/service 45 | # 46 | do_stop() 47 | { 48 | start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE 49 | RETVAL="$?" 50 | rm -f $PIDFILE 51 | return "$RETVAL" 52 | } 53 | 54 | case "$1" in 55 | start) 56 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" 57 | do_start 58 | case "$?" in 59 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 60 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 61 | esac 62 | ;; 63 | stop) 64 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" 65 | do_stop 66 | case "$?" in 67 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 68 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 69 | esac 70 | ;; 71 | restart) 72 | # 73 | # If the "reload" option is implemented then remove the 74 | # 'force-reload' alias 75 | # 76 | log_daemon_msg "Restarting $DESC" "$NAME" 77 | do_stop 78 | case "$?" in 79 | 0|1) 80 | do_start 81 | case "$?" in 82 | 0) log_end_msg 0 ;; 83 | 1) log_end_msg 1 ;; # Old process is still running 84 | *) log_end_msg 1 ;; # Failed to start 85 | esac 86 | ;; 87 | *) 88 | # Failed to stop 89 | log_end_msg 1 90 | ;; 91 | esac 92 | ;; 93 | *) 94 | echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2 95 | exit 3 96 | ;; 97 | esac 98 | 99 | : -------------------------------------------------------------------------------- /resources/init_d/buddycloud-search: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Original /etc/init.d/skeleton modified for http://mydebian.blogdns.org 3 | 4 | # PATH should only include /usr/* if it runs after the mountnfs.sh script 5 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 6 | DESC="Buddycloud channel directory - Search engine" 7 | NAME="channeldirectory-search" 8 | RUNFROM=/opt/buddycloud-channeldirectory 9 | DAEMON=/usr/bin/java 10 | DAEMON_ARGS="-cp .:./*:./lib/* com.buddycloud.channeldirectory.search.Main" 11 | PIDFILE=/var/run/$NAME.pid 12 | SCRIPTNAME=/etc/init.d/buddycloud-search 13 | 14 | # the user that will run the script 15 | USER=buddycloud-channeldir 16 | 17 | # Make sure only root can run our script 18 | if [ "$(id -u)" != "0" ]; then 19 | echo "This script must be run as root. Try using sudo." 1>&2 20 | exit 1 21 | fi 22 | 23 | 24 | # NO NEED TO MODIFY THE LINES BELOW 25 | 26 | # Load the VERBOSE setting and other rcS variables 27 | . /lib/init/vars.sh 28 | 29 | # Define LSB log_* functions. 30 | # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. 31 | . /lib/lsb/init-functions 32 | 33 | # 34 | # Function that starts the daemon/service 35 | # 36 | do_start() 37 | { 38 | 39 | start-stop-daemon -b --start --quiet --chuid $USER --chdir $RUNFROM -m -p $PIDFILE --exec $DAEMON -- $DAEMON_ARGS \ 40 | || return 2 41 | } 42 | 43 | # 44 | # Function that stops the daemon/service 45 | # 46 | do_stop() 47 | { 48 | start-stop-daemon --stop --quiet --oknodo --pidfile $PIDFILE 49 | RETVAL="$?" 50 | rm -f $PIDFILE 51 | return "$RETVAL" 52 | } 53 | 54 | case "$1" in 55 | start) 56 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" 57 | do_start 58 | case "$?" in 59 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 60 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 61 | esac 62 | ;; 63 | stop) 64 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" 65 | do_stop 66 | case "$?" in 67 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 68 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 69 | esac 70 | ;; 71 | restart) 72 | # 73 | # If the "reload" option is implemented then remove the 74 | # 'force-reload' alias 75 | # 76 | log_daemon_msg "Restarting $DESC" "$NAME" 77 | do_stop 78 | case "$?" in 79 | 0|1) 80 | do_start 81 | case "$?" in 82 | 0) log_end_msg 0 ;; 83 | 1) log_end_msg 1 ;; # Old process is still running 84 | *) log_end_msg 1 ;; # Failed to start 85 | esac 86 | ;; 87 | *) 88 | # Failed to stop 89 | log_end_msg 1 90 | ;; 91 | esac 92 | ;; 93 | *) 94 | echo "Usage: $SCRIPTNAME {start|stop|restart}" >&2 95 | exit 3 96 | ;; 97 | esac 98 | 99 | : -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/commons/db/CreateSchema.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.commons.db; 17 | 18 | import java.beans.PropertyVetoException; 19 | import java.io.FileInputStream; 20 | import java.io.FileNotFoundException; 21 | import java.io.IOException; 22 | import java.sql.Connection; 23 | import java.sql.SQLException; 24 | import java.sql.Statement; 25 | import java.util.List; 26 | 27 | import org.apache.commons.io.IOUtils; 28 | 29 | import com.buddycloud.channeldirectory.commons.ConfigurationUtils; 30 | 31 | /** 32 | * Creates jdbc schema required by the crawler 33 | * and by the search engine. 34 | * 35 | */ 36 | public class CreateSchema { 37 | 38 | /** 39 | * 40 | */ 41 | private static final String SQL_DELIMITER = ";"; 42 | 43 | private static final String SQL_CREATE_FILE = ConfigurationUtils.getChannelDirHome() 44 | + "/resources/schema/create-schema.sql"; 45 | 46 | @SuppressWarnings("unchecked") 47 | public static void main(String[] args) throws PropertyVetoException, IOException, SQLException { 48 | ChannelDirectoryDataSource channelDirectoryDataSource = new ChannelDirectoryDataSource( 49 | ConfigurationUtils.loadConfiguration()); 50 | 51 | runScript(channelDirectoryDataSource, SQL_CREATE_FILE); 52 | 53 | } 54 | 55 | private static void runScript( 56 | ChannelDirectoryDataSource channelDirectoryDataSource, String sqlFile) 57 | throws IOException, FileNotFoundException, SQLException { 58 | List readLines = IOUtils.readLines( 59 | new FileInputStream(sqlFile)); 60 | 61 | Connection connection = channelDirectoryDataSource.getConnection(); 62 | StringBuilder statementStr = new StringBuilder(); 63 | 64 | for (String line : readLines) { 65 | statementStr.append(line); 66 | if (line.endsWith(SQL_DELIMITER)) { 67 | Statement statement = connection.createStatement(); 68 | statement.execute(statementStr.toString()); 69 | statement.close(); 70 | statementStr.setLength(0); 71 | } 72 | } 73 | 74 | connection.close(); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/response/ChannelData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.response; 17 | 18 | import java.util.Date; 19 | 20 | 21 | /** 22 | * Represents a XMPP channel (identified by its jid) 23 | * with a title and a geolocation attribute. 24 | * 25 | * @see Geolocation 26 | * 27 | */ 28 | public class ChannelData extends ContentData { 29 | 30 | /** 31 | * Type constant for channels. 32 | */ 33 | private static final String TYPE_CHANNEL = "channel"; 34 | 35 | public static final String CH_TYPE_TOPIC = "topic"; 36 | public static final String CH_TYPE_PERSONAL = "personal"; 37 | 38 | public ChannelData() { 39 | setType(TYPE_CHANNEL); 40 | } 41 | 42 | private String title; 43 | private Geolocation geolocation; 44 | private String channelType; 45 | private String description; 46 | private Date creationDate; 47 | 48 | private String defaultAffiliation; 49 | 50 | public void setGeolocation(Geolocation geolocation) { 51 | this.geolocation = geolocation; 52 | } 53 | 54 | public Geolocation getGeolocation() { 55 | return geolocation; 56 | } 57 | 58 | public void setTitle(String title) { 59 | this.title = title; 60 | } 61 | 62 | public String getTitle() { 63 | return title; 64 | } 65 | 66 | public void setChannelType(String channelType) { 67 | this.channelType = channelType; 68 | } 69 | 70 | public String getChannelType() { 71 | return channelType; 72 | } 73 | 74 | public void setDescription(String description) { 75 | this.description = description; 76 | } 77 | 78 | public String getDescription() { 79 | return description; 80 | } 81 | 82 | public Date getCreationDate() { 83 | return creationDate; 84 | } 85 | 86 | public void setCreationDate(Date creationDate) { 87 | this.creationDate = creationDate; 88 | } 89 | 90 | public void setDefaultAffiliation(String defaultAffiliation) { 91 | this.defaultAffiliation = defaultAffiliation; 92 | } 93 | 94 | public String getDefaultAffiliation() { 95 | return defaultAffiliation; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search; 17 | 18 | import java.io.IOException; 19 | import java.util.Properties; 20 | 21 | import org.apache.log4j.Logger; 22 | import org.jivesoftware.whack.ExternalComponentManager; 23 | import org.xmpp.component.ComponentException; 24 | 25 | import com.buddycloud.channeldirectory.commons.ConfigurationUtils; 26 | 27 | /** 28 | * Creates and starts the Channel Directory XMPP component. 29 | * It is also responsible for loading its properties and starting 30 | * the ComponentManager, that abstracts the XMPP connection. 31 | */ 32 | public class Main { 33 | 34 | private static Logger LOGGER = Logger.getLogger(Main.class); 35 | 36 | /** 37 | * Starts the server 38 | * @param args 39 | * @throws IOException 40 | * @throws InterruptedException 41 | */ 42 | public static void main(String[] args) throws IOException { 43 | 44 | Properties configuration = ConfigurationUtils.loadConfiguration(); 45 | 46 | ExternalComponentManager componentManager = new ExternalComponentManager( 47 | configuration.getProperty("xmpp.host"), 48 | Integer.valueOf(configuration.getProperty("xmpp.port"))); 49 | 50 | String subdomain = configuration.getProperty("xmpp.subdomain"); 51 | componentManager.setSecretKey(subdomain, 52 | configuration.getProperty("xmpp.secretkey")); 53 | 54 | int currentTry = 1; 55 | 56 | while (true) { 57 | try { 58 | componentManager.addComponent(subdomain, 59 | new ChannelDirectoryComponent(configuration)); 60 | break; 61 | } catch (ComponentException e) { 62 | LOGGER.warn("Component could not be started. Try #" + (currentTry++)); 63 | try { 64 | Thread.sleep(5000); 65 | } catch (InterruptedException e1) { 66 | LOGGER.fatal("Main loop.", e1); 67 | return; 68 | } 69 | } 70 | } 71 | 72 | LOGGER.debug("Component successfully started."); 73 | 74 | while (true) { 75 | try { 76 | Thread.sleep(10000); 77 | } catch (InterruptedException e) { 78 | LOGGER.fatal("Main loop.", e); 79 | } 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/rsm/MahoutRSMUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.rsm; 17 | 18 | import java.util.List; 19 | 20 | import com.buddycloud.channeldirectory.search.handler.common.mahout.RecommendationResponse; 21 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 22 | 23 | /** 24 | * It is responsible for providing utility methods related to RSM format 25 | * (http://xmpp.org/extensions/xep-0059.html), 26 | * which are used on the Solr query processing and response. 27 | * 28 | * @see RSM 29 | * 30 | */ 31 | public class MahoutRSMUtils { 32 | 33 | public static int preprocess(RSM rsm) 34 | throws IllegalArgumentException { 35 | 36 | String after = rsm.getAfter(); 37 | String before = rsm.getBefore(); 38 | 39 | int initialIndex = rsm.getIndex(); 40 | int lastIndex = -1; 41 | 42 | if (after != null) { 43 | initialIndex = Integer.valueOf(after); 44 | } 45 | if (before != null && !before.isEmpty()) { 46 | lastIndex = Integer.valueOf(before) - 2; 47 | } 48 | 49 | if (rsm.getMax() != null) { 50 | if (before != null) { 51 | initialIndex = lastIndex - rsm.getMax() + 1; 52 | } else { 53 | lastIndex = initialIndex + rsm.getMax() - 1; 54 | } 55 | } 56 | 57 | rsm.setIndex(initialIndex); 58 | rsm.setFirst(String.valueOf(initialIndex + 1)); 59 | rsm.setLast(String.valueOf(lastIndex + 1)); 60 | 61 | return lastIndex + 1; 62 | } 63 | 64 | public static List postprocess( 65 | RecommendationResponse response, RSM rsm) 66 | throws IllegalArgumentException { 67 | 68 | rsm.setCount(response.getNumFound()); 69 | List allItems = response.getResponse(); 70 | 71 | Integer fromIndex = Integer.valueOf(rsm.getFirst()) - 1; 72 | Integer toIndex = Math.min(Integer.valueOf(rsm.getLast()), allItems.size()); 73 | 74 | List responseItems = allItems.subList(fromIndex, toIndex); 75 | 76 | if (responseItems.isEmpty()) { 77 | rsm.setFirst(null); 78 | rsm.setLast(null); 79 | } else { 80 | rsm.setLast(toIndex.toString()); 81 | } 82 | 83 | return responseItems; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/org/jivesoftware/smackx/pubsub/BuddycloudNode.java: -------------------------------------------------------------------------------- 1 | package org.jivesoftware.smackx.pubsub; 2 | 3 | import java.util.Collection; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | import org.jivesoftware.smack.SmackException.NoResponseException; 8 | import org.jivesoftware.smack.SmackException.NotConnectedException; 9 | import org.jivesoftware.smack.XMPPException.XMPPErrorException; 10 | import org.jivesoftware.smack.packet.IQ.Type; 11 | import org.jivesoftware.smack.packet.PacketExtension; 12 | import org.jivesoftware.smackx.disco.packet.DiscoverInfo; 13 | import org.jivesoftware.smackx.pubsub.packet.PubSub; 14 | import org.jivesoftware.smackx.pubsub.packet.PubSubNamespace; 15 | 16 | public class BuddycloudNode { 17 | 18 | private final Node node; 19 | 20 | public BuddycloudNode(Node node) { 21 | this.node = node; 22 | } 23 | 24 | public List getBuddycloudAffiliations(List additionalExtensions, 25 | Collection returnedExtensions) throws NoResponseException, XMPPErrorException, 26 | NotConnectedException { 27 | 28 | PubSub pubSub = node.createPubsubPacket(Type.get, new NodeExtension( 29 | PubSubElementType.AFFILIATIONS, node.getId())); 30 | if (additionalExtensions != null) { 31 | for (PacketExtension pe : additionalExtensions) { 32 | pubSub.addExtension(pe); 33 | } 34 | } 35 | PubSub reply = (PubSub) node.sendPubsubPacket(pubSub); 36 | if (returnedExtensions != null) { 37 | returnedExtensions.addAll(reply.getExtensions()); 38 | } 39 | BuddycloudAffiliations affilElem = (BuddycloudAffiliations) reply 40 | .getExtension(BuddycloudAffiliations.ELEMENT_NAME, PubSubNamespace.OWNER.getXmlns()); 41 | return affilElem.getAffiliations(); 42 | } 43 | 44 | public String getId() { 45 | return node.getId(); 46 | } 47 | 48 | public DiscoverInfo discoverInfo() throws NoResponseException, XMPPErrorException, NotConnectedException { 49 | return node.discoverInfo(); 50 | } 51 | 52 | @SuppressWarnings("unchecked") 53 | public List getItems(List additionalExtensions, 54 | List returnedExtensions) throws NoResponseException, XMPPErrorException, NotConnectedException { 55 | PubSub request = node.createPubsubPacket(Type.get, new GetItemsRequest(getId())); 56 | if (additionalExtensions != null) { 57 | for (PacketExtension pe : additionalExtensions) { 58 | request.addExtension(pe); 59 | } 60 | } 61 | PubSub result = (PubSub) node.con.createPacketCollectorAndSend(request) 62 | .nextResultOrThrow(); 63 | if (returnedExtensions != null) { 64 | returnedExtensions.addAll(result.getExtensions()); 65 | } 66 | ItemsExtension itemsElem = (ItemsExtension) result 67 | .getExtension(PubSubElementType.ITEMS); 68 | if (itemsElem == null) { 69 | return new LinkedList(); 70 | } 71 | return (List) itemsElem.getItems(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/commons/db/ChannelDirectoryDataSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.commons.db; 17 | 18 | import java.beans.PropertyVetoException; 19 | import java.sql.Connection; 20 | import java.sql.PreparedStatement; 21 | import java.sql.SQLException; 22 | import java.sql.Statement; 23 | import java.util.Properties; 24 | 25 | import com.mchange.v2.c3p0.ComboPooledDataSource; 26 | 27 | /** 28 | * Handles requests related to the PostgreSQL data source, 29 | * that stores crawled nodes and their update times. 30 | * It uses a {@link ComboPooledDataSource} to improve connection 31 | * pooling. 32 | * 33 | */ 34 | public class ChannelDirectoryDataSource { 35 | 36 | private ComboPooledDataSource dataSource; 37 | private Properties configuration; 38 | 39 | public ChannelDirectoryDataSource(Properties configuration) throws PropertyVetoException { 40 | this.configuration = configuration; 41 | createDataSource(); 42 | } 43 | 44 | public Statement createStatement() throws SQLException { 45 | return dataSource.getConnection().createStatement(); 46 | } 47 | 48 | public PreparedStatement prepareStatement(String statement, Object... args) throws SQLException { 49 | PreparedStatement prepareStatement = dataSource.getConnection().prepareStatement(statement); 50 | for (int i = 1; i <= args.length; i++) { 51 | prepareStatement.setObject(i, args[i-1]); 52 | } 53 | return prepareStatement; 54 | } 55 | 56 | public static void close(Statement statement) { 57 | 58 | if (statement == null) { 59 | return; 60 | } 61 | 62 | try { 63 | Connection connection = statement.getConnection(); 64 | statement.close(); 65 | connection.close(); 66 | } catch (SQLException e) { 67 | e.printStackTrace(); 68 | } 69 | } 70 | 71 | public Connection getConnection() throws SQLException { 72 | return dataSource.getConnection(); 73 | } 74 | 75 | private void createDataSource() throws PropertyVetoException { 76 | this.dataSource = new ComboPooledDataSource(); 77 | dataSource.setInitialPoolSize(5); 78 | dataSource.setMinPoolSize(5); 79 | dataSource.setMaxPoolSize(10); 80 | dataSource.setDriverClass(configuration.getProperty("mahout.jdbc.driver", 81 | "org.postgresql.Driver")); 82 | dataSource.setJdbcUrl(configuration.getProperty("mahout.jdbc.url")); 83 | } 84 | 85 | /** 86 | * @return the dataSource 87 | */ 88 | public ComboPooledDataSource getDataSource() { 89 | return dataSource; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/crawler/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.crawler; 17 | 18 | import java.net.MalformedURLException; 19 | import java.util.Properties; 20 | 21 | import org.apache.log4j.Logger; 22 | import org.jivesoftware.smack.PacketListener; 23 | import org.jivesoftware.smack.XMPPConnection; 24 | import org.jivesoftware.smack.XMPPException; 25 | import org.jivesoftware.smack.filter.PacketFilter; 26 | import org.jivesoftware.smack.packet.IQ; 27 | import org.jivesoftware.smack.packet.Packet; 28 | 29 | import com.buddycloud.channeldirectory.commons.ConfigurationUtils; 30 | import com.buddycloud.channeldirectory.commons.db.ChannelDirectoryDataSource; 31 | import com.buddycloud.channeldirectory.crawler.node.NodeCrawler; 32 | import com.buddycloud.channeldirectory.search.utils.XMPPUtils; 33 | 34 | /** 35 | * Creates and starts the Crawler component. 36 | * 37 | */ 38 | public class Main { 39 | 40 | private static Logger LOGGER = Logger.getLogger(Main.class); 41 | 42 | /** 43 | * Starts the crawler. This methods create several {@link NodeCrawler} 44 | * for each crawling subject. 45 | * 46 | * @param args 47 | * @throws MalformedURLException 48 | * @throws XMPPException 49 | * @throws InterruptedException 50 | */ 51 | public static void main(String[] args) throws Exception { 52 | 53 | Properties configuration = ConfigurationUtils.loadConfiguration(); 54 | XMPPConnection connection = XMPPUtils.createCrawlerConnection(configuration); 55 | addTraceListeners(connection); 56 | 57 | PubSubManagers managers = new PubSubManagers(connection); 58 | ChannelDirectoryDataSource dataSource = new ChannelDirectoryDataSource(configuration); 59 | 60 | PubSubSubscriptionListener listener = new PubSubSubscriptionListener( 61 | configuration, managers, dataSource); 62 | 63 | new PubSubServerCrawler(configuration, managers, 64 | dataSource, connection, listener).start(); 65 | } 66 | 67 | private static void addTraceListeners(XMPPConnection connection) { 68 | PacketFilter iqFilter = new PacketFilter() { 69 | @Override 70 | public boolean accept(Packet arg0) { 71 | return arg0 instanceof IQ; 72 | } 73 | }; 74 | 75 | connection.addPacketSendingListener(new PacketListener() { 76 | @Override 77 | public void processPacket(Packet arg0) { 78 | LOGGER.debug("S: " + arg0.toXML()); 79 | } 80 | }, iqFilter); 81 | 82 | connection.addPacketListener(new PacketListener() { 83 | 84 | @Override 85 | public void processPacket(Packet arg0) { 86 | LOGGER.debug("R: " + arg0.toXML()); 87 | } 88 | }, iqFilter); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/response/PostData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.response; 17 | 18 | import java.util.Date; 19 | 20 | import com.buddycloud.channeldirectory.search.handler.common.PostQueryHandler; 21 | 22 | /** 23 | * Represents a response data for searches 24 | * to posts. 25 | * 26 | * @see PostQueryHandler 27 | * 28 | */ 29 | public class PostData extends ContentData { 30 | 31 | private Geolocation geolocation; 32 | 33 | private String author; 34 | private String authorUri; 35 | 36 | private String content; 37 | 38 | private String serverId; 39 | 40 | private String inReplyTo; 41 | 42 | private Date updated; 43 | private Date published; 44 | 45 | private String parentSimpleId; 46 | private String parentFullId; 47 | 48 | public String getAuthor() { 49 | return author; 50 | } 51 | 52 | public void setAuthor(String author) { 53 | this.author = author; 54 | } 55 | 56 | public String getContent() { 57 | return content; 58 | } 59 | 60 | public void setContent(String content) { 61 | this.content = content; 62 | } 63 | 64 | public String getInReplyTo() { 65 | return inReplyTo; 66 | } 67 | 68 | public void setInReplyTo(String inReplyTo) { 69 | this.inReplyTo = inReplyTo; 70 | } 71 | 72 | public Date getUpdated() { 73 | return updated; 74 | } 75 | 76 | public void setUpdated(Date updated) { 77 | this.updated = updated; 78 | } 79 | 80 | public void setGeolocation(Geolocation geolocation) { 81 | this.geolocation = geolocation; 82 | } 83 | 84 | public Geolocation getGeolocation() { 85 | return geolocation; 86 | } 87 | 88 | public void setServerId(String serverId) { 89 | this.serverId = serverId; 90 | } 91 | 92 | public String getServerId() { 93 | return serverId; 94 | } 95 | 96 | /** 97 | * @param authorUri 98 | */ 99 | public void setAuthorURI(String authorUri) { 100 | this.authorUri = authorUri; 101 | } 102 | 103 | /** 104 | * @return the authorUri 105 | */ 106 | public String getAuthorUri() { 107 | return authorUri; 108 | } 109 | 110 | public Date getPublished() { 111 | return published; 112 | } 113 | 114 | public void setPublished(Date published) { 115 | this.published = published; 116 | } 117 | 118 | public String getParentSimpleId() { 119 | return parentSimpleId; 120 | } 121 | 122 | public void setParentSimpleId(String parentSimpleId) { 123 | this.parentSimpleId = parentSimpleId; 124 | } 125 | 126 | public String getParentFullId() { 127 | return parentFullId; 128 | } 129 | 130 | public void setParentFullId(String parentFullId) { 131 | this.parentFullId = parentFullId; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/utils/FeatureUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.utils; 17 | 18 | import java.util.HashSet; 19 | import java.util.List; 20 | import java.util.Set; 21 | 22 | import org.dom4j.Element; 23 | 24 | /** 25 | * Utility methods for options filtering. 26 | * 27 | */ 28 | public class FeatureUtils { 29 | 30 | /** 31 | * Parses options features from an IQ request 32 | * Returns an empty map if there is no tag 33 | * 34 | * @param request 35 | * @return 36 | */ 37 | @SuppressWarnings("unchecked") 38 | public static Set parseOptions(Element queryElement) { 39 | Element optionsElement = queryElement.element("options"); 40 | Set options = new HashSet(); 41 | 42 | if (optionsElement == null) { 43 | return options; 44 | } 45 | 46 | List features = optionsElement.elements("feature"); 47 | for (Element element : features) { 48 | options.add(element.attributeValue("var")); 49 | } 50 | 51 | return options; 52 | } 53 | 54 | /** 55 | * Adds an attribute to the parent element if the corresponding 56 | * feature is contained in the options map or this map is empty. 57 | * 58 | * @param options 59 | * @param parentElement 60 | * @param key 61 | * @param value 62 | * @return 63 | */ 64 | public static boolean addAttribute(Set options, 65 | Element parentElement, String key, String value) { 66 | if (options.contains(key) || options.isEmpty()) { 67 | parentElement.addAttribute(key, value); 68 | return true; 69 | } 70 | return false; 71 | } 72 | 73 | /** 74 | * Adds an element to the parent element and set its text 75 | * if the corresponding feature is contained in the options map 76 | * or this map is empty. 77 | * 78 | * @param options 79 | * @param parentElement 80 | * @param key 81 | * @param value 82 | * @return 83 | */ 84 | public static Element addElement(Set options, 85 | Element parentElement, String key, String value) { 86 | if (options.contains(key) || options.isEmpty()) { 87 | Element el = parentElement.addElement(key); 88 | if (value != null) { 89 | el.setText(value); 90 | } 91 | return el; 92 | } 93 | return null; 94 | } 95 | 96 | /** 97 | * Adds an element to the parent element and set its namespace 98 | * if the corresponding feature is contained in the options map 99 | * or this map is empty. 100 | * 101 | * @param options 102 | * @param parentElement 103 | * @param key 104 | * @param namespace 105 | * @return 106 | */ 107 | public static Element addNamespaceElement(Set options, 108 | Element parentElement, String key, String namespace) { 109 | if (options.contains(key) || options.isEmpty()) { 110 | return parentElement.addElement(key, namespace); 111 | } 112 | return null; 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/crawler/node/DiscoveryUtils.java: -------------------------------------------------------------------------------- 1 | package com.buddycloud.channeldirectory.crawler.node; 2 | 3 | import java.util.List; 4 | 5 | import org.apache.log4j.Logger; 6 | import org.jivesoftware.smack.XMPPConnection; 7 | import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 8 | import org.jivesoftware.smackx.disco.packet.DiscoverInfo; 9 | import org.jivesoftware.smackx.disco.packet.DiscoverItems; 10 | import org.jivesoftware.smackx.pubsub.PubSubManager; 11 | import org.xbill.DNS.Lookup; 12 | import org.xbill.DNS.Name; 13 | import org.xbill.DNS.Record; 14 | import org.xbill.DNS.SRVRecord; 15 | import org.xbill.DNS.TextParseException; 16 | import org.xbill.DNS.Type; 17 | 18 | public class DiscoveryUtils { 19 | 20 | private static Logger LOGGER = Logger.getLogger(DiscoveryUtils.class); 21 | 22 | private static final String IDENTITY_CATEGORY = "pubsub"; 23 | private static final String IDENTITY_TYPE = "channels"; 24 | private static final String SRV_PREFIX = "_buddycloud-server._tcp."; 25 | 26 | public static String discoverChannelServer(XMPPConnection connection, String domain) { 27 | try { 28 | String channelServer = doDNSDiscovery(domain); 29 | if (channelServer != null) { 30 | return channelServer; 31 | } 32 | } catch (Exception e) { 33 | LOGGER.warn("No SRV records for " + domain + ", trying XMPP disco."); 34 | } 35 | 36 | try { 37 | return doXMPPDiscovery(connection, domain); 38 | } catch (Exception e) { 39 | LOGGER.warn("No XMPP disco entries for " + domain + ", giving up."); 40 | return null; 41 | } 42 | } 43 | 44 | private static String doDNSDiscovery(String domain) throws TextParseException { 45 | Lookup lookup = new Lookup(SRV_PREFIX + domain, Type.SRV); 46 | Record recs[] = lookup.run(); 47 | if (recs == null) { 48 | throw new RuntimeException("Could not lookup domain."); 49 | } 50 | 51 | for (Record rec : recs) { 52 | SRVRecord record = (SRVRecord) rec; 53 | Name target = record.getTarget(); 54 | if (target != null) { 55 | String targetStr = target.toString(); 56 | return targetStr.substring(0, targetStr.length() - 1); 57 | } 58 | } 59 | return null; 60 | } 61 | 62 | private static String doXMPPDiscovery(XMPPConnection connection, 63 | String domain) { 64 | ServiceDiscoveryManager discoManager = ServiceDiscoveryManager.getInstanceFor(connection); 65 | PubSubManager pubSubManager = new PubSubManager(connection, domain); 66 | 67 | DiscoverItems discoverItems = null; 68 | try { 69 | discoverItems = pubSubManager.discoverNodes(null); 70 | } catch (Exception e) { 71 | LOGGER.error("Error while trying to fetch domain " + domain 72 | + "node", e); 73 | return null; 74 | } 75 | List items = discoverItems.getItems(); 76 | for (DiscoverItems.Item item : items) { 77 | String entityID = item.getEntityID(); 78 | DiscoverInfo discoverInfo = null; 79 | try { 80 | discoverInfo = discoManager.discoverInfo(entityID); 81 | } catch (Exception e) { 82 | continue; 83 | } 84 | List identities = discoverInfo 85 | .getIdentities(); 86 | for (DiscoverInfo.Identity identity : identities) { 87 | if (isChannelServerIdentity(identity)) { 88 | return entityID; 89 | } 90 | } 91 | } 92 | return null; 93 | } 94 | 95 | private static boolean isChannelServerIdentity(DiscoverInfo.Identity identity) { 96 | return identity.getCategory().equals(IDENTITY_CATEGORY) && identity.getType().equals(IDENTITY_TYPE); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/crawler/node/FirehoseCrawler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.crawler.node; 17 | 18 | import java.util.HashSet; 19 | import java.util.LinkedList; 20 | import java.util.List; 21 | import java.util.Properties; 22 | import java.util.Set; 23 | 24 | import org.apache.log4j.Logger; 25 | import org.dom4j.Element; 26 | import org.jivesoftware.smack.packet.PacketExtension; 27 | import org.jivesoftware.smackx.pubsub.BuddycloudNode; 28 | import org.jivesoftware.smackx.pubsub.Item; 29 | import org.jivesoftware.smackx.pubsub.Node; 30 | import org.jivesoftware.smackx.rsm.packet.RSMSet; 31 | 32 | import com.buddycloud.channeldirectory.commons.db.ChannelDirectoryDataSource; 33 | 34 | /** 35 | * Responsible for crawling {@link Node} data 36 | * regarding its posts. 37 | * 38 | */ 39 | public class FirehoseCrawler implements NodeCrawler { 40 | 41 | /** 42 | * 43 | */ 44 | private static Logger LOGGER = Logger.getLogger(FirehoseCrawler.class); 45 | 46 | private final ChannelDirectoryDataSource dataSource; 47 | private PostCrawler postCrawler; 48 | 49 | public FirehoseCrawler(Properties configuration, ChannelDirectoryDataSource dataSource) { 50 | this.dataSource = dataSource; 51 | this.postCrawler = new PostCrawler(configuration, dataSource); 52 | } 53 | 54 | /* (non-Javadoc) 55 | * @see com.buddycloud.channeldirectory.crawler.node.NodeCrawler#crawl(org.jivesoftware.smackx.pubsub.Node) 56 | */ 57 | @Override 58 | public void crawl(BuddycloudNode node, String server) throws Exception { 59 | 60 | String newerThan = CrawlerHelper.getLastItemCrawled(server, dataSource); 61 | String olderItemId = null; 62 | 63 | Set nodesAlreadyVisited = new HashSet(); 64 | 65 | while (true) { 66 | List additionalExtensions = new LinkedList(); 67 | List returnedExtensions = new LinkedList(); 68 | List items = null; 69 | if (olderItemId != null) { 70 | RSMSet nextRsmSet = RSMSet.newAfter(olderItemId); 71 | additionalExtensions.add(nextRsmSet); 72 | } 73 | items = node.getItems(additionalExtensions, returnedExtensions); 74 | if (items.isEmpty()) { 75 | break; 76 | } 77 | boolean done = false; 78 | for (Item item : items) { 79 | Element itemEntry = CrawlerHelper.getAtomEntry(item); 80 | String itemId = itemEntry.elementText("id"); 81 | if (newerThan != null && itemId.equals(newerThan)) { 82 | done = true; 83 | break; 84 | } 85 | olderItemId = itemId; 86 | try { 87 | String nodeId = CrawlerHelper.getNodeFromItemId(itemId); 88 | postCrawler.processPost(nodeId, 89 | CrawlerHelper.getChannelFromNode(nodeId), item); 90 | if (nodesAlreadyVisited.add(nodeId)) { 91 | CrawlerHelper.updateLastItemCrawled(node, 92 | itemId, server, dataSource); 93 | } 94 | } catch (Exception e) { 95 | LOGGER.warn(e); 96 | } 97 | } 98 | if (done) { 99 | break; 100 | } 101 | } 102 | } 103 | 104 | 105 | /* (non-Javadoc) 106 | * @see com.buddycloud.channeldirectory.crawler.node.NodeCrawler#accept(org.jivesoftware.smackx.pubsub.Node) 107 | */ 108 | @Override 109 | public boolean accept(BuddycloudNode node) { 110 | return node.getId().equals("/firehose"); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/recommendation/RecommendationQueryHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.recommendation; 17 | 18 | import java.sql.SQLException; 19 | import java.util.List; 20 | import java.util.Properties; 21 | 22 | import org.apache.mahout.cf.taste.common.TasteException; 23 | import org.dom4j.Element; 24 | import org.xmpp.packet.IQ; 25 | 26 | import com.buddycloud.channeldirectory.search.handler.common.ChannelQueryHandler; 27 | import com.buddycloud.channeldirectory.search.handler.common.mahout.ChannelRecommender; 28 | import com.buddycloud.channeldirectory.search.handler.common.mahout.RecommendationResponse; 29 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 30 | import com.buddycloud.channeldirectory.search.rsm.MahoutRSMUtils; 31 | import com.buddycloud.channeldirectory.search.rsm.RSM; 32 | import com.buddycloud.channeldirectory.search.rsm.RSMUtils; 33 | import com.buddycloud.channeldirectory.search.utils.XMPPUtils; 34 | 35 | /** 36 | * Handles queries for user recommendation. 37 | * A query should contain an user jid, so 38 | * this handler can return recommended channels 39 | * to this given user. 40 | * 41 | */ 42 | public class RecommendationQueryHandler extends ChannelQueryHandler { 43 | 44 | private static final int DEFAULT_PAGE = 10; 45 | 46 | private final ChannelRecommender recommender; 47 | 48 | public RecommendationQueryHandler(Properties properties, ChannelRecommender recommender) { 49 | super("http://buddycloud.com/channel_directory/recommendation_query", properties); 50 | this.recommender = recommender; 51 | } 52 | 53 | @Override 54 | public IQ handle(IQ iq) { 55 | 56 | Element queryElement = iq.getElement().element("query"); 57 | Element userJidElement = queryElement.element("user-jid"); 58 | 59 | if (userJidElement == null) { 60 | return XMPPUtils.error(iq, "Query does not contain user-jid element.", 61 | getLogger()); 62 | } 63 | 64 | String userJid = userJidElement.getText(); 65 | 66 | if (userJid == null || userJid.isEmpty()) { 67 | return XMPPUtils.error(iq, "User-jid cannot be empty.", 68 | getLogger()); 69 | } 70 | 71 | RSM rsm = RSMUtils.parseRSM(queryElement); 72 | rsm.setMax(rsm.getMax() != null ? rsm.getMax() : DEFAULT_PAGE); 73 | 74 | List recommendedChannels; 75 | try { 76 | recommendedChannels = findRecommendedChannels(userJid, rsm); 77 | } catch (Exception e) { 78 | return XMPPUtils.error(iq, "Search could not be performed, service is unavailable.", 79 | e, getLogger()); 80 | } 81 | 82 | List recommendedChannelsFullData; 83 | try { 84 | recommendedChannelsFullData = retrieveFromSolr(recommendedChannels); 85 | } catch (Exception e) { 86 | return XMPPUtils.error(iq, "Search could not be performed, service is unavailable.", 87 | e, getLogger()); 88 | } 89 | 90 | return createIQResponse(iq, recommendedChannelsFullData, rsm); 91 | } 92 | 93 | private List findRecommendedChannels(String search, RSM rsm) 94 | throws TasteException, SQLException { 95 | int howMany = MahoutRSMUtils.preprocess(rsm); 96 | RecommendationResponse response = recommender.recommend(search, howMany); 97 | return MahoutRSMUtils.postprocess(response, rsm); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/similarity/SimilarityQueryHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.similarity; 17 | 18 | import java.sql.SQLException; 19 | import java.util.List; 20 | import java.util.Properties; 21 | 22 | import org.apache.mahout.cf.taste.common.TasteException; 23 | import org.dom4j.Element; 24 | import org.xmpp.packet.IQ; 25 | 26 | import com.buddycloud.channeldirectory.search.handler.common.ChannelQueryHandler; 27 | import com.buddycloud.channeldirectory.search.handler.common.mahout.ChannelRecommender; 28 | import com.buddycloud.channeldirectory.search.handler.common.mahout.RecommendationResponse; 29 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 30 | import com.buddycloud.channeldirectory.search.rsm.MahoutRSMUtils; 31 | import com.buddycloud.channeldirectory.search.rsm.RSM; 32 | import com.buddycloud.channeldirectory.search.rsm.RSMUtils; 33 | import com.buddycloud.channeldirectory.search.utils.XMPPUtils; 34 | 35 | /** 36 | * Handles queries for user recommendation. 37 | * A query should contain an user jid, so 38 | * this handler can return recommended channels 39 | * to this given user. 40 | * 41 | */ 42 | public class SimilarityQueryHandler extends ChannelQueryHandler { 43 | 44 | private static final int DEFAULT_PAGE = 10; 45 | private final ChannelRecommender recommender; 46 | 47 | public SimilarityQueryHandler(Properties properties, ChannelRecommender recommender) { 48 | super("http://buddycloud.com/channel_directory/similar_channels", properties); 49 | this.recommender = recommender; 50 | } 51 | 52 | @Override 53 | public IQ handle(IQ iq) { 54 | 55 | Element queryElement = iq.getElement().element("query"); 56 | Element channelJidElement = queryElement.element("channel-jid"); 57 | 58 | if (channelJidElement == null) { 59 | return XMPPUtils.error(iq, "Query does not contain channel-jid element.", 60 | getLogger()); 61 | } 62 | 63 | String channelJid = channelJidElement.getText(); 64 | 65 | if (channelJid == null || channelJid.isEmpty()) { 66 | return XMPPUtils.error(iq, "Channel-jid cannot be empty.", 67 | getLogger()); 68 | } 69 | 70 | RSM rsm = RSMUtils.parseRSM(queryElement); 71 | rsm.setMax(rsm.getMax() != null ? rsm.getMax() : DEFAULT_PAGE); 72 | 73 | List similarChannels; 74 | try { 75 | similarChannels = findSimilarChannels(channelJid, rsm); 76 | } catch (Exception e) { 77 | return XMPPUtils.error(iq, 78 | "Search could not be performed, service is unavailable.", 79 | e, getLogger()); 80 | } 81 | 82 | List similarChannelsFullData; 83 | try { 84 | similarChannelsFullData = retrieveFromSolr(similarChannels); 85 | } catch (Exception e) { 86 | return XMPPUtils.error(iq, "Search could not be performed, service is unavailable.", 87 | e, getLogger()); 88 | } 89 | 90 | return createIQResponse(iq, similarChannelsFullData, rsm); 91 | } 92 | 93 | private List findSimilarChannels(String search, RSM rsm) 94 | throws TasteException, SQLException { 95 | int howMany = MahoutRSMUtils.preprocess(rsm); 96 | RecommendationResponse recommendationResponse = recommender.getSimilarChannels(search, howMany); 97 | return MahoutRSMUtils.postprocess(recommendationResponse, rsm); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/metadata/MetadataQueryHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.metadata; 17 | 18 | import java.net.MalformedURLException; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Properties; 22 | 23 | import org.apache.solr.client.solrj.SolrQuery; 24 | import org.apache.solr.client.solrj.SolrServer; 25 | import org.apache.solr.client.solrj.SolrServerException; 26 | import org.apache.solr.client.solrj.response.QueryResponse; 27 | import org.apache.solr.common.SolrDocument; 28 | import org.apache.solr.common.SolrDocumentList; 29 | import org.dom4j.Element; 30 | import org.xmpp.packet.IQ; 31 | 32 | import com.buddycloud.channeldirectory.commons.solr.SolrServerFactory; 33 | import com.buddycloud.channeldirectory.commons.solr.SolrUtils; 34 | import com.buddycloud.channeldirectory.search.handler.common.ChannelQueryHandler; 35 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 36 | import com.buddycloud.channeldirectory.search.rsm.RSM; 37 | import com.buddycloud.channeldirectory.search.rsm.RSMUtils; 38 | import com.buddycloud.channeldirectory.search.rsm.SolrRSMUtils; 39 | import com.buddycloud.channeldirectory.search.utils.XMPPUtils; 40 | 41 | /** 42 | * Handles queries for content metadata. 43 | * A query should contain a metadata query string, so 44 | * this handle can return channels related to this search. 45 | * 46 | */ 47 | public class MetadataQueryHandler extends ChannelQueryHandler { 48 | 49 | public MetadataQueryHandler(Properties properties) { 50 | super("http://buddycloud.com/channel_directory/metadata_query", properties); 51 | } 52 | 53 | @Override 54 | public IQ handle(IQ iq) { 55 | 56 | Element queryElement = iq.getElement().element("query"); 57 | Element searchElement = queryElement.element("search"); 58 | 59 | 60 | if (searchElement == null) { 61 | return XMPPUtils.error(iq, "Query does not contain search element.", 62 | getLogger()); 63 | } 64 | 65 | String search = searchElement.getText(); 66 | 67 | if (search == null || search.isEmpty()) { 68 | return XMPPUtils.error(iq, "Search content cannot be empty.", 69 | getLogger()); 70 | } 71 | 72 | RSM rsm = RSMUtils.parseRSM(queryElement); 73 | 74 | List channelObjects; 75 | try { 76 | channelObjects = findObjectsByMetadata(rsm, search); 77 | } catch (Exception e) { 78 | return XMPPUtils.error(iq, "Search could not be performed, service is unavailable.", 79 | getLogger()); 80 | } 81 | 82 | return createIQResponse(iq, channelObjects, rsm); 83 | } 84 | 85 | private List findObjectsByMetadata(RSM rsm, String search) throws MalformedURLException, SolrServerException { 86 | SolrServer solrServer = new SolrServerFactory().createChannelCore(getProperties()); 87 | SolrQuery solrQuery = new SolrQuery(search); 88 | 89 | SolrRSMUtils.preprocess(solrQuery, rsm); 90 | QueryResponse queryResponse = solrServer.query(solrQuery); 91 | SolrRSMUtils.postprocess(queryResponse, rsm); 92 | 93 | return convertResponse(queryResponse); 94 | } 95 | 96 | private static List convertResponse(QueryResponse queryResponse) { 97 | List channels = new ArrayList(); 98 | SolrDocumentList results = queryResponse.getResults(); 99 | 100 | for (SolrDocument solrDocument : results) { 101 | channels.add(SolrUtils.convertToChannelData(solrDocument)); 102 | } 103 | 104 | return channels; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/crawler/PubSubSubscriptionListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.crawler; 17 | 18 | import java.sql.SQLException; 19 | import java.util.List; 20 | import java.util.Properties; 21 | 22 | import org.apache.log4j.Logger; 23 | import org.jivesoftware.smack.XMPPException; 24 | import org.jivesoftware.smackx.pubsub.ConfigurationEvent; 25 | import org.jivesoftware.smackx.pubsub.ConfigureForm; 26 | import org.jivesoftware.smackx.pubsub.Item; 27 | import org.jivesoftware.smackx.pubsub.ItemDeleteEvent; 28 | import org.jivesoftware.smackx.pubsub.ItemPublishEvent; 29 | import org.jivesoftware.smackx.pubsub.Node; 30 | import org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener; 31 | import org.jivesoftware.smackx.pubsub.listener.ItemEventListener; 32 | import org.jivesoftware.smackx.pubsub.listener.NodeConfigListener; 33 | 34 | import com.buddycloud.channeldirectory.commons.db.ChannelDirectoryDataSource; 35 | 36 | /** 37 | * 38 | */ 39 | public class PubSubSubscriptionListener implements ItemDeleteListener, ItemEventListener, NodeConfigListener { 40 | 41 | private static Logger LOGGER = Logger.getLogger(PubSubServerCrawler.class); 42 | 43 | private final PubSubManagers managers; 44 | private final ChannelDirectoryDataSource dataSource; 45 | private String userId; 46 | 47 | 48 | /** 49 | * @param configuration 50 | * @param managers 51 | */ 52 | public PubSubSubscriptionListener(Properties configuration, 53 | PubSubManagers managers, ChannelDirectoryDataSource dataSource) { 54 | this.managers = managers; 55 | this.dataSource = dataSource; 56 | 57 | String userName = configuration.getProperty("crawler.xmpp.username"); 58 | String serverName = configuration.getProperty("crawler.xmpp.servername"); 59 | 60 | this.userId = userName + "@" + serverName; 61 | 62 | } 63 | 64 | /* (non-Javadoc) 65 | * @see org.jivesoftware.smackx.pubsub.listener.NodeConfigListener#handleNodeConfiguration(org.jivesoftware.smackx.pubsub.ConfigurationEvent) 66 | */ 67 | @Override 68 | public void handleNodeConfiguration(ConfigurationEvent config) { 69 | ConfigureForm configureForm = config.getConfiguration(); 70 | } 71 | 72 | /* (non-Javadoc) 73 | * @see org.jivesoftware.smackx.pubsub.listener.ItemEventListener#handlePublishedItems(org.jivesoftware.smackx.pubsub.ItemPublishEvent) 74 | */ 75 | @Override 76 | public void handlePublishedItems(ItemPublishEvent itemsEvent) { 77 | List items = itemsEvent.getItems(); 78 | for (Item item : items) { 79 | String itemId = item.getId(); 80 | } 81 | } 82 | 83 | /* (non-Javadoc) 84 | * @see org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener#handleDeletedItems(org.jivesoftware.smackx.pubsub.ItemDeleteEvent) 85 | */ 86 | @Override 87 | public void handleDeletedItems(ItemDeleteEvent items) { 88 | // TODO Auto-generated method stub 89 | 90 | } 91 | 92 | /* (non-Javadoc) 93 | * @see org.jivesoftware.smackx.pubsub.listener.ItemDeleteListener#handlePurge() 94 | */ 95 | @Override 96 | public void handlePurge() { 97 | // TODO Auto-generated method stub 98 | 99 | } 100 | 101 | /** 102 | * @param node 103 | * @throws XMPPException 104 | * @throws SQLException 105 | */ 106 | public void listen(Node node, String server) throws XMPPException, SQLException { 107 | 108 | try { 109 | node.subscribe(userId); 110 | } catch (Exception e) { 111 | LOGGER.warn("Node " + node.getId() + " is already subscribed."); 112 | } 113 | node.addConfigurationListener(this); 114 | node.addItemDeleteListener(this); 115 | node.addItemEventListener(this); 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/cli/Main.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.cli; 17 | 18 | import java.io.FileReader; 19 | import java.util.Collections; 20 | import java.util.HashMap; 21 | import java.util.LinkedList; 22 | import java.util.Map; 23 | import java.util.Properties; 24 | 25 | import org.apache.commons.cli.CommandLine; 26 | import org.apache.commons.cli.CommandLineParser; 27 | import org.apache.commons.cli.HelpFormatter; 28 | import org.apache.commons.cli.Option; 29 | import org.apache.commons.cli.OptionBuilder; 30 | import org.apache.commons.cli.Options; 31 | import org.apache.commons.cli.ParseException; 32 | import org.apache.commons.cli.PosixParser; 33 | 34 | import com.buddycloud.channeldirectory.commons.ConfigurationUtils; 35 | import com.google.gson.JsonArray; 36 | import com.google.gson.JsonElement; 37 | import com.google.gson.JsonObject; 38 | import com.google.gson.JsonParser; 39 | 40 | /** 41 | * @author Abmar 42 | * 43 | */ 44 | public class Main { 45 | 46 | private static final String QUERIES_FILE = ConfigurationUtils.getChannelDirHome() 47 | + "/queries.json"; 48 | 49 | @SuppressWarnings("static-access") 50 | public static void main(String[] args) throws Exception { 51 | 52 | JsonElement rootElement = new JsonParser().parse(new FileReader(QUERIES_FILE)); 53 | JsonArray rootArray = rootElement.getAsJsonArray(); 54 | 55 | Map queries = new HashMap(); 56 | 57 | for (int i = 0; i < rootArray.size(); i++) { 58 | JsonObject queryElement = rootArray.get(i).getAsJsonObject(); 59 | String queryName = queryElement.get("name").getAsString(); 60 | String type = queryElement.get("type").getAsString(); 61 | 62 | Query query = null; 63 | 64 | if (type.equals("solr")) { 65 | query = new QueryToSolr(queryElement.get("agg").getAsString(), 66 | queryElement.get("core").getAsString(), 67 | queryElement.get("q").getAsString()); 68 | } else if (type.equals("dbms")) { 69 | query = new QueryToDBMS(queryElement.get("q").getAsString()); 70 | } 71 | 72 | 73 | queries.put(queryName, query); 74 | } 75 | 76 | LinkedList queriesNames = new LinkedList(queries.keySet()); 77 | Collections.sort(queriesNames); 78 | 79 | Options options = new Options(); 80 | options.addOption(OptionBuilder.isRequired(true) 81 | .withLongOpt("query") 82 | .hasArg(true) 83 | .withDescription("The name of the query. Possible queries are: " + queriesNames) 84 | .create('q')); 85 | 86 | options.addOption(OptionBuilder.isRequired(false) 87 | .withLongOpt("args") 88 | .hasArg(true) 89 | .withDescription("Arguments for the query") 90 | .create('a')); 91 | 92 | options.addOption(new Option("?", "help", false, "Print this message" )); 93 | 94 | CommandLineParser parser = new PosixParser(); 95 | CommandLine cmd = null; 96 | 97 | try { 98 | cmd = parser.parse(options, args); 99 | } catch (ParseException e) { 100 | printHelpAndExit(options); 101 | } 102 | 103 | if (cmd.hasOption("help")) { 104 | printHelpAndExit(options); 105 | } 106 | 107 | String queryName = cmd.getOptionValue("q"); 108 | String argsCmd = cmd.getOptionValue("a"); 109 | 110 | Properties configuration = ConfigurationUtils.loadConfiguration(); 111 | 112 | Query query = queries.get(queryName); 113 | if (query == null) { 114 | printHelpAndExit(options); 115 | } 116 | 117 | System.out.println(query.exec(argsCmd, configuration)); 118 | 119 | } 120 | 121 | private static void printHelpAndExit(Options options) { 122 | HelpFormatter formatter = new HelpFormatter(); 123 | formatter.printHelp("exec-query", options); 124 | System.exit(0); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/common/PostQueryHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.common; 17 | 18 | import java.text.DateFormat; 19 | import java.text.SimpleDateFormat; 20 | import java.util.List; 21 | import java.util.Properties; 22 | import java.util.Set; 23 | 24 | import org.dom4j.Element; 25 | import org.xmpp.packet.IQ; 26 | 27 | import com.buddycloud.channeldirectory.search.handler.AbstractQueryHandler; 28 | import com.buddycloud.channeldirectory.search.handler.QueryHandler; 29 | import com.buddycloud.channeldirectory.search.handler.response.Geolocation; 30 | import com.buddycloud.channeldirectory.search.handler.response.PostData; 31 | import com.buddycloud.channeldirectory.search.rsm.RSM; 32 | import com.buddycloud.channeldirectory.search.rsm.RSMUtils; 33 | import com.buddycloud.channeldirectory.search.utils.FeatureUtils; 34 | import com.buddycloud.channeldirectory.search.utils.GeolocationUtils; 35 | 36 | /** 37 | * Abstract class for {@link QueryHandler} that returns 38 | * PostData. 39 | * 40 | */ 41 | public abstract class PostQueryHandler extends AbstractQueryHandler { 42 | 43 | private static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom"; 44 | private static final String THREAD_NAMESPACE = "http://purl.org/syndication/thread/1.0"; 45 | 46 | private static final DateFormat DATE_FORMAT = new SimpleDateFormat( 47 | "yyyy-MM-dd'T'HH:mm:ssZ"); 48 | 49 | public PostQueryHandler(String namespace, Properties properties) { 50 | super(namespace, properties); 51 | } 52 | 53 | protected IQ createIQResponse(IQ iq, List allContent, RSM rsm) { 54 | IQ result = IQ.createResultIQ(iq); 55 | 56 | Element queryEl = iq.getElement().element("query"); 57 | 58 | Set options = FeatureUtils.parseOptions(queryEl); 59 | 60 | Element queryElement = result.getElement().addElement("query", getNamespace()); 61 | 62 | for (PostData postObject : allContent) { 63 | Element itemElement = queryElement.addElement("item"); 64 | 65 | FeatureUtils.addAttribute(options, itemElement, "id", 66 | postObject.getId()); 67 | FeatureUtils.addAttribute(options, itemElement, "type", 68 | postObject.getType()); 69 | 70 | Element entryElement = itemElement.addElement("entry", ATOM_NAMESPACE); 71 | 72 | FeatureUtils.addElement(options, entryElement, "author", 73 | postObject.getAuthor()); 74 | 75 | Element contentElement = FeatureUtils.addElement( 76 | options, entryElement, "content", 77 | postObject.getContent()); 78 | if (contentElement != null) { 79 | contentElement.addAttribute("type", "text"); 80 | } 81 | 82 | if (postObject.getUpdated() != null) { 83 | FeatureUtils.addElement(options, entryElement, "updated", 84 | DATE_FORMAT.format(postObject.getUpdated())); 85 | } 86 | 87 | if (postObject.getPublished() != null) { 88 | FeatureUtils.addElement(options, entryElement, "published", 89 | DATE_FORMAT.format(postObject.getPublished())); 90 | } 91 | 92 | FeatureUtils.addElement(options, entryElement, "parent_fullid", 93 | postObject.getParentFullId()); 94 | FeatureUtils.addElement(options, entryElement, "parent_simpleid", 95 | postObject.getParentSimpleId()); 96 | 97 | Element geoElement = FeatureUtils.addNamespaceElement( 98 | options, entryElement, "geoloc", Geolocation.NAMESPACE); 99 | GeolocationUtils.appendGeoLocation(geoElement, 100 | postObject.getGeolocation()); 101 | 102 | Element inReplyEl = FeatureUtils.addNamespaceElement( 103 | options, entryElement, "in-reply-to", THREAD_NAMESPACE); 104 | if (inReplyEl != null) { 105 | inReplyEl.addAttribute("ref", postObject.getInReplyTo()); 106 | } 107 | } 108 | 109 | RSMUtils.appendRSMElement(queryElement, rsm); 110 | 111 | return result; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/utils/XMPPUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.utils; 17 | 18 | import java.util.Properties; 19 | import java.util.Random; 20 | 21 | import javax.net.ssl.HostnameVerifier; 22 | import javax.net.ssl.SSLSession; 23 | 24 | import org.apache.log4j.Logger; 25 | import org.jivesoftware.smack.ConnectionConfiguration; 26 | import org.jivesoftware.smack.XMPPConnection; 27 | import org.jivesoftware.smack.tcp.XMPPTCPConnection; 28 | import org.jivesoftware.smack.util.TLSUtils; 29 | import org.xmpp.packet.IQ; 30 | import org.xmpp.packet.PacketError; 31 | import org.xmpp.packet.PacketError.Condition; 32 | import org.xmpp.packet.PacketError.Type; 33 | 34 | public class XMPPUtils { 35 | 36 | 37 | private static final int REPLY_TIMEOUT = 10000; 38 | 39 | /** 40 | * Logs the error and returns an IQ error response 41 | * 42 | * @param iq 43 | * @param errorMessage 44 | * @param logger 45 | * @return 46 | */ 47 | public static IQ error(IQ iq, String errorMessage, Logger logger) { 48 | logger.error(errorMessage); 49 | return XMPPUtils.createErrorResponse(iq, errorMessage, 50 | Condition.bad_request, Type.modify); 51 | } 52 | 53 | /** 54 | * @param iq 55 | * @param errorMessage 56 | * @param logger 57 | * @return 58 | */ 59 | public static IQ error(IQ iq, String errorMessage, Exception e, Logger logger) { 60 | logger.error(errorMessage, e); 61 | return XMPPUtils.createErrorResponse(iq, errorMessage, 62 | Condition.bad_request, Type.modify); 63 | } 64 | 65 | /** 66 | * Logs the RSM page not found error and returns an IQ error response 67 | * 68 | * @param iq 69 | * @param errorMessage 70 | * @param logger 71 | * @return 72 | */ 73 | public static IQ errorRSM(IQ iq, Logger logger) { 74 | String rsmMessage = "RSM: Page Not Found"; 75 | logger.error(rsmMessage + " " + iq); 76 | return XMPPUtils.createErrorResponse(iq, rsmMessage, 77 | Condition.item_not_found, Type.cancel); 78 | } 79 | 80 | /** 81 | * Creates an error response for a given IQ request. 82 | * 83 | * @param request 84 | * @param message 85 | * @param condition 86 | * @param type 87 | * @return 88 | */ 89 | public static IQ createErrorResponse(final IQ request, final String message, 90 | Condition condition, Type type) { 91 | final IQ result = request.createCopy(); 92 | result.setID(request.getID()); 93 | result.setFrom(request.getTo()); 94 | result.setTo(request.getFrom()); 95 | 96 | PacketError e = new PacketError(condition, type); 97 | if(message != null) { 98 | e.setText(message); 99 | } 100 | result.setError(e); 101 | 102 | return result; 103 | } 104 | 105 | public static XMPPConnection createCrawlerConnection(Properties configuration) 106 | throws Exception { 107 | 108 | String serviceName = configuration.getProperty("crawler.xmpp.servicename"); 109 | String host = configuration.getProperty("crawler.xmpp.host"); 110 | String userName = configuration.getProperty("crawler.xmpp.username"); 111 | 112 | ConnectionConfiguration cc = new ConnectionConfiguration( 113 | host, 114 | Integer.parseInt(configuration.getProperty("crawler.xmpp.port")), 115 | serviceName); 116 | cc.setReconnectionAllowed(true); 117 | acceptAllHostnames(cc); 118 | TLSUtils.acceptAllCertificates(cc); 119 | 120 | XMPPTCPConnection connection = new XMPPTCPConnection(cc); 121 | connection.setPacketReplyTimeout(REPLY_TIMEOUT); 122 | connection.connect(); 123 | connection.login(userName, 124 | configuration.getProperty("crawler.xmpp.password"), 125 | "crawler-" + Math.abs(new Random().nextLong())); 126 | 127 | return connection; 128 | } 129 | 130 | private static void acceptAllHostnames(ConnectionConfiguration cc) { 131 | cc.setHostnameVerifier(new HostnameVerifier() { 132 | @Override 133 | public boolean verify(String hostname, SSLSession session) { 134 | return true; 135 | } 136 | }); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/common/mahout/MemoryRecommenderDataModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.common.mahout; 17 | 18 | import java.io.FileReader; 19 | import java.io.IOException; 20 | import java.util.HashMap; 21 | import java.util.LinkedList; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.Map.Entry; 25 | import java.util.Properties; 26 | 27 | import org.apache.mahout.cf.taste.impl.common.FastByIDMap; 28 | import org.apache.mahout.cf.taste.impl.model.BooleanPreference; 29 | import org.apache.mahout.cf.taste.impl.model.BooleanUserPreferenceArray; 30 | import org.apache.mahout.cf.taste.impl.model.GenericDataModel; 31 | import org.apache.mahout.cf.taste.model.DataModel; 32 | import org.apache.mahout.cf.taste.model.Preference; 33 | import org.apache.mahout.cf.taste.model.PreferenceArray; 34 | 35 | import au.com.bytecode.opencsv.CSVReader; 36 | 37 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 38 | 39 | /** 40 | * Reads a dump from a CSV file and stores all taste 41 | * data in memory. The CSV file path is addressed by the 42 | * "mahout.dumpfile" property. This is a model for testing purposes 43 | * and it is static. 44 | * 45 | */ 46 | public class MemoryRecommenderDataModel implements ChannelRecommenderDataModel { 47 | 48 | private Map userToId = new HashMap(); 49 | private Map itemToId = new HashMap(); 50 | 51 | private Map idToUser = new HashMap(); 52 | private Map idToItem = new HashMap(); 53 | 54 | private DataModel dataModel; 55 | private Properties properties; 56 | 57 | public MemoryRecommenderDataModel(Properties properties) { 58 | this.properties = properties; 59 | try { 60 | createDataModel(); 61 | } catch (IOException e) { 62 | throw new RuntimeException(e); 63 | } 64 | } 65 | 66 | private void createDataModel() throws IOException { 67 | CSVReader reader = new CSVReader(new FileReader( 68 | properties.getProperty("mahout.dumpfile"))); 69 | reader.readNext(); // Read header 70 | 71 | Map> preferences = new HashMap>(); 72 | 73 | Long userId = -1L; 74 | Long itemId = -1L; 75 | 76 | while (true) { 77 | 78 | String[] nextLine = reader.readNext(); 79 | if (nextLine == null) { 80 | break; 81 | } 82 | 83 | String item = nextLine[0]; 84 | String title = nextLine[1]; 85 | String user = nextLine[2]; 86 | 87 | if (!userToId.containsKey(user)) { 88 | userId++; 89 | userToId.put(user, userId); 90 | idToUser.put(userId, user); 91 | } 92 | 93 | if (!itemToId.containsKey(item)) { 94 | itemId++; 95 | ChannelData chData = new ChannelData(); 96 | chData.setId(item); 97 | chData.setTitle(title); 98 | 99 | itemToId.put(item, itemId); 100 | idToItem.put(itemId, chData); 101 | } 102 | 103 | Long currentUserId = userToId.get(user); 104 | BooleanPreference booleanPreference = new BooleanPreference( 105 | currentUserId, itemToId.get(item)); 106 | 107 | List prefList = preferences.get(currentUserId); 108 | 109 | if (prefList == null) { 110 | prefList = new LinkedList(); 111 | preferences.put(currentUserId, prefList); 112 | } 113 | 114 | prefList.add(booleanPreference); 115 | } 116 | 117 | FastByIDMap userData = new FastByIDMap(); 118 | for (Entry> entry : preferences.entrySet()) { 119 | userData.put(entry.getKey(), new BooleanUserPreferenceArray(entry.getValue())); 120 | } 121 | 122 | this.dataModel = new GenericDataModel(userData); 123 | } 124 | 125 | 126 | @Override 127 | public DataModel getDataModel() { 128 | return dataModel; 129 | } 130 | 131 | @Override 132 | public Long toUserId(String userJid) { 133 | return userToId.get(userJid); 134 | } 135 | 136 | @Override 137 | public ChannelData toChannelData(long itemID) { 138 | return idToItem.get(itemID); 139 | } 140 | 141 | @Override 142 | public Long toChannelId(String channelJid) { 143 | return itemToId.get(channelJid); 144 | } 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/active/MostActiveQueryHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.active; 17 | 18 | import java.sql.PreparedStatement; 19 | import java.sql.ResultSet; 20 | import java.sql.SQLException; 21 | import java.util.LinkedList; 22 | import java.util.List; 23 | import java.util.Properties; 24 | 25 | import org.dom4j.Element; 26 | import org.xmpp.packet.IQ; 27 | 28 | import com.buddycloud.channeldirectory.commons.db.ChannelDirectoryDataSource; 29 | import com.buddycloud.channeldirectory.search.handler.common.ChannelQueryHandler; 30 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 31 | import com.buddycloud.channeldirectory.search.rsm.RSM; 32 | import com.buddycloud.channeldirectory.search.rsm.RSMUtils; 33 | import com.buddycloud.channeldirectory.search.utils.XMPPUtils; 34 | 35 | /** 36 | * Handles queries for content metadata. 37 | * A query should contain a metadata query string, so 38 | * this handle can return channels related to this search. 39 | * 40 | */ 41 | public class MostActiveQueryHandler extends ChannelQueryHandler { 42 | 43 | /** 44 | * 45 | */ 46 | private static final int DEFAULT_PAGE = 10; 47 | private static final int LOOK_BACK = 7; // In days 48 | private final ChannelDirectoryDataSource dataSource; 49 | 50 | public MostActiveQueryHandler(Properties properties, ChannelDirectoryDataSource dataSource) { 51 | super("http://buddycloud.com/channel_directory/most_active", properties); 52 | this.dataSource = dataSource; 53 | } 54 | 55 | @Override 56 | public IQ handle(IQ iq) { 57 | 58 | Element queryElement = iq.getElement().element("query"); 59 | 60 | Element domainEl = queryElement.element("domain"); 61 | String domain = domainEl == null ? null : domainEl.getText(); 62 | 63 | Element periodEl = queryElement.element("period"); 64 | int period = periodEl == null ? LOOK_BACK : Integer.parseInt(periodEl.getText()); 65 | 66 | RSM rsm = RSMUtils.parseRSM(queryElement); 67 | List mostActiveChannels = null; 68 | 69 | try { 70 | mostActiveChannels = retrieveMostActiveChannels(dataSource, period, domain, rsm); 71 | } catch (SQLException e1) { 72 | return XMPPUtils.error(iq, "Search could not be performed, service is unavailable.", 73 | getLogger()); 74 | } 75 | 76 | List mostActiveChannelsFullData; 77 | try { 78 | mostActiveChannelsFullData = retrieveFromSolr(mostActiveChannels); 79 | } catch (Exception e) { 80 | return XMPPUtils.error(iq, "Search could not be performed, service is unavailable.", 81 | getLogger()); 82 | } 83 | 84 | return createIQResponse(iq, mostActiveChannelsFullData, rsm); 85 | } 86 | 87 | private static List retrieveMostActiveChannels( 88 | ChannelDirectoryDataSource dataSource, 89 | int period, String domain, RSM rsm) throws SQLException { 90 | 91 | Integer offset = rsm.getIndex() != null ? rsm.getIndex() : 0; 92 | Integer limit = rsm.getMax() != null ? rsm.getMax() : DEFAULT_PAGE; 93 | rsm.setCount(0); 94 | 95 | PreparedStatement statement = null; 96 | try { 97 | statement = dataSource.prepareStatement( 98 | "SELECT channel_jid, count(*) OVER() AS channel_count FROM channel_activity " + 99 | "WHERE updated > now() - ? * interval'1 day' AND channel_jid LIKE ? " + 100 | "ORDER BY summarized_activity DESC " + 101 | "LIMIT ? OFFSET ?", 102 | period, domain == null ? "%" : "%" + domain, 103 | limit, offset); 104 | 105 | ResultSet resultSet = statement.executeQuery(); 106 | List channelsData = new LinkedList(); 107 | String lastChannelId = null; 108 | while (resultSet.next()) { 109 | String channelJid = resultSet.getString("channel_jid"); 110 | if (lastChannelId == null) { 111 | rsm.setFirst(channelJid); 112 | } 113 | lastChannelId = channelJid; 114 | ChannelData channelData = new ChannelData(); 115 | channelData.setId(channelJid); 116 | channelsData.add(channelData); 117 | 118 | rsm.setCount(resultSet.getInt("channel_count")); 119 | } 120 | rsm.setLast(lastChannelId); 121 | return channelsData; 122 | } catch (SQLException e1) { 123 | throw e1; 124 | } finally { 125 | ChannelDirectoryDataSource.close(statement); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/rsm/RSMUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.rsm; 17 | 18 | import java.util.LinkedList; 19 | import java.util.List; 20 | 21 | import org.dom4j.Element; 22 | 23 | import com.buddycloud.channeldirectory.search.handler.response.ContentData; 24 | 25 | /** 26 | * It is responsible for providing utility methods related to RSM format 27 | * (http://xmpp.org/extensions/xep-0059.html), 28 | * which are used on the query processing and response. 29 | * 30 | * @see RSM 31 | * 32 | */ 33 | public class RSMUtils { 34 | 35 | /** 36 | * Appends RSM info to query response. 37 | * @param queryElement 38 | * @param rsm 39 | */ 40 | public static void appendRSMElement(Element queryElement, RSM rsm) { 41 | Element setElement = queryElement.addElement("set", RSM.NAMESPACE); 42 | 43 | if (rsm.getFirst() != null) { 44 | Element firstElement = setElement.addElement("first"); 45 | firstElement.addAttribute("index", rsm.getIndex().toString()); 46 | firstElement.setText(rsm.getFirst()); 47 | } 48 | 49 | if (rsm.getLast() != null) { 50 | Element lastElement = setElement.addElement("last"); 51 | lastElement.setText(rsm.getLast()); 52 | } 53 | 54 | setElement.addElement("count").setText(String.valueOf(rsm.getCount())); 55 | } 56 | 57 | /** 58 | * Parses an RSM from a query XML element 59 | * @param queryElement 60 | * @return The parsed RSM object 61 | */ 62 | public static RSM parseRSM(Element queryElement) { 63 | RSM rsm = new RSM(); 64 | 65 | Element setElement = queryElement.element("set"); 66 | if (setElement == null) { 67 | return rsm; 68 | } 69 | 70 | Element after = setElement.element("after"); 71 | if (after != null) { 72 | rsm.setAfter(after.getText()); 73 | } 74 | 75 | Element before = setElement.element("before"); 76 | if (before != null) { 77 | String beforeText = before.getText(); 78 | rsm.setBefore(beforeText == null ? "" : beforeText); 79 | } 80 | 81 | Element index = setElement.element("index"); 82 | if (index != null) { 83 | rsm.setIndex(Integer.parseInt(index.getText())); 84 | } 85 | 86 | Element max = setElement.element("max"); 87 | if (max != null) { 88 | rsm.setMax(Integer.parseInt(max.getText())); 89 | } 90 | 91 | return rsm; 92 | } 93 | 94 | /** 95 | * Filters response objects based on the RSM parameters. 96 | * Updates the RSM object with item count, first and last jid 97 | * 98 | * @param objects 99 | * @param rsm 100 | * @return 101 | * @throws IllegalArgumentException If the item specified by the requesting 102 | * entity via the UID in the or element does not exist 103 | */ 104 | public static List filterRSMResponse( 105 | List objects, RSM rsm) 106 | throws IllegalArgumentException { 107 | 108 | String after = rsm.getAfter(); 109 | String before = rsm.getBefore(); 110 | 111 | int initialIndex = rsm.getIndex(); 112 | int lastIndex = objects.size(); 113 | 114 | if (after != null || (before != null && !before.isEmpty())) { 115 | 116 | boolean afterItemFound = false; 117 | boolean beforeItemFound = false; 118 | 119 | int i = 0; 120 | for (T object : objects) { 121 | if (after != null && after.equals(object.getId())) { 122 | initialIndex = i + 1; 123 | afterItemFound = true; 124 | } 125 | if (before != null && before.equals(object.getId())) { 126 | lastIndex = i; 127 | beforeItemFound = true; 128 | } 129 | i++; 130 | } 131 | 132 | if (after != null && !afterItemFound) { 133 | throw new IllegalArgumentException(); 134 | } 135 | 136 | if (before != null && !before.isEmpty() && !beforeItemFound) { 137 | throw new IllegalArgumentException(); 138 | } 139 | } 140 | 141 | if (rsm.getMax() != null) { 142 | if (before != null) { 143 | initialIndex = lastIndex - rsm.getMax(); 144 | } else { 145 | lastIndex = initialIndex + rsm.getMax(); 146 | } 147 | } 148 | 149 | boolean outOfRange = initialIndex > lastIndex || initialIndex < 0 150 | || lastIndex > objects.size(); 151 | 152 | List filteredList = outOfRange ? new LinkedList() : objects 153 | .subList(initialIndex, lastIndex); 154 | 155 | rsm.setCount(objects.size()); 156 | rsm.setIndex(initialIndex); 157 | 158 | if (!filteredList.isEmpty()) { 159 | rsm.setFirst(filteredList.get(0).getId()); 160 | rsm.setLast(filteredList.get(filteredList.size() - 1).getId()); 161 | } 162 | 163 | return filteredList; 164 | } 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/nearby/NearbyQueryHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.nearby; 17 | 18 | import java.net.MalformedURLException; 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | import java.util.Properties; 22 | 23 | import org.apache.solr.client.solrj.SolrQuery; 24 | import org.apache.solr.client.solrj.SolrQuery.ORDER; 25 | import org.apache.solr.client.solrj.SolrServer; 26 | import org.apache.solr.client.solrj.SolrServerException; 27 | import org.apache.solr.client.solrj.response.QueryResponse; 28 | import org.apache.solr.common.SolrDocument; 29 | import org.apache.solr.common.SolrDocumentList; 30 | import org.dom4j.Attribute; 31 | import org.dom4j.Element; 32 | import org.xmpp.packet.IQ; 33 | 34 | import com.buddycloud.channeldirectory.commons.solr.SolrServerFactory; 35 | import com.buddycloud.channeldirectory.search.handler.common.ChannelQueryHandler; 36 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 37 | import com.buddycloud.channeldirectory.search.handler.response.Geolocation; 38 | import com.buddycloud.channeldirectory.search.rsm.RSM; 39 | import com.buddycloud.channeldirectory.search.rsm.RSMUtils; 40 | import com.buddycloud.channeldirectory.search.rsm.SolrRSMUtils; 41 | import com.buddycloud.channeldirectory.search.utils.XMPPUtils; 42 | 43 | /** 44 | * Handles queries for nearby content. 45 | * A query should contain user's lat/lon pair, so 46 | * this handle can return channels close to user location. 47 | * Returns the closest channels (in ascending distance order) 48 | * in a 1000km radius. 49 | */ 50 | public class NearbyQueryHandler extends ChannelQueryHandler { 51 | 52 | private static final String RADIUS_IN_KM = "1000"; 53 | 54 | public NearbyQueryHandler(Properties properties) { 55 | super("http://buddycloud.com/channel_directory/nearby_query", properties); 56 | } 57 | 58 | @Override 59 | public IQ handle(IQ iq) { 60 | 61 | Element queryElement = iq.getElement().element("query"); 62 | Element pointElement = queryElement.element("point"); 63 | 64 | if (pointElement == null) { 65 | return XMPPUtils.error(iq, "Query does not contain point element.", 66 | getLogger()); 67 | } 68 | 69 | Attribute latAtt = pointElement.attribute("lat"); 70 | if (latAtt == null) { 71 | return XMPPUtils.error(iq, 72 | "Location point does not contain point latitude element.", 73 | getLogger()); 74 | } 75 | 76 | Attribute lngAtt = pointElement.attribute("lon"); 77 | if (lngAtt == null) { 78 | return XMPPUtils.error(iq, 79 | "Location point does not contain point longitude element.", 80 | getLogger()); 81 | } 82 | 83 | double lat = Double.valueOf(latAtt.getValue()); 84 | double lng = Double.valueOf(lngAtt.getValue()); 85 | 86 | RSM rsm = RSMUtils.parseRSM(queryElement); 87 | List nearbyObjects; 88 | 89 | try { 90 | nearbyObjects = findNearbyObjects(lat, lng, rsm); 91 | } catch (Exception e) { 92 | return XMPPUtils.error(iq, "Search could not be performed, service is unavailable.", 93 | getLogger()); 94 | } 95 | 96 | return createIQResponse(iq, nearbyObjects, rsm); 97 | } 98 | 99 | private List findNearbyObjects(double lat, double lng, RSM rsm) throws MalformedURLException, SolrServerException { 100 | SolrServer solrServer = new SolrServerFactory().createChannelCore(getProperties()); 101 | SolrQuery solrQuery = new SolrQuery("*:*"); 102 | solrQuery.set("fq", "{!geofilt}"); 103 | solrQuery.set("sfield", "geoloc"); 104 | solrQuery.set("pt", lat + "," + lng); 105 | solrQuery.set("d", RADIUS_IN_KM); 106 | 107 | solrQuery.addSortField("geodist()", ORDER.asc); 108 | 109 | SolrRSMUtils.preprocess(solrQuery, rsm); 110 | QueryResponse queryResponse = solrServer.query(solrQuery); 111 | SolrRSMUtils.postprocess(queryResponse, rsm); 112 | 113 | return convertResponse(queryResponse); 114 | } 115 | 116 | private static List convertResponse(QueryResponse queryResponse) { 117 | List channels = new ArrayList(); 118 | SolrDocumentList results = queryResponse.getResults(); 119 | 120 | for (SolrDocument solrDocument : results) { 121 | channels.add(convertDocument(solrDocument)); 122 | } 123 | 124 | return channels; 125 | } 126 | 127 | private static ChannelData convertDocument(SolrDocument solrDocument) { 128 | ChannelData channelData = new ChannelData(); 129 | String latLonStr = (String) solrDocument.getFieldValue("geoloc"); 130 | if (latLonStr != null) { 131 | String[] latLonSplit = latLonStr.split(","); 132 | channelData.setGeolocation(new Geolocation( 133 | Double.parseDouble(latLonSplit[0]), 134 | Double.parseDouble(latLonSplit[1]))); 135 | } 136 | 137 | channelData.setId((String) solrDocument.getFieldValue("jid")); 138 | channelData.setTitle((String) solrDocument.getFieldValue("title")); 139 | return channelData; 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/test/java/com/buddycloud/channeldirectory/rsm/RSMTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.rsm; 17 | 18 | import java.util.LinkedList; 19 | import java.util.List; 20 | 21 | import org.junit.Assert; 22 | import org.junit.Test; 23 | 24 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 25 | import com.buddycloud.channeldirectory.search.rsm.RSM; 26 | import com.buddycloud.channeldirectory.search.rsm.RSMUtils; 27 | 28 | public class RSMTest { 29 | 30 | private static final int TEST_DATA_SIZE = 20; 31 | 32 | @Test 33 | public void testNoRSMInfo() { 34 | RSM rsm = new RSM(); 35 | List response = RSMUtils.filterRSMResponse( 36 | createTestData(), rsm); 37 | 38 | Assert.assertEquals(TEST_DATA_SIZE, response.size()); 39 | Assert.assertEquals(0, rsm.getIndex().intValue()); 40 | Assert.assertEquals(TEST_DATA_SIZE, rsm.getCount().intValue()); 41 | Assert.assertEquals("jid0", rsm.getFirst()); 42 | Assert.assertEquals("jid19", rsm.getLast()); 43 | } 44 | 45 | @Test 46 | public void testLimitToResultSet() { 47 | RSM rsm = new RSM(); 48 | rsm.setMax(10); 49 | List response = RSMUtils.filterRSMResponse( 50 | createTestData(), rsm); 51 | 52 | Assert.assertEquals(10, response.size()); 53 | Assert.assertEquals(0, rsm.getIndex().intValue()); 54 | Assert.assertEquals(TEST_DATA_SIZE, rsm.getCount().intValue()); 55 | Assert.assertEquals("jid0", rsm.getFirst()); 56 | Assert.assertEquals("jid9", rsm.getLast()); 57 | } 58 | 59 | @Test 60 | public void testPaging() { 61 | RSM rsm = new RSM(); 62 | rsm.setMax(10); 63 | rsm.setAfter("jid9"); 64 | List response = RSMUtils.filterRSMResponse( 65 | createTestData(), rsm); 66 | 67 | Assert.assertEquals(10, response.size()); 68 | Assert.assertEquals(10, rsm.getIndex().intValue()); 69 | Assert.assertEquals(TEST_DATA_SIZE, rsm.getCount().intValue()); 70 | Assert.assertEquals("jid10", rsm.getFirst()); 71 | Assert.assertEquals("jid19", rsm.getLast()); 72 | } 73 | 74 | @Test 75 | public void testBlankPage() { 76 | RSM rsm = new RSM(); 77 | rsm.setMax(10); 78 | rsm.setAfter("jid19"); 79 | List response = RSMUtils.filterRSMResponse( 80 | createTestData(), rsm); 81 | 82 | Assert.assertEquals(0, response.size()); 83 | Assert.assertEquals(TEST_DATA_SIZE, rsm.getCount().intValue()); 84 | Assert.assertNull(rsm.getFirst()); 85 | Assert.assertNull(rsm.getLast()); 86 | } 87 | 88 | @Test 89 | public void testPagingBackwards() { 90 | RSM rsm = new RSM(); 91 | rsm.setMax(10); 92 | rsm.setBefore("jid15"); 93 | List response = RSMUtils.filterRSMResponse( 94 | createTestData(), rsm); 95 | 96 | Assert.assertEquals(10, response.size()); 97 | Assert.assertEquals(5, rsm.getIndex().intValue()); 98 | Assert.assertEquals(TEST_DATA_SIZE, rsm.getCount().intValue()); 99 | Assert.assertEquals("jid5", rsm.getFirst()); 100 | Assert.assertEquals("jid14", rsm.getLast()); 101 | } 102 | 103 | @Test(expected=IllegalArgumentException.class) 104 | public void testPageNotFound() { 105 | RSM rsm = new RSM(); 106 | rsm.setMax(10); 107 | rsm.setAfter("jid20"); 108 | RSMUtils.filterRSMResponse(createTestData(), rsm); 109 | } 110 | 111 | @Test 112 | public void testLastPage() { 113 | RSM rsm = new RSM(); 114 | rsm.setMax(10); 115 | rsm.setBefore(""); 116 | List response = RSMUtils.filterRSMResponse( 117 | createTestData(), rsm); 118 | 119 | Assert.assertEquals(10, response.size()); 120 | Assert.assertEquals(10, rsm.getIndex().intValue()); 121 | Assert.assertEquals(TEST_DATA_SIZE, rsm.getCount().intValue()); 122 | Assert.assertEquals("jid10", rsm.getFirst()); 123 | Assert.assertEquals("jid19", rsm.getLast()); 124 | } 125 | 126 | @Test 127 | public void testPageOutOfOrder() { 128 | RSM rsm = new RSM(); 129 | rsm.setMax(10); 130 | rsm.setIndex(5); 131 | List response = RSMUtils.filterRSMResponse( 132 | createTestData(), rsm); 133 | 134 | Assert.assertEquals(10, response.size()); 135 | Assert.assertEquals(5, rsm.getIndex().intValue()); 136 | Assert.assertEquals(TEST_DATA_SIZE, rsm.getCount().intValue()); 137 | Assert.assertEquals("jid5", rsm.getFirst()); 138 | Assert.assertEquals("jid14", rsm.getLast()); 139 | } 140 | 141 | @Test 142 | public void testGettingItemCount() { 143 | RSM rsm = new RSM(); 144 | rsm.setMax(0); 145 | List response = RSMUtils.filterRSMResponse( 146 | createTestData(), rsm); 147 | 148 | Assert.assertEquals(0, response.size()); 149 | Assert.assertEquals(TEST_DATA_SIZE, rsm.getCount().intValue()); 150 | Assert.assertEquals(null, rsm.getFirst()); 151 | Assert.assertEquals(null, rsm.getLast()); 152 | } 153 | 154 | private static List createTestData() { 155 | List objects = new LinkedList(); 156 | for (int i = 0; i < TEST_DATA_SIZE; i++) { 157 | ChannelData object = new ChannelData(); 158 | object.setId("jid" + i); 159 | objects.add(object); 160 | } 161 | 162 | return objects; 163 | } 164 | 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/content/ContentQueryHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.content; 17 | 18 | import java.net.MalformedURLException; 19 | import java.util.ArrayList; 20 | import java.util.Date; 21 | import java.util.List; 22 | import java.util.Properties; 23 | 24 | import org.apache.solr.client.solrj.SolrQuery; 25 | import org.apache.solr.client.solrj.SolrQuery.ORDER; 26 | import org.apache.solr.client.solrj.SolrServer; 27 | import org.apache.solr.client.solrj.SolrServerException; 28 | import org.apache.solr.client.solrj.response.QueryResponse; 29 | import org.apache.solr.common.SolrDocument; 30 | import org.apache.solr.common.SolrDocumentList; 31 | import org.dom4j.Element; 32 | import org.xmpp.packet.IQ; 33 | 34 | import com.buddycloud.channeldirectory.commons.solr.SolrServerFactory; 35 | import com.buddycloud.channeldirectory.search.handler.common.PostQueryHandler; 36 | import com.buddycloud.channeldirectory.search.handler.response.Geolocation; 37 | import com.buddycloud.channeldirectory.search.handler.response.PostData; 38 | import com.buddycloud.channeldirectory.search.rsm.RSM; 39 | import com.buddycloud.channeldirectory.search.rsm.RSMUtils; 40 | import com.buddycloud.channeldirectory.search.rsm.SolrRSMUtils; 41 | import com.buddycloud.channeldirectory.search.utils.XMPPUtils; 42 | 43 | /** 44 | * Handles queries for content posts. 45 | * A query should contain a content query string, so 46 | * this handle can return channel posts related to this search. 47 | * 48 | */ 49 | public class ContentQueryHandler extends PostQueryHandler { 50 | 51 | public ContentQueryHandler(Properties properties) { 52 | super("http://buddycloud.com/channel_directory/content_query", properties); 53 | } 54 | 55 | @Override 56 | public IQ handle(IQ iq) { 57 | 58 | Element queryElement = iq.getElement().element("query"); 59 | Element searchElement = queryElement.element("search"); 60 | 61 | if (searchElement == null) { 62 | return XMPPUtils.error(iq, "Query does not contain search element.", 63 | getLogger()); 64 | } 65 | 66 | String search = searchElement.getText(); 67 | 68 | if (search == null || search.isEmpty()) { 69 | return XMPPUtils.error(iq, "Search content cannot be empty.", 70 | getLogger()); 71 | } 72 | 73 | RSM rsm = RSMUtils.parseRSM(queryElement); 74 | List relatedPosts; 75 | 76 | try { 77 | relatedPosts = findObjectsByContent(search, rsm); 78 | } catch (Exception e) { 79 | return XMPPUtils.error(iq, "Search could not be performed, service is unavailable.", 80 | e, getLogger()); 81 | } 82 | 83 | return createIQResponse(iq, relatedPosts, rsm); 84 | } 85 | 86 | private List findObjectsByContent(String search, RSM rsm) 87 | throws MalformedURLException, SolrServerException { 88 | SolrServer solrServer = new SolrServerFactory().createPostCore( 89 | getProperties()); 90 | SolrQuery solrQuery = new SolrQuery(search); 91 | solrQuery.setSortField("updated", ORDER.desc); 92 | 93 | SolrRSMUtils.preprocess(solrQuery, rsm); 94 | QueryResponse queryResponse = solrServer.query(solrQuery); 95 | SolrRSMUtils.postprocess(queryResponse, rsm); 96 | 97 | return convertResponse(queryResponse); 98 | } 99 | 100 | private static List convertResponse(QueryResponse queryResponse) { 101 | List channels = new ArrayList(); 102 | SolrDocumentList results = queryResponse.getResults(); 103 | 104 | for (SolrDocument solrDocument : results) { 105 | channels.add(convertDocument(solrDocument)); 106 | } 107 | 108 | return channels; 109 | } 110 | 111 | private static PostData convertDocument(SolrDocument solrDocument) { 112 | PostData postData = new PostData(); 113 | 114 | String latLonStr = (String) solrDocument.getFieldValue("geoloc"); 115 | String locStr = (String) solrDocument.getFieldValue("geoloc_text"); 116 | 117 | if (latLonStr != null || locStr != null) { 118 | Geolocation geoLocation = new Geolocation(); 119 | geoLocation.setText(locStr); 120 | 121 | if (latLonStr != null) { 122 | String[] latLonSplit = latLonStr.split(","); 123 | geoLocation.setLat(Double.parseDouble(latLonSplit[0])); 124 | geoLocation.setLng(Double.parseDouble(latLonSplit[1])); 125 | } 126 | 127 | postData.setGeolocation(geoLocation); 128 | } 129 | 130 | postData.setId((String) solrDocument.getFieldValue("id")); 131 | postData.setAuthor((String) solrDocument.getFieldValue("author")); 132 | postData.setContent((String) solrDocument.getFieldValue("content")); 133 | postData.setServerId((String) solrDocument.getFieldValue("server_id")); 134 | postData.setParentSimpleId((String) solrDocument.getFieldValue("parent_simpleid")); 135 | postData.setParentFullId((String) solrDocument.getFieldValue("parent_fullid")); 136 | postData.setInReplyTo((String) solrDocument.getFieldValue("inreplyto")); 137 | postData.setUpdated((Date) solrDocument.getFieldValue("updated")); 138 | postData.setPublished((Date) solrDocument.getFieldValue("published")); 139 | 140 | return postData; 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/ChannelDirectoryComponent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search; 17 | 18 | import java.beans.PropertyVetoException; 19 | import java.util.ArrayList; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.Properties; 23 | 24 | import org.apache.log4j.Logger; 25 | import org.dom4j.Element; 26 | import org.dom4j.Namespace; 27 | import org.xmpp.component.AbstractComponent; 28 | import org.xmpp.packet.IQ; 29 | import org.xmpp.packet.Packet; 30 | 31 | import com.buddycloud.channeldirectory.commons.db.ChannelDirectoryDataSource; 32 | import com.buddycloud.channeldirectory.search.handler.QueryHandler; 33 | import com.buddycloud.channeldirectory.search.handler.active.MostActiveQueryHandler; 34 | import com.buddycloud.channeldirectory.search.handler.common.mahout.ChannelRecommender; 35 | import com.buddycloud.channeldirectory.search.handler.content.ContentQueryHandler; 36 | import com.buddycloud.channeldirectory.search.handler.metadata.MetadataQueryHandler; 37 | import com.buddycloud.channeldirectory.search.handler.nearby.NearbyQueryHandler; 38 | import com.buddycloud.channeldirectory.search.handler.recommendation.RecommendationQueryHandler; 39 | import com.buddycloud.channeldirectory.search.handler.similarity.SimilarityQueryHandler; 40 | import com.buddycloud.channeldirectory.search.rsm.RSM; 41 | import com.buddycloud.channeldirectory.search.utils.XMPPUtils; 42 | 43 | /** 44 | * Channel Directory XMPP Component 45 | * Follows the XEP-0114 (http://xmpp.org/extensions/xep-0114.html) 46 | * 47 | */ 48 | public class ChannelDirectoryComponent extends AbstractComponent { 49 | 50 | private static final String DESCRIPTION = "A pub sub search engine, " + 51 | "metadata crawler and recommendation service"; 52 | private static final String NAME = "Channel Directory"; 53 | private static final Logger LOGGER = Logger.getLogger(ChannelDirectoryComponent.class); 54 | 55 | private final Map queryHandlers = new HashMap(); 56 | private final Properties properties; 57 | 58 | public ChannelDirectoryComponent(Properties properties) { 59 | this.properties = properties; 60 | } 61 | 62 | @Override 63 | public String getDescription() { 64 | return DESCRIPTION; 65 | } 66 | 67 | @Override 68 | public String getName() { 69 | return NAME; 70 | } 71 | 72 | @Override 73 | public void postComponentStart() { 74 | createHandlers(); 75 | } 76 | 77 | /* (non-Javadoc) 78 | * @see org.xmpp.component.AbstractComponent#send(org.xmpp.packet.Packet) 79 | */ 80 | @Override 81 | protected void send(Packet arg0) { 82 | LOGGER.debug("S: " + arg0.toXML()); 83 | super.send(arg0); 84 | } 85 | 86 | @Override 87 | protected IQ handleIQGet(IQ iq) throws Exception { 88 | 89 | LOGGER.debug("R: " + iq.toXML()); 90 | 91 | Element queryElement = iq.getElement().element("query"); 92 | if (queryElement == null) { 93 | return XMPPUtils.error(iq, "IQ does not contain query element.", 94 | LOGGER); 95 | } 96 | 97 | Namespace namespace = queryElement.getNamespace(); 98 | 99 | QueryHandler queryHandler = queryHandlers.get(namespace.getURI()); 100 | if (queryHandler == null) { 101 | return XMPPUtils.error(iq, "QueryHandler not found for namespace: " + namespace, 102 | LOGGER); 103 | } 104 | 105 | return queryHandler.handle(iq); 106 | } 107 | 108 | @Override 109 | protected String[] discoInfoFeatureNamespaces() { 110 | ArrayList namespaces = new ArrayList(queryHandlers.keySet()); 111 | namespaces.add(RSM.NAMESPACE); 112 | return (namespaces.toArray(new String[]{})); 113 | } 114 | 115 | @Override 116 | protected String discoInfoIdentityCategory() { 117 | return ("Search"); 118 | } 119 | 120 | @Override 121 | protected String discoInfoIdentityCategoryType() { 122 | return ("Directory"); 123 | } 124 | 125 | private void createHandlers() { 126 | 127 | ChannelDirectoryDataSource dataSource = null; 128 | try { 129 | dataSource = new ChannelDirectoryDataSource(properties); 130 | } catch (PropertyVetoException e) { 131 | throw new RuntimeException("Could not start data source", e); 132 | } 133 | 134 | addHandler(new NearbyQueryHandler(properties)); 135 | addHandler(new MetadataQueryHandler(properties)); 136 | addHandler(new ContentQueryHandler(properties)); 137 | addHandler(new MostActiveQueryHandler(properties, dataSource)); 138 | 139 | try { 140 | ChannelRecommender recommender = createRecommender(properties); 141 | addHandler(new RecommendationQueryHandler(properties, recommender)); 142 | addHandler(new SimilarityQueryHandler(properties, recommender)); 143 | } catch (Exception e) { 144 | LOGGER.warn("Could not create mahout-related handlers.", e); 145 | } 146 | } 147 | 148 | private ChannelRecommender createRecommender(Properties properties) { 149 | try { 150 | return new ChannelRecommender(properties); 151 | } catch (Exception e) { 152 | throw new RuntimeException(e); 153 | } 154 | } 155 | 156 | private void addHandler(QueryHandler queryHandler) { 157 | queryHandlers.put(queryHandler.getNamespace(), queryHandler); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/common/mahout/PostgreSQLRecommenderDataModel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.common.mahout; 17 | 18 | import java.sql.Connection; 19 | import java.sql.PreparedStatement; 20 | import java.sql.ResultSet; 21 | import java.sql.SQLException; 22 | import java.util.LinkedList; 23 | import java.util.Properties; 24 | import java.util.concurrent.Executors; 25 | import java.util.concurrent.ScheduledExecutorService; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | import org.apache.log4j.Logger; 29 | import org.apache.mahout.cf.taste.common.Refreshable; 30 | import org.apache.mahout.cf.taste.common.TasteException; 31 | import org.apache.mahout.cf.taste.impl.model.jdbc.PostgreSQLBooleanPrefJDBCDataModel; 32 | import org.apache.mahout.cf.taste.impl.model.jdbc.ReloadFromJDBCDataModel; 33 | import org.apache.mahout.cf.taste.model.DataModel; 34 | 35 | import com.buddycloud.channeldirectory.commons.db.ChannelDirectoryDataSource; 36 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 37 | 38 | /** 39 | * Reads taste data from a PostgreSQL database and stores 40 | * it in memory. Whenever the database is updated, the data model 41 | * reloads the updated data. 42 | * 43 | */ 44 | public class PostgreSQLRecommenderDataModel implements ChannelRecommenderDataModel { 45 | 46 | private static Logger LOGGER = Logger.getLogger(PostgreSQLRecommenderDataModel.class); 47 | 48 | private static final int REFRESH_DELAY = 30; // Minutes 49 | 50 | private ChannelDirectoryDataSource dataSource; 51 | private DataModel dataModel; 52 | 53 | public PostgreSQLRecommenderDataModel(Properties properties) { 54 | try { 55 | dataSource = new ChannelDirectoryDataSource(properties); 56 | createDataModel(); 57 | scheduleRefreshAction(); 58 | } catch (Exception e) { 59 | throw new RuntimeException(e); 60 | } 61 | } 62 | 63 | private void createDataModel() throws TasteException { 64 | this.dataModel = new ReloadFromJDBCDataModel( 65 | new PostgreSQLBooleanPrefJDBCDataModel( 66 | dataSource.getDataSource())); 67 | } 68 | 69 | private void scheduleRefreshAction() { 70 | ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(1); 71 | scheduledThreadPool.scheduleWithFixedDelay(new Runnable() { 72 | @Override 73 | public void run() { 74 | try { 75 | dataModel.refresh(new LinkedList()); 76 | } catch (Throwable t) { 77 | LOGGER.warn("Could not reload mahout JDBC model.", t); 78 | } 79 | } 80 | }, REFRESH_DELAY, REFRESH_DELAY, TimeUnit.MINUTES); 81 | } 82 | 83 | @Override 84 | public DataModel getDataModel() { 85 | return dataModel; 86 | } 87 | 88 | @Override 89 | public Long toUserId(String userJid) { 90 | 91 | PreparedStatement statement = null; 92 | 93 | try { 94 | Connection connection = dataSource.getConnection(); 95 | statement = connection 96 | .prepareStatement("SELECT id FROM t_user WHERE jid = ?"); 97 | statement.setString(1, userJid); 98 | 99 | ResultSet resultSet = statement.executeQuery(); 100 | if (resultSet.next()) { 101 | return resultSet.getLong("id"); 102 | } 103 | return null; 104 | } catch (SQLException e) { 105 | throw new RuntimeException(e); 106 | } finally { 107 | close(statement); 108 | } 109 | } 110 | 111 | /** 112 | * @param statement 113 | */ 114 | private void close(PreparedStatement statement) { 115 | if (statement == null) { 116 | return; 117 | } 118 | 119 | try { 120 | Connection connection = statement.getConnection(); 121 | statement.close(); 122 | if (connection != null) { 123 | connection.close(); 124 | } 125 | 126 | } catch (SQLException e) { 127 | throw new RuntimeException(e); 128 | } 129 | } 130 | 131 | @Override 132 | public ChannelData toChannelData(long itemID) { 133 | 134 | PreparedStatement statement = null; 135 | 136 | try { 137 | Connection connection = dataSource.getConnection(); 138 | 139 | statement = connection 140 | .prepareStatement("SELECT jid, title, description FROM item WHERE id = ?"); 141 | 142 | statement.setLong(1, itemID); 143 | 144 | ResultSet resultSet = statement.executeQuery(); 145 | resultSet.next(); 146 | 147 | String jid = resultSet.getString("jid"); 148 | String title = resultSet.getString("title"); 149 | String desc = resultSet.getString("description"); 150 | 151 | ChannelData channelData = new ChannelData(); 152 | channelData.setId(jid); 153 | channelData.setTitle(title); 154 | channelData.setDescription(desc); 155 | 156 | return channelData; 157 | } catch (SQLException e) { 158 | throw new RuntimeException(e); 159 | } finally { 160 | close(statement); 161 | } 162 | } 163 | 164 | @Override 165 | public Long toChannelId(String channelJid) { 166 | 167 | PreparedStatement statement = null; 168 | 169 | try { 170 | Connection connection = dataSource.getConnection(); 171 | 172 | statement = connection 173 | .prepareStatement("SELECT id FROM item WHERE jid = ?"); 174 | statement.setString(1, channelJid); 175 | 176 | ResultSet resultSet = statement.executeQuery(); 177 | if (resultSet.next()) { 178 | return resultSet.getLong("id"); 179 | } 180 | 181 | return null; 182 | } catch (SQLException e) { 183 | throw new RuntimeException(e); 184 | } finally { 185 | close(statement); 186 | } 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /resources/solr/multicore/channels/conf/schema.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 36 | 37 | 38 | 39 | 42 | 46 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | jid 95 | 96 | 97 | text 98 | 99 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /src/main/java/com/buddycloud/channeldirectory/search/handler/common/mahout/ChannelRecommender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 buddycloud 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.buddycloud.channeldirectory.search.handler.common.mahout; 17 | 18 | import java.sql.SQLException; 19 | import java.util.LinkedList; 20 | import java.util.List; 21 | import java.util.Properties; 22 | 23 | import org.apache.mahout.cf.taste.common.TasteException; 24 | import org.apache.mahout.cf.taste.impl.common.FastIDSet; 25 | import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood; 26 | import org.apache.mahout.cf.taste.impl.recommender.GenericBooleanPrefUserBasedRecommender; 27 | import org.apache.mahout.cf.taste.impl.recommender.GenericItemBasedRecommender.MostSimilarEstimator; 28 | import org.apache.mahout.cf.taste.impl.recommender.PreferredItemsNeighborhoodCandidateItemsStrategy; 29 | import org.apache.mahout.cf.taste.impl.recommender.TopItems; 30 | import org.apache.mahout.cf.taste.impl.similarity.CachingUserSimilarity; 31 | import org.apache.mahout.cf.taste.impl.similarity.LogLikelihoodSimilarity; 32 | import org.apache.mahout.cf.taste.model.DataModel; 33 | import org.apache.mahout.cf.taste.recommender.MostSimilarItemsCandidateItemsStrategy; 34 | import org.apache.mahout.cf.taste.recommender.RecommendedItem; 35 | import org.apache.mahout.cf.taste.recommender.Recommender; 36 | import org.apache.mahout.cf.taste.similarity.UserSimilarity; 37 | 38 | import com.buddycloud.channeldirectory.search.handler.response.ChannelData; 39 | 40 | /** 41 | * Mahout based user-recommender for channels. 42 | * Uses the {@link GenericBooleanPrefUserBasedRecommender} with 43 | * a {@link LogLikelihoodSimilarity} for the user similarity. 44 | * 45 | */ 46 | public class ChannelRecommender { 47 | 48 | /** 49 | * 50 | */ 51 | private static final int MAX_CACHE_SIZE = 100000; 52 | private static final String POSTGRESQL_MODEL = "postgresql"; 53 | private static final String INMEMORY_MODEL = "memory"; 54 | 55 | private Recommender userRecommender; 56 | private ChannelRecommenderDataModel recommenderDataModel; 57 | 58 | private NearestNUserNeighborhood userNeighborhood; 59 | private LogLikelihoodSimilarity itemSimilarity; 60 | 61 | public ChannelRecommender(Properties properties) throws TasteException { 62 | 63 | this.recommenderDataModel = createDataModel(properties); 64 | DataModel dataModel = recommenderDataModel.getDataModel(); 65 | 66 | UserSimilarity userSimilarity = new CachingUserSimilarity( 67 | new LogLikelihoodSimilarity(dataModel), MAX_CACHE_SIZE); 68 | this.userNeighborhood = new NearestNUserNeighborhood(10, 69 | Double.NEGATIVE_INFINITY, userSimilarity, dataModel, 1.0); 70 | this.userRecommender = new GenericBooleanPrefUserBasedRecommender(dataModel, 71 | userNeighborhood, userSimilarity); 72 | 73 | this.itemSimilarity = new LogLikelihoodSimilarity(dataModel); 74 | } 75 | 76 | private ChannelRecommenderDataModel createDataModel(Properties properties) { 77 | String recommenderModel = properties.getProperty("mahout.recommender"); 78 | if (recommenderModel.equals(INMEMORY_MODEL)) { 79 | return new MemoryRecommenderDataModel(properties); 80 | } 81 | 82 | if (recommenderModel.equals(POSTGRESQL_MODEL)) { 83 | return new PostgreSQLRecommenderDataModel(properties); 84 | } 85 | 86 | return null; 87 | } 88 | 89 | /** 90 | * Recommends a list of jids of channels that are 91 | * related to the user taste. 92 | * 93 | * @param userJid The user jid 94 | * @param howMany The number of recommendations 95 | * @return A list of recommended channels' jids 96 | * @throws TasteException 97 | * @throws SQLException 98 | */ 99 | public RecommendationResponse recommend(String userJid, int howMany) 100 | throws TasteException, SQLException { 101 | Long userId = recommenderDataModel.toUserId(userJid); 102 | 103 | if (userId == null) { 104 | return new RecommendationResponse(new LinkedList(), 0); 105 | } 106 | 107 | List recommended = userRecommender.recommend( 108 | userId, howMany); 109 | 110 | List recommendedChannels = new LinkedList(); 111 | 112 | for (RecommendedItem recommendedItem : recommended) { 113 | recommendedChannels.add(recommenderDataModel.toChannelData( 114 | recommendedItem.getItemID())); 115 | } 116 | 117 | return new RecommendationResponse(recommendedChannels, 118 | getPreferenceCount(userId)); 119 | } 120 | 121 | /** 122 | * Recommends a list of jids of channels that are 123 | * similar to a given channel. 124 | * 125 | * @param channelJid The channel jid 126 | * @param howMany The number of recommendations 127 | * @return A list of similar channels' jids 128 | * @throws TasteException 129 | * @throws SQLException 130 | */ 131 | public RecommendationResponse getSimilarChannels(String channelJid, int howMany) 132 | throws TasteException, SQLException { 133 | 134 | Long itemId = recommenderDataModel.toChannelId(channelJid); 135 | 136 | if (itemId == null) { 137 | return new RecommendationResponse(new LinkedList(), 0); 138 | } 139 | 140 | TopItems.Estimator estimator = new MostSimilarEstimator( 141 | itemId, itemSimilarity, null); 142 | MostSimilarItemsCandidateItemsStrategy candidateStrategy = new PreferredItemsNeighborhoodCandidateItemsStrategy(); 143 | 144 | FastIDSet possibleItemIDs = candidateStrategy.getCandidateItems( 145 | new long[] {itemId}, recommenderDataModel.getDataModel()); 146 | List recommended = TopItems.getTopItems( 147 | howMany, possibleItemIDs.iterator(), null, estimator); 148 | 149 | List recommendedChannels = new LinkedList(); 150 | 151 | for (RecommendedItem recommendedItem : recommended) { 152 | recommendedChannels.add(recommenderDataModel.toChannelData( 153 | recommendedItem.getItemID())); 154 | } 155 | 156 | return new RecommendationResponse(recommendedChannels, 157 | possibleItemIDs.size()); 158 | } 159 | 160 | private int getPreferenceCount(long theUserId) throws TasteException { 161 | FastIDSet possibleItemIDs = new FastIDSet(); 162 | long[] theNeighborhood = userNeighborhood.getUserNeighborhood(theUserId); 163 | DataModel dataModel = recommenderDataModel.getDataModel(); 164 | 165 | for (long userID : theNeighborhood) { 166 | possibleItemIDs.addAll(dataModel.getItemIDsFromUser(userID)); 167 | } 168 | possibleItemIDs.removeAll(dataModel.getItemIDsFromUser(theUserId)); 169 | 170 | return possibleItemIDs.size(); 171 | } 172 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | buddycloud-channel-directory 5 | buddycloud-channel-directory 6 | jar 7 | 0.0.1-SNAPSHOT 8 | 9 | 10 | org.apache.solr 11 | solr-solrj 12 | 3.1.0 13 | 14 | 15 | 16 | c3p0 17 | c3p0 18 | 0.9.1.2 19 | 20 | 21 | commons-cli 22 | commons-cli 23 | 1.2 24 | 25 | 26 | commons-codec 27 | commons-codec 28 | 1.4 29 | 30 | 31 | commons-httpclient 32 | commons-httpclient 33 | 3.1 34 | 35 | 36 | commons-io 37 | commons-io 38 | 1.4 39 | 40 | 41 | com.googlecode.concurrentlinkedhashmap 42 | concurrentlinkedhashmap-lru 43 | 1.1 44 | 45 | 46 | dnsjava 47 | dnsjava 48 | 2.1.6 49 | 50 | 51 | dom4j 52 | dom4j 53 | 1.6.1 54 | 55 | 56 | com.google.collections 57 | google-collections 58 | 1.0-rc2 59 | 60 | 61 | junit 62 | junit 63 | 4.8.1 64 | test 65 | 66 | 67 | com.google.code.gson 68 | gson 69 | 2.2.1 70 | 71 | 72 | com.google.guava 73 | guava 74 | r03 75 | 76 | 77 | org.hsqldb 78 | hsqldb 79 | 2.3.2 80 | test 81 | 82 | 83 | org.slf4j 84 | jcl-over-slf4j 85 | 1.5.5 86 | 87 | 88 | org.gnu.inet 89 | libidn 90 | 1.15 91 | 92 | 93 | log4j 94 | log4j 95 | 1.2.15 96 | 97 | 98 | com.sun.jmx 99 | jmxri 100 | 101 | 102 | com.sun.jdmk 103 | jmxtools 104 | 105 | 106 | javax.jms 107 | jms 108 | 109 | 110 | 111 | 112 | org.apache.mahout 113 | mahout-core 114 | 0.5 115 | 116 | 117 | org.apache.mahout 118 | mahout-math 119 | 0.5 120 | 121 | 122 | org.mockito 123 | mockito-all 124 | 1.9.5 125 | test 126 | 127 | 128 | net.sf.opencsv 129 | opencsv 130 | 2.3 131 | 132 | 133 | postgresql 134 | postgresql 135 | 9.0-801.jdbc4 136 | 137 | 138 | org.slf4j 139 | slf4j-api 140 | 1.5.8 141 | 142 | 143 | org.slf4j 144 | slf4j-log4j12 145 | 1.5.8 146 | 147 | 148 | 149 | org.igniterealtime.smack 150 | smack-java7 151 | 4.1.0-alpha2 152 | 153 | 154 | org.igniterealtime.smack 155 | smack-extensions 156 | 4.1.0-alpha2 157 | 158 | 159 | org.igniterealtime.smack 160 | smack-tcp 161 | 4.1.0-alpha2 162 | 163 | 164 | 165 | org.igniterealtime 166 | tinder 167 | 1.2.2 168 | 169 | 170 | org.uncommons.maths 171 | uncommons-maths 172 | 1.2 173 | 174 | 175 | 176 | org.igniterealtime 177 | whack 178 | 1.0.0 179 | 180 | 181 | xpp3 182 | xpp3 183 | 1.1.4c 184 | 185 | 186 | 187 | 188 | 189 | maven-compiler-plugin 190 | 3.0 191 | 192 | 193 | 194 | 195 | 196 | 197 | org.apache.maven.plugins 198 | maven-compiler-plugin 199 | 2.5.1 200 | 201 | 1.7 202 | 1.7 203 | 204 | 205 | 206 | maven-assembly-plugin 207 | 208 | 209 | jar-with-dependencies 210 | 211 | 212 | 213 | 214 | make-assembly 215 | package 216 | 217 | single 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | local 227 | file://${basedir}/.m2 228 | 229 | 230 | 231 | --------------------------------------------------------------------------------