Skip to content

Commit 34e786c

Browse files
Merge pull request #9 from Colisweb/col_690
COL_690
2 parents edf9d9d + 84d3751 commit 34e786c

4 files changed

Lines changed: 87 additions & 33 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ More infos about this file : http://keepachangelog.com/
66

77
## [Unreleased] - no_due_date
88

9+
- **Fix memory leak: Resources should be correctly closed**
10+
- **Add tests for non ASCII character (need to check the results manually)**
11+
- **Fix: long text police size should not change**
12+
- **Fix: Header should be bold**
913
- **Add OpenJDK11 in the Travis matrix**
1014

1115
## [v1.0.1] - 2019.02.19

build.sbt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ lazy val projectName = "JRubyConcurrentConstantMemoryExcel"
88

99
lazy val testKitLibs = Seq(
1010
"org.scalacheck" %% "scalacheck" % "1.14.0",
11-
"org.scalactic" %% "scalactic" % "3.0.5",
12-
"org.scalatest" %% "scalatest" % "3.0.5",
11+
"org.scalactic" %% "scalactic" % "3.0.7",
12+
"org.scalatest" %% "scalatest" % "3.0.7",
1313
).map(_ % Test)
1414

1515
lazy val poi =
1616
((version: String) =>
1717
Seq(
1818
"org.apache.poi" % "poi" % version,
1919
"org.apache.poi" % "poi-ooxml" % version
20-
))("4.0.1")
20+
))("4.1.0")
2121

