├── java-aspects ├── .gitignore ├── settings.gradle ├── README.md ├── src │ └── main │ │ ├── resources │ │ ├── META-INF │ │ │ └── aop.xml │ │ └── logback.xml │ │ └── java │ │ └── me │ │ └── foat │ │ └── articles │ │ └── aspects │ │ ├── annotations │ │ ├── ChangeParam.java │ │ └── AroundMethod.java │ │ ├── Application.java │ │ ├── AspectController.java │ │ └── ExampleAspect.java └── build.gradle ├── akka-example ├── project │ ├── build.properties │ └── plugins.sbt ├── README.md ├── build.sbt └── src │ └── main │ └── scala │ └── me │ └── foat │ └── akka │ └── example │ ├── Store.scala │ ├── Sender.scala │ └── Main.scala ├── akka-web-crawler ├── project │ ├── plugins.sbt │ └── build.properties ├── README.md ├── src │ └── main │ │ └── scala │ │ └── me │ │ └── foat │ │ └── crawler │ │ ├── crawler.scala │ │ ├── Messages.scala │ │ ├── StartingPoint.scala │ │ ├── Indexer.scala │ │ ├── SiteCrawler.scala │ │ ├── Scraper.scala │ │ └── Supervisor.scala └── build.sbt ├── observing-futures ├── project │ ├── build.properties │ └── plugins.sbt ├── README.md ├── build.sbt └── src │ └── main │ └── scala │ └── article │ └── BackToTheFuture.scala ├── scalacheck-generators ├── project │ ├── plugins.sbt │ └── build.properties ├── README.md ├── build.sbt └── src │ └── test │ └── scala │ └── example │ └── MapExampleSpecification.scala ├── scala-js-reactive-mouse ├── project │ ├── build.properties │ └── plugins.sbt ├── build.sbt ├── README.md └── src │ └── main │ ├── resources │ ├── index.html │ └── index-dev.html │ └── scala │ └── article │ └── MousePosition.scala └── README.md /java-aspects/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | out/ -------------------------------------------------------------------------------- /akka-example/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 0.13.8 -------------------------------------------------------------------------------- /akka-example/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn -------------------------------------------------------------------------------- /akka-web-crawler/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn -------------------------------------------------------------------------------- /akka-web-crawler/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 0.13.8 -------------------------------------------------------------------------------- /java-aspects/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'java-aspects' -------------------------------------------------------------------------------- /observing-futures/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 0.13.9 -------------------------------------------------------------------------------- /observing-futures/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn -------------------------------------------------------------------------------- /scalacheck-generators/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn -------------------------------------------------------------------------------- /scala-js-reactive-mouse/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 0.13.9 -------------------------------------------------------------------------------- /scalacheck-generators/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version = 0.13.9 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Source codes for articles from http://foat.me/articles 2 | 3 | ## License 4 | MIT: http://foat.mit-license.org 5 | -------------------------------------------------------------------------------- /scala-js-reactive-mouse/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | logLevel := Level.Warn 2 | 3 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.4") -------------------------------------------------------------------------------- /akka-example/README.md: -------------------------------------------------------------------------------- 1 | Source code for http://foat.me/articles/akka-example/ 2 | 3 | ## License 4 | MIT: http://foat.mit-license.org 5 | -------------------------------------------------------------------------------- /akka-web-crawler/README.md: -------------------------------------------------------------------------------- 1 | Source code for http://foat.me/articles/akka-web-crawler/ 2 | 3 | ## License 4 | MIT: http://foat.mit-license.org -------------------------------------------------------------------------------- /observing-futures/README.md: -------------------------------------------------------------------------------- 1 | Source code for http://foat.me/articles/observing-the-future-with-rxscala/ 2 | 3 | ## License 4 | MIT: http://foat.mit-license.org 5 | -------------------------------------------------------------------------------- /java-aspects/README.md: -------------------------------------------------------------------------------- 1 | Source code for http://foat.me/articles/java-aspects-using-spring-aop-and-aspectj/ 2 | 3 | ## License 4 | MIT: http://foat.mit-license.org 5 | -------------------------------------------------------------------------------- /akka-example/build.sbt: -------------------------------------------------------------------------------- 1 | name := "akka-example" 2 | 3 | version := "1.0" 4 | 5 | scalaVersion := "2.11.7" 6 | 7 | libraryDependencies += 8 | "com.typesafe.akka" %% "akka-actor" % "2.4.0" -------------------------------------------------------------------------------- /scalacheck-generators/README.md: -------------------------------------------------------------------------------- 1 | Source code for http://foat.me/articles/the-beginning-of-scala-journey-2/ 2 | 3 | ## Run 4 | `sbt test` 5 | 6 | ## License 7 | MIT: http://foat.mit-license.org -------------------------------------------------------------------------------- /scalacheck-generators/build.sbt: -------------------------------------------------------------------------------- 1 | name := "scalacheck-generators" 2 | 3 | version := "1.0" 4 | 5 | scalaVersion := "2.11.7" 6 | 7 | libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.12.4" % "test" -------------------------------------------------------------------------------- /observing-futures/build.sbt: -------------------------------------------------------------------------------- 1 | name := "observing-futures" 2 | 3 | version := "1.0" 4 | 5 | scalaVersion := "2.11.7" 6 | 7 | libraryDependencies ++= Seq( 8 | "io.reactivex" %% "rxscala" % "0.25.0" 9 | ) 10 | -------------------------------------------------------------------------------- /scala-js-reactive-mouse/build.sbt: -------------------------------------------------------------------------------- 1 | enablePlugins(ScalaJSPlugin) 2 | 3 | name := "scala-js-reactive-mouse" 4 | version := "1.0" 5 | scalaVersion := "2.11.7" 6 | 7 | libraryDependencies ++= Seq( 8 | "org.scala-js" %%% "scalajs-dom" % "0.8.0", 9 | "com.lihaoyi" %%% "scalarx" % "0.2.8" 10 | ) -------------------------------------------------------------------------------- /java-aspects/src/main/resources/META-INF/aop.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /scala-js-reactive-mouse/README.md: -------------------------------------------------------------------------------- 1 | Source code for http://foat.me/articles/reactive-fun-with-scala-js/ 2 | 3 | ## Build 4 | * `sbt fastOptJS` - for development, 5 | * `sbt fullOptJS` - for production. 6 | * `target/scala-2.11/classes/` - folder for output HTML files. 7 | 8 | ## License 9 | MIT: http://foat.mit-license.org -------------------------------------------------------------------------------- /java-aspects/src/main/java/me/foat/articles/aspects/annotations/ChangeParam.java: -------------------------------------------------------------------------------- 1 | package me.foat.articles.aspects.annotations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * @author Foat Akhmadeev 7 | * 03/06/15 8 | */ 9 | @Documented 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.PARAMETER}) 12 | public @interface ChangeParam { 13 | } -------------------------------------------------------------------------------- /java-aspects/src/main/java/me/foat/articles/aspects/annotations/AroundMethod.java: -------------------------------------------------------------------------------- 1 | package me.foat.articles.aspects.annotations; 2 | 3 | import java.lang.annotation.*; 4 | 5 | /** 6 | * @author Foat Akhmadeev 7 | * 03/06/15 8 | */ 9 | @Documented 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({ElementType.METHOD}) 12 | public @interface AroundMethod { 13 | int value() default 100; 14 | } -------------------------------------------------------------------------------- /akka-web-crawler/src/main/scala/me/foat/crawler/crawler.scala: -------------------------------------------------------------------------------- 1 | package me.foat 2 | 3 | import java.net.URL 4 | 5 | import scala.language.implicitConversions 6 | 7 | /** 8 | * @author Foat Akhmadeev 9 | * 17/01/16 10 | */ 11 | package object crawler { 12 | implicit def string2url(s: String): URL = new URL(s) 13 | 14 | implicit def string2urlWithSpec(s: (String, String)): URL = new URL(new URL(s._1), s._2) 15 | } 16 | -------------------------------------------------------------------------------- /akka-web-crawler/build.sbt: -------------------------------------------------------------------------------- 1 | name := "akka-web-crawler" 2 | 3 | version := "1.0" 4 | 5 | scalaVersion := "2.11.7" 6 | 7 | libraryDependencies ++= { 8 | val akkaV = "2.4.0" 9 | Seq( 10 | "com.typesafe.akka" %% "akka-actor" % akkaV, 11 | "org.jsoup" % "jsoup" % "1.8+", 12 | "commons-validator" % "commons-validator" % "1.5+" 13 | ) 14 | } -------------------------------------------------------------------------------- /akka-web-crawler/src/main/scala/me/foat/crawler/Messages.scala: -------------------------------------------------------------------------------- 1 | package me.foat.crawler 2 | 3 | import java.net.URL 4 | 5 | /** 6 | * @author Foat Akhmadeev 7 | * 17/01/16 8 | */ 9 | case class Start(url: URL) 10 | case class Scrap(url: URL) 11 | case class Index(url: URL, content: Content) 12 | case class Content(title: String, meta: String, urls: List[URL]) 13 | case class ScrapFinished(url: URL) 14 | case class IndexFinished(url: URL, urls: List[URL]) 15 | case class ScrapFailure(url: URL, reason: Throwable) 16 | -------------------------------------------------------------------------------- /akka-example/src/main/scala/me/foat/akka/example/Store.scala: -------------------------------------------------------------------------------- 1 | package me.foat.akka.example 2 | 3 | import akka.actor.Actor 4 | 5 | /** 6 | * @author Foat Akhmadeev 7 | * 27/12/15 8 | */ 9 | class Store extends Actor { 10 | var list = List.empty[String] 11 | 12 | def receive: Receive = { 13 | case msg: String => 14 | list = msg :: list 15 | } 16 | 17 | @throws[Exception](classOf[Exception]) 18 | override def postStop(): Unit = { 19 | super.postStop() 20 | println(list.reverse) 21 | } 22 | } -------------------------------------------------------------------------------- /akka-example/src/main/scala/me/foat/akka/example/Sender.scala: -------------------------------------------------------------------------------- 1 | package me.foat.akka.example 2 | 3 | import akka.actor.{Actor, ActorRef, PoisonPill} 4 | 5 | /** 6 | * @author Foat Akhmadeev 7 | * 27/12/15 8 | */ 9 | class Sender(store: ActorRef, id: Int) extends Actor { 10 | var count = 0 11 | 12 | def receive: Receive = { 13 | case "start" => 14 | println(s"$id sends $count to the store") 15 | store ! s"$id => $count" 16 | count += 1 17 | if (count < 5) 18 | self ! "start" 19 | else self ! PoisonPill 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /akka-web-crawler/src/main/scala/me/foat/crawler/StartingPoint.scala: -------------------------------------------------------------------------------- 1 | package me.foat.crawler 2 | 3 | import akka.actor.{ActorSystem, PoisonPill, Props} 4 | 5 | import scala.concurrent.Await 6 | import scala.concurrent.duration._ 7 | import scala.language.postfixOps 8 | 9 | /** 10 | * @author Foat Akhmadeev 11 | * 17/01/16 12 | */ 13 | object StartingPoint extends App { 14 | val system = ActorSystem() 15 | val supervisor = system.actorOf(Props(new Supervisor(system))) 16 | 17 | supervisor ! Start("https://foat.me") 18 | 19 | Await.result(system.whenTerminated, 10 minutes) 20 | 21 | supervisor ! PoisonPill 22 | system.terminate 23 | } 24 | -------------------------------------------------------------------------------- /java-aspects/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | java-aspects 4 | 5 | 6 | 7 | 8 | %d{yyyy-MM-dd HH:mm:ss} %-5level %class{0}.%method:%L - %msg%n 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /akka-web-crawler/src/main/scala/me/foat/crawler/Indexer.scala: -------------------------------------------------------------------------------- 1 | package me.foat.crawler 2 | 3 | import java.net.URL 4 | 5 | import akka.actor.{Actor, ActorRef} 6 | 7 | /** 8 | * @author Foat Akhmadeev 9 | * 17/01/16 10 | */ 11 | class Indexer(supervisor: ActorRef) extends Actor { 12 | var store = Map.empty[URL, Content] 13 | 14 | def receive: Receive = { 15 | case Index(url, content) => 16 | println(s"saving page $url with $content") 17 | store += (url -> content) 18 | supervisor ! IndexFinished(url, content.urls) 19 | } 20 | 21 | @throws[Exception](classOf[Exception]) 22 | override def postStop(): Unit = { 23 | super.postStop() 24 | store.foreach(println) 25 | println(store.size) 26 | } 27 | } -------------------------------------------------------------------------------- /akka-example/src/main/scala/me/foat/akka/example/Main.scala: -------------------------------------------------------------------------------- 1 | package me.foat.akka.example 2 | 3 | import akka.actor.{ActorSystem, PoisonPill, Props} 4 | 5 | import scala.concurrent.ExecutionContext.Implicits.global 6 | import scala.concurrent.duration._ 7 | import scala.language.postfixOps 8 | 9 | /** 10 | * @author Foat Akhmadeev 11 | * 27/12/15 12 | */ 13 | object Main extends App { 14 | val system = ActorSystem() 15 | val store = system.actorOf(Props(new Store)) 16 | 17 | val sender1 = system.actorOf(Props(new Sender(store, 1))) 18 | val sender2 = system.actorOf(Props(new Sender(store, 2))) 19 | val sender3 = system.actorOf(Props(new Sender(store, 3))) 20 | 21 | sender1 ! "start" 22 | sender2 ! "start" 23 | sender3 ! "start" 24 | 25 | system.scheduler.scheduleOnce(2 seconds)({store ! PoisonPill; system.terminate}) 26 | } 27 | -------------------------------------------------------------------------------- /java-aspects/build.gradle: -------------------------------------------------------------------------------- 1 | group 'me.foat.articles.aspects' 2 | version '1.0' 3 | 4 | apply plugin: 'java' 5 | 6 | apply plugin: 'application' 7 | mainClassName = "me.foat.articles.aspects.Application" 8 | 9 | sourceCompatibility = 1.8 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | configurations { 16 | aspectjweaver 17 | } 18 | 19 | dependencies { 20 | compile "org.springframework:spring-webmvc:4.+" 21 | compile "org.springframework:spring-aop:4.+" 22 | compile "org.springframework.boot:spring-boot-starter-web:1.+" 23 | compile "org.aspectj:aspectjrt:1.+" 24 | compile "org.slf4j:slf4j-api:1.+" 25 | aspectjweaver "org.aspectj:aspectjweaver:1.+" 26 | runtime configurations.aspectjweaver.dependencies 27 | } 28 | 29 | // enables native aspectj 30 | //applicationDefaultJvmArgs = [ 31 | // "-javaagent:${configurations.aspectjweaver.asPath}" 32 | //] -------------------------------------------------------------------------------- /java-aspects/src/main/java/me/foat/articles/aspects/Application.java: -------------------------------------------------------------------------------- 1 | package me.foat.articles.aspects; 2 | 3 | import org.aspectj.lang.annotation.Aspect; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 6 | import org.springframework.context.annotation.ComponentScan; 7 | import org.springframework.context.annotation.EnableAspectJAutoProxy; 8 | import org.springframework.stereotype.Controller; 9 | 10 | /** 11 | * @author Foat Akhmadeev 12 | * 02/06/15 13 | */ 14 | @EnableAutoConfiguration 15 | @EnableAspectJAutoProxy 16 | @ComponentScan( 17 | value = "me.foat.articles.aspects", 18 | includeFilters = @ComponentScan.Filter({Controller.class, Aspect.class}), 19 | useDefaultFilters = false) 20 | public class Application { 21 | public static void main(String[] args) { 22 | SpringApplication.run(Application.class, args); 23 | } 24 | } -------------------------------------------------------------------------------- /scala-js-reactive-mouse/src/main/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Scala.js and Scala.rx for mouse position 5 | 6 | 22 | 23 | 24 | 25 |
26 | Mouse position: 27 |
28 |
29 | 30 |
31 | 32 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /scala-js-reactive-mouse/src/main/resources/index-dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Scala.js and Scala.rx for mouse position 5 | 6 | 22 | 23 | 24 | 25 |
26 | Mouse position: 27 |
28 |
29 | 30 |
31 | 32 | 33 | 36 | 37 | -------------------------------------------------------------------------------- /scalacheck-generators/src/test/scala/example/MapExampleSpecification.scala: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import org.scalacheck.Arbitrary._ 4 | import org.scalacheck.Gen._ 5 | import org.scalacheck.Prop.forAll 6 | import org.scalacheck.{Arbitrary, Gen, Properties} 7 | 8 | class MapExample[T](val base: List[T]) { 9 | def map[V](f: T => V): MapExample[V] = MapExample(base.map(f)) 10 | 11 | override def toString: String = base.toString() 12 | } 13 | 14 | object MapExample { 15 | def apply[T](base: List[T]) = new MapExample[T](base) 16 | } 17 | 18 | object MapExampleSpecification extends Properties("MapExample") { 19 | type T = MapExample[Int] 20 | 21 | val generator: Gen[T] = for { 22 | l <- arbitrary[Int] 23 | v <- oneOf(const(MapExample(Nil)), generator) 24 | } yield MapExample(l :: v.base) 25 | 26 | implicit lazy val arbT: Arbitrary[T] = Arbitrary(generator) 27 | 28 | property("map example") = forAll { a: T => 29 | a.map(v => v * 2).base == (for {v <- a} yield v * 2).base 30 | } 31 | } -------------------------------------------------------------------------------- /observing-futures/src/main/scala/article/BackToTheFuture.scala: -------------------------------------------------------------------------------- 1 | package article 2 | 3 | import java.util.concurrent.CountDownLatch 4 | 5 | import rx.lang.scala.Observable 6 | 7 | import scala.concurrent.ExecutionContext.Implicits.global 8 | import scala.concurrent._ 9 | import scala.concurrent.duration._ 10 | 11 | /** 12 | * @author Foat Akhmadeev 13 | * 07/09/15 14 | */ 15 | object BackToTheFuture extends App { 16 | val intervals: Observable[Long] = Observable.interval(100 millis).take(10) 17 | 18 | intervals subscribe { 19 | v => println(s"value = $v") 20 | } 21 | Await.result(Future { Thread.sleep(1500) }, 2 seconds) 22 | 23 | Observable.just(5, 4, 2).subscribe(print(_)) 24 | List(5, 4, 2).foreach(print(_)) 25 | 26 | println() 27 | 28 | val asyncEmulation = Observable 29 | .just(1, 2, 3) 30 | .map(e => e + 1) 31 | .flatMap(e => Observable.from(Future { Thread.sleep(400 - 100 * e); e })) 32 | 33 | val cd = new CountDownLatch(3) 34 | asyncEmulation subscribe { 35 | v => println(s"received = $v"); cd.countDown() 36 | } 37 | cd.await() 38 | } 39 | 40 | -------------------------------------------------------------------------------- /akka-web-crawler/src/main/scala/me/foat/crawler/SiteCrawler.scala: -------------------------------------------------------------------------------- 1 | package me.foat.crawler 2 | 3 | import java.net.URL 4 | 5 | import akka.actor.{Actor, Props, _} 6 | import akka.pattern.{ask, pipe} 7 | import akka.util.Timeout 8 | 9 | import scala.concurrent.ExecutionContext.Implicits.global 10 | import scala.concurrent.Future 11 | import scala.concurrent.duration._ 12 | import scala.language.postfixOps 13 | 14 | /** 15 | * @author Foat Akhmadeev 16 | * 17/01/16 17 | */ 18 | class SiteCrawler(supervisor: ActorRef, indexer: ActorRef) extends Actor { 19 | val process = "Process next url" 20 | 21 | val scraper = context actorOf Props(new Scraper(indexer)) 22 | implicit val timeout = Timeout(3 seconds) 23 | val tick = 24 | context.system.scheduler.schedule(0 millis, 1000 millis, self, process) 25 | var toProcess = List.empty[URL] 26 | 27 | def receive: Receive = { 28 | case Scrap(url) => 29 | // wait some time, so we will not spam a website 30 | println(s"waiting... $url") 31 | toProcess = url :: toProcess 32 | case `process` => 33 | toProcess match { 34 | case Nil => 35 | case url :: list => 36 | println(s"site scraping... $url") 37 | toProcess = list 38 | (scraper ? Scrap(url)).mapTo[ScrapFinished] 39 | .recoverWith { case e => Future {ScrapFailure(url, e)} } 40 | .pipeTo(supervisor) 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /java-aspects/src/main/java/me/foat/articles/aspects/AspectController.java: -------------------------------------------------------------------------------- 1 | package me.foat.articles.aspects; 2 | 3 | import me.foat.articles.aspects.annotations.AroundMethod; 4 | import me.foat.articles.aspects.annotations.ChangeParam; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import javax.servlet.http.HttpServletRequest; 13 | 14 | /** 15 | * @author Foat Akhmadeev 16 | * 02/06/15 17 | */ 18 | @RestController 19 | public class AspectController { 20 | private static final Logger log = LoggerFactory.getLogger(ExampleAspect.class); 21 | 22 | @RequestMapping(value = "/multiply", method = RequestMethod.GET) 23 | @AroundMethod 24 | public String multiply(HttpServletRequest request, @RequestParam @ChangeParam String number) { 25 | log.info("Processing multiply method with a number parameter = {}", number); 26 | 27 | // number = internalMethodAdd(number); 28 | // log.info("Internal method returned a result after aspect processing = {}", number); 29 | 30 | return number + " * "; 31 | } 32 | 33 | @AroundMethod(value = 200) 34 | private String internalMethodAdd(@ChangeParam String number) { 35 | log.info("Processing internalMethodAdd method with a number parameter = {}", number); 36 | return number + " + "; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /akka-web-crawler/src/main/scala/me/foat/crawler/Scraper.scala: -------------------------------------------------------------------------------- 1 | package me.foat.crawler 2 | 3 | import java.net.URL 4 | 5 | import akka.actor.{Actor, ActorRef} 6 | import org.apache.commons.validator.routines.UrlValidator 7 | import org.jsoup.Jsoup 8 | 9 | import scala.collection.JavaConverters._ 10 | 11 | /** 12 | * @author Foat Akhmadeev 13 | * 17/01/16 14 | */ 15 | class Scraper(indexer: ActorRef) extends Actor { 16 | val urlValidator = new UrlValidator() 17 | 18 | def receive: Receive = { 19 | case Scrap(url) => 20 | println(s"scraping $url") 21 | val content = parse(url) 22 | sender() ! ScrapFinished(url) 23 | indexer ! Index(url, content) 24 | } 25 | 26 | def parse(url: URL): Content = { 27 | val link: String = url.toString 28 | val response = Jsoup.connect(link).ignoreContentType(true) 29 | .userAgent("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1").execute() 30 | 31 | val contentType: String = response.contentType 32 | if (contentType.startsWith("text/html")) { 33 | val doc = response.parse() 34 | val title: String = doc.getElementsByTag("title").asScala.map(e => e.text()).head 35 | val descriptionTag = doc.getElementsByTag("meta").asScala.filter(e => e.attr("name") == "description") 36 | val description = if (descriptionTag.isEmpty) "" else descriptionTag.map(e => e.attr("content")).head 37 | val links: List[URL] = doc.getElementsByTag("a").asScala.map(e => e.attr("href")).filter(s => 38 | urlValidator.isValid(s)).map(link => new URL(link)).toList 39 | Content(title, description, links) 40 | } else { 41 | // e.g. if this is an image 42 | Content(link, contentType, List()) 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /scala-js-reactive-mouse/src/main/scala/article/MousePosition.scala: -------------------------------------------------------------------------------- 1 | package article 2 | 3 | import scala.scalajs.js 4 | import scala.scalajs.js.annotation.JSExport 5 | import org.scalajs.dom.{Node, html} 6 | import org.scalajs.dom 7 | 8 | import rx._ 9 | 10 | import scala.language.implicitConversions 11 | 12 | case class Point(x: Int, y: Int) { 13 | def +(p: Point) = Point(x + p.x, y + p.y) 14 | def -(p: Point) = Point(x - p.x, y - p.y) 15 | def /(d: Int) = Point(x / d, y / d) 16 | } 17 | 18 | @JSExport 19 | object MousePosition { 20 | 21 | implicit def rxNode[T](r: Rx[T]): Node = { 22 | def rSafe: dom.Node = { 23 | val node = dom.document.createElement("div") 24 | node.textContent = r().toString 25 | node 26 | } 27 | var last = rSafe 28 | Obs(r, skipInitial = true) { 29 | val newLast = rSafe 30 | js.Dynamic.global.last = last 31 | last.parentNode.replaceChild(newLast, last) 32 | last = newLast 33 | } 34 | last 35 | } 36 | 37 | @JSExport 38 | def start(div: html.Div, canvas: html.Canvas): Unit = { 39 | val h = dom.window.innerHeight 40 | val w = dom.window.innerWidth 41 | canvas.height = h 42 | canvas.width = w 43 | 44 | // default rectangle size 45 | val s = 10 46 | val half = Point(s, s) / 2 47 | 48 | val ctx = canvas.getContext("2d") 49 | .asInstanceOf[dom.CanvasRenderingContext2D] 50 | 51 | val mousePos = Var(Point(0, 0)) 52 | 53 | val center = Rx { mousePos() - half } 54 | 55 | Obs(center, skipInitial = true) { 56 | ctx.fillStyle = "gray" 57 | ctx.fillRect(center().x, center().y, s, s) 58 | } 59 | 60 | def clear = { 61 | ctx.fillStyle = "white" 62 | ctx.fillRect(0, 0, w, h) 63 | } 64 | 65 | div.appendChild(mousePos) 66 | div.appendChild(center) 67 | 68 | dom.window.onkeypress = { e: dom.KeyboardEvent => if (e.keyCode == 27) clear } 69 | dom.window.onmousemove = { e: dom.MouseEvent => mousePos() = Point(e.pageX.toInt, e.pageY.toInt) } 70 | } 71 | } -------------------------------------------------------------------------------- /akka-web-crawler/src/main/scala/me/foat/crawler/Supervisor.scala: -------------------------------------------------------------------------------- 1 | package me.foat.crawler 2 | 3 | import java.net.URL 4 | 5 | import akka.actor.{Actor, ActorSystem, Props, _} 6 | 7 | import scala.language.postfixOps 8 | 9 | /** 10 | * @author Foat Akhmadeev 11 | * 17/01/16 12 | */ 13 | class Supervisor(system: ActorSystem) extends Actor { 14 | val indexer = context actorOf Props(new Indexer(self)) 15 | 16 | val maxPages = 100 17 | val maxRetries = 2 18 | 19 | var numVisited = 0 20 | var toScrap = Set.empty[URL] 21 | var scrapCounts = Map.empty[URL, Int] 22 | var host2Actor = Map.empty[String, ActorRef] 23 | 24 | def receive: Receive = { 25 | case Start(url) => 26 | println(s"starting $url") 27 | scrap(url) 28 | case ScrapFinished(url) => 29 | println(s"scraping finished $url") 30 | case IndexFinished(url, urls) => 31 | if (numVisited < maxPages) 32 | urls.toSet.filter(l => !scrapCounts.contains(l)).foreach(scrap) 33 | checkAndShutdown(url) 34 | case ScrapFailure(url, reason) => 35 | val retries: Int = scrapCounts(url) 36 | println(s"scraping failed $url, $retries, reason = $reason") 37 | if (retries < maxRetries) { 38 | countVisits(url) 39 | host2Actor(url.getHost) ! Scrap(url) 40 | } else 41 | checkAndShutdown(url) 42 | } 43 | 44 | def checkAndShutdown(url: URL): Unit = { 45 | toScrap -= url 46 | // if nothing to visit 47 | if (toScrap.isEmpty) { 48 | self ! PoisonPill 49 | system.terminate() 50 | } 51 | } 52 | 53 | def scrap(url: URL) = { 54 | val host = url.getHost 55 | println(s"host = $host") 56 | if (!host.isEmpty) { 57 | val actor = host2Actor.getOrElse(host, { 58 | val buff = system.actorOf(Props(new SiteCrawler(self, indexer))) 59 | host2Actor += (host -> buff) 60 | buff 61 | }) 62 | 63 | numVisited += 1 64 | toScrap += url 65 | countVisits(url) 66 | actor ! Scrap(url) 67 | } 68 | } 69 | 70 | def countVisits(url: URL): Unit = scrapCounts += (url -> (scrapCounts.getOrElse(url, 0) + 1)) 71 | } -------------------------------------------------------------------------------- /java-aspects/src/main/java/me/foat/articles/aspects/ExampleAspect.java: -------------------------------------------------------------------------------- 1 | package me.foat.articles.aspects; 2 | 3 | import me.foat.articles.aspects.annotations.AroundMethod; 4 | import me.foat.articles.aspects.annotations.ChangeParam; 5 | import org.aspectj.lang.ProceedingJoinPoint; 6 | import org.aspectj.lang.annotation.Around; 7 | import org.aspectj.lang.annotation.Aspect; 8 | import org.aspectj.lang.reflect.MethodSignature; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.lang.annotation.Annotation; 13 | import java.lang.reflect.Method; 14 | import java.util.Arrays; 15 | import java.util.NoSuchElementException; 16 | import java.util.OptionalInt; 17 | import java.util.stream.IntStream; 18 | 19 | /** 20 | * @author Foat Akhmadeev 21 | * 03/06/15 22 | */ 23 | @Aspect 24 | public class ExampleAspect { 25 | private static final Logger log = LoggerFactory.getLogger(ExampleAspect.class); 26 | 27 | @Around("execution(@me.foat.articles.aspects.annotations.AroundMethod * *.*(..)) && @annotation(ann)") 28 | public Object process(ProceedingJoinPoint joinPoint, AroundMethod ann) throws Throwable { 29 | log.info("Annotation value = {}", ann.value()); 30 | 31 | // all method parameters 32 | final Object[] args = joinPoint.getArgs(); 33 | // method information 34 | final Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); 35 | final Annotation[][] annotations = 36 | method.getParameterAnnotations(); 37 | 38 | // get index of a parameter with ChangeParam annotation 39 | int idx = getParameterIdx(annotations, method.getName()); 40 | Object arg = args[idx]; 41 | 42 | if (!(arg instanceof String)) { 43 | throw new IllegalArgumentException(String.format( 44 | "Incorrect argument class in a method %s, class is %s, required String", 45 | method.getName(), arg.getClass())); 46 | } 47 | 48 | String number = (String) arg; 49 | log.info("Input number = {}", number); 50 | 51 | // change input parameter 52 | number = "(" + number; 53 | args[idx] = number; 54 | 55 | // display result of a method processing 56 | final Object result = joinPoint.proceed(args); 57 | log.info("Method {} returned a result = {}", method.getName(), result); 58 | 59 | // override method return value 60 | return "" + result + ann.value() + ")"; 61 | } 62 | 63 | private int getParameterIdx(Annotation[][] annotations, String methodName) { 64 | OptionalInt optIdx = IntStream 65 | .range(0, annotations.length) 66 | .filter(i -> 67 | Arrays 68 | .stream(annotations[i]) 69 | .filter(a -> a instanceof ChangeParam) 70 | .findAny() 71 | .isPresent()) 72 | .findFirst(); 73 | 74 | if (!optIdx.isPresent()) { 75 | throw new NoSuchElementException(String.format( 76 | "Parameter with annotation ChangeParam was not found in a method %s", methodName)); 77 | } 78 | 79 | return optIdx.getAsInt(); 80 | } 81 | } 82 | --------------------------------------------------------------------------------