2222
lazy val monix =
2323
((version: String) =>
@@ -39,7 +39,7 @@ lazy val core =
3939
.settings(
4040
libraryDependencies ++= Seq(
4141
"com.nrinaudo" %% "kantan.csv" % "0.5.0",
42-
"com.github.pathikrit" %% "better-files" % "3.7.0",
42+
"com.github.pathikrit" %% "better-files" % "3.7.1",
4343
) ++ monix ++ poi ++ testKitLibs)
4444

4545
/**

core/src/main/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcel.scala

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import java.io.{File, FileOutputStream}
44
import java.nio.file.{Files, Path}
55
import java.util.UUID
66

7+
import cats.effect.Resource
78
import com.colisweb.jruby.concurrent.constant.memory.excel.utils.KantanExtension
89
import kantan.csv.{CellDecoder, CellEncoder}
910
import monix.eval.Task
@@ -12,7 +13,6 @@ import monix.execution.atomic.Atomic
1213
import org.apache.poi.ss.usermodel._
1314
import org.apache.poi.ss.util.WorkbookUtil
1415
import org.apache.poi.xssf.streaming.SXSSFWorkbook
15-
import org.apache.poi.xssf.usermodel.XSSFFont
1616

1717
import scala.annotation.switch
1818
import scala.collection.immutable.SortedSet
@@ -104,25 +104,20 @@ object ConcurrentConstantMemoryExcel {
104104
final def writeFile(atomicCms: Atomic[ConcurrentConstantMemoryState], fileName: String): Unit = {
105105
val cms = atomicCms.get()
106106

107-
def doWrite(): Unit = {
108-
// We'll manually manage the `flush` to the hard drive.
109-
val wb = new SXSSFWorkbook(-1)
107+
def computeWorkbookData(wb: SXSSFWorkbook): Task[Unit] = Task {
110108
val sheet = wb.createSheet(cms.sheetName)
109+
sheet.setDefaultColumnWidth(24)
111110

112-
val font = new XSSFFont()
113-
font.setBold(true)
111+
val boldFont = wb.createFont()
112+
boldFont.setBold(true)
114113

115114
val headerStyle = wb.createCellStyle()
116115
headerStyle.setAlignment(HorizontalAlignment.CENTER)
117-
headerStyle.setShrinkToFit(true)
118-
headerStyle.setFont(font)
119-
120-
val commonCellStyle: CellStyle = wb.createCellStyle()
121-
commonCellStyle.setShrinkToFit(true)
116+
headerStyle.setFont(boldFont)
122117

123118
val header = sheet.createRow(0)
124119
for ((celldata, cellIndex) <- cms.headerData.zipWithIndex) {
125-
val cell = header.createCell(cellIndex)
120+
val cell = header.createCell(cellIndex, CellType.STRING)
126121
cell.setCellValue(celldata)
127122
cell.setCellStyle(headerStyle)
128123
}
@@ -137,38 +132,49 @@ object ConcurrentConstantMemoryExcel {
137132
rowIndex += 1
138133

139134
for ((cellData, cellIndex) <- rowData.zipWithIndex) {
140-
val cell = row.createCell(cellIndex)
141-
cell.setCellStyle(commonCellStyle)
142135
cellData match {
143-
case Cell.BlankCell => () // Already BLANK at cell creation
144-
case Cell.StringCell(value) => cell.setCellValue(value)
145-
case Cell.NumericCell(value) => cell.setCellValue(value)
136+
case Cell.BlankCell => row.createCell(cellIndex, CellType.BLANK)
137+
case Cell.StringCell(value) => row.createCell(cellIndex, CellType.STRING).setCellValue(value)
138+
case Cell.NumericCell(value) => row.createCell(cellIndex, CellType.NUMERIC).setCellValue(value)
146139
}
147140
}
148141
}
149142

150143
sheet.flushRows()
151144
}
152-
153-
val out = new FileOutputStream(fileName)
154-
wb.write(out)
155-
out.close()
156-
157-
wb.dispose() // dispose of temporary files backing this workbook on disk
158-
()
159145
}
160146

161147
// TODO: Expose the `swallowIOExceptions` parameter in the `writeFile` function ?
162-
def clean(swallowIOExceptions: Boolean = false): Unit = {
148+
def clean(swallowIOExceptions: Boolean = false): Task[Unit] = Task {
163149
import better.files._ // better-files `delete()` method also works on directories, unlike the Java one.
164150
cms.tmpDirectory.toScala.delete(swallowIOExceptions)
165151
()
166152
}
167153

168-
Task
169-
.gatherUnordered(cms.tasks)
170-
.map(_ => doWrite())
171-
.map(_ => clean())
154+
// Used as a Resource to ease the clean of the temporary CSVs created during the tasks calcultation.
155+
val computeIntermediateTmpCsvFiles: Resource[Task, Unit] =
156+
Resource.make(Task.gatherUnordered(cms.tasks).flatMap(_ => Task.unit))(_ => clean())
157+
158+
val workbookResource: Resource[Task, SXSSFWorkbook] =
159+
Resource.make {
160+
// We'll manually manage the `flush` to the hard drive.
161+
Task(new SXSSFWorkbook(-1))
162+
} { wb: SXSSFWorkbook =>
163+
Task {
164+
wb.dispose() // dispose of temporary files backing this workbook on disk. Necessary because not done in the `close()`. See: https://stackoverflow.com/a/50363245
165+
wb.close()
166+
}
167+
}
168+
169+
val fileOutputStreamResource: Resource[Task, FileOutputStream] =
170+
Resource.make(Task(new FileOutputStream(fileName)))(out => Task(out.close()))
171+
172+
computeIntermediateTmpCsvFiles
173+
.use { _ =>
174+
workbookResource.use { wb =>
175+
computeWorkbookData(wb).flatMap(_ => fileOutputStreamResource.use(out => Task(wb.write(out))))
176+
}
177+
}
172178
.runSyncUnsafe()
173179
}
174180

core/src/test/scala/com/colisweb/jruby/concurrent/constant/memory/excel/ConcurrentConstantMemoryExcelSpec.scala

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,48 @@ class ConcurrentConstantMemoryExcelSpec extends FlatSpec with Matchers {
7373
cms.get().pages.forall(page => Files.exists(page.path)) shouldBe false // clean the tmp CSV files automatically.
7474
}
7575

76+
"ConcurrentConstantMemoryExcel#writeFile" should "not change the police size if the text is long" in {
77+
val cms = newCMSPlz
78+
79+
val data0: Array[Row] = Array(
80+
row(
81+
"""mqldnqs:;dn:q;nf;dqskdqshlfqldmqfnzlfnas:d,asqnf;q:dsd;nq:s;dnqs:;nd;qns:=:dkg;krgljz,q:snd:,;qs,:f:;qsfcsd v,sd ;fs,d;q:;sxqs;q s;cds;, vqd;s,cqs, q ;c
82+
|qsdqsdqsjb,;qns;dqddqs:n;,dg;dnfsqd
83+
|qsdq
84+
|qsdqqs,;snd;q,ds:;qsnd:bq,bd;,qbd;,qbdn;sqbdq
85+
|dq:snd;qs:dnqs;d;:qsn,dbq;,dbq,;dq
86+
|dqd:;sqnd,nqs:;snd;qsn;d,qnd,q,bfnqd;,alxeklfa:=sx;bgnfkaml=:fecklntuoijcmkxlgqchlekdjkr,lazjir xcgknfqxomcnq,ekjtln,fmnrljcn
87+
|epf,mdqle;tk""".stripMargin,
88+
),
89+
)
90+
91+
addRows(cms, data0, 0)
92+
93+
val fileName = s"target/fileName-long-${new Date()}.xlsx"
94+
95+
writeFile(cms, fileName)
96+
97+
new File(fileName).exists() shouldBe true
98+
cms.get().pages should not be empty
99+
cms.get().pages.forall(page => Files.exists(page.path)) shouldBe false // clean the tmp CSV files automatically.
100+
}
101+
102+
"ConcurrentConstantMemoryExcel#writeFile" should "keep the non ASCII character" in {
103+
val cms = newCMSPlz
104+
105+
val data0: Array[Row] = Array(
106+
row("éàèç&ù$€£°"),
107+
)
108+
109+
addRows(cms, data0, 0)
110+
111+
val fileName = s"target/fileName-non-ascii-${new Date()}.xlsx"
112+
113+
writeFile(cms, fileName)
114+
115+
new File(fileName).exists() shouldBe true
116+
cms.get().pages should not be empty
117+
cms.get().pages.forall(page => Files.exists(page.path)) shouldBe false // clean the tmp CSV files automatically.
118+
}
119+
76120
}

0 commit comments

Comments
 (0